آموزش کوبرنتیز؛ شب دوازدهم: چرخهٔ حیات Pod و خاتمهٔ آرام¶
امشب راهاندازی اولیه، Signalها، Hookها، Grace Period، Restart و خاتمهٔ آرام را هنگام یک Rollout مشاهده میکنیم.
مدل ذهنی چرخهٔ حیات¶
schedule Pod
→ run init containers in order
→ start app containers
→ Pod becomes Ready
→ deletion/rollout begins
→ run preStop hook
→ send SIGTERM to container process
→ wait within termination grace period
→ send SIGKILL if still running
حذف Endpoint و خاموش شدن Container بهوسیلهٔ Controllerهای توزیعشده انجام میشود؛ بنابراین برنامه باید یک بازهٔ کوتاه همگرایی را تحمل کند. Readiness، تخلیهٔ Connection و Grace Time کافی در کنار هم کار میکنند.
restartPolicy به Containerهای داخل Pod مربوط است و مقدار پیشفرضش Always است؛ Podهای Deployment نیز باید Always باشند. Containerای که Restart میشود در همان Pod باقی میماند، اما Pod جایگزین نام و UID تازهای دارد.
Manifest آزمایش¶
apiVersion: v1
kind: ConfigMap
metadata:
name: lifecycle-scripts
namespace: course-night-12
data:
run.sh: |
#!/bin/sh
trap 'echo "$(date -Iseconds) SIGTERM received"; sleep 5; echo "$(date -Iseconds) shutdown complete"; exit 0' TERM
echo "$(date -Iseconds) application started"
while true; do sleep 1; done
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: lifecycle-demo
namespace: course-night-12
spec:
replicas: 2
strategy:
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
selector:
matchLabels:
app: lifecycle-demo
template:
metadata:
labels:
app: lifecycle-demo
spec:
terminationGracePeriodSeconds: 20
initContainers:
- name: initialize
image: busybox:1.36
command: ["/bin/sh", "-c"]
args: ["echo initialized > /work/status"]
volumeMounts:
- name: work
mountPath: /work
containers:
- name: app
image: busybox:1.36
command: ["/bin/sh", "/scripts/run.sh"]
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "echo preStop-called; sleep 3"]
volumeMounts:
- name: scripts
mountPath: /scripts
readOnly: true
- name: work
mountPath: /work
volumes:
- name: scripts
configMap:
name: lifecycle-scripts
defaultMode: 0555
- name: work
emptyDir: {}
استقرار و بررسی Initialization¶
kubectl create namespace course-night-12
kubectl apply -f app.yaml
kubectl rollout status deployment/lifecycle-demo --namespace course-night-12
kubectl get pods --namespace course-night-12
kubectl get pod <pod-name> --namespace course-night-12 \
-o jsonpath='{range .status.initContainerStatuses[*]}{.name}{" state="}{.state}{"\n"}{end}'
kubectl exec <pod-name> --namespace course-night-12 -- cat /work/status
kubectl logs <pod-name> --namespace course-night-12 --container app
Init Container باید پیش از شروع App Container با موفقیت تمام شود. هر دو Container مسیر /work را میبینند، زیرا emptyDir متعلق به Pod است و تا پایان عمر همان Pod باقی میماند.
مشاهدهٔ خاموش شدن آرام¶
در یک Terminal اجرا کنید:
در Terminal دیگر، نام یکی از Podهای فعلی را ذخیره و Pod Template را تغییر دهید:
old_pod="$(kubectl get pod --namespace course-night-12 \
-l app=lifecycle-demo -o jsonpath='{.items[0].metadata.name}')"
kubectl patch deployment lifecycle-demo --namespace course-night-12 \
--type merge \
--patch '{"spec":{"template":{"metadata":{"annotations":{"course.example/restarted":"night-12"}}}}}'
kubectl logs "$old_pod" --namespace course-night-12 --container app --follow
Hook مربوط به preStop پیش از ارسال TERM اجرا میشود. چون Hook یک Process جداست، نمایش خروجی آن در Log مربوط به Process اصلی تضمین نمیشود؛ اما باید Signal گرفتهشده و پیامهای Shutdown را ببینید.
kubectl rollout status deployment/lifecycle-demo --namespace course-night-12
kubectl get events --namespace course-night-12 \
--sort-by=.metadata.creationTimestamp
تمرین خرابی: Grace Period کوتاه¶
terminationGracePeriodSeconds را به 5 تغییر دهید. Hook مربوط به preStop حدود سه ثانیه زمان میگیرد و برنامه نیز میکوشد پنج ثانیه صرف خاموش شدن کند؛ هر دو از یک بودجهٔ مشترک استفاده میکنند. Apply کنید و Rollout دیگری بسازید:
kubectl apply -f app.yaml
kubectl patch deployment lifecycle-demo --namespace course-night-12 \
--type merge \
--patch '{"spec":{"template":{"metadata":{"annotations":{"course.example/restarted":"too-short"}}}}}'
kubectl rollout status deployment/lifecycle-demo --namespace course-night-12
ممکن است برنامه پیش از خاتمهٔ اجباری فرصت ثبت shutdown complete را پیدا نکند. Grace Period را به ۲۰ ثانیه برگردانید و دوباره کل توالی را بررسی کنید.
راهنمای طراحی¶
- Shutdown را Idempotent طراحی کنید.
- ابتدا پذیرش کار جدید را متوقف، سپس کارهای در حال انجام و محدود را تمام کنید و بعد خارج شوید.
- Grace Period باید از مجموع زمان
preStopو بدترین زمان معتبر Drain بیشتر باشد. preStopجایگزین مدیریتSIGTERMدر خود برنامه نیست.- PID 1 باید Signal را دریافت یا Forward کند؛ Wrapperهای Shell ممکن است آن را ناخواسته ببلعند.
- Init Containerها باید تکرارپذیر باشند، زیرا Pod بازسازیشده دوباره آنها را اجرا میکند.
چکلیست پایان¶
- تکمیل Init را پیش از شروع برنامه مشاهده کردهام.
- Restart شدن Container را از جایگزینی Pod تشخیص میدهم.
-
SIGTERMو Shutdown آرام را هنگام Rollout ثبت کردهام. - نشان دادهام بودجهٔ Grace Period شامل زمان اجرای
preStopنیز هست. - Grace Period امن را بازگرداندهام.