[Kubernetes] ว่าด้วยเรื่อง Services แต่ละประเภท
โดยปกติแล้ว Pod จะมี Life cycle ของมัน หาก Node ตาย ReplicationController จะทำการปลุก Pod เหล่านั้นขึ้นมาใหม่ โดยจะพยายามทำตาม desired state ที่กำหนดไว้โดยไม่ต้องสนใจว่า Pod เหล่านั้นจะอยู่ที่ Node ไหน
ฝั่ง Front-end ยิ่งไม่ต้องสนใจว่า Pod นั่นทำงานยังไง รันอยู่ที่ไหน แม้ว่าแต่ละ Pod จะมี Unique IP (แม้ Pod จะอยู่ Node เดียวกันก็ยังมี IP ต่างกัน)
Service ก็เป็นสิ่งที่พยายาม abstract ทุกอย่างเพื่ออนุญาตให้ Pod สามารถตายได้โดยไม่ส่งผลกระทบกับ Front-end และหน้าที่ที่สำคัญอีกอย่างคือทำการ routing เส้นทางไปยัง Pod ให้ถูกต้อง
แม้ว่าแต่ละ Pod จะมี Unique IP แต่ IP เหล่านั้นจะใช้กันอยู่แค่ภายใน (ถ้าจะเรียกจากนอก cluster จะต้องทำการ routing table ให้ถูกต้อง) ซึ่ง Service จะทำหน้าที่ Group หลายๆ endpoint ของแต่ละ Pod ให้มีแค่ endpoint เดียว
Kubernates มี Service type ให้เลือก 4 ประเภท โดยบทความนี้จะอธิบายถึงความแตกต่างของแต่ละประเภท (แถมเรื่อง Ingress ให้นิดหน่อย) มาดูกันครับว่ามีอะไรบ้าง
ClusterIP
ClusterIP คือเราต้องการ expose Service เพื่อใช้กันภายใน ไม่สามารถเข้าถึงได้จากโลกภายนอก ตัวอย่างเช่น เราต้องการสร้าง Service ฐานข้อมูล แน่นอนว่าเราไม่ต้องการ Client เรียกฐานข้อมูลโดยตรง จะต้องเรียกผ่าน back-end ก่อน ซึ่ง back-end จะติดต่อกับฐานข้อมูลผ่านทาง ClusterIP ได้ แต่มีข้อแม้ว่าต้องอยู่ใน network เดียวกันนะ (ยังมี use cases อีกหลายๆ แบบ เช่นรวมฐานข้อมูลกับ back-end ไว้เป็น Deployment ทำงานใกล้ชิดกัน ไม่ต้อง expose ออกมา)
คำถาม : แล้วถ้าอยากให้โลกภายนอกเข้าถึงล่ะ พอจะทำได้มั้ย ?
คำตอบคือ ทำได้ผ่านสิ่งที่เรียกว่า proxy แต่วัตถุประสงค์ของมันจริงๆ ใช้สำหรับการ debug Service หรือต้องการเรียก Service จากเครื่องของ developer เท่านั้น (ต้องใช้ Cert ในการเข้าถึง) ห้ามใช้วิธีนี้ในการ Delivery ข้อมูลบน Production
วิธีทำคือ รันคำสั่ง kubectl proxy [--port=8080]
ต้องมีไฟล์ config ใน ~/.kube/config ก่อนนะ หลังจากนั้นก็สามารถ Debug ได้โดยผ่าน Browser หรือ Terminal โดยมี format ของ URL ดังนี้
http://localhost:8080/api/v1/proxy/namespaces/<NAMESPACE>/services/<SERVICE-NAME>:<PORT-NAME>/
อีกหนึ่ง use case คือใช้สำหรับเข้าใช้งาน internal service เช่น dashboard สำหรับจัดการ Kubernetes cluster (ต้องติดตั้ง Dashboard ก่อนนะ ซึ่งเราจะไม่พูดถึงในบทความนี้)
แล้วถ้ายังอยากให้โลกภายนอกเข้าถึงแบบไม่ต้องใช้ Proxy ล่ะต้องทำยังไง ก็เลยมี Service ประเภทต่อไปชื่อว่า NodePort
NodePort
NodePort คือการ expose service ออกไปสู่โลกภายนอกโดยใช้ public IP ของ Node ที่รัน Cluster นั้นอยู่ แต่จะมีการกำหนด TCP port แบบ random ระหว่าง 30000–32767 (สามารถกำหนดค่าเองได้ แต่ต้อง make sure ว่าทุก node ต้องมี port นั้นว่างจริงๆ และต้องอยู่ในช่วง 30000–32767 ด้วยนะ) หากเราทำการ re-deploy service นี้ หมายเลข port อาจถูกเปลี่ยนแปลงได้
ข้อดี
- ง่าย สามารถ expose ให้ client ได้เลย
ข้อเสีย
- ถ้า IP ของ Node เปลี่ยน client ก็จะเรียก service ไม่ได้
LoadBalancer
จริงๆ Services ที่กล่าวมาข้างต้นนั้นมีการทำ Load Balancing อยู่แล้ว แต่เป็น Internal Load Balancer หมายความว่าทุกๆ Pod ที่ label ตรงกับ selector ของ Service จะได้รับ load เท่าๆ กัน ซึ่งตัว Service จะเป็นตัวกระจายงาน
แต่ถ้าพูดถึง Type ของ Service ที่ชื่อว่า LoadBalancer จะกล่าวถึง External Load Balancer หมายความว่า LoadBalancer แต่ละอันจะได้รับการ assign public IP ทำให้ client เข้าถึงได้โดยจะเป็นลักษณะของ Fixed IP ซึ่ง Service แบบนี้แหละที่เหมาะสำหรับการ expose Service เพื่อให้ client เรียกใช้ได้บนระบบ production
ข้อดี
- สามารถกำหนดเป็น Static IP ได้
- รองรับ Protocol ทุกรูปแบบ เช่น HTTP, TCP, UDP, Websockets, gRPC
ข้อเสีย
- Cloud Provider ต้อง Support Layer 4 Load Balancer (e.g. GCP, AWS, Azure)
- ต้องจ่ายค่า Public IP (แต่ส่วนใหญ่จะให้บริการฟรี)
- ถ้ารันแบบ Bare Metal ต้องติดตั้ง MetalLB (Beta) เพิ่ม ซึ่งจะมาอธิบายในบทความ [Kubernetes] อยากใช้ Load Balancer แต่รัน Cluster บน Bare Metal ทำยังไงดี ?
ExternalName
ผมยังไม่เคยใช้ Service ประเภทนี้ แต่คอนเซ็ปต์คร่าวๆ คือการ Map service เข้ากับ CNAME แทนที่จะใช้ selector ในการเลือก เช่นถ้าเรียก Service นี้จะได้ CNAME record ออกมาแทน ทำให้เกิดการ redirect โดยใช้ DNS ไม่ต้องเสีย resource เพื่อ proxy หลายรอบ ซึ่งบทความนี้จะไม่ลงรายละเอียด หากต้องการดูข้อมูลเพิ่มเติมสามารถดูได้ที่ https://kubernetes.io/docs/concepts/services-networking/service/#externalname
หมายเหตุ: ต้องใช้ kube-dns เวอร์ชัน 1.7 หรือสูงกว่าเท่านั้นนะ
Ingress
Ingress คิดง่ายๆ คือ ไฟล์ nginx.conf ซึ่งมันตอบโจทย์หลายๆ อย่างไม่ว่าจะเป็นการทำ Virtual Host (1 IP แต่มีหลาย Domain) จัดการเรื่อง Certificate สำหรับ HTTPS และการทำ Reverse proxy ถ้าจะให้อธิบายว่า Ingress คืออะไร มันก็คือ set of rules ที่ส่งต่อไปให้ controller ซึ่ง LoadBalancer ก็เป็นตัวนึงที่พร้อมจะรับ Ingress rules เหล่านั้น
แต่การจะติดตั้ง Ingress นั้น จะต้องรันบน Cloud Provider เนื่องจากต้องการ External Load Balance ซึ่งตอนนี้ยังไม่รองรับการรันบน Bare Metal (แม้จะติดตั้ง MetalLB ก็ตาม) อ่านเพิ่มเติม : https://github.com/google/metallb/issues/260
ขอจบเพียงเท่านี้ หากมีตรงไหนผิดพลาดรบกวนช่วยคอมเมนต์ด้านล่างจะเป็นพระคุณยิ่งครับ
ขอบคุณท่าน acoshift ที่ช่วยแก้ไขบทความครับ 🎉
References