จัดการกับ External Service อย่างไรบน Kubernetes
ในการพัฒนาซอฟต์แวร์นั้น นอกเหนือจาก Service ที่เราเขียนแล้ว Deploy ใช้งานกันเอง ยังมี Service อื่นๆ ที่จะต้องไปเชื่อมต่อ ซึ่งเมื่อเราคิดถึง Principle ในการออกแบบ Application เพื่อให้เหมาะสำหรับ Cloud Native ที่ได้ยินกันคุ้นหู นั่นก็คือ 12 Factors
ในข้อที่ 7 ของ 12 Factors คือ Port Binding
VII. Port binding
Export services via port binding
The web app exports HTTP as a service by binding to a port, and listening to requests coming in on that port
หมายถึง เวลาที่ Application คุยกับ Service อื่นๆ สิ่งที่คุยกันให้ binding ผ่าน port ไม่ใช่ IP เพราะ หากเราใช้ IP ในการ binding service ลองจินตนาการว่า server ที่เรามีเป็นร้อยตัว ถ้าจะต้องมานั่งแก้ Config IP ทุกครั้งที่ Scale Service ก็คงจะไม่ Productive เท่าไหร่
แล้วทำอย่างไรล่ะ
ใน Docker และ Kubernetes มีสิ่งหนึ่งที่ถูก Build-in อยู่ภายในอยู่แล้ว ที่เราไม่รู้นั่นก็คือ Service Discovery หากเราสังเกตุใน Docker เราจะสามารถเรียกใช้ container แต่ละตัวได้ด้วยชื่อของ Container name ไม่ใช่วง IP ของแต่ละ Container ภายใน ใน docker compose ที่เราสามารถ scale service ขึ้นหลายๆ ตัวแต่เรียกใช้ผ่านชื่อ service เหมือนเดิม โดยการ route request จะเข้าไปใน service แบบ Round robin
การ Mapping External Service ใน Kubernetes
ในการ Mapping External Service จะมีอยู่ 2 อย่างที่ต้องใช้ คือ Service และ Endpoint ซึ่งทำได้ 3 แบบดังนี้
1. Service ที่อยู่ภายนอก Cluster ที่เป็น IP Address
ยกตัวอย่างเช่น Database โดยส่วนมากเราจะสร้าง database instance แยกออกมาจาก kubernetes cluster
อันดับแรก เพิ่ม service เพื่อทำให้ส่ง request มายัง service ได้
//Service
kind: Service
apiVersion: v1
metadata:
name: mongo
Spec:
type: ClusterIP
ports:
- port: 27017
targetPort: 27017
จากนั้นสร้าง Endpoint Object เพื่อรับ traffic ที่เข้ามาจาก Service
kind: Endpoints
apiVersion: v1
metadata:
name: mongo
subsets:
- addresses:
- ip: 10.240.0.4
ports:
- port: 27017
เราสามารถเรียกใช้ผ่าน ชื่อ service ได้เลยดังนี้
mongodb://mongo
2. Service จากภายนอกที่เป็นลักษณะ URL
กรณีใช้ third party service อย่างเช่น mlab ที่เป็น Database-as-a-service for Mongo
mongodb://<dbuser>:<dbpassword>@ds149763.mlab.com:49763/dev
เราสามารถกำหนดชื่อใหม่โดยเปลี่ยนจาก ds149763.mlab.com
เป็น mongo
ได้ดังนี้
kind: Service
apiVersion: v1
metadata:
name: mongo
spec:
type: ExternalName
externalName: ds149763.mlab.com
ก็จะสามารถเข้าถึงได้โดยใช้ชื่อ mongo
mongodb://<dbuser>:<dbpassword>@mongo:<port>/dev
3. Service จากภายนอกที่มีลักษณะของ Dynamic Port
ในกรณีที่ third party service ไม่ได้เป็น fixed port อย่างใน mlab.com ที่ instance ที่เราสร้างจะ random port มาให้
เราสามารถ nslookup เพื่อเช็คก่อนว่า IP อะไร
nslookup ds149763.mlab.com
จากนั้นสร้าง service, endpoint เพื่อใช้ map จาก port ที่ไม่ได้เป็น specific ให้ application ในแต่ละ environment ไม่ต้องแก้ config แต่ละ environment ให้มาแก้ที่ endpoint กับ service แทน
kind: Service
apiVersion: v1
metadata:
name: mongo
spec:
ports:
- port: 27017
targetPort: 49763
---
kind: Endpoints
apiVersion: v1
metadata:
name: mongo
subsets:
- addresses:
- ip: 35.188.8.12
ports:
- port: 49763
ก็จะสามารถ access จากภายใน cluster ผ่านทาง mongo
ได้
mongodb://<dbuser>:<dbpassword>@mongo/dev
การ Mapping External service ทำให้เราไม่ต้องเปลี่ยนแปลง config ของ Application ทุกครั้งที่มีการเปลี่ยนแปลงกับ external service
Reference:
https://cloud.google.com/blog/products/gcp/kubernetes-best-practices-mapping-external-services