Service: ClusterIP, NodePort, LoadBalancer
ไอเดียในหนึ่งประโยค
หัวข้อที่มีชื่อว่า “ไอเดียในหนึ่งประโยค”Service ให้กลุ่ม Pod ที่เปลี่ยนไปเรื่อย ๆ มี virtual IP กับ DNS name เดียวที่เสถียร แล้ว load balance traffic ไปยัง Pod ที่ match และ healthy อยู่ในขณะนั้น
ปัญหา: Pod IP ไม่เสถียร
หัวข้อที่มีชื่อว่า “ปัญหา: Pod IP ไม่เสถียร”Pod เป็นของใช้แล้วทิ้ง Deployment ย้าย Pod ที่ crash ไปโหนดอื่น rollout แทนที่ Pod ทุกตัวด้วยตัวใหม่ และ cluster autoscaler เพิ่มลดโหนดไปเรื่อย ๆ เหตุการณ์พวกนี้แต่ละอย่างทำให้ Pod ได้ IP ใหม่ได้ตลอด อะไรก็ตามที่ hardcode Pod IP ไว้จะพังทันทีที่ Pod นั้นถูกแทนที่
Service แก้ปัญหานี้ด้วยการยืนอยู่หน้ากลุ่ม Pod โดยเลือกสมาชิกด้วย label selector และติดตามว่า Pod ไหนพร้อมใช้งานอยู่ผ่าน Endpoints object (หรือ EndpointSlice ที่เป็นเวอร์ชันใหม่ที่ scale ได้ดีกว่า) พอ Pod เกิดหรือหายไป controller จะอัปเดต Endpoints/EndpointSlice ให้อัตโนมัติ client ไม่ต้องรู้เรื่องนี้เลย
apiVersion: v1kind: Servicemetadata: name: paymentsspec: selector: app: payments ports: - port: 80 targetPort: 8080# See which Pods this Service is currently routing tokubectl get endpoints paymentskubectl get endpointslices -l kubernetes.io/service-name=paymentsPod ตัวไหนก็ตามที่ label ตรงกับ app: payments และผ่าน readiness probe จะถูกเพิ่มเข้าลิสต์นี้อัตโนมัติ ลบ Pod ทิ้งก็หลุดจากลิสต์ไปเอง Pod ตัวใหม่ที่ label เดียวกันก็ถูกเพิ่มกลับเข้ามาแทน โดยที่ IP กับ DNS name ของ Service ไม่เปลี่ยนเลย
Service สามแบบ ต่อยอดกันเป็นชั้น ๆ
หัวข้อที่มีชื่อว่า “Service สามแบบ ต่อยอดกันเป็นชั้น ๆ”spec.type กำหนดว่า Service ถูกเข้าถึงยังไง และสามแบบหลักที่ใช้กันบ่อยต่อยอดกันเป็นขั้นบันได แต่ละแบบสร้างบนแบบก่อนหน้า
- ClusterIP (ค่า default) virtual IP ที่เข้าถึงได้แค่จากภายใน cluster การสื่อสารระหว่าง Pod ส่วนใหญ่ใช้แบบนี้
- NodePort ทำทุกอย่างที่ ClusterIP ทำ บวกกับเปิด port เดียวกันบนทุกโหนดใน cluster อยู่ในช่วง
30000-32767โดย default ยิงไปที่<ip-โหนดไหนก็ได้>:<nodePort>ก็เข้าถึง Service จากนอก cluster ได้ - LoadBalancer ทำทุกอย่างที่ NodePort ทำ บวกกับให้ cluster ขอ cloud provider provision external load balancer ที่ forward ต่อไปยัง NodePort
apiVersion: v1kind: Servicemetadata: name: payments-publicspec: type: LoadBalancer selector: app: payments ports: - port: 80 targetPort: 8080 nodePort: 30080kubectl get svc payments-public# EXTERNAL-IP column is populated once the cloud provider finishes provisioningkube-proxy คือตัวที่ทำให้ virtual IP ใช้งานได้จริง
หัวข้อที่มีชื่อว่า “kube-proxy คือตัวที่ทำให้ virtual IP ใช้งานได้จริง”ClusterIP ของ Service ไม่ได้มี network interface จริงอยู่ที่ไหนเลย เป็นแค่ address เสมือน kube-proxy ที่รันอยู่บนทุกโหนดคอย watch API server ดู Service กับ Endpoints/EndpointSlice แล้วเขียน rule ในเครื่อง (ปกติเป็น iptables หรือ IPVS แล้วแต่ mode ที่ตั้งไว้) เพื่อให้ packet ที่ส่งไปหา virtual IP ถูก rewrite แล้วส่งต่อไปยัง Pod ตัวใดตัวหนึ่งที่อยู่เบื้องหลังโดยอัตโนมัติ นี่คือเหตุผลที่ Service ทำงานเหมือนกันไม่ว่า client Pod จะอยู่โหนดไหน
flowchart LR client["Client Pod"] --> vip["Service virtual IP\n(ClusterIP)"] vip --> kp["kube-proxy rules\n(iptables / IPVS)"] kp --> p1["Pod 1"] kp --> p2["Pod 2"] kp --> p3["Pod 3"]