Probe: Liveness, Readiness, และ Startup
ไอเดียในหนึ่งประโยค
หัวข้อที่มีชื่อว่า “ไอเดียในหนึ่งประโยค”probe สามแบบถามคำถามคนละแบบ คือยังทำงานอยู่ไหม พร้อมรับ traffic ตอนนี้ไหม กับ start เสร็จหรือยัง แล้ว Kubernetes ก็ตอบสนองต่อแต่ละคำตอบไม่เหมือนกัน
Liveness vs Readiness: restart กับ remove-from-Service
หัวข้อที่มีชื่อว่า “Liveness vs Readiness: restart กับ remove-from-Service”Liveness probe ตอบคำถามว่า “container ตัวนี้ยังทำงานอยู่ไหม” ถ้า fail ติดกันจนครบจำนวนที่กำหนด kubelet จะฆ่า container แล้วให้ restartPolicy เป็นตัวตัดสินว่าจะทำอะไรต่อ (ปกติคือ restart ให้ใหม่) probe แบบนี้เหมาะกับ container ที่ยังรันอยู่แต่ค้าง เช่น deadlock หรือขยับต่อไม่ได้ ซึ่ง restart คือทางแก้
Readiness probe ตอบคำถามคนละเรื่องเลย คือ “container ตัวนี้พร้อมรับ traffic ตอนนี้ ไหม” ถ้า readiness probe fail จะ ไม่ restart อะไรทั้งนั้น สิ่งที่เกิดขึ้นคือ Pod ถูกเอาออกจาก Endpoints/EndpointSlice ของ Service ทำให้ Service หยุดส่ง traffic มาที่ Pod นั้น container ยังรันต่อไปตามปกติ ไม่ถูกแตะต้อง แล้วพอ probe กลับมาผ่านอีกครั้ง Pod ก็ถูกเพิ่มกลับเข้าไปใหม่
ความต่างตรงนี้แหละที่ทำให้ rolling update กับแอปที่ warm-up ช้าทำงานได้ถูกต้อง
apiVersion: v1kind: Podmetadata: name: web labels: app: webspec: containers: - name: web image: web-app:1.4.0 ports: - containerPort: 8080 livenessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 10 periodSeconds: 5 timeoutSeconds: 3 failureThreshold: 3 readinessProbe: httpGet: path: /ready port: 8080 periodSeconds: 5 failureThreshold: 1ความผิดพลาดที่เจอบ่อยในโลกจริงคือการไม่ใส่ readiness probe เลย พอไม่มี readiness probe Kubernetes จะถือว่า container พร้อมทันทีที่ start ขึ้นมา ทั้งที่ตัวแอปข้างในอาจจะยังโหลด config อยู่ warm cache อยู่ หรือกำลังเปิด connection กับ database ระหว่าง rollout แบบนี้ traffic จริงจะถูกส่งตรงเข้าไปยัง container ที่ยังรับมือไม่ไหว ที่เป็นสาเหตุที่พบบ่อยของ error ที่พุ่งขึ้นทันทีหลัง deploy ทุกครั้ง
Startup probe: ให้เวลา container ที่ start ช้า
หัวข้อที่มีชื่อว่า “Startup probe: ให้เวลา container ที่ start ช้า”container บางตัว เช่นแอป JVM เก่า หรือตัวที่ทำ migration หนัก ๆ หรือ preload cache ตอน boot อาจใช้เวลาหลายนาทีกว่าจะตอบสนองได้ การตั้ง initialDelaySeconds สั้น ๆ บน liveness probe ไม่พอ แต่ถ้าตั้งยาวไปก็ทำให้ตรวจจับปัญหาค้างจริง ๆ หลัง app ขึ้นมาแล้วช้าตามไปด้วย
Startup probe แก้ปัญหานี้ ตราบใดที่ startup probe ยังไม่ผ่าน liveness กับ readiness probe จะถูกปิดไปเลย container ที่ boot ช้าจึงไม่โดน liveness probe ที่ใจร้อนฆ่าทิ้งก่อนเวลา พอ startup probe ผ่านครั้งเดียว probe ตัวนี้ก็หยุดรันถาวร แล้วให้ liveness/readiness ทำงานแทนต่อไปตลอดอายุ container
startupProbe: httpGet: path: /healthz port: 8080 failureThreshold: 30 periodSeconds: 10ด้วย failureThreshold: 30 และ periodSeconds: 10 container ตัวนี้มีเวลาสูงสุด 300 วินาทีให้กลายเป็น healthy ก่อนที่ kubelet จะยอมแพ้แล้ว restart container
# Watch probe-driven restarts and readiness statekubectl get pods -wkubectl describe pod web | grep -A5 -E 'Liveness|Readiness|Startup'กลไกการเช็ค probe
หัวข้อที่มีชื่อว่า “กลไกการเช็ค probe”probe ทุกแบบ (liveness, readiness, startup) เลือกใช้กลไกการเช็คแบบใดแบบหนึ่งได้ดังนี้
httpGetยิง HTTP GET ไป status code ตั้งแต่ 200 จนถึงก่อน 400 ถือว่าผ่านtcpSocketพยายามเปิด TCP connection ไปยัง port ที่กำหนด เชื่อมต่อสำเร็จถือว่าผ่านexecรันคำสั่งข้างใน container exit code0ถือว่าผ่านgrpcเรียก gRPC health-checking service ของ container (container ต้อง implement gRPC health protocol มาตรฐานเอง)
readinessProbe: grpc: port: 5000field สำหรับปรับจูนใช้ได้กับทุกกลไก คือ initialDelaySeconds (รอก่อนเช็คครั้งแรก) periodSeconds (เช็คถี่แค่ไหน) timeoutSeconds (รอ response นานแค่ไหน) failureThreshold (fail ติดกันกี่ครั้งถึงจะสั่งการ) และ successThreshold (สำเร็จติดกันกี่ครั้งถึงจะกลับมา healthy ซึ่งของ liveness กับ startup ต้องเป็น 1 เท่านั้น)
flowchart LR
boot["Container starts"] --> sp{"startupProbe\nsucceeded?"}
sp -- "no, keep trying" --> sp
sp -- "yes" --> live["livenessProbe\nruns continuously"]
sp -- "yes" --> ready["readinessProbe\nruns continuously"]
live -- "fails" --> restart["kubelet restarts container"]
ready -- "fails" --> remove["Pod removed from Service Endpoints"]
ready -- "succeeds again" --> add["Pod added back to Endpoints"]