เมื่อคืนไม่ได้โดนปลุกนะ แต่ลองนึกย้อนดูว่าครั้งล่าสุดที่โดน ใช้เวลาไปกับอะไรมากที่สุด คำตอบมันน่าอายนิดนึง ไม่ใช่การแก้ปัญหา แต่เป็นการนั่งนึกว่า "jsonpath ของ lastState มันเขียนยังไงวะ" แล้วก็เปิด Google ไปสามแท็บ ขณะที่ alert ยังดังอยู่
เลยตัดสินใจรวบรวมคำสั่งที่ใช้จริงบ่อยที่สุดตอน incident มาไว้ที่เดียว ไม่ใช่ cheat sheet ยาวสามหน้าที่ไม่มีใครอ่าน แต่เป็นชุดเล็กๆ ที่เรียงตามลำดับที่ควรไล่จริงตอนของพัง ทุกคำสั่งในนี้ใช้ได้กับ cluster ทั่วไป ไม่ผูกกับ EKS ยกเว้นข้อที่บอกไว้
1. ดู Warning event ทั้ง cluster ก่อนอย่างอื่น
kubectl events -A --types=Warning
ข้อนี้คือข้อที่อยากให้ทุกคนจำได้ที่สุด เพราะมันตอบคำถาม "ตอนนี้มีอะไรแปลกๆ เกิดขึ้นบ้าง" ได้ในคำสั่งเดียว — FailedScheduling, BackOff, FailedMount, Unhealthy โผล่มาหมด
ถ้า kubectl เก่ากว่า 1.28 ที่ kubectl events ยังไม่ stable ใช้ตัวคลาสสิกแทน:
kubectl get events -A --field-selector type=Warning --sort-by=.metadata.creationTimestamp
ที่เลือก sort ด้วย creationTimestamp แทน .lastTimestamp เพราะ event รุ่นใหม่บางตัวไม่มี lastTimestamp เลย sort แล้วลำดับเพี้ยน เคยนั่งงงอยู่ห้านาทีว่าทำไม event ล่าสุดไปอยู่บนสุด อีกเรื่องที่ต้องรู้ไว้คือ event มีอายุสั้น (ค่า default ของ API server อยู่ที่ราว 1 ชั่วโมง) ถ้าตื่นมาดูช้า หลักฐานหายไปแล้ว อันนี้เป็นเหตุผลที่ควรส่ง event ไปเก็บที่อื่นด้วยตั้งแต่แรก
2. หา pod ที่ไม่ปกติ แบบไม่ต้องไล่ทั้ง namespace
kubectl get pods -A --field-selector=status.phase!=Running,status.phase!=Succeeded
กรอง Running กับ Succeeded ออก ที่เหลือคือ Pending, Failed, Unknown ล้วนๆ จุดที่ต้องระวังคือ pod ที่ CrashLoopBackOff ส่วนใหญ่ phase ยังเป็น Running อยู่นะ มันเลยไม่โผล่ในคำสั่งนี้ ต้องเสริมด้วย:
kubectl get pods -A | grep -vE 'Running|Completed' ; \
kubectl get pods -A --sort-by='.status.containerStatuses[0].restartCount' | tail -10
ตัวหลังเรียง restart count จากน้อยไปมาก tail 10 ตัวท้ายคือพวกที่รีสตาร์ทเยอะสุด ส่วนมากต้นเหตุอยู่แถวนี้แหละ
3. ถามว่า container ตายเพราะอะไร โดยไม่ต้อง describe ยาวเหยียด
kubectl get pod <pod> -o jsonpath='{range .status.containerStatuses[*]}{.name}{"\t"}{.restartCount}{"\t"}{.lastState.terminated.reason}{"\t"}{.lastState.terminated.exitCode}{"\n"}{end}'
นี่แหละตัวที่เคยต้องเปิด Google สามแท็บ ตอนนี้ทำเป็น alias ไว้แล้ว output จะได้หน้าตาประมาณ app 7 OOMKilled 137 อ่านปุ๊บรู้ปั๊บ
-
OOMKilled/ 137 → memory limit ต่ำไป หรือแอป leak -
Error/ 1 → แอปตายเอง ไปดู log ต่อ - 143 → โดน SIGTERM ปกติ มักเกี่ยวกับ probe fail แล้วโดน kill
kubectl describe pod ก็ให้ข้อมูลเดียวกัน แต่ต้องเลื่อนหาท่ามกลาง volume, toleration และอีกร้อยบรรทัด ตอนตี 2 สายตาไม่ไหวอ่ะ
4. Log ของรอบก่อนตาย ไม่ใช่รอบปัจจุบัน
kubectl logs <pod> -c <container> --previous --tail=100
พลาดกันบ่อยมาก (ตัวเองด้วย) คือรัน kubectl logs เฉยๆ แล้วเห็น log สวยงามของ container ที่เพิ่ง start ใหม่ ไม่มี error สักบรรทัด แล้วก็งงว่าทำไมมันพัง เพราะ error อยู่ใน instance ก่อนหน้าที่ตายไปแล้วไง --previous คือตัวช่วยชีวิต
5. ใครกิน memory เยอะสุดตอนนี้
kubectl top pod -A --containers --sort-by=memory | head -15
ต้องมี metrics-server ก่อนนะ ถ้ารันแล้วได้ error: Metrics API not available แปลว่ายังไม่ได้ลง บน EKS ลงเป็น add-on ได้เลย ใส่ --containers เพื่อให้เห็นราย container เพราะหลายครั้งตัวกิน memory ไม่ใช่แอปหลัก แต่เป็น sidecar ที่ไม่มีใครตั้ง limit ให้
6. เข้าไปส่องใน pod ที่ไม่มี shell
kubectl debug -it <pod> --image=busybox:1.36 --target=<container>
Image สมัยนี้เป็น distroless กันเยอะ ซึ่งดีด้าน security แต่พอจะ exec เข้าไปดูอะไร ก็เจอ executable file not found ทันที ephemeral container แก้ปัญหานี้ ได้ shell ที่แชร์ process namespace กับ container เป้าหมาย ดู /proc, เช็ค DNS, เช็ค network ได้โดยไม่ต้อง rebuild image
มุม DevSecOps ที่ต้องพูดถึง: สิทธิ์ pods/ephemeralcontainers มีพลังพอๆ กับ pods/exec เลยนะ ถ้าให้ใครก็ได้ใน cluster ใช้ ก็เท่ากับเปิดประตูหลังเอง ควรจำกัดไว้ที่ role ของ on-call และให้ audit log จับไว้ด้วย ใน kubectl รุ่นใหม่ใส่ --profile=restricted ได้เพื่อไม่ให้ debug container ได้ privilege เกินจำเป็น
7. ตรวจว่า pod ถือ IAM role ไหนอยู่ (เฉพาะ EKS)
kubectl get sa <sa-name> -n <ns> -o jsonpath='{.metadata.annotations.eks\.amazonaws\.com/role-arn}{"\n"}'
kubectl exec <pod> -- printenv AWS_ROLE_ARN
บรรทัดแรกดูว่า ServiceAccount ผูก role ไว้ยังไง บรรทัดสองดูว่า pod ที่รันอยู่จริงได้ role นั้นมาหรือยัง เพราะเคยเจอเคสแก้ annotation แล้วลืม restart pod — SA บอกว่าถูก แต่ pod เก่ายังไม่มี env ตัวนี้เลย AccessDenied รัวๆ
ถ้า cluster ใช้ EKS Pod Identity แทน IRSA จะไม่มี AWS_ROLE_ARN แต่จะเห็น AWS_CONTAINER_CREDENTIALS_FULL_URI แทน อย่าเพิ่งตกใจว่ามันหาย ส่วนการ printenv ทั้งก้อนออกมาดู ไม่แนะนำ เพราะบางทีมยัง inject secret ผ่าน env อยู่ ระบุชื่อ env ที่ต้องการไปเลยดีกว่า
8. Rollback แบบรู้ว่ากำลังย้อนไปไหน
kubectl rollout history deploy/<name>
kubectl rollout history deploy/<name> --revision=<N>
kubectl rollout undo deploy/<name> --to-revision=<N>
kubectl rollout undo เฉยๆ จะถอยไปหนึ่ง revision ฟังดูโอเค จนวันที่มีคน deploy ซ้อนกันสองรอบในสิบนาที แล้วถอยหนึ่งขั้นก็ยังไปเจอเวอร์ชันพังอยู่ดี ดู history ก่อนเสมอ บรรทัดที่สองโชว์ image tag ของ revision นั้นให้ด้วย จะได้มั่นใจว่ากำลังย้อนไปตัวที่ดีจริง
ข้อสังเกตสำหรับทีมที่ใช้ GitOps (Argo CD/Flux): rollback ด้วย kubectl ตรงๆ จะโดน sync ทับกลับภายในไม่กี่นาที ต้อง revert ที่ Git หรือ pause auto-sync ก่อน ไม่งั้นจะงงว่าทำไม rollback แล้วมันกลับมาพังเอง
9. Control plane ยังดีอยู่มั้ย
kubectl get --raw='/readyz?verbose'
ข้อนี้ไม่ค่อยได้ใช้ แต่ตอนที่ต้องใช้คือสำคัญมาก โดยเฉพาะเวลาที่ kubectl ช้าผิดปกติ หรือ webhook timeout รัวๆ ผลลัพธ์จะไล่ check ทีละตัว [+]etcd ok, [+]informer-sync ok ถ้ามีตัวไหนเป็น [-] ก็รู้เลยว่าไม่ใช่ปัญหาที่แอป
บน EKS ปกติ control plane AWS ดูแลให้ แต่ admission webhook ที่เราลงเอง (เช่น policy engine) ถ้าล่มหรือช้า ก็ทำให้ทุก request ไป API server ช้าตามได้ เคสนี้ดู kubectl get validatingwebhookconfigurations ประกอบ แล้วเช็คว่า failurePolicy ตั้งไว้เป็นอะไร
ทำเป็น alias ไว้ ไม่ต้องจำ
ไม่มีใครจำ jsonpath ได้หรอก 555 ใส่ลง ~/.zshrc หรือ ~/.bashrc ไปเลย:
alias kwarn='kubectl events -A --types=Warning'
alias kbad='kubectl get pods -A --field-selector=status.phase!=Running,status.phase!=Succeeded'
alias ktopmem='kubectl top pod -A --containers --sort-by=memory | head -15'
kwhy() {
kubectl get pod "$1" -n "${2:-default}" -o jsonpath='{range .status.containerStatuses[*]}{.name}{"\t"}{.restartCount}{"\t"}{.lastState.terminated.reason}{"\t"}{.lastState.terminated.exitCode}{"\n"}{end}'
}
ใช้ kwhy my-pod prod แล้วได้คำตอบเลย ไม่ต้องเปิดแท็บไหนทั้งนั้น
ลำดับที่ใช้จริง
ถ้าต้องสรุปเป็นลำดับเดียวตอนโดนปลุก: เริ่มจาก event (ข้อ 1) → หา pod ที่ผิดปกติ (ข้อ 2) → ถามว่าตายเพราะอะไร (ข้อ 3) → อ่าน log รอบก่อน (ข้อ 4) → ถ้าเป็นเรื่อง resource ดู top (ข้อ 5) → ถ้ายังไม่เจอ ค่อยเข้าไปส่อง (ข้อ 6) → ถ้าเป็นเรื่องสิทธิ์ AWS ไปข้อ 7 → ถ้าต้องหยุดเลือดก่อน rollback (ข้อ 8)
ส่วนใหญ่จบที่ข้อ 3-4 แล้ว ที่เหลือเป็นของสำรองไว้วันที่ดวงไม่ดี
ไม่ได้บอกว่านี่คือชุดคำสั่งที่ดีที่สุดในโลกนะ แต่เป็นชุดที่หยิบใช้จริงบ่อยสุดจากที่ผ่านมา ใครมี one-liner ตัวเด็ดที่ช่วยชีวิตตอนดึกๆ มาแชร์กันได้ อยากขโมยไปใส่ alias เพิ่มเหมือนกัน

Top comments (0)