ต้องรอนานแค่ไหน Pod ถึงจะถูกลบ

ช่วงเวลาเพียงเสี้ยววินาทีที่ pod ไม่สามารถใช้งานได้ แต่ผู้ใช้ยังพยายามที่จะติดต่อเข้ามายัง application ใน pod นั้น ก็อาจจะส่งผลกระทบในมุมลบในการให้บริการได้
ต้องรอนานแค่ไหน Pod ถึงจะถูกลบ

การที่ Pod ถูกลบออกจาก Kubernetes Cluster เกิดขึ้นในหลากหลายสถานกาณ์ ทั้งที่ผู้ใช้ลบเอง, ในขณะที่ kubectl drain เพื่อเหตุผลบางอย่างเช่นต้องการ update หรือในขั้นตอนการทำ RollingUpdate ซึ่งอาจดูเป็นเรื่องไม่สำคัญ และไม่มีผลกระทบอะไรมากนัก
แต่ในระบบขนาดใหญ่ที่มี node จำนวนมาก ต้องรองรับผู้ใช้งานปริมาณมหาศาล และยังต้องจัดการให้ user experience อยู่ในระดับดีมาก ช่วงเวลาเพียงเสี้ยววินาทีที่ pod ไม่สามารถใช้งานได้ แต่ผู้ใช้ยังพยายามที่จะติดต่อเข้ามายัง application ใน pod นั้น ก็อาจจะส่งผลกระทบในมุมลบในการให้บริการได้ การเข้าใจในกลไกในช่วงขณะที่ pod กำลังจะถูกลบ (Termination of Pods) อ่านเพิ่มเติมได้จาก Kubernetes ทำ Zero Downtime Deployment ให้เราจริงเหรอ?

สมมุติว่า อ่านจบแล้ว … จะเห็นได้ว่า การเข้าใจกลไกในขั้นตอน Termination of Pods สามารถแก้ไข error ที่ผู้ใช้จะได้รับได้

ผมมีปัญหาอะไร ผมสงสัยอะไร!!!

ผมสงสัยว่า !!!

เวลาที่ kubernetes เริ่มนับที่เวลาที่รอการหยุดการทำงานที่เรียกว่า terminiation grace period จากตรงไหนกันแน่ เอกสารส่วนใหญ่ถูกเขียนไว้ว่า เริ่มนับตั้งแต่ในขั้นตอน preStop … แต่ตอนที่ผมทำการทดลอง พบว่ามันไม่ได้เป็นไปตามเอกสารที่เขียนผม … ผมทำผิดตรงไหน ผมเข้าใจอะไรผิด ขึ้นตอนการทดสอบผมผิดไหม ผลการทดสอบของผม ถึงออกมาเป็นอย่างนี้ … เรื่องราวในการทดสอบของผมทั้งหมด ก็จะประมาณนี้ครับ

ทดสอบด้วย Kubernetes v1.25.5

d8k-a2m2 > preStop kubectl get nodes
NAME             STATUS   ROLES           AGE     VERSION
cluster4-cp0     Ready    control-plane   6m27s   v1.25.5
cluster4-cp1     Ready    control-plane   4m16s   v1.25.5
cluster4-cp2     Ready    control-plane   4m17s   v1.25.5
cluster4-node0   Ready    <none>          6m5s    v1.25.5
cluster4-node1   Ready    <none>          6m5s    v1.25.5

Pod Spec ที่ใช้ในการทดสอบ

pod ที่ใช้ในการทดสอบ ถูกเรียกจาก image ที่ชื่อว่า damrongsak/d8k-termloop  ซึ่งในนั้นจะเป็นโปรแกรมที่เขียนด้วย python ที่ไม่ได้ทำงานใด ๆ แต่ถ้าได้รับ SIGTERM จะไม่หยุดการทำงาน แต่จะแสดงข้อความว่า “SIGTERM cannot kill me !!!” ออกมาทุก ๆ 1 วินาที ส่วน preStop.sh เป็น shell script  ที่จะแสดงข้อความทุก ๆ 1 วินาที เป็นระยะเวลา 20 วินาที แล้วจึงหยุดทำงาน โดยที่กำหนดให้ terminationGracePeriodSeconds มีค่าเป็น 24 วินาที

apiVersion: v1
kind: Pod
metadata:
  name: abc
spec:
  terminationGracePeriodSeconds: 24
  containers:
  - image: damrongsak/d8k-termloop
    name: abc
    lifecycle:
      preStop:
        exec:
          command: ["./preStop.sh"]

ผลการทดสอบ Pod Termination วิเคราะห์ผลการทดสอบ

จากผลการทดสอบ เห็นว่าเมื่อส่งคำสั่ง kubectl delete pod ก็เข้าสู่ขั้นตอน termination จากนั้น .spec.containers[].lifecycle.preStop  ก็เริ่มทำงานทันที เป็นระยะเวลา 20 วินาที หลังจากที่ preStop ทำงานเสร็จ  SIGTERM ก็จะถูกส่งไป application ที่อยู่ใน container ใน pod แต่ก็จะเห็นว่า application ยังทำงานอยู่อีกเป็นเวลา 24 วินาที จึงหยุดทำงานจาก SIGKILL สังเกตุได้ว่า เวลาที่ application ยังทำงานอยู่หลังจากที่ได้รับ SIGTERM เป็นเวลาเท่ากับค่าที่กำหนดให้กับ termination grace period สรูปให้เข้าใจให้จำได้ง่าย ๆ ก็คือ termination grace period จะเริ่มนับตอนที่ preStop ทำงานจบ (แอบสปอยล์ไว้นิดหนึ่ง อย่าเพิ่งด่วนสรูปว่ามันจะเป็นอย่างนี้เสมอ ตอนท้ายมีการทดสอบไปรูปแบบอื่นอีก)

Pod Termination


เริ่มต้นหาคำตอบ

เกิดอะไรขึ้น … ทำไมผลการทดสอบไม่เป็นไปตามทฤษฎี … ผมก็เริ่มทักน้องที่เขียนบทความเรื่องนั้น ทักไปยังเพื่อนพี่น้องที่เราคิดว่าคุ้นเคยกับ Kubernetes รวมถึงโพสต์ใน facebook ของตัวเอง … วันนั้นเป็นวันที่สนุกมาก ได้พูดคุยกับหลาย ๆ คน หลาย ๆ session แลกเปลี่ยนความคิดเห็นกันว่า ทำไมผลการทดสอบมันถึงออกมาเป็นอย่างนี้ … ระหว่างการพูดคุย น้องโจโจ้ จากเพจ Jumpbox ทักขึ้นมาว่า “พี่ทดสอบด้วย Kubernetes version อะไรเหรอครับ” เฮ้ย …. !!!! …. เราลืมนึกถึงตัวแปรเรื่องนี้ ก็เลยบอกน้องโจโจ้ไปว่า ขอเวลาพี่ 10 นาที ก็รีบไปสร้าง Kubernetes v1.19.xx ขี้นมาทดสอบ

ผลการทดสอบจาก Kubernetes v1.19.16

Pod Termination

ปรากฎว่า ผลที่ได้ต่างจากสิ่งที่ผมทดสอบ แต่ว่าเป็นไปตามเอกสารที่มีอยู่ทั่วไป เมื่อส่งคำสั่ง kubectl delete pod ก็เข้าสู่ขั้นตอน termination จากนั้น .spec.containers[].lifecycle.preStop  ก็เริ่มทำงานทันที เป็นระยะเวลา 20 วินาที หลังจากที่ preStop ทำงานเสร็จ  SIGTERM ก็จะถูกส่งไป application ที่อยู่ใน container ใน pod แต่ก็จะเห็นว่า application ยังทำงานอยู่อีกเพียง 4 วินาที จึงหยุดทำงานจาก SIGKILL สังเกตุได้ว่า เวลาที่ preStop ทำงาน รวมกัน เวลา application ยังทำงานอยู่หลังจากที่ได้รับ SIGTERM เป็นเวลาเท่ากับค่าที่กำหนดให้กับ termination grace period สรูปให้เข้าใจให้จำได้ง่าย ๆ ก็คือ termination grace period จะเริ่มนับตอนที่ preStop เริ่มทำงาน

Pod Termination


ความสงสัยยังไม่จบ

“แล้ววิธีการเริ่มจับเวลา termination grace period ที่ไม่เหมือนเดิม มันเริ่มเปลี่ยนแปลงมาตั้งแต่ Kubernetes version ไหน” … ผมก็เลยเริ่มค่อย ๆ ทดสอบ Kubernetes ทีละ version

Kubernetes VersionWhen start termination grace period
v1.19preStop เริ่มทำงาน
v1.20preStop เริ่มทำงาน
v1.21preStop เริ่มทำงาน
v1.22preStop ทำงานจบ
v1.23preStop ทำงานจบ
v1.24preStop ทำงานจบ
v1.25preStop ทำงานจบ

ความเปลี่ยนแปลงในการเริ่มนับ termination grace period เกิดขึ้นที่ v1.22


ถ้า ถ้า ถ้า ???

ถ้า preStop ทำงานนานมาก ๆ นานกว่า termination grace period จะเกิดอะไรเกิดขึ้น … จัดไปครับ ผมทำการทดสอบให้ 3 กรณี

  1. กรณีปกติ preStop ทำงานเสร็จ ใช้เวลาน้อยกว่า termination grace period [ t(preStop) < terminationGracePeriodSeconds ]
  2. กรณี preStop ทำงานเสร็จ ใช้เวลามากกว่า termination grace period แต่ น้อยกว่า 2 เท่าของ termination grace period [ t(preStop) < 2*terminationGracePeriodSeconds ]
  3. กรณี preStop ทำงานเสร็จ ใช้เวลามากกว่า termination grace period และ มากกว่า 2 เท่าของ termination grace period [ t(preStop) > terminationGracePeriodSeconds ]

ผลก็เป็นประมาณนี้ครับ

Pod Termination

ประเด็นที่น่ากังวล คือเราตั้ง termination grace period สั้นมาก ๆ อาจจะทำให้ SIGKILL ถูกส่งออกมาก่อนที่ preStop จะทำงานเสร็จ อาจจะส่งผลกระทบจากที่คาดหวังไว้ก็ได้

Made by มูลค่าความสุข