ข้ามไปยังเนื้อหา

Pod: หน่วยที่เล็กที่สุดใน Kubernetes

Pod คือหน่วยที่เล็กที่สุดที่ deploy ได้ใน Kubernetes container หนึ่งตัวหรือมากกว่านั้นที่รันอยู่บน node เดียวกันเสมอ แชร์ network namespace เดียวกัน และแชร์ volume ได้ด้วยถ้าต้องการ

container ทุกตัวใน Pod เดียวกันได้ IP address เดียวกันและ network namespace เดียวกัน หมายความว่า container สองตัวใน Pod เดียวกันคุยกันผ่าน localhost ได้เลย ที่ port ไหนก็ได้ที่อีกตัวเปิดรอ ไม่ต้องผ่าน networking ของ cluster เลย container ใน Pod เดียวกันยังแชร์ volume เดียวกันได้ด้วย นั่นคือวิธีที่ sidecar อ่านไฟล์ที่ container หลักเขียนไว้

Terminal window
# From inside the "app" container, a sidecar in the same Pod is reachable on localhost
kubectl 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 ได้เท่านั้น

สอง pattern นี้ครอบคลุมเกือบทุกเหตุผลที่จะใส่ container มากกว่าหนึ่งตัวใน Pod เดียว

  • Sidecar container ผู้ช่วยที่รันคู่กับ container หลักตลอดอายุของ Pod เช่น log shipper ที่ tail log file ของ container หลักแล้วส่งต่อ หรือ proxy ที่จัดการ TLS termination
  • Init container container ที่รัน จนจบ ตามลำดับ ก่อนที่ container หลักตัวไหนจะเริ่มทำงาน ใช้บ่อยเพื่อรอให้ dependency พร้อมใช้งาน หรือรัน migration ครั้งเดียวก่อน container หลักจะบูต
apiVersion: v1
kind: Pod
metadata:
name: app-with-sidecar
spec:
restartPolicy: Always
initContainers:
- name: wait-for-db
image: busybox:1.36
command: ['sh', '-c', 'until nc -z db 5432; do sleep 1; done']
containers:
- name: app
image: myregistry/app:1.4.0
ports:
- containerPort: 8080
- name: log-shipper
image: myregistry/log-shipper:2.1.0
volumeMounts:
- name: logs
mountPath: /var/log/app
volumes:
- name: logs
emptyDir: {}

wait-for-db จะบล็อกไม่ให้ Pod เริ่มทำงานจนกว่าจะเชื่อมต่อ database ได้ แล้ว exit สำเร็จ จากนั้น app กับ log-shipper ถึงจะเริ่ม โดยแชร์ network namespace และ volume logs ของ Pod ร่วมกัน

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
One Pod: an init container runs first, then the main container and a sidecar share one network namespace

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 แบบครั้งเดียว)

Terminal window
# Watch a Pod move through its phases
kubectl get pod app-with-sidecar --watch
# Container-level detail: state (Waiting/Running/Terminated), restart count, exit code
kubectl describe pod app-with-sidecar

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 พวกนั้น

container ทุกตัวใน Pod เดียวกันแชร์อะไรกันโดย default
พฤติกรรมหลักของ init container คืออะไร
Pod เปล่า ๆ (create ตรง ๆ ไม่มี controller) container ตัวเดียวข้างในพังซ้ำ ๆ แล้ว node ของตัวเองก็ล่มไปเลย เกิดอะไรขึ้นกับ Pod
restartPolicy ของ Pod รับค่าอะไรได้บ้าง