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

آموزش کوبرنتیز؛ شب سیزدهم: الگوهای Pod چندکانتینری

امشب Containerهایی می‌سازیم که از طریق یک Volume مشترک همکاری می‌کنند و تصمیم می‌گیریم چه زمانی وابستگی چند Container واقعاً باید داخل یک Pod قرار بگیرد.

چرا Containerها یک Pod را شریک می‌شوند؟

Containerهای یک Pod موارد زیر را به اشتراک می‌گذارند:

  • یک Network Namespace و IP مربوط به Pod؛
  • localhost، پس درگاه‌ها نباید با هم تداخل داشته باشند؛
  • Volumeهای Mountشدهٔ Pod؛
  • Scheduling و چرخهٔ حیات؛ واحد جای‌گذاری و Scale همان Pod است.

آن‌ها به‌طور پیش‌فرض Filesystem مربوط به Image یا Processها را به اشتراک نمی‌گذارند. فقط وقتی چند Container باید هم‌مکان باشند و چرخهٔ حیات مشترکی داشته باشند، آن‌ها را در یک Pod قرار دهید. سرویس‌هایی که مستقل Scale می‌شوند، معمولاً باید Workload جدا داشته باشند و از طریق Service ارتباط برقرار کنند.

الگوهای رایج

الگو هدف نمونه
Init تکمیل آماده‌سازی پیش از شروع برنامه تولید پیکربندی
Sidecar کمک پیوسته به برنامه Refresh فایل یا ارسال Log
Adapter تبدیل خروجی به یک شکل مشترک یکدست کردن Metricها
Ambassador Proxy کردن یک Dependency محلی Proxy محلی TLS یا دیتابیس

این نام‌ها معماری را توصیف می‌کنند و نوع ویژه‌ای از Object کوبرنتیز نیستند.

Manifest یک Pod مشارکتی

apiVersion: v1
kind: Pod
metadata:
  name: cooperative
  namespace: course-night-13
  labels:
    app: cooperative
spec:
  restartPolicy: Always
  initContainers:
    - name: initialize
      image: busybox:1.36
      command: ["/bin/sh", "-c"]
      args:
        - echo "Shared file initialized by init container" > /shared/index.html
      volumeMounts:
        - name: shared
          mountPath: /shared
  containers:
    - name: writer
      image: busybox:1.36
      command: ["/bin/sh", "-c"]
      args:
        - |
          while true; do
            echo "Updated at $(date -Iseconds)" > /shared/index.html.tmp
            mv /shared/index.html.tmp /shared/index.html
            sleep 5
          done
      volumeMounts:
        - name: shared
          mountPath: /shared
    - name: web
      image: nginx:1.27-alpine
      ports:
        - name: http
          containerPort: 80
      volumeMounts:
        - name: shared
          mountPath: /usr/share/nginx/html
          readOnly: true
  volumes:
    - name: shared
      emptyDir: {}

استقرار Pod مشارکتی

kubectl create namespace course-night-13
kubectl apply -f app.yaml
kubectl get pod cooperative --namespace course-night-13 --watch

وقتی هر دو App Container در وضعیت Ready قرار گرفتند، Watch را متوقف و وضعیت همه را بررسی کنید:

kubectl get pod cooperative --namespace course-night-13 \
  -o jsonpath='{range .status.initContainerStatuses[*]}init/{.name}={.state.terminated.reason}{"\n"}{end}{range .status.containerStatuses[*]}app/{.name} ready={.ready} restarts={.restartCount}{"\n"}{end}'
kubectl logs cooperative --namespace course-night-13 --container writer
kubectl exec cooperative --namespace course-night-13 --container web -- \
  cat /usr/share/nginx/html/index.html

Init Container نخستین فایل را می‌سازد. Sidecar نویسنده با الگوی Write-then-rename آن را به‌شکل Atomic به‌روزرسانی می‌کند. NGINX همان emptyDir را به‌صورت فقط‌خواندنی سرو می‌کند.

آزمایش از طریق Web Container

kubectl port-forward --namespace course-night-13 pod/cooperative 8080:80

در Terminal دیگری اجرا کنید:

for i in 1 2 3; do curl --fail http://127.0.0.1:8080; sleep 5; done

پاسخ در حال تغییر ثابت می‌کند دو Container بلندمدت از طریق Volume مشترک همکاری می‌کنند. آن‌ها می‌توانستند از طریق localhost نیز ارتباط داشته باشند.

تمرین خرابی

موقتاً Mount Path مربوط به Writer را از /shared به /wrong تغییر دهید، اما Script آن را دست‌نخورده بگذارید:

kubectl apply -f app.yaml
kubectl get pod cooperative --namespace course-night-13
kubectl describe pod cooperative --namespace course-night-13
kubectl logs cooperative --namespace course-night-13 --container writer

Writer به‌دلیل نبود دایرکتوری موردانتظار وارد حلقهٔ Restart می‌شود؛ در همین زمان ممکن است NGINX همچنان فایل قدیمی Init Container را سرو کند. این یک خرابی جزئی داخل یک Pod است و Phase کلی Pod به‌تنهایی اطلاعات کافی نمی‌دهد.

Mount Path را تعمیر کنید. چون بیشتر فیلدهای Pod Spec تغییرناپذیرند، Object را حذف و دوباره ایجاد کنید:

kubectl delete -f app.yaml
kubectl apply -f app.yaml
kubectl wait --namespace course-night-13 \
  --for=condition=Ready pod/cooperative --timeout=60s

مرور Trade-offها

Sidecar منابع مصرف می‌کند و در محیط تولید باید Request، Limit، Security Context، Log و راهبرد سلامت خودش را داشته باشد. Scale کردن این Pod هر دو Container را با هم Scale می‌کند و به‌روزرسانی هرکدام، تمام Pod را جایگزین می‌کند. این هزینه‌ها فقط وقتی توجیه دارند که نیاز جدی به هم‌مکانی و چرخهٔ حیات مشترک وجود داشته باشد.

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

  • توضیح می‌دهم Containerهای یک Pod چه چیزهایی را به اشتراک می‌گذارند و چه چیزهایی را نه.
  • رفتار Init و Sidecar را از طریق یک Volume مشترک مشاهده کرده‌ام.
  • برای logs و exec، Container درست را صریح انتخاب کرده‌ام.
  • خرابی جزئی در یک Pod چندکانتینری را تشخیص داده‌ام.
  • توضیح می‌دهم چه زمانی Deploymentهای جدا طراحی بهتری هستند.
kubectl delete namespace course-night-13