Kubernetes
אורקסטרציה של containers בproduction: Pods, Deployments, Services, Ingress ו-ops יומיומי
תיאוריה
Kubernetes בשוק העבודה הישראלי — למה זה הכי נדרש
Kubernetes מופיע ב-12.9% ממשרות DevOps בישראל — המספר הגבוה ביותר מבין כל הכלים הנסרקים. כמעט כל חברה שמריצה microservices או שירותים בענן מצפה לידע K8s. אבל הקורסים הסטנדרטיים מלמדים 'הגדר Pod'. הפערים האמיתיים:
- **Readiness vs Liveness probes** — בלעדיהם, traffic נשלח לcontainer שעדיין לא מוכן
- **Resource requests + limits** — בלעדיהם, pods מורידים nodes
- **Rolling updates + rollback** — כיצד לעדכן בלי downtime ולחזור אחורה בשניות
- **kubectl debug** — כיצד לחקור pod קרוס בproduction
- **Namespaces + RBAC** — הפרדת סביבות ב-cluster אחד
- **ConfigMap + Secret** — inject configuration בלי לstick env vars לimage
נתון חשוב: בישראל, EKS (AWS) הוא ה-distribution הנפוץ ביותר, אחריו GKE. self-managed clusters נדירים מחוץ לבנקים ורפואה.
ארכיטקטורת Kubernetes — Control Plane, Nodes, etcd
Kubernetes cluster מורכב משני רכיבים עיקריים:
**Control Plane** — 'המוח' של ה-cluster:
- kube-apiserver — API endpoint לכל פקודות kubectl
- etcd — distributed key-value store שמאחסן את כל state ה-cluster
- kube-scheduler — מחליט על איזה node לרוץ כל Pod
- kube-controller-manager — מנהל controllers (ReplicaSet, Deployment, Node)
**Worker Nodes** — 'הגוף' שמריץ containers:
- kubelet — agent על כל node, מתקשר עם Control Plane
- kube-proxy — ניתוב network traffic ל-Services
- container runtime — containerd (ברוב distributions מ-K8s 1.24+)
**הקשר לDocker:** Kubernetes לא מריץ Docker containers ישירות — הוא משתמש ב-containerd (ה-runtime) שמריץ OCI-compatible images. כל image שבנינו עם Docker תקף ל-Kubernetes.
**etcd — לב ה-cluster:**
# בדיקת health של etcd (רק ב-control plane)
kubectl get componentstatuses
# מספר nodes בcluster
kubectl get nodes -o wide
# מידע מפורט על node ספציפי
kubectl describe node <node-name>Pods, ReplicaSets, ו-Deployments — hierarchy של אובייקטים
**Pod** — היחידה הבסיסית. container אחד או יותר שחולקים network namespace ו-storage:
apiVersion: v1
kind: Pod
metadata:
name: my-pod
labels:
app: my-app
spec:
containers:
- name: app
image: nginx:1.25
ports:
- containerPort: 80
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "128Mi"
cpu: "500m"לא יוצרים Pods ידנית בproduction — **Deployment** מנהל Pods דרך ReplicaSet:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: ghcr.io/myorg/my-app:sha-abc1234
ports:
- containerPort: 3000
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "500m"**פקודות יומיומיות:**
kubectl apply -f deployment.yaml
kubectl get deployments -n production
kubectl get pods -n production -l app=my-app
kubectl describe pod <pod-name> -n productionLiveness ו-Readiness Probes — production gap קריטי
**production gap: health probes for zero-downtime deployments**
בלי probes, Kubernetes שולח traffic לContainer שעדיין בstartup, גורם ל-502 errors.
**Readiness Probe** — האם ה-container מוכן לקבל traffic?
**Liveness Probe** — האם ה-container עדיין פועל (לא תקוע בdead-lock)?
spec:
containers:
- name: app
image: my-app:latest
readinessProbe:
httpGet:
path: /health/ready
port: 3000
initialDelaySeconds: 10 # המתן לפני בדיקה ראשונה
periodSeconds: 5 # בדוק כל 5 שניות
failureThreshold: 3 # 3 כשלונות → Pod לא ready
successThreshold: 1 # כשלון אחד מספיק להוציא מtraffic
livenessProbe:
httpGet:
path: /health/live
port: 3000
initialDelaySeconds: 30 # אחרי שreadiness הצליח
periodSeconds: 10
failureThreshold: 3 # 3 כשלונות → restart container
timeoutSeconds: 5
startupProbe: # עבור apps עם startup ארוך (Java, Scala)
httpGet:
path: /health/live
port: 3000
failureThreshold: 30 # 30 * 10s = 5 דקות startup timeout
periodSeconds: 10**מה קורה בRolling Update עם readiness:**
1. Kubernetes מפעיל Pod חדש
2. ממתין עד שreadiness Probe מצליח
3. רק אז מסיר Pod ישן מה-load balancer
4. מחכה שreplica ישן יסיים requests קיימות (terminationGracePeriodSeconds)
5. עובר ל-Pod הבא
בלי readiness Probe: שלב 3 קורה מיד אחרי container start — לפני שהapp מוכן.
Services ו-Ingress — ניתוב traffic
**Service** — stable DNS name ו-IP עבור קבוצת Pods:
apiVersion: v1
kind: Service
metadata:
name: my-app-svc
namespace: production
spec:
selector:
app: my-app # כל Pods עם label זה
ports:
- port: 80 # port שנחשף ע"י ה-Service
targetPort: 3000 # port שהcontainer מאזין עליו
type: ClusterIP # רק בתוך ה-cluster (ברירת מחדל)**Service types:**
| Type | שימוש |
|------|-------|
| ClusterIP | בתוך cluster בלבד (ברירת מחדל) |
| NodePort | חושף port על כל node (dev/testing) |
| LoadBalancer | יוצר AWS/GCP LB (יקר, לאפליקציות ישירות) |
| ExternalName | alias ל-external DNS |
**Ingress** — reverse proxy ב-cluster, L7 routing:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app-ingress
namespace: production
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: nginx
tls:
- hosts:
- api.mycompany.com
secretName: api-tls-cert
rules:
- host: api.mycompany.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app-svc
port:
number: 80**nginx-ingress controller** הוא ה-reverse proxy שמפעיל את ה-Ingress rules — בדיוק כמו nginx שלמדנו, אבל מנוהל ע"י Kubernetes.
ConfigMaps ו-Secrets — inject configuration
**ConfigMap** — configuration non-sensitive:
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: production
data:
NODE_ENV: production
LOG_LEVEL: info
DB_HOST: postgres.production.svc.cluster.local
DB_PORT: "5432"**Secret** — data sensitive (base64 encoded, לא encrypted בברירת מחדל!):
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
namespace: production
type: Opaque
data:
DB_PASSWORD: c3VwM3JzM2NyM3Qh # base64 encoded
API_KEY: bXlhcGlrZXkxMjM=# יצירת secret מvalue ישיר (מקודד אוטומטית)
kubectl create secret generic app-secrets \
--from-literal=DB_PASSWORD=sup3rs3cr3t! \
--from-literal=API_KEY=myapikey123 \
-n production**Inject ל-container:**
spec:
containers:
- name: app
envFrom:
- configMapRef:
name: app-config
- secretRef:
name: app-secrets
# או env var ספציפי:
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: app-secrets
key: DB_PASSWORD**חשוב:** K8s Secrets הם base64, לא encrypted בetcd כברירת מחדל. בproduction: הפעל Encryption at Rest, השתמש ב-External Secrets Operator עם AWS Secrets Manager/Vault, או Sealed Secrets.
Rolling Updates ו-Rollback — zero-downtime deployments
**production gap: rolling update strategy and kubectl rollout**
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0 # אף Pod לא מוריד — חובה לzero-downtime
maxSurge: 1 # Pod נוסף אחד בזמן update**עדכון image ב-CI/CD:**
# ב-GitHub Actions deploy job:
kubectl set image deployment/my-app \
app=ghcr.io/myorg/my-app:sha-${{ github.sha }} \
-n production
# המתן שה-rollout יסתיים (timeout 5 דקות)
kubectl rollout status deployment/my-app -n production --timeout=5m**Rollback — חזרה לversions קודמות:**
# בדוק history
kubectl rollout history deployment/my-app -n production
# חזרה לversion קודם
kubectl rollout undo deployment/my-app -n production
# חזרה לversion ספציפי
kubectl rollout undo deployment/my-app --to-revision=3 -n production
# בדוק שה-rollback הצליח
kubectl rollout status deployment/my-app -n production**Deployment pause/resume לcanary manual:**
# עצור rollout אחרי Pod הראשון (canary manual)
kubectl rollout pause deployment/my-app -n production
# בדוק metrics על ה-canary Pod
# אם הכל תקין:
kubectl rollout resume deployment/my-app -n productionkubectl debugging — חקירת בעיות בproduction
**production gap: kubectl debug and log inspection**
# בדיקת מצב כללי
kubectl get pods -n production -o wide
kubectl get events -n production --sort-by='.lastTimestamp'
# Pod לא עולה — describe מציג events ו-conditions
kubectl describe pod <pod-name> -n production
# חפש ב-Events: OOMKilled, ImagePullBackOff, CrashLoopBackOff
# Logs מcontainer רץ
kubectl logs <pod-name> -n production
kubectl logs <pod-name> -n production --previous # מcontainer קרוס
kubectl logs <pod-name> -n production -f # streaming
kubectl logs -l app=my-app -n production --tail=100 # מכל pods עם label
# Shell בתוך container (debug)
kubectl exec -it <pod-name> -n production -- /bin/sh
# kubectl debug — ephemeral container (K8s 1.23+)
# שימושי כשה-main container לא כולל shell
kubectl debug -it <pod-name> -n production \
--image=busybox --target=app
# Port forward לגישה ל-service מהמחשב המקומי
kubectl port-forward svc/my-app-svc 8080:80 -n production
# עכשיו: curl http://localhost:8080/health
# בדיקת resource usage
kubectl top pods -n production
kubectl top nodes**פירוש error states נפוצים:**
| State | משמעות | פתרון |
|-------|---------|--------|
| CrashLoopBackOff | container קורס שוב ושוב | kubectl logs --previous |
| OOMKilled | חרג מlimit memory | הגדל memory limit |
| ImagePullBackOff | לא הצליח להוריד image | בדוק imagePullSecrets, registry creds |
| Pending (no nodes match) | אין node מתאים | kubectl describe pod — בדוק node selectors/taints |
Namespaces ו-Resource Quotas — הפרדת סביבות
Namespaces מאפשרים הפרדה לוגית ב-cluster אחד:
kubectl create namespace staging
kubectl create namespace production
kubectl create namespace monitoring
# פקודות עם namespace
kubectl get pods -n staging
kubectl get all -n production
# הגדר namespace default ל-context
kubectl config set-context --current --namespace=production**ResourceQuota — הגבלת resources לnamespace:**
apiVersion: v1
kind: ResourceQuota
metadata:
name: production-quota
namespace: production
spec:
hard:
requests.cpu: "4"
requests.memory: 8Gi
limits.cpu: "8"
limits.memory: 16Gi
pods: "20"
services: "10"**LimitRange — הגבלות per-container:**
apiVersion: v1
kind: LimitRange
metadata:
name: container-limits
namespace: production
spec:
limits:
- type: Container
default: # default limit אם לא הוגדר
cpu: "500m"
memory: "256Mi"
defaultRequest: # default request
cpu: "100m"
memory: "64Mi"
max: # מקסימום מותר
cpu: "2"
memory: "2Gi"HorizontalPodAutoscaler ו-resource requests — scaling אוטומטי
**production gap: resource requests/limits and HPA configuration**
HPA מוסיף ומסיר Pods לפי load:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: my-app-hpa
namespace: production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # scale up כש-CPU > 70%
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80**חשוב:** HPA דורש שcpu/memory **requests** יהיו מוגדרים בDeployment — בלעדיהם HPA לא יכול לחשב % utilization.
**Requests vs Limits:**
- requests — מה ה-scheduler מבטיח לPod (כמה CPU/memory node חייב לשמור)
- limits — מקסימום שcontainer יכול לצרוך
- container שעובר memory limit → **OOMKilled**
- container שעובר CPU limit → **throttled** (לא killed)
# בדוק HPA status
kubectl get hpa -n production
kubectl describe hpa my-app-hpa -n production
# בדוק events scaling
kubectl get events -n production | grep HorizontalPodAutoscalerתרגול מעשי
1.
2.
3.
שאלות חיבור
חבר את מה שלמדת בנושא זה לנושאים קודמים. אין תשובה אחת נכונה — חשיבה ביקורתית היא המטרה.
שאלת חיבור 1
Kubernetes Ingress controller הוא nginx שרץ בתוך cluster — ישירות מחבר ל-nginx + Web Servers (topic #5). הסבר: (1) מה ההבדל בין nginx שהגדרנו ב-topic 5 (reverse proxy על VPS) לבין nginx-ingress controller ב-Kubernetes, (2) כיצד rate limiting שלמדנו (limit_req_zone) מוגדר ב-Ingress annotations, ו-(3) כיצד TLS termination ב-Kubernetes Ingress מקביל ל-SSL termination שלמדנו ב-nginx.
שאלת חיבור 2
GitHub Actions CI/CD (topic #8) מחולל Docker image ודופס ל-ghcr.io — ואז Kubernetes צריך לדפלוי אותו. הסבר את ה-end-to-end flow: (1) כיצד ה-deploy job ב-GitHub Actions מעדכן את ה-Deployment ב-Kubernetes, (2) כיצד imagePullSecrets מאפשר ל-Kubernetes לשלוף image מ-ghcr.io, ו-(3) מה יקרה אם `kubectl rollout status` timeout ב-5 דקות ומה ה-pipeline צריך לעשות.
מוכן לבחינה?
בצע הערכה תיאורטית, תרגול CLI ושאלות חיבור כדי לסיים את הנושא.