Kubernetes Interview Prep — คำถามที่พบบ่อย
1. Pod, Deployment, ReplicaSet สัมพันธ์กันยังไง อธิบายด้วยตัวอย่าง
Deployment เป็น object ระดับสูงสุดที่ user สั่งงานตรงๆ (บอก desired image/replicas) — Deployment สร้างและคุม ReplicaSet (ซึ่งมีหน้าที่แค่รักษาจำนวน Pod ให้ตรง desired count) — ReplicaSet สร้าง/ลบ Pod ตามจำนวนที่ต้องการ เมื่อ update image, Deployment สร้าง ReplicaSet ใหม่และค่อยๆ สลับ Pod จาก ReplicaSet เก่าไปใหม่ (rolling update) โดย ReplicaSet เก่ายังถูก เก็บไว้ (scale เป็น 0) เพื่อให้ rollback ได้ทันที
จำสายการบังคับบัญชา "Deployment สั่ง → ReplicaSet นับ → Pod เกิด" — Beyond: วิธีพิสูจน์
ความสัมพันธ์นี้ให้เห็นจริงในหน้าสัมภาษณ์คือสั่ง kubectl get rs แล้วดู column OWNER REFERENCES หรือ kubectl describe pod <name> แล้วดู field ownerReferences ที่ชี้กลับไปยัง
ReplicaSet (ซึ่งมี ownerReferences ชี้กลับไปยัง Deployment อีกที) — Kubernetes ใช้กลไกนี้
(owner reference) เป็น garbage collection chain: ถ้าลบ Deployment ทิ้ง ReplicaSet และ Pod
ทั้งหมดที่ชี้กลับมาจะถูกลบตามแบบ cascade โดยอัตโนมัติ เป็นกลไกเดียวกับที่ kubectl delete deployment my-app ไม่ทิ้ง Pod ค้างไว้ให้ต้องไปตามลบเอง
2. ถ้า Pod อยู่ในสถานะ CrashLoopBackOff จะ debug ยังไง
ลำดับการ debug ที่ควรพูดถึง:
kubectl describe pod <pod-name> # ดู Events ล่าสุด เช่น OOMKilled, probe failed
kubectl logs <pod-name> # ดู log รอบล่าสุด
kubectl logs <pod-name> --previous # ดู log ของ container ที่ crash ไปแล้ว (สำคัญมาก!)
สาเหตุที่พบบ่อย: application error ตอน startup, config/secret ที่ขาดหาย, liveness probe
fail เร็วเกินไปก่อนแอป boot เสร็จ (แก้ด้วย startupProbe), resource limit ต่ำเกินจน OOMKilled
จำว่า "BackOff ไม่ใช่ error มันคือการลงโทษที่เพิ่มขึ้นเรื่อยๆ" — Beyond: "CrashLoopBack
Off" ไม่ใช่แค่ชื่อเล่น มันคือพฤติกรรมจริงของ kubelet: หลัง container crash kubelet จะรอ
exponential backoff ก่อน restart ครั้งถัดไป (10s → 20s → 40s → ... สูงสุด 5 นาที) ไม่ใช่
retry รัวๆ ทันที ดังนั้นถ้าเพิ่งแก้บั๊กแล้ว apply manifest ใหม่ Pod อาจยังค้างรอ backoff timer
เดิมอยู่ (ไม่ restart ทันที) วิธีเร่งให้ restart ทันทีคือ kubectl delete pod <name> (ให้
controller สร้าง Pod ใหม่ที่ backoff counter เริ่มนับใหม่) แทนที่จะรอ kubelet เอง
3. kubectl get pods ขึ้น Pending ตลอด แปลว่าอะไร
แปลว่า scheduler ยังหา Node ที่เหมาะสมไม่ได้ — สาเหตุที่พบบ่อย: cluster ไม่มี Node ที่มี
resource เหลือพอตาม requests ที่ขอ, มี nodeSelector/affinity ที่ไม่มี Node ไหน match,
หรือ taint บน Node ที่ Pod ไม่มี toleration รองรับ ใช้
kubectl describe pod <name> ดู Events ส่วน FailedScheduling เพื่อดูเหตุผลที่ชัดเจน
จำว่า "Pending = ยังไม่มีใครรับเป็นเจ้าของ" (ไม่มี Node ไหนถูก assign ให้ Pod นี้เลย) —
Beyond: ความแตกต่างระหว่าง nodeSelector, nodeAffinity, และ taint/toleration ที่คนมักสับสน
คือ ทิศทางของการ "เลือก": nodeSelector/nodeAffinity คือ Pod เลือก Node ("ฉันอยากไปลง
Node ที่มี label นี้") ส่วน taint/toleration คือ Node ปฏิเสธ Pod ("Node นี้ไม่รับใครที่ไม่มี
toleration นี้") สองกลไกนี้ ทำงานคนละทิศทางและใช้ร่วมกันได้พร้อมกัน — Node ที่เป็น GPU
instance มักใช้ taint กันไม่ให้ workload ทั่วไปหลงไปลง (ประหยัด GPU node ไว้) แล้วให้ Pod ที่
ต้องการ GPU จริงๆ มี both toleration (ผ่านด่าน taint) และ nodeAffinity (เลือกไปลง Node ที่มี
GPU label) พร้อมกันทั้งคู่
4. ทำไม Service หา Pod ไม่เจอ (ไม่มี Endpoint)
kubectl get endpoints <service-name>
ถ้าว่างเปล่า มักเกิดจาก: label บน Pod ไม่ match กับ selector ของ Service, หรือ Pod ยังไม่
Ready (readinessProbe ไม่ผ่าน — Service ไม่ route ไป Pod ที่ไม่ Ready โดยตั้งใจ)
จำว่า "Endpoints ว่าง = ไม่มีใคร 'ยกมือ' ว่าพร้อม" — Beyond: Endpoints object (เอกพจน์
ไม่มี s ต่อท้ายใน API เก่า) เป็น object ที่ Kubernetes สร้างและอัปเดตให้อัตโนมัติ คุณไม่ต้อง
สร้างเองยกเว้นต้องการ route ไปยัง IP ภายนอก cluster (เช่นทำ Service ชี้ไปยัง database
on-premise ที่อยู่นอก cluster) โดยสร้าง Service ที่ไม่มี selector แล้วสร้าง Endpoints
object เองเพื่อชี้ IP ไปตรงๆ — เป็นเทคนิคที่ใช้เชื่อมระบบ legacy เข้ากับ cluster โดยไม่ต้อง
ย้ายทุกอย่างเข้า Kubernetes พร้อมกัน
5. etcd คืออะไร และสำคัญยังไง
etcd คือ distributed key-value store ที่เก็บ source of truth ทั้งหมดของ cluster
(ทุก object: Pod, Deployment, Secret ฯลฯ ถูกเก็บที่นี่) kube-apiserver เป็นตัวกลางเดียวที่
อ่าน/เขียน etcd โดยตรง ถ้า etcd ล่ม cluster ยังรัน workload เดิมต่อได้ชั่วคราว แต่
ไม่สามารถ schedule หรือเปลี่ยนแปลงอะไรใหม่ได้เลย จนกว่า etcd จะกลับมา — เป็นเหตุผลว่าทำไม
etcd backup/HA (มักรัน 3 หรือ 5 node แบบ Raft consensus) ถึงสำคัญมาก
จำว่า "etcd ตาย = cluster แช่แข็ง ไม่ใช่ cluster ดับ" (workload ที่รันอยู่ยังรันต่อได้ แค่สั่งอะไรใหม่ไม่ได้) — Beyond: เหตุผลที่ etcd นิยมรัน 3 หรือ 5 node (ไม่ใช่เลขคู่อย่าง 2 หรือ 4) เพราะ Raft consensus ต้องการ majority quorum ถึงจะเขียนข้อมูลได้ — cluster 3 node ทนได้ 1 node ตาย (ยังมี quorum 2/3), cluster 5 node ทนได้ 2 node ตาย (quorum 3/5) แต่ cluster 4 node ทนได้แค่ 1 node ตายเหมือนกับ 3 node (quorum ต้องการ 3/4) ใช้ hardware เพิ่มขึ้นโดยไม่ ได้ fault tolerance เพิ่มขึ้นเลย — นี่คือเหตุผลทางคณิตศาสตร์ล้วนๆ ที่ทำให้ไม่มีใครรัน etcd เลขคู่ใน production จริง
6. Horizontal Pod Autoscaler ทำงานยังไง และต้องมีอะไรก่อน
HPA polling metric (ผ่าน metrics-server) เป็นระยะ เทียบกับ target ที่ตั้งไว้ (เช่น CPU 70%)
แล้วคำนวณ replica ใหม่ด้วยสูตรประมาณ:
desiredReplicas = ceil(currentReplicas × currentMetricValue / targetMetricValue)
ต้องมี resources.requests ตั้งไว้ก่อนเสมอ เพราะ % utilization คำนวณเทียบกับค่า request
และต้องมี metrics-server รันอยู่ใน cluster ถึงจะมี metric ให้ HPA อ่าน
จำสูตรย่อ "replica ใหม่ = replica เก่า × (ของจริง ÷ เป้าหมาย)" ปัดขึ้นเสมอ — Beyond:
metrics-server เก็บ metric จาก cgroup ของ container บน Node โดยตรง (ไม่ใช่จาก
Prometheus หรือ monitoring stack ใดๆ) และเก็บแค่ in-memory ล่าสุดเท่านั้น ไม่มี
historical data (ถาม kubectl top pod ย้อนหลังไม่ได้) ถ้าต้องการ scale ตาม custom metric
ที่ไม่ใช่ CPU/memory (เช่น queue length, request latency) ต้องติดตั้ง Prometheus Adapter
หรือ metric adapter อื่นแยกต่างหาก เพื่อแปลง metric จาก Prometheus ให้ HPA (ผ่าน
external.metrics.k8s.io API) อ่านเข้าใจได้ — metrics-server เพียงอย่างเดียวตอบได้แค่
CPU/memory เท่านั้น
7. ความแตกต่างระหว่าง kubectl apply กับ kubectl create
create คือ imperative — สร้าง object ใหม่ตรงๆ ถ้ามีอยู่แล้วจะ error
apply คือ declarative — เทียบ YAML ที่ส่งเข้ามากับ state ปัจจุบัน แล้ว patch เฉพาะส่วนต่าง
ใช้ซ้ำได้ (idempotent) เหมาะกับ GitOps/CI-CD ที่ apply ไฟล์เดิมซ้ำๆ ได้โดยไม่ error
จำว่า "create = สร้างใหม่เป๊ะๆ ครั้งเดียว, apply = ปรับให้ตรงเป้าซ้ำได้เรื่อยๆ" —
Beyond: kubectl apply เก็บ "ประวัติ" ของ config ที่เคย apply ไว้ล่าสุดใน annotation
kubectl.kubernetes.io/last-applied-configuration (เป็น JSON ฝังอยู่ใน object เอง) ใช้คำนวณ
3-way merge patch (เทียบ 3 เวอร์ชัน: last-applied, current live state, และ new config
ที่กำลัง apply) เพื่อรู้ว่า field ไหนที่ "คุณตั้งใจลบออก" ต่างจาก field ที่ "คนอื่นเพิ่มเข้ามา
ทีหลังโดยที่ YAML ของคุณไม่รู้จัก" (เช่น field ที่ admission webhook หรือ controller อื่นเติม
ให้อัตโนมัติ) — field หลังนี้จะไม่ถูกลบแม้จะไม่อยู่ใน YAML ที่ apply เพราะ 3-way merge
ฉลาดพอจะรู้ว่ามันไม่ใช่ field ที่คุณเคยตั้งใจตั้งค่าไว้ตั้งแต่แรก — เป็นกลไกที่ซับซ้อนกว่าที่
คนทั่วไป (ที่คิดว่า apply แค่ "เขียนทับ") จะรู้มาก
8. อธิบาย Rolling Update ว่าทำไมถึง zero-downtime ได้
Rolling update สร้าง Pod ใหม่ก่อน (ตาม maxSurge) แล้วรอให้ readinessProbe ผ่านก่อนถึงจะเริ่ม
route traffic ไปหา จากนั้นค่อยลบ Pod เก่าทีละตัว (ตาม maxUnavailable) — ถ้า readinessProbe
ของ Pod ใหม่ไม่ผ่าน Deployment จะหยุด rollout อัตโนมัติ ไม่ลบ Pod เก่าต่อ ป้องกันการ deploy
version ที่พังไปทับของเดิมจนหมด
จำว่า "สร้างใหม่ก่อน เช็คพร้อมก่อน ค่อยฆ่าเก่า" เป็นลำดับตายตัว — Beyond: "zero-downtime"
ที่แท้จริงต้องพ่วงกับ preStop hook + terminationGracePeriodSeconds ด้วย ไม่ใช่แค่
readiness เท่านั้น เพราะตอน Pod เก่าถูกสั่งลบ มันมี race condition ธรรมชาติ: kube-proxy
อาจยังไม่ทันอัปเดต routing rule ให้หยุดส่ง traffic ไปที่ Pod ที่กำลังจะตาย ในขณะที่ kubelet
เริ่มส่ง SIGTERM ให้ container ไปแล้ว ทำให้ request ที่เพิ่งถูกส่งเข้ามาอาจตกหล่น วิธีแก้คือ
ใส่ preStop hook ที่ sleep สักครู่ (เช่น 5-10 วินาที) ก่อนให้ container จัดการ
SIGTERM จริง เพื่อให้เวลา routing rule อัปเดตตามทันก่อน — รายละเอียดนี้คือสิ่งที่แยกทีมที่
เจอ "sporadic 502 ตอน deploy" ออกจากทีมที่ deploy ได้ zero-downtime จริงๆ
Common Kubernetes interview themes: the Deployment → ReplicaSet → Pod ownership chain (traceable
via ownerReferences, which also drives cascading garbage collection); debugging
CrashLoopBackOff (note the exponential backoff timer — kubectl delete pod resets it) and
Pending pods (nodeSelector/nodeAffinity is the Pod choosing a Node; taints/tolerations is
the Node rejecting Pods — opposite directions, often combined for GPU nodes); missing Service
endpoints (and the selector-less Service + manual Endpoints pattern for external services); etcd
as the cluster's source of truth, and why odd-numbered clusters (3 or 5) are standard for Raft
quorum math; HPA's dependency on resources.requests and metrics-server (which only covers
CPU/memory — custom metrics need a Prometheus Adapter); apply's three-way merge using the
last-applied-configuration annotation to safely preserve fields added by other controllers;
and why true zero-downtime rolling updates also need a preStop hook to cover the routing-
update race condition, not just readiness probes alone.