Kubernetes Zero-2-Hero EP.5 สร้าง Health Check ด้วย Liveness Readiness และ การใช้งาน Rollout

by. Settakit

settakit
Sirisoft
8 min readAug 27, 2021

--

สวัสดีครับ กลับมาเจอกันอีกครั้งกับ Series “Kubernetes Zero-2-Hero” ใน EP. นี้ ก็จะเป็น EP สุดท้ายกันแล้ว ก็ต้องขอขอบคุณมากนะครับ ที่ติดตามกันมาจนถึง EP นี้ โดยใน EP สุดท้ายนี้ เราก็จะมาพูดถึงเรื่องของ Kubernetes Good to Know ซึ่งเรื่องที่ผมจะมาพูดนั้นก็จะมีอยู่ 3 เรื่องดังนี้

Liveness Probe

Readiness Probe

Rollout

Liveness and Readiness Probe

Liveness Probe

liveness จะทำงานเมื่อ Container ภายใน Pod เริ่มทำงานจะเป็นการตรวจสอบว่า Container ของเราทำงานแล้ว ถ้าหากตรวจสอบตามเงื่อนไขแล้วไม่ผ่านการตรวจสอบ Container ภายใน Pod ก็จะถูกสั่งให้ Restart อีกครั้ง และตรวจสอบใหม่จนกว่าจะตรวจสอบผ่าน

source: https://cloud.google.com/blog/products/containers-kubernetes/kubernetes-best-practices-setting-up-health-checks-with-readiness-and-liveness-probes

Readiness Probe

Readiness เป็นการตรวจสอบความพร้อมใช้งานของ Container ภายใน Pod โดยจะเริ่มตรวจสอบหลังจาก Container เริ่มทำงานขึ้นมา โดยเมื่อตรวจสอบตามเงื่อนไขที่ตั้งไว้แล้วไม่ผ่าน ก็จะรอตามเวลาที่กำหนด แล้วตรวจสอบใหม่อีกครั้ง และเมื่อตรวจสอบผ่านแล้ว ก็จะปล่อยให้ Traffic จาก Service เข้ามาภายใน Container ได้ เพื่อป้องกันการปล่อย Traffic มายัง Container ที่ไม่พร้อมใช้งาน แล้วเกิดข้อผิดพลาดกับ Application ตัวอื่นที่เรียกใช้งาน Container ตัวนี้

source: https://cloud.google.com/blog/products/containers-kubernetes/kubernetes-best-practices-setting-up-health-checks-with-readiness-and-liveness-probes

ข้อแตกต่างระหว่าง Liveness และ Readiness

ถ้าเห็นชัด ๆ เลยก็จะเป็นเรื่องของการที่ Liveness จะ Restart Container ส่วน Readiness จะรอแล้วถึงจะตรวจสอบใหม่อีกครั้ง

ส่วนในมุมของการใช้งานอาจมี Container ที่ Start ขึ้นมาแล้ว แต่อาจจะยังไม่พร้อมใช้งาน เช่น Container เป็น Database ซึ่ง Container ทำงานแล้ว แต่ว่ายังไม่พร้อมให้บริการเนื่องจากยัง Intitial Database ไม่เสร็จ ซึ่งหากเราใช้ Liveness กับเรื่องนี้ Container ก็จะถูก Restart อีกครั้ง และถูกตรวจสอบใหม่ ในขณะที่ Container ถูก Restart ก็จะต้องใช้ทรัพยากรในการ Start Container ขึ้นมา ซึ่งหากในเคสนี้เราใช้งาน Readiness ผลที่ออกมาก็จะต่างออกไป ทันทีที่ Readiness ตรวจสอบแล้วไม่ผ่าน ก็จะรอตามเวลาที่กำหนด แล้วตรวจสอบใหม่อีกครั้งโดยไม่จำเป็นต้อง Restart Contianer ให้เสียทรัพยากรในการ Start ขึ้นมา เนื่องจาก Contianer ไม่ได้มีปัญหาอะไรยังทำงานอยู่ แต่แค่ยังไม่พร้อมให้บริการเท่านั้น

เงื่อนไขที่สามารถใช้ Liveness และ Readiness Probe ได้

สามารถเช็คได้ 3 แบบ คือ​

  • HTTP GET= Status Code 200-399Success (ใน Application ที่จะ Deploy ต้องมี Path สำหรับตรวจสอบสถานะด้วย)
  • Command = Any Command Return Zero Code Success​
  • TCP = Allow Ports

Configuration

  • initialDelaySeconds = เวลาหลังจาก Container ถูกสร้างให้ตรวจสอบสถานะ (Default 0)​
  • periodSeconds = ความถี่ในการตรวจสอบ (Default 10, Min 1)​
  • timeoutSeconds = จำกัดเวลาในการตรวจสอบสถานะ (Default 1, Min 1)​
  • successThreshold = จำนวนครั้งที่ถือว่าผ่านหลังจากที่ตรวจสอบแล้วไม่ผ่านในครั้งก่อน (Default 1)​
  • failureThreshold = จำนวนครั้งที่ตรวจสอบไม่ผ่านได้กี่ครั้ง จะเลิกตรวจสอบ (Default 3)​

ที่เราจะกำหนดเองหลัก ๆ จะเป็นเรื่อง initialDelaySeconds และ initialDelaySeconds

Liveness and Readiness Probe HTTP

ต่อไปจะเป็นตัวอย่างการใช้งาน Liveness และ Readiness โดยใช้งาน HTTP GET

# pod-probe-http-fail.yamlapiVersion: v1
kind: Pod
metadata:
labels:
run: pod-probe-http-fail
name: pod-probe-http-fail
spec:
containers:
- image: nginx
name: pod-probe-http-fail
ports:
- containerPort: 80
resources: {}
livenessProbe: # ระบุ Liveness
httpGet:
path: /test # ระบุ Path ที่ไม่มีอยู่ใน Container เพื่อให้ Probe fail
port: 80
initialDelaySeconds: 3
periodSeconds: 3
readinessProbe: # ระบุ Readiness
httpGet:
path: /test # ระบุ Path ที่ไม่มีอยู่ใน Container เพื่อให้ Probe fail
port: 80
initialDelaySeconds: 3
periodSeconds: 3

dnsPolicy: ClusterFirst
restartPolicy: Always
kubectl create -f pod-probe-http-fail.yaml# output
pod/pod-probe-http-fail created

จากนั้นเรียก Pod ออกมาดู

kubectl get pod pod-probe-http-fail# output
NAME READY STATUS RESTARTS AGE
pod-probe-http-fail 0/1 Running 2 40s

จะพบว่าในสถานะจะมีการ Restart เกิดขึ้นเนื่องจาก Liveness ตรวจสอบแล้วไม่ผ่าน

จากนั้นเมื่อลองดูรายละเอียดภายใน Pod จะพบกับปัญหาดังนี้

kubectl describe pod pod-probe-http-fail# output
...
Warning Unhealthy 0s (x6 over 21s) kubelet Readiness probe failed: HTTP probe failed with statuscode: 404
Warning Unhealthy 0s (x6 over 21s) kubelet Liveness probe failed: HTTP probe failed with statuscode: 404
Normal Killing 0s (x2 over 15s) kubelet Container pod-probe-http failed liveness probe, will be restarted

...

ต่อไปทดลองลบ Pod ที่ชื่อว่า “pod-probe-http-fail” แล้วสร้างขึ้นมาใหม่โดยแก้ไขไฟล์ดังนี้

# pod-probe-http-fail.yamlapiVersion: v1
kind: Pod
metadata:
labels:
run: pod-probe-http-fail
name: pod-probe-http-fail
spec:
containers:
- image: nginx
name: pod-probe-http-fail
ports:
- containerPort: 80
resources: {}
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 3
periodSeconds: 3
readinessProbe:
httpGet:
path: /test
port: 80
initialDelaySeconds: 3
periodSeconds: 3

dnsPolicy: ClusterFirst
restartPolicy: Always
kubectl create -f pod-probe-http-fail.yaml# output
pod/pod-probe-http-fail created

ลองเรียก Pod ขึ้นมาดูอีกครั้ง

kubectl get po pod-probe-http-fail# output
NAME READY STATUS RESTARTS AGE
pod-probe-http-fail 0/1 Running 0 2m53s

จะพบว่า Restart จะเป็น 0 เนื่องจากเราได้แก้ไขให้ Liveness ตรวจสอบผ่าน แล้ว แต่เรายังไม่ได้แก้ไขให้ Readiness ตรวจสอบผ่าน แต่ในเคสนี้จะเห็นความแตกต่างระหว่าง Liveness กับ Readiness ชัดมากยิ่งขึ้น

ต่อไปลบ Pod ที่ชื่อว่า “pod-probe-http-fail” จากนั้นสร้าง Pod ใหม่ขึ้นมาอีกครั้งตาม YAML ดังนี้

# pod-probe-http.yamlapiVersion: v1
kind: Pod
metadata:
labels:
run: pod-probe-http
name: pod-probe-http
spec:
containers:
- image: nginx
name: pod-probe-http
ports:
- containerPort: 80
resources: {}
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 3
periodSeconds: 3
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 3
periodSeconds: 3

dnsPolicy: ClusterFirst
restartPolicy: Always
kubectl create -f pod-probe-http.yaml# output
pod/pod-probe-http created

ลองเรียก Pod มาดูอีกครั้งจะพบว่า Pod พร้อมที่จะทำงาน เนื่องจากเราได้แก้ไข Liveness และ Readiness ให้ตรวจสอบผ่านแล้ว

kubectl get pod pod-probe-http# output
NAME READY STATUS RESTARTS AGE
pod-probe-http 1/1 Running 0 79s

Liveness and Readiness Probe Command

การทำงานจะทำงานคล้ายกันหมดในการตรวจสอบ ต่างกันตรงที่รูปแบบนี้จะใช้ Command ในการตรวจสอบ โดยเราจะยกตัวอย่างเคสที่ตรวจสอบผ่าน และไม่ผ่าน เพื่อให้เห็นภาพการใช้งานมากขึ้น

# pod-probe-cmd-fail.yamlapiVersion: v1
kind: Pod
metadata:
labels:
run: pod-probe-cmd
name: pod-probe-cmd
spec:
containers:
- image: nginx
name: pod-probe-cmd
ports:
- containerPort: 80
resources: {}
livenessProbe:
exec:
command: # ใช้ Command อ่านไฟล์ที่ไม่มีอยู่จริง
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5
readinessProbe:
exec:
command: # ใช้ Command อ่านไฟล์ที่ไม่มีอยู่จริง
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5

dnsPolicy: ClusterFirst
restartPolicy: Always
kubectl create -f pod-probe-cmd-fail.yaml# output
pod/pod-probe-cmd-fail created

เมื่อลองเรียก Pod ออกมาดูจะพบว่า Pod ไม่พร้อมใช้งาน

kubectl get pod pod-probe-cmd-fail# output
NAME READY STATUS RESTARTS AGE
pod-probe-cmd-fail 0/1 Running 1 30s

จากนั้นลบ Pod ที่สร้างขึ้นขึ้นมา แล้วสร้างขึ้นใหม่โดยการแก้ไขตาม Config ดังนี้

# pod-probe-cmd.yamlapiVersion: v1
kind: Pod
metadata:
labels:
run: pod-probe-cmd
name: pod-probe-cmd
spec:
containers:
- image: nginx
name: pod-probe-cmd
ports:
- containerPort: 80
resources: {}
livenessProbe:
exec: # แก้ไขให้อ่านไฟล์ที่มีอยู่จริง
command:
- cat
- /usr/share/nginx/html/index.html
initialDelaySeconds: 5
periodSeconds: 5
readinessProbe:
exec: # แก้ไขให้อ่านไฟล์ที่มีอยู่จริง
command:
- cat
- /usr/share/nginx/html/index.html
initialDelaySeconds: 5
periodSeconds: 5

dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
kubectl create -f pod-probe-cmd.yaml# output
pod/pod-probe-cmd created

เมื่อเรียก Pod ออกมาดูก็จะพบว่าพร้อมทำงานแล้ว

kubectl get pod pod-probe-cmd# output
NAME READY STATUS RESTARTS AGE
pod-probe-cmd 1/1 Running 0 35s

Liveness and Readiness Probe TCP Port

จะตรวจสอบในส่วนของ TCP Port ภายใน Container ว่า Allow อยู่หรือไม่ ถ้า Allow ก็จะถือว่าตรวจสอบผ่าน ตามตัวอย่างดังนี้

# pod-probe-tcp-fail.yamlapiVersion: v1
kind: Pod
metadata:
labels:
run: pod-probe-tcp-fail
name: pod-probe-tcp-fail
spec:
containers:
- image: nginx
name: pod-probe-tcp-fail
ports:
- containerPort: 80
resources: {}
readinessProbe:
tcpSocket: # ระบุ Port ที่ไม่ได้ Allow ไว้ที่ Container
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket: # ระบุ Port ที่ไม่ได้ Allow ไว้ที่ Container
port: 8080
initialDelaySeconds: 15
periodSeconds: 20

dnsPolicy: ClusterFirst
restartPolicy: Always
kubectl create -f pod-probe-tcp-fail.yaml# output
pod/pod-probe-tcp-fail created

เรียก Pod ออกมาดู จะพบว่า Pod ไม่พร้อมทำงาน

kubectl get pod pod-probe-tcp-fail# output
NAME READY STATUS RESTARTS AGE
pod-probe-tcp-fail 0/1 Running 0 46s

ทดลองลบ Pod ที่สร้างขึ้น จากนั้นสร้าง Pod ตาม YAML ดังนี้

# pod-probe-tcp.yamlapiVersion: v1
kind: Pod
metadata:
labels:
run: pod-probe-tcp
name: pod-probe-tcp
spec:
containers:
- image: nginx
name: pod-probe-tcp
ports:
- containerPort: 80
resources: {}
readinessProbe:
tcpSocket: # ระบุ Port ที่ allow ใน Container
port: 80
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket: # ระบุ Port ที่ allow ใน Container
port: 80
initialDelaySeconds: 15
periodSeconds: 20

dnsPolicy: ClusterFirst
restartPolicy: Always
kubectl create -f pod-probe-tcp.yaml# output
pod/pod-probe-tcp created

เมื่อเรียก Pod ออกมาดู ก็จะพบว่า Pod พร้อมใช้งานแล้ว

kubectl get pod pod-probe-tcp# output
NAME READY STATUS RESTARTS AGE
pod-probe-tcp 1/1 Running 0 39s

Rollout

Rollout เป็นเครื่องมือที่เอาไว้จัดการเวอร์ชันของ Deployment เมื่อมีการอัพเดทเวอร์ชันของ Image ภายใน Deployment

โดยประโยชน์ของมันก็คือ หากว่าเราอัพเดทเวอร์ชัน Image ไปแล้วเกิดมีปัญหาเราก็สามารถกลับไปใช้งานเวอร์ชันก่อนหน้านั้นได้ทันที

มาเริ่มกันเลยกับการใช้งาน rollout

ก่อนอื่นสร้าง Deployment ใหม่ขึ้นมาด้วย command ดังนี้

kubectl create deployment deploy-rollout --image=nginx# output
deployment.apps/deploy-rollout created

จากนั้นดูรายละเอียดภายในของ Deployment ในส่วนของการใช้งาน Image

kubectl describe deployment deploy-rollout | grep Image# output
Image: nginx

จะเห็นว่าตอนนี้ Deployment ใช้ Image “nginx”

จากนั้นให้ใช้ Command ด้านล่าง เพื่อดูประวัติการอัพเดทของ Deployment

# kubectl rollout history deployment <DEPLOYMENT_NAME>kubectl rollout history deployment deploy-rollout# output
deployment.apps/deploy-rollout
REVISION CHANGE-CAUSE
1 <none>

จากนั้นทดลองเปลี่ยนเวอร์ชันของ Image ให้กับ Deployment

# kubectl set image <POD|DEPLOYMENT> <NAME> <CONTAINER_NAME>=<IMAGE> kubectl set image deployment deploy-rollout nginx=nginx:1.19# output
deployment.apps/deploy-rollout image updated

สามารถดูชื่อ Container ได้ด้วย Command หาดูในส่วนของ Containers

kubectl describe deployment deploy-rollout

จากนั้นเมื่อลองตรวจสอบเวอร์ชันของ Image ดูอีกครั้งจะพบว่า Image ได้เปลี่ยนไปแล้ว

kubectl describe deployment deploy-rollout  | grep Image# output
Image: nginx:1.19

จากนั้นให้ใช้ Command ด้านล่าง เพื่อดูประวัติการอัพเดทของ Deployment

# kubectl rollout history deployment <DEPLOYMENT_NAME>kubectl rollout history deployment deploy-rollout# output
deployment.apps/deploy-rollout
REVISION CHANGE-CAUSE
1 <none>
2 <none>

จะพบว่าตอนนี้มีการอัพเดทเวอร์ชันเพิ่มขึ้นมาอีกหนึ่งอัน ซึ่งเราสามารถดูรายละเอียดของแต่ละเวอร์ชันได้ด้วย Command ดังนี้

# kubectl rollout history deployment <NAME> --revision=<REVISION_NAMEBER>kubectl rollout history deployment deploy-rollout --revision=2# output
deployment.apps/deploy-rollout with revision #2
Pod Template:
Labels: app=deploy-rollout
pod-template-hash=59d88496f8
Containers:
nginx:
Image: nginx:1.19
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>

จากนั้นทดลองอัพเดทเวอร์ชันของ Image อีกครั้ง ตาม Command ด้านล่าง

kubectl set image deployment deploy-rollout nginx=nginx:1.199# output
deployment.apps/deploy-rollout image updated

จากนั้นเรียก Pod ออกมาดู จะพบว่า Pod อยู่ในสถานะ Error

kubectl get pod -l app=deploy-rollout# output
NAME READY STATUS RESTARTS AGE
deploy-rollout-55dc5f48c9-sbdrs 0/1 ImagePullBackOff 0 78s
deploy-rollout-59d88496f8-4b92v 1/1 Running 0 13m

Pod ที่รันอยู่เป็น Pod เก่าซึ่งจะทำลายตัวเองก็ต่อเมื่อมี Podใหม่เกิดขึ้นมา นี่คือเหตุผลว่าทำไม Kubernetes ถึงมี Down Time น้อย

ต่อไปมาดูสถานะของการ rollout ก็จะพบว่า การ rollout ยังไม่สำเร็จ

# kubectl rollout status deployment <NAME>kubectl rollout status deployment deploy-rollout# output
Waiting for deployment "deploy-rollout" rollout to finish: 1 old replicas are pending termination...

ต่อไปทดลองการใช้งาน rollout สำหรับการกลับไปใช้งานเวอร์ชันก่อนหน้านี้ด้วย command ดังนี้

# kubectl rollout undo deployment <NAME>kubectl rollout undo deployment deploy-rollout# output
deployment.apps/deploy-rollout rolled back

ดูสถานะการ rollout อีกครั้งก็จะพบว่าการเปลี่ยนแปลง Image สำเร็จแล้ว

kubectl rollout status deployment deploy-rollout# output
deployment "deploy-rollout" successfully rolled out

ตรวจสอบการทำงานของ Pod ก็จะกลับมาทำงานปกติ

kubectl get pod -l app=deploy-rollout# output
deploy-rollout-59d88496f8-4b92v 1/1 Running 0 20m

จากนั้นดูเวอร์ชันของ Image ใน Deployment ก็จะพบว่า ตอนนี้กลับไปใช้งาน Image เวอร์ชันก่อนหน้านี้

kubectl describe deployment deploy-rollout  | grep Image# output
Image: nginx:1.19

หากลองตรวจสอบประวัติการเปลี่ยนแปลงก็จะพบว่าเวอร์ชันที่ 2 หายไป และมี เวอร์ชันที่ 4 เพิ่มมาแทน ส่วนเวอร์ชันที่ 3 ก็จะเป็นเวอร์ชันที่ Image เกิดปัญหา ลองตรวจสอบ Image ในเวอร์ชันที่ 3 และ 4

kubectl rollout history deployment deploy-rollout --revision=3 | grep Image# output
Image: nginx:1.199
kubectl rollout history deployment deploy-rollout --revision=4 | grep Image# output
Image: nginx:1.19

เราสามารถกลับไปใช้งานเวอร์ชันก่อนได้ด้วยการระบุหมายเลขของ Revision ตาม Command ดังนี้

kubectl rollout undo deployment deploy-rollout --to-revision=1# output
deployment.apps/deploy-rollout rolled back

เมื่อตรวจสอบ Deployment จะพบว่าตอนนี้กลับไปใช้ Image อันแรกสุดที่ใช้ Deploy

kubectl describe deployment deploy-rollout  | grep Image# output
Image: nginx

สามารถกลับไปใช้เวอร์ชันเก่าตามหมายเลขที่อยู่ในประวัติเท่านั้น

Conclusion

  1. Liveness Probe ใช้ตรวจสอบสถานะของ Contianer ภายใน Pod ว่าทำงานอยู่หรือไม่ ถ้าไม่ทำงาน Liveness จะสั่งให้ Restart Contianer นั้น แล้วตรวจสอบใหม่อีกครั้ง
  2. Readiness Probe ใช้ตรวจสอบความพร้อมทำงานของ Container ภายใน Pod ซึ่งถ้าตรวจสอบแล้วไม่ผ่านก็จะยังไม่ปล่อยให้ Traffic เข้ามายัง Pod เพื่อป้องกันข้อผิดพลาดของระบบที่จะมาใช้งาน Pod ตัวนั้น และจะรอเพื่อตรวจสอบใหม่อีกครั้ง เมื่อตรวจสอบว่าผ่านแล้วก็จะปล่อย Traffic เข้ามายัง Pod
  3. Rollout เป็นเครื่องที่ใช้จัดการเรื่องเวอร์ชันของ Deployment สามารถดูประวัติ รายละเอียดของแต่ละเวอร์ชัน และย้อนกลับไปใช้เวอร์ชันเก่า ๆ ภายในประวัติได้

ก็จบกันไปแล้วนะครับสำหรับ EP นี้ ซึ่งก็เป็น EP สุดท้ายของ Series “Kubernetes Zero-2-Hero” ตอนนี้มีใครเป็นฮีโร่แล้วก็ลอง Comment บอกกันได้เลยนะครับ ส่วนใครงง หรือสงสัยติดปัญหาในตอนไหนก็สามารถ Comment ถามกันเข้ามาได้เลยนะครับ จะพยายามหาคำตอบมาตอบให้ครับผม สำหรับวันนี้ก็ขอขอบคุณสำหรับการติดตามตั้ง EP.1 จนถึง EP นี้ ขอบคุณมากครับ

เพื่อนๆสามารถติดตามกันได้ที่ https://www.facebook.com/sirisoft แล้วพบกันใหม่ ขอบคุณครับ 👋🏻👋🏻👋🏻

--

--