Pod: หน่วยที่เล็กที่สุดใน Kubernetes
ไอเดียในหนึ่งประโยค
หัวข้อที่มีชื่อว่า “ไอเดียในหนึ่งประโยค”Pod คือหน่วยที่เล็กที่สุดที่ deploy ได้ใน Kubernetes container หนึ่งตัวหรือมากกว่านั้นที่รันอยู่บน node เดียวกันเสมอ แชร์ network namespace เดียวกัน และแชร์ volume ได้ด้วยถ้าต้องการ
สิ่งที่ container ใน Pod เดียวกันแชร์กัน
หัวข้อที่มีชื่อว่า “สิ่งที่ container ใน Pod เดียวกันแชร์กัน”container ทุกตัวใน Pod เดียวกันได้ IP address เดียวกันและ network namespace เดียวกัน หมายความว่า container สองตัวใน Pod เดียวกันคุยกันผ่าน localhost ได้เลย ที่ port ไหนก็ได้ที่อีกตัวเปิดรอ ไม่ต้องผ่าน networking ของ cluster เลย container ใน Pod เดียวกันยังแชร์ volume เดียวกันได้ด้วย นั่นคือวิธีที่ sidecar อ่านไฟล์ที่ container หลักเขียนไว้
# From inside the "app" container, a sidecar in the same Pod is reachable on localhostkubectl exec -it app-with-sidecar -c app -- curl -s http://localhost:9090/metricsการแชร์ namespace แบบนี้คือเหตุผลทั้งหมดที่ Pod มีอยู่เป็น concept แยกต่างหาก Pod คือขอบเขตที่ทำให้ “container พวกนี้อยู่ node เดียวกันเสมอและคุยกันผ่าน localhost ได้” เป็นจริงเสมอ container ที่อยู่คนละ Pod ไม่ได้สิ่งนี้ฟรี ๆ ต้องผ่าน Service และ address ที่ route ได้เท่านั้น
Multi-container pattern: sidecar และ init container
หัวข้อที่มีชื่อว่า “Multi-container pattern: sidecar และ init container”สอง pattern นี้ครอบคลุมเกือบทุกเหตุผลที่จะใส่ container มากกว่าหนึ่งตัวใน Pod เดียว
- Sidecar container ผู้ช่วยที่รันคู่กับ container หลักตลอดอายุของ Pod เช่น log shipper ที่ tail log file ของ container หลักแล้วส่งต่อ หรือ proxy ที่จัดการ TLS termination
- Init container container ที่รัน จนจบ ตามลำดับ ก่อนที่ container หลักตัวไหนจะเริ่มทำงาน ใช้บ่อยเพื่อรอให้ dependency พร้อมใช้งาน หรือรัน migration ครั้งเดียวก่อน container หลักจะบูต
apiVersion: v1kind: Podmetadata: name: app-with-sidecarspec: restartPolicy: Always initContainers: - name: wait-for-db image: busybox:1.38 command: ['sh', '-c', 'until nc -z db 5432; do sleep 1; done'] - name: log-shipper image: myregistry/log-shipper:2.1.0 restartPolicy: Always volumeMounts: - name: logs mountPath: /var/log/app containers: - name: app image: myregistry/app:1.4.0 ports: - containerPort: 8080 volumeMounts: - name: logs mountPath: /var/log/app volumes: - name: logs emptyDir: {}wait-for-db จะบล็อกไม่ให้ Pod เริ่มทำงานจนกว่าจะเชื่อมต่อ database ได้ แล้ว exit สำเร็จ จากนั้น initContainers ตัวที่เหลือและ containers หลักถึงจะเริ่มตามลำดับ สังเกตว่า log-shipper ก็อยู่ใต้ initContainers เหมือนกัน ไม่ได้อยู่ใน containers แต่มันตั้ง restartPolicy: Always ให้ตัวเอง แค่ field เดียวนี้แหละที่ทำให้มันกลายเป็น native sidecar แทนที่จะเป็น init container ธรรมดา kubelet จะ start มัน (แล้ว restart ให้ถ้า exit) ก่อน app จะเริ่ม และตอน Pod กำลังจะ terminate ก็จะสั่งหยุด app ก่อนแล้วค่อยหยุด sidecar ตัวนี้ทีหลัง log shipper เลย stream log บรรทัดสุดท้าย ๆ ได้ครบโดยไม่ถูกฆ่าทิ้งก่อนเวลา ฟีเจอร์นี้เป็น stable/GA ตั้งแต่ Kubernetes v1.33 (เริ่มมีตั้งแต่ v1.28) ก่อนหน้านี้วิธีเดียวที่จะรัน sidecar คือใส่เป็น container ธรรมดาใน containers ซึ่งไม่มี guarantee เรื่องลำดับ start/stop เทียบกับ container หลักเลย app กับ log-shipper แชร์ network namespace และ volume logs ของ Pod ร่วมกัน ซึ่งเป็นวิธีที่ log-shipper อ่านไฟล์ที่ app เขียนได้
flowchart TB
subgraph pod["Pod app-with-sidecar (one IP, one network namespace)"]
init["initContainer: wait-for-db (runs first, to completion)"] --> app["container: app"]
app -->|localhost| sidecar["container: log-shipper (sidecar)"]
end Lifecycle ของ Pod: phase, state และ restartPolicy
หัวข้อที่มีชื่อว่า “Lifecycle ของ Pod: phase, state และ restartPolicy”Pod เดินผ่านชุด phase เล็ก ๆ ได้แก่ Pending (ถูกรับเข้าระบบแล้วแต่ container ยังไม่ได้รันครบทุกตัว มักติดที่การ schedule หรือดึง image) Running (bound กับ node แล้ว มี container อย่างน้อยหนึ่งตัวกำลังรัน) Succeeded หรือ Failed (container ทุกตัว terminate แล้ว) และบางครั้งก็ Unknown (ดึง state ของ Pod ไม่ได้) ใต้ phase ลงไป container แต่ละตัวมี state ของตัวเอง คือ Waiting, Running หรือ Terminated
สิ่งที่เกิดขึ้นเมื่อ container exit ถูกควบคุมด้วย restartPolicy บน Pod spec Always restart container ที่ exit ทุกครั้งไม่มีสิ้นสุด (ค่า default ใช้กับ service ที่รันระยะยาว) OnFailure restart เฉพาะตอน exit code ไม่เป็นศูนย์ (ใช้กับ Job) และ Never ไม่ restart เลย (ก็ใช้กับ Job บ่อยเหมือนกัน หรือ Pod สำหรับ debug แบบครั้งเดียว)
# Watch a Pod move through its phaseskubectl get pod app-with-sidecar --watch
# Container-level detail: state (Waiting/Running/Terminated), restart count, exit codekubectl describe pod app-with-sidecarPod termination lifecycle: SIGTERM, grace period และ preStop
หัวข้อที่มีชื่อว่า “Pod termination lifecycle: SIGTERM, grace period และ preStop”kubectl delete pod ไม่ได้ไปฆ่า container ของ Pod ตรง ๆ มันตั้ง metadata.deletionTimestamp แล้วสองอย่างนี้เริ่มทำงาน พร้อมกัน
- endpoint controller เริ่มเอา Pod ออกจาก
Endpoints/EndpointSliceของทุก Service ที่ Pod นี้เป็น backend อยู่ - kubelet รัน
preStophook ของ container (ถ้ามี) แล้วส่ง SIGTERM ไปที่ PID 1 ของแต่ละ container แล้วรอ ถ้า container ยังไม่ exit จนกว่างบประมาณterminationGracePeriodSecondsทั้งหมด (ซึ่งเวลาที่preStophook รันก็นับรวมอยู่ในนั้นด้วย) หมด kubelet จะส่ง SIGKILL
$ kubectl explain pod.spec.terminationGracePeriodSecondsKIND: PodVERSION: v1
FIELD: terminationGracePeriodSeconds <integer>
DESCRIPTION: Optional duration in seconds the pod needs to terminate gracefully. May be decreased in delete request. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). If this value is nil, the default grace period will be used instead. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. Defaults to 30 seconds.sequenceDiagram
participant K as kubectl delete
participant EP as Endpoint controller
participant Kubelet
participant C as Container "main"
K->>Kubelet: pod.deletionTimestamp set
par endpoint removal
EP->>EP: remove Pod from EndpointSlice
and preStop hook
Kubelet->>C: run preStop hook
end
Kubelet->>C: SIGTERM (sent only after preStop returns)
Note over C: remaining grace = terminationGracePeriodSeconds minus preStop's own duration
Kubelet->>C: SIGKILL (if still running once remaining grace runs out) พิสูจน์: การแข่งกันระหว่างการเอาออกจาก endpoint กับ preStop ด้วย timestamp จริง
หัวข้อที่มีชื่อว่า “พิสูจน์: การแข่งกันระหว่างการเอาออกจาก endpoint กับ preStop ด้วย timestamp จริง”Pod (terminationGracePeriodSeconds: 15) ที่ trap SIGTERM แล้วพอรับ signal ก็ sleep 15 วินาทีก่อน exit ส่วน preStop hook ของมัน sleep 5 วินาที มันอยู่หลัง ClusterIP Service ตัวหนึ่ง พอ delete Pod นี้พร้อมกับ poll EndpointSlice ที่อยู่ข้างหลังทุก 300ms และ stream log ที่มี timestamp ของ container เอง จะได้ timeline จริงแบบนี้ (log ของ container เป็น UTC ส่วน endpoint poll เป็นเวลา local UTC+7 ดูแค่ผลต่างของเวลาก็พอ):
# container log (kubectl logs -f term-demo --timestamps)2026-09-26T07:50:24.632391178Z main started2026-09-26T07:50:32.496035750Z preStop start2026-09-26T07:50:37.500057586Z preStop end2026-09-26T07:50:37.649504711Z SIGTERM received, sleeping 15s before exit# (never logged: "main exiting now" -- SIGKILL cut it off, see below)# endpoint poll (kubectl get endpointslice -l kubernetes.io/service-name=term-demo, every 300ms)14:50:31.946884000 ready=true addrs=[10.244.2.2]14:50:32.428941000 DELETE ISSUED14:50:32.424496000 ready=true addrs=[10.244.2.2] # still true -- preStop hasn't even started yet14:50:32.902948000 ready=false addrs=[10.244.2.2] # <-- endpoint marked not-ready ~0.47s after delete...(unchanged ready=false for the next ~15s)...14:50:47.575059000 ready=false addrs=[] # <-- endpoint entry fully removed14:50:47.925334000 DELETE RETURNED# kubectl get events --field-selector involvedObject.name=term-demoLAST SEEN REASON MESSAGE0s Killing Stopping container mainอ่าน log สองอันคู่กัน: entry ของ Endpoints/EndpointSlice กลายเป็น not-ready ภายในครึ่งวินาทีหลัง kubectl delete ก่อนที่ preStop hook 5 วินาทีจะเสร็จด้วยซ้ำ (07:50:37.500) ยิ่งไม่ต้องพูดถึงก่อนที่ SIGTERM จะถูกส่ง (07:50:37.649) นี่แหละคือการแข่งที่ spec เตือนไว้ การเอา readiness ออกไม่ได้เรียงมาหลัง preStop มันเกิดพร้อม ๆ กันเลย client ที่ยังส่ง traffic ตาม endpoint list ที่ cache ไว้ (client-side load balancer, proxy ใน cluster ที่ยังไม่รู้ว่ามีการอัปเดต) ยังยิงมาที่ Pod ได้ตลอดช่วง preStop เลย นั่นแหละคือปัญหาที่ preStop: sleep N มีไว้แก้ มันซื้อเวลาที่การอัปเดต Service/kube-proxy/Ingress dataplane จริง ๆ ต้องใช้เพื่อ propagate ให้ครบก่อนที่ container จะหยุดรับ connection ใหม่จริง ๆ ไม่ใช่ การการันตีว่าจะไม่มี traffic เข้ามาระหว่างนั้น
ภาพรวมทั้งหมด: preStop รันไป 5 วินาที SIGTERM มาถึงตอน 07:50:37.649 แล้ว trap handler ของ container ก็ขอเวลาอีก 15 วินาทีเพื่อ exit อย่างสวยงาม แต่ Pod หายไปเกลี้ยงตอน 14:50:47.925 แค่ ~15.1 วินาทีหลัง delete ถูกยิงออกไป ไม่ใช่ ~20 วินาทีที่ container ขอ (5 วินาที preStop + 15 วินาที graceful exit) บรรทัด "main exiting now" ไม่เคยปรากฏใน log เลย มันถูก SIGKILL กลางคัน นี่คือ terminationGracePeriodSeconds: 15 ทำงานตามที่มันบอกไว้เป๊ะ ๆ งบประมาณถูกใช้ตั้งแต่วินาทีที่ kubectl delete ถูกยิง preStop 5 วินาทีของมันเองก็มาจากงบ 15 วินาทีนี้เหมือนกัน เหลือแค่ ~10 วินาทีหลัง SIGTERM ให้ container exit จริง ๆ
terminationGracePeriodSeconds: 0 อันตราย
หัวข้อที่มีชื่อว่า “terminationGracePeriodSeconds: 0 อันตราย”การตั้งเป็น 0 ไม่ได้ทำให้ container shutdown เร็วแบบสวยงาม มันข้าม sequence ข้างบนเกือบทั้งหมด Pod เดิม terminationGracePeriodSeconds: 0:
$ kubectl apply --dry-run=server -f term-demo-zero.yamlpod/term-demo-zero created (server dry run) # the API server accepts it -- no validation rejects grace period 0# container log2026-09-26T07:51:15.005753381Z main started2026-09-26T07:51:16.088548048Z preStop ran # preStop *started* and logged its first line...# ...and nothing else. No "SIGTERM received" line was ever logged.$ time kubectl delete pod term-demo-zeropod "term-demo-zero" deleted# real 0m0.055s# kubectl get events --field-selector involvedObject.name=term-demo-zeroLAST SEEN REASON MESSAGE3s Killing Stopping container main0s FailedPreStopHook PreStopHook faileddelete call return กลับมาใน 55 มิลลิวินาที preStop hook ถูก start ขึ้นมา log บรรทัดแรกของมันได้ แล้วก็ถูกฆ่ากลางคันก่อนที่ sleep 5 ของมันเองจะจบ event จริงของ kubelet คือ FailedPreStopHook: PreStopHook failed trap handler SIGTERM ของ container เองไม่เคยถูกรันเลย เพราะไม่มีเวลา grace เหลือให้ deliver แล้วรอเลย kubelet เลยไป SIGKILL ตรง ๆ terminationGracePeriodSeconds: 0 เหมาะกับ Pod ที่คุณมั่นใจว่าไม่มี state และไม่ต้องการ cleanup อะไรเลย (debug/test workload บางแบบ) มันไม่ใช่วิธีแก้ที่ถูกสำหรับ “rollout/delete ของฉันช้าไป” เพราะมันทิ้งทั้ง in-flight request draining, log flushing และ endpoint-removal grace window ที่พูดถึงข้างบนไปด้วย
native sidecar หยุดทีหลังสุด พิสูจน์แล้ว
หัวข้อที่มีชื่อว่า “native sidecar หยุดทีหลังสุด พิสูจน์แล้ว”ต่อยอดจากตัวอย่าง wait-for-db / log-shipper ก่อนหน้านี้ ตอน Pod ถูก delete kubelet จะหยุด container ธรรมดา (main) ก่อน ส่ง SIGTERM ให้ native sidecar (initContainers entry ที่มี restartPolicy: Always) ก็ต่อเมื่อ main container ทุกตัว exit ไปแล้วเท่านั้น แล้วค่อยเริ่มนับถอยหลังไปสู่การ SIGKILL sidecar ด้วย พิสูจน์ terminationGracePeriodSeconds: 10, main sleep 2 วินาทีหลัง SIGTERM ก่อน exit ส่วน sidecar แค่ trap แล้ว exit ทันที:
# container "main"2026-09-26T07:51:35.310165627Z [main] started2026-09-26T07:51:37.315952878Z [main] SIGTERM received, sleeping 2s2026-09-26T07:51:39.320772795Z [main] exiting
# container "log-shipper" (native sidecar, initContainers + restartPolicy: Always)2026-09-26T07:51:35.245861918Z [sidecar] started2026-09-26T07:51:40.267421296Z [sidecar] SIGTERM received, exiting$ kubectl get events --field-selector involvedObject.name=term-demo-sidecarLAST SEEN REASON MESSAGEKilling Stopping container log-shipperKilling Stopping container mainmain ได้รับ SIGTERM ตอน 07:51:37.315 และ exit เสร็จตอน 07:51:39.320; log-shipper ไม่ได้รับ SIGTERM ของตัวเองจนกระทั่ง 07:51:40.267 หลังจาก main หายไปเรียบร้อยแล้ว นี่คือกลไกเบื้องหลัง guarantee “log shipper stream บรรทัดสุดท้ายได้ครบ” จากก่อนหน้านี้ sidecar ถูกออกแบบมาให้เป็นสิ่งสุดท้ายที่ยังยืนอยู่โดยตั้งใจ
ทำไมถึงไม่รัน Pod เปล่า ๆ ตรง ๆ ในทางปฏิบัติ
หัวข้อที่มีชื่อว่า “ทำไมถึงไม่รัน Pod เปล่า ๆ ตรง ๆ ในทางปฏิบัติ”Pod ถูกออกแบบมาให้เป็น ephemeral และแทบไม่มีใคร create Pod ตรง ๆ ถ้า kubectl apply Pod เปล่า ๆ แล้ว node ที่ Pod นั้นอยู่ล่ม หรือ container ข้างในพัง เกินกว่าที่ restartPolicy จะช่วยได้ ไม่มีอะไรมา recreate Pod นั้นให้ คือหายไปเฉย ๆ ในทางปฏิบัติคุณแทบจะ create Deployment, StatefulSet หรือ DaemonSet แทนเสมอ controller เหล่านั้นถือ Pod template ไว้ คอย watch state จริงของ cluster และ recreate Pod ทุกครั้งที่ของจริงเบี่ยงไปจากจำนวนที่ต้องการ ส่วนที่เหลือของ module นี้คือ controller พวกนั้น
อ่านโค้ดที่ AI เขียน
หัวข้อที่มีชื่อว่า “อ่านโค้ดที่ AI เขียน”ตอน review Pod spec ที่ AI generate มา (หรือที่ฝังอยู่ใน template ของ Deployment/StatefulSet/DaemonSet) เช็คพวกนี้
- ตั้ง
terminationGracePeriodSeconds: 0“เพื่อให้ delete เร็วขึ้น” ไหม พิสูจน์แล้วข้างบนว่านี่ทำให้preStophook ถูกฆ่ากลางคัน (FailedPreStopHook) และไม่มีการ deliverSIGTERMเลย มันไม่ได้ทำให้ shutdown ปลอดภัยหรือสะอาดขึ้น - container ที่อยู่หลัง Service ไม่มี
preStopเลยไหม พิสูจน์แล้วข้างบนว่าEndpointSliceentry จะ not-ready ภายใน ~0.5 วินาทีหลังkubectl deleteแต่ถ้าไม่มีpreStopsleep ก็ไม่มีอะไรมาเชื่อมช่องว่างระหว่างที่การเอาออกนั้น propagate ผ่าน kube-proxy/Ingress/mesh dataplane เลย terminationGracePeriodSecondsตั้งไว้ต่ำกว่าเวลาที่ shutdown code ของแอปเองต้องใช้ (flush buffer, drain connection, ปิด DB pool) ไหม container จะโดนSIGKILLกลางคัน shutdown พิสูจน์แล้วข้างบนด้วย sleep 15 วินาทีของ main container ที่ถูกตัดที่ ~10 วินาที- “sidecar” ที่ตั้ง
restartPolicy: Alwaysเขียนเป็น entry ธรรมดาใต้containers:แทนที่จะเป็นinitContainers:ที่มีrestartPolicy: Alwaysไหม แบบนั้นมันคือ container ธรรมดาที่ไม่มี guarantee เรื่องลำดับ startup/shutdown เทียบกับ main container เลย ไม่ใช่ native sidecar - Pod สมมติว่า
restartPolicy: Alwaysrestart container ที่ exit0ด้วยไหม ใช่ จริง ๆAlwaysrestart ทุก exit ไม่ว่าจะสำเร็จหรือพัง ซึ่งถูกต้องสำหรับ service ที่รันระยะยาว แต่เป็นจุดสับสนที่พบบ่อยเวลาคนคาดว่า “restart แค่ตอน crash เท่านั้น”
🐛 หา bug ในโค้ดนี้
apiVersion: v1kind: Podmetadata:name: weblabels: app: webspec:terminationGracePeriodSeconds: 0containers: - name: web image: myregistry/web:1.4.0 ports: - containerPort: 8080เฉลย
ดูเหมือนเป็นการปรับแบบไม่มีพิษภัยเพื่อให้ “deploy/delete ไวขึ้น” แต่จริง ๆ คืออันตรายที่พิสูจน์ไว้ข้างบนเป๊ะ ๆ ด้วย terminationGracePeriodSeconds: 0, kubelet ไม่รอให้ preStop hook รันจบ (ตัวจริงบน cluster เดียวกันนี้ถูกตัดกลางคัน log event FailedPreStopHook: PreStopHook failed) และไม่ deliver SIGTERM เลย มันไป SIGKILL ตรง ๆ วัดได้ข้างบนแค่ 55 มิลลิวินาที จาก kubectl delete จน Pod หายไป สำหรับ Pod ที่ serve traffic จริงอยู่หลัง Service นั่นหมายความว่า in-flight request ถูกฆ่าทิ้งตรง ๆ แอปไม่มีโอกาส close connection แบบสวยงามเลย ซ้อนทับกับปัญหาการแข่งกันเรื่องเอา endpoint ออกที่พูดถึงข้างบนอีก วิธีแก้คือเอา field นี้ออกไปเลย (จะกลับไปใช้ default 30 วินาที) หรือใส่ค่าที่ตรงกับความจริง (terminationGracePeriodSeconds: 15) ให้พอดีกับเวลา shutdown จริงของแอป แล้วเพิ่ม preStop sleep ถ้าแอปอยู่หลัง Service
Mental Model
หัวข้อที่มีชื่อว่า “Mental Model”การ shutdown ของ Pod ไม่ใช่แค่ “kill process” มันคือ การส่งมอบงานแบบมี deadline cluster เริ่มเอา Pod ออกจาก service discovery และรันโค้ด cleanup ของคุณพร้อมกันในจังหวะเดียวกัน และมันจะบังคับจบ process ทันทีที่นาฬิกาที่คุณตั้ง (terminationGracePeriodSeconds) หมดเวลา ไม่ว่า cleanup ของคุณจะไปถึงไหนแล้วก็ตาม preStop กับ SIGTERM handler ของแอปเองไม่ใช่สอง phase อิสระที่มีงบของตัวเอง มันแชร์งบ เดียวกัน นับตั้งแต่วินาทีที่ kubectl delete รัน guarantee เรื่องลำดับของ native sidecar (start ก่อน หยุดทีหลัง) มีไว้เพื่อให้ support container (log shipper, proxy) ไม่มีวันถูกดึงออกจากใต้สิ่งที่มันกำลัง support อยู่
ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| ทางเลือก | ได้อะไร | เสียอะไร |
|---|---|---|
terminationGracePeriodSeconds นานขึ้น | แอปมีเวลาจริง ๆ ในการ drain connection/flush data (พิสูจน์ข้างบน: sleep 15 วินาทีของแอปถูกตัดก่อนจบที่งบ default) | kubectl delete/rollout ช้าลง; container ที่ hang จริง ๆ ก็ใช้เวลานานขึ้นกว่าจะถูกบังคับเอาออก |
preStop: sleep N | เชื่อมช่องว่างจริงระหว่าง “Pod ถูก mark ให้ delete” กับ “ทุก dataplane component หยุด route มาแล้ว” (พิสูจน์ข้างบน: endpoint ไปที่ not-ready ใน ~0.5 วินาที ก่อน preStop 5 วินาทีจะจบด้วยซ้ำ) | เพิ่ม N วินาทีให้ทุกการ terminate ของ Pod ไม่ว่าจะมี in-flight traffic ให้ drain จริงหรือไม่ |
terminationGracePeriodSeconds: 0 | เอา Pod ออกได้เร็วระดับเสี้ยววินาที (พิสูจน์ข้างบน: 55ms) | ไม่มี preStop จบ ไม่มี SIGTERM deliver ไม่มี graceful shutdown เลย พิสูจน์ข้างบนด้วย event FailedPreStopHook จริง |
native sidecar (initContainers + restartPolicy: Always) | รับประกันลำดับหยุดทีหลังสุด (พิสูจน์ข้างบน) สำหรับ log shipper/proxy ที่ต้องอยู่นานกว่า main container | container เพิ่มอีกตัวที่กินทรัพยากรและต้องดูแลให้ healthy ตลอดอายุ Pod |
ความเข้าใจผิดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ความเข้าใจผิดที่พบบ่อย”- “preStop กับ SIGTERM handler ของแอปได้ terminationGracePeriodSeconds เต็ม ๆ คนละชุด” พิสูจน์แล้วข้างบน มันแชร์งบเดียวกัน
preStop5 วินาทีเทียบกับ grace period 15 วินาที เหลือแค่ ~10 วินาทีให้แอป shutdown แบบสวยงามหลังSIGTERM - “Pod หยุดรับ traffic ก็ต่อเมื่อ container exit จริง ๆ แล้ว” พิสูจน์แล้วข้างบน
EndpointSliceentry ไปที่ not-ready ~0.5 วินาทีหลังkubectl deleteก่อน container จะได้รับSIGTERMตั้งหลายวินาที การเอา readiness ออกวิ่งแซงหน้า lifecycle ของ container มันไม่รอ - “terminationGracePeriodSeconds: 0 แค่ข้ามการรอ ขั้นตอน shutdown ยังรันอยู่ แค่เร็วขึ้น” พิสูจน์แล้วข้างบนด้วย event
FailedPreStopHookจริง preStop hook ถูกฆ่ากลางคันและSIGTERMไม่เคยถูก deliver เลย มันไม่ใช่ graceful shutdown แบบเร็ว มันคือไม่มี graceful shutdown เลย - “native sidecar กับ main container หยุดพร้อมกันเพราะอยู่ Pod เดียวกัน” พิสูจน์แล้วข้างบนด้วย timestamp จริงของแต่ละ container sidecar ได้
SIGTERMช้ากว่า main container exit ไปแล้วเต็ม ๆ หนึ่งวินาที