Kubernetes Intermediate (LV.2)
Kubernetes Probes
Liveness Probe
Kubelet ใช้ probe นี้เพื่อตรวจสอบว่า application ยังทำงานได้ปกติหรือไม่ หาก application ไม่ตอบสนองแล้ว จะได้ทำการ restart Pod
Readiness Probe
Kubelet ใช้ probe นี้เพื่อตรวจสอบว่า Pod ยังสามารถรับ traffic ได้หรือไม่ ถ้าไม่ได้จะทำการกันไม่ให้ traffic จาก service วิ่งเข้า Pod นี้
Startup Probe
Kubelet ใช้ probe นี้เพื่อตรวจสอบว่า application start แล้วหรือยัง ถ้ามีการ config startup probe ไว้ จะไม่ทำการส่ง liveness probe และ readiness probe จนกว่า startup probe จะผ่านเพื่อลดการรบกวน application ขณะ start และช่วยในเรื่องของ slow-start application
Probe Config
initialDelaySeconds (default: 0, min:0) เป็นระยะเวลาก่อนเรื่อง Probe หากค่า
periodSeconds
สูงกว่าinitialDelaySeconds
จะถือว่า ignoreinitialDelaySeconds
periodSeconds (default: 10, min:1) เป็นการกำหนดความถี่ในการ probe
timeoutSeconds (default: 1, min:1) เป็นระยะเวลาก่อนที่จะนับว่า Probe แต่ละรอบ timeout
successThreshold (default: 1, min:1) เป็นจำนวนขั้นต่ำที่ Probe success ต่อเนื่องกัน เพื่อถือว่า healthy
failureThreshold (default: 3, min:1) เป็นจำนวนขั้นต่ำที่ Probe failed ต่อเนื่องกัน เพื่อถือว่า unhealthy
Probe Design Pattern
HTTP Probe เป็น Probe ที่จะส่ง http get request ไปยัง Pod
gRPC Probe เป็น Probe โดยใช้ gRPC healthcheck
TCP Probe เป็น Probe ที่จะทดสอบ tcp socket
Command Probe เป็น Probe ที่จะทดสอบสั่ง command
Real-world use case http probe
Resource
การจัดการ Resource ของ Pod ทำได้โดยกำหนดค่าใน section resource ใต้ section container
Request คือ resource ส่วนที่ reserve ให้กับ Pod นั้นๆ โดยใครจะมาแย่งไปไม่ได้
Limit คือ resource ส่วนที่กำหนดไว้ เพื่อป้องกันไม่ให้ Pod นั้นๆ ไปรบกวน Pod อื่นๆในระบบ หากมีการทำงานผิดพลาดจากปกติ เช่น Memory Leak
Behavior ของ Limit resource CPU และ Memory จะแตกต่างกัน โดย Limit ของ CPU จะเป็น hard limit ไม่ให้ใช้ CPU เกินค่านั้นได้ แต่ Limit ของ Memory จะทำให้เกิดการ kill (restart Pod) หาก Pod ใช้ Memory เกินค่า Limit ที่กำหนด
Limit Memory ไม่ได้เป็น hard limit มันไม่ได้ป้องกันไม่ให้ Pod ไม่สามารถใช้ Memory เกินที่กำหนด ดังนั้นมันจะยังสามารถไปรบกวน Pod อื่นหรือ kubelet ได้ช่วงสั้นๆ ในบางเคสที่กำหนดค่า kubelet config ไว้ไม่ดี อาจจะทำให้ kubelet process ตายได้ และเกิดการ down ทั้ง node นั้น
เราสามารถกำหนดค่า Request และ Limit เป็นค่าเดียวกันได้ แต่ห้ามให้ค่า Limit ต่ำกว่า Request
Practice ในการตั้งค่า Resource
ดูค่า usage ย้อนหลังจาก Monitoring tools เช่น Prometheus ย้อนหลัง 30 วัน
กำหนดค่า CPU Request ตาม percentile 95 ของ usage ย้อนหลัง 30 วัน
กำหนดค่า Memory Limit และ Request ตาม Max memory usage และ บวกเพิ่มอีก 15% buffer
ในส่วน CPU Limit ไม่ต้องกำหนด
NodeSelector
nodeSelector
เป็นวิธีง่ายสุดในการกำหนดว่าจะให้ Pod ถูก deploy อยู่บนเครื่องกลุ่มใดได้บ้าง จะใช้การ select จาก node ที่มี label ตรงกับที่ระบุ
ตัวอย่างดังต่อไปนี้เป็นการกำหนดให้ Pod ถูก deploy บน Node Linux และสถาปัตยกรรม x86
Affinity
nodeSelector
สามารถกำหนดเงื่อนไขอย่างง่ายๆได้ว่าจะให้ Pod สามารถลงได้บน node กลุ่มใด แต่ไม่สามารถทำเงื่อนไขที่ซับซ้อนได้ ดังนั้น affinity และ anti-affinity จึงเข้ามาชดเชยในส่วนนี้
affinity สามารถทำเงื่อนไขหลักๆได้ 2 แบบ
node affinity ซึ่งเป็นการกำหนดเงื่อนไขกับ node คล้ายกับ nodeSelector
pod affinity ซึ่งเป็นการกำหนดเงื่อนไขกับ pod อื่นๆ
Node affinity
คล้ายกับ nodeSelector ทำให้กำหนดเงื่อนไขกับ node ที่ระบุได้ มีด้วยกัน 2 ชนิด
requiredDuringSchedulingIgnoredDuringExecution
จะทำให้ scheduler ไม่สามารถ schedule Pod ลง node ใดๆได้ หากไม่ตรงตามเงื่อนไขpreferredDuringSchedulingIgnoredDuringExecution
จะทำให้ scheduler พยายามหา node ที่ตรงเงื่อนไขก่อน หากไม่มี node ใดตรงเงื่อนไข จึงจะยอมให้ schedule Pod ลง node อื่นๆได้
nodeAffinity
กำหนด config ได้ที่ field .spec.affinity.nodeAffinity
เช่น
operator
สามารถเป็นได้ดังนี้ In
, NotIn
, Exists
, DoesNotExist
, Gt
and Lt
weight
ใช้กับ preferredDuringSchedulingIgnoredDuringExecution
มีค่าได้ตั้งแต่ 1 ถึง 100
scheduler จะเอาค่าไปคำนวณรวมกัน แล้วเลือก node ที่มี score มากที่สุดในการ schedule Pod ลง
Pod affinity และ anti-affinity
คล้ายกับ node affinity สามารถระบุเงื่อนไขได้ 2 ชนิด
requiredDuringSchedulingIgnoredDuringExecution
preferredDuringSchedulingIgnoredDuringExecution
pod affinity มักจะถูกใช้ในการระบุ pod 2 กลุ่มให้อยู่ใน zone เดียวกัน เนื่องจากมีการส่งข้อมูลระหว่างกันเยอะ
pod anti-affinity มักจะถูกใช้ในการกระจายไม่ให้ pod ใน app เดียวกัน อยู่บนเครื่องเดียวกัน เพื่อเพิ่ม availability
ตัวอย่าง pod anti-affinity
Pod Disruption Budget (PDB)
Pod Disruption Budget (PDB) คือกลไกใน Kubernetes ที่ใช้สำหรับกำหนดจำนวน Pod อย่างต่ำที่ต้องทำงานอยู่เสมอในระบบ เพื่อป้องกันการที่ Pod ถูกหยุดทำงาน (disruptions) มากเกินไป ซึ่งอาจเกิดจากสาเหตุทั้งที่เป็นการควบคุมโดยผู้ดูแลระบบ (เช่น การอัพเดต node หรือการปรับเปลี่ยนการตั้งค่า) หรือเหตุการณ์ที่ไม่คาดคิด การใช้ PDB จะเข้ามาช่วยป้องกันไม่ให้เกิด downtime ในระบบ application ที่สำคัญได้
PDB สามารถตั้งค่าได้ 2 รูปแบบ
minAvailable
: จำนวนขั้นต่ำของ Pod ที่ต้องทำงานอยู่เสมอในช่วงเวลาที่มีการหยุดการทำงานmaxUnavailable
: จำนวนสูงสุดของ Pod ที่สามารถหยุดทำงานได้ในเวลาเดียวกัน
ห้ามใช้ minAvailable
และ maxUnavailable
ร่วมกันใน target workload เดียวกัน
ตัวอย่าง
และ
Use Case ของ PDB
ใช้ minAvailable
กับ Deployment (stateless app) เพื่อการันตีว่ามี workload อย่างน้อยกี่ Pod ที่จะยังทำงาน
สมมติ Deployment มี 10 Pod และ minAvailable
เป็น 7 เมื่อ drain node ก็จะสามารถ down ได้ทีละ 3 pod
ทำให้ drain / rolling update ได้รวดเร็ว
ใช้ maxUnavailable
กับ StatefulSet หรือ application ที่เป็น cluster เช่น Redis cluster / Database cluster / Elasticsearch / Kafka
เช่น app cluster มี 3 Pod และกำหนด maxUnavailable
เป็น 1 เพื่อให้ระบบ down ทีละ 1 Pod
หรือ app cluster มี 5 Pod และกำหนด maxUnavailable
เป็น 2 เพื่อให้ระบบ down ทีละ 2 Pod
ในเคสนี้ Raft algorithm จะยังทำงานได้ตามปกติ
DNS resolution และ ndots
โดยปกติการ solve DNS ของ Kubernetes จะถูกตั้ง policy (ClusterFirst
) ให้ solve DNS name จากใน cluster ก่อน หากไม่พบจึงค่อยไป lookup จากภายนอก cluster
ตัวอย่างไฟล์ /etc/resolve.conf
จากภายใน container เป็นดังนี้
จาก config จะให้ lookup dns จาก 10.43.0.10
(ในที่นี้เป็น DNS server ของ cluster นี้; kubernetes service ที่ชี้ไปยัง coredns pod)
กรณี name เป็น FQDN (ลงท้ายด้วย full stop; .
) ก็จะ solve name ตรงๆ เช่น google.com.
หาก name นั้นไม่ได้เป็น FQDN จะทำการ search จาก local domain ด้วย
ตามที่ config ซึ่งในที่นี้คือ default.svc.cluster.local
, svc.cluster.local
, cluster.local
เช่น google.com ก็จะไป lookup name ดังนี้ google.com.default.svc.cluster.local
, google.com.svc.cluster.local
, google.com.cluster.local
การที่ DNS จะตัดสินว่าจะ lookup จากภายใน local domain ก่อน หรือจาก DNS ภายนอกก่อน ตัดสินได้จาก ndots
ในที่นี้ ndots เป็น 5 หมายถึง หากภายใน name มี dot (.
) น้อยกว่า 5 ให้ทำการ solve จาก local domain ก่อนไป lookup จาก DNS ภายนอก
จากเคสตัวอย่างจะพบว่า ndots เป็น 5 ไม่เหมาะกับ name google.com
เพราะจะทำให้เกิดการ request fail จำนวนมาก ก่อนไป lookup หา google.com
จาก DNS ภายนอก และได้ค่าที่ถูกต้องมา
ทำให้ DNS latency สูง และเกิด workload ไม่จำเป็นบน coredns จำนวนมาก
อย่างไรก็ตาม การกำหนด ndots เป็น 1 ก็ไม่เหมาะสมในกรณีที่ cluster ใช้ pattern เช่น service-name.namespace
เช่นกัน เพราะจะทำให้เกิดการ lookup ออกไปภายนอกจำนวนมาก และ fail แล้วจึงค่อยมา solve local domain ทำให้ช้ากว่ามาก
ใน community ไม่ได้กำหนดชัดเจนว่าควรใช้เป็นค่าเท่าไหร่ เนื่องจากขึ้นกับ pattern การใช้ของแต่ละองค์กร แต่หากใช้ ndots เป็น 5 แล้วสร้างภาระให้ coredns ก็สามารถลดมาเป็น 3 หรือ 2 ได้
วิธีอื่นที่ช่วยในการลด latency ของการ call ไปยัง external name มีดังนี้
ปรับ domain name ที่เรียกเป็น FQDN ในทุก application
Headless service
Headless service เป็น service ประเภทหนึ่งของ Kubernetes Service ที่ ไม่มี Cluster IP โดยเราสามารถสร้าง headless service ได้โดยการตั้งค่า clusterIP: None
ใน manifest
เมื่อ clusterIP
ถูกตั้งเป็น None
Kubernetes จะไม่สร้าง IP ให้กับ Service นี้ แต่จะทำการสร้าง DNS record แบบ A หรือ SRV ที่ชี้ไปยังแต่ละ Pod แทน
ปกติ headless service จะถูกใช้เมื่อเราต้องการให้ client เข้าถึง pod โดยตรง เช่น MongoDB replicaset, Kafka หรือ Elasticsearch
การใช้ headless service ร่วมกับ Statefulset จะทำให้เราสามารถ refer ถึง pod ภายใต้ Statefulset ได้ผ่านการ refer DNS ใน pattern <pod-name>.<service-name>
ตัวอย่างการใช้งาน
สร้างไฟล์ชื่อ headless.yaml
โดยมี content ดังนี้
yaml ข้างต้นจะเป็นการระบุให้สร้าง statefulset โดยใช้ image nginx และ mount configmap เพื่อ return hostname ของ pod เมื่อถูก client เรียกเข้ามา และมีการกำหนดให้ headless service refer ไปยัง pod ของ Statefulset ที่กำหนด
สั่ง apply และทำการทดสอบเรียก DNS ดังนี้
จะได้ผลลัพธ์กลับมาดังนี้
จะเห็นว่าเราสามารถระบุได้ว่าจะ connect pod ไหนโดยการอ้างอิง DNS name ในรูป <pod-name>.<service-name>
Application ที่จำเป็นต้องให้แต่ละ pod connect ถึงกันตรง จึงใช้ headless service นี้ เป็นวิธีการในการ connect หากันและกัน
cleanup
สิ่งที่ต้องระวังในการ config Statefulset และ headless service คือ serviceName ใน spec ของ Statefulset จะต้องตรงกับชื่อของ headless service เพราะ config ในจุดนี้เป็นการทำให้เกิด DNS record ถ้าค่าในจุดนี้ไม่ตรง จะ lookup DNS record ไม่เจอ
Downward API
เวลาเราสร้าง Pod หรือ Deployment ใน Kubernetes หลายครั้งเราอาจต้องการให้ Container รู้ข้อมูลเกี่ยวกับตัวมันเอง เช่น:
ชื่อ Pod
Namespace
Label หรือ Annotation
Resource ที่ถูกกำหนดให้ใช้งาน
Kubernetes มีฟีเจอร์ที่เรียกว่า Downward API ซึ่งเปิดโอกาสให้เราสามารถ “ส่งข้อมูล metadata ของ Pod” เข้าไปยัง Container ผ่าน Environment Variables หรือ Mount เป็นไฟล์ ได้โดยไม่ต้อง hardcode
ตัวอย่างบางส่วนของ code การใช้ผ่าน Environment Variable
ตัวอย่างบางส่วนของ code การ mount เป็น file ผ่าน volume
ทดสอบใช้ downward API
สร้างไฟล์ชื่อ downward.yaml
โดยมี content ดังนี้
apply และทดสอบดังนี้
จะได้ผลลัพธ์เป็นชื่อ pod ซึ่งถูก pass มาจาก metadata ผ่าน downward API
cleanup
Last updated
Was this helpful?