پرش به محتویات

آموزش کوبرنتیز؛ شب چهاردهم: بازبینی Workload محیط تولید

در پایان هفتهٔ دوم، Config خارجی، Probeها، منابع، ظرفیت امن Rollout و خاتمهٔ آرام را در یک Workload مربوط به NGINX ترکیب می‌کنیم. سپس Rolling Update را اندازه می‌گیریم و ثابت می‌کنیم Service هیچ درخواست ناموفقی برنمی‌گرداند.

این تمرین شبیه محیط تولید است، اما خودبه‌خود Production-ready نیست. مقدارها هنوز به اندازه‌گیری برنامه، بازبینی امنیتی، چند Failure Domain، Monitoring، Disruption Policy و Image Digest تغییرناپذیر نیاز دارند.

قرارداد کامل را بخوانید

پیش از Apply، موارد زیر را در app.yaml پیدا کنید:

  • اتصال ConfigMap به Volume؛
  • اتصال Selector مربوط به Deployment به Label مربوط به Pod؛
  • Selector سرویس و Target Port نام‌گذاری‌شده؛
  • Requestهای مورد استفاده در Scheduling و Limitهای اعمال‌شده در زمان اجرا؛
  • مسئولیت Startup، Readiness و Liveness؛
  • مقدارهای maxUnavailable: 0 و maxSurge: 1؛
  • تأخیر preStop و Termination Grace Period سی‌ثانیه‌ای؛
  • غیرفعال بودن Mount خودکار Token مربوط به Service Account.

Rollout ممکن است موقتاً از چهار Pod استفاده کند: سه نمونهٔ مطلوب و یک Surge. کلاستر باید ظرفیت Pod اضافی را داشته باشد.

Manifest نهایی

apiVersion: v1
kind: ConfigMap
metadata:
  name: web-content
  namespace: course-night-14
data:
  index.html: |
    <!doctype html>
    <html><body><h1>Production review: version 1</h1></body></html>
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  namespace: course-night-14
  labels:
    app.kubernetes.io/name: web
    app.kubernetes.io/part-of: night-school
spec:
  replicas: 3
  minReadySeconds: 5
  progressDeadlineSeconds: 120
  revisionHistoryLimit: 5
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0
      maxSurge: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: web
  template:
    metadata:
      labels:
        app.kubernetes.io/name: web
        app.kubernetes.io/part-of: night-school
    spec:
      automountServiceAccountToken: false
      terminationGracePeriodSeconds: 30
      containers:
        - name: nginx
          image: nginx:1.27-alpine
          ports:
            - name: http
              containerPort: 80
          resources:
            requests:
              cpu: 25m
              memory: 32Mi
            limits:
              cpu: 250m
              memory: 128Mi
          startupProbe:
            httpGet:
              path: /
              port: http
            periodSeconds: 2
            failureThreshold: 15
          readinessProbe:
            httpGet:
              path: /
              port: http
            periodSeconds: 3
            failureThreshold: 2
          livenessProbe:
            httpGet:
              path: /
              port: http
            periodSeconds: 10
            failureThreshold: 3
          lifecycle:
            preStop:
              exec:
                command: ["/bin/sh", "-c", "sleep 5"]
          volumeMounts:
            - name: content
              mountPath: /usr/share/nginx/html
              readOnly: true
      volumes:
        - name: content
          configMap:
            name: web-content
---
apiVersion: v1
kind: Service
metadata:
  name: web
  namespace: course-night-14
spec:
  selector:
    app.kubernetes.io/name: web
  ports:
    - name: http
      port: 80
      targetPort: http

اعتبارسنجی و استقرار

kubectl create namespace course-night-14
kubectl apply --dry-run=server -f app.yaml
kubectl diff -f app.yaml
kubectl apply -f app.yaml
kubectl rollout status deployment/web --namespace course-night-14

kubectl get deployment,replicaset,pod,service,endpointslice \
  --namespace course-night-14 -o wide
kubectl get pods --namespace course-night-14 \
  -o custom-columns='NAME:.metadata.name,READY:.status.containerStatuses[0].ready,QOS:.status.qosClass,CPU_REQ:.spec.containers[0].resources.requests.cpu,MEM_LIMIT:.spec.containers[0].resources.limits.memory'

توضیح دهید چرا QoS این Workload از نوع Burstable است، نه Guaranteed.

راه‌اندازی Client پیوسته

در Terminal نخست یک Client بلندمدت بسازید:

kubectl run client --namespace course-night-14 \
  --restart=Never --image=curlimages/curl -- \
  sh -c 'i=0; while true; do i=$((i+1)); code=$(curl -sS -o /dev/null -w "%{http_code}" --connect-timeout 1 --max-time 2 http://web) && echo "$i $code" || echo "$i FAIL"; sleep 0.2; done'
kubectl logs client --namespace course-night-14 --follow

اجازه دهید این Client پیش از Update، هنگام آن و پس از آن اجرا شود.

اجرای Rolling Update

در Terminal دوم، متن ConfigMap را از version 1 به version 2 تغییر دهید. تغییر تنها در ConfigMap یک ReplicaSet تازه نمی‌سازد؛ ابتدا این رفتار را مشاهده کنید:

kubectl diff -f app.yaml
kubectl apply -f app.yaml
kubectl rollout history deployment/web --namespace course-night-14

اکنون Annotation مربوط به Pod Template را تغییر دهید:

kubectl patch deployment web --namespace course-night-14 --type merge \
  --patch '{"spec":{"template":{"metadata":{"annotations":{"course.example/config-version":"2"}}}}}'
kubectl get pods --namespace course-night-14 --watch

در Terminal سوم، Endpointها و ReplicaSetها را ببینید:

kubectl get replicaset,endpointslice --namespace course-night-14 --watch

پس از پایان کار هر دو Watch را متوقف کنید:

kubectl rollout status deployment/web --namespace course-night-14
kubectl rollout history deployment/web --namespace course-night-14

Podهای جدید باید Probeهای Startup و Readiness را پاس کنند و پیش از حذف ظرفیت قدیمی، به‌اندازهٔ minReadySeconds آماده بمانند. preStop فرصت می‌دهد تغییر Endpointها پیش از خروج NGINX در کلاستر منتشر شود.

اندازه‌گیری نتیجه

فرمان kubectl logs --follow را با Ctrl+C متوقف کنید:

kubectl logs client --namespace course-night-14 > /tmp/night-14-client.log
awk '$2 != 200 {print}' /tmp/night-14-client.log
wc -l /tmp/night-14-client.log
kubectl get deployment web --namespace course-night-14 \
  -o jsonpath='desired={.spec.replicas} updated={.status.updatedReplicas} ready={.status.readyReplicas} available={.status.availableReplicas}{"\n"}'

شرط پایان این است که هنگام Update مشاهده‌شده، تعداد خط‌های FAIL یا Status Code غیر از ۲۰۰ برابر صفر باشد. این آزمایش احتمال خرابی موردانتظار Rollout را کاهش می‌دهد، اما Zero Downtime همیشگی را ثابت نمی‌کند. Connectionهای بلندمدت، Shutdown برنامه، خرابی Node، رفتار Ingress و Load Balancerهای خارجی آزمایش جدا می‌خواهند.

آخرین تمرین خرابی

با تغییر مسیر Readiness به /missing آن را خراب و سپس Apply کنید. پیش از بررسی خروجی، نتیجه را پیش‌بینی کنید:

kubectl apply -f app.yaml
kubectl rollout status deployment/web --namespace course-night-14 --timeout=60s
kubectl get pods,endpointslices --namespace course-night-14
kubectl describe deployment web --namespace course-night-14
kubectl get events --namespace course-night-14 \
  --sort-by=.metadata.creationTimestamp

چون ظرفیت غیرآماده صفر است، Replicaهای قدیمی و Ready باید از سرویس محافظت کنند، درحالی‌که Rollout متوقف می‌شود. مسیر را تعمیر، Apply و Rollout را بررسی کنید؛ سپس آزمایش Client پیوسته را تکرار کنید.

پرسش‌های مرور هفتهٔ دوم

  1. کدام تغییرهای پیکربندی به Restart نیاز دارند و چرا؟
  2. چرا Base64 برای محافظت از Secret کافی نیست؟
  3. تفاوت عملیاتی Readiness و Liveness چیست؟
  4. چرا ممکن است Pod با وجود مصرف کم Node در وضعیت Pending بماند؟
  5. کدام Process، SIGTERM را دریافت می‌کند و پس از Grace Period چه رخ می‌دهد؟
  6. چه زمانی دو Process باید یک Pod را شریک شوند، نه دو Deployment جدا را؟
  7. این راهبرد Rolling چه مقدار ظرفیت موقت لازم دارد؟

چک‌لیست پایان

  • همهٔ فیلدهای شبیه محیط تولید را در Manifest توضیح داده‌ام.
  • Workload را پیش از آزمایش اعتبارسنجی و بررسی کرده‌ام.
  • Endpointهای Ready را در تمام مدت Rollout مشاهده کرده‌ام.
  • Log ثبت‌شدهٔ Client هیچ درخواست ناموفق یا غیر ۲۰۰ ندارد.
  • Rollout متوقف‌شده را با شواهد Readiness تشخیص داده و تعمیر کرده‌ام.
  • به هر هفت پرسش مرور با زبان خودم پاسخ داده‌ام.
  • محدودیت‌های آنچه این آزمایش تک‌Node ثابت می‌کند را ثبت کرده‌ام.
kubectl delete namespace course-night-14
rm -f /tmp/night-14-client.log