เบื้องหลัง Compose on Kubernetes มีอะไรอยู่ในนั้น
หลังจากที่เราได้นำเสนอกันไปในบทความแรกแล้ว ถึง effect ของการที่ Docker เปิดตัวโปรเจ็ค Compose on Kubernetes อย่างเป็นทางการอีกทั้งเปิด open source ด้วย เราจะมาลองดูว่าเบื้องหลัง API ที่เราสามารถเรียกใช้อย่างเรียบง่ายนั้นมีอะไรซ่อนอยู่กัน
โดย หลักการทำงานของ Compose on Kubernetes คือ
เขียน Custom Resource ใน Kubernetes เพื่อใช้ในการ convert stack ที่ได้จาก compose file ให้เป็น kubernetes services
สำหรับใครที่ยังไม่รู้เราสามารถเขียน custom resourceใน Kubernetes ได้ สองแบบ ถ้ามีโอกาสจะลองสรุปให้ฟังต่อไป
ซึ่งใน Compose on kubernetes จะใช้เป็น Aggregated API โดยต้องเขียนด้วยภาษา Go เท่านั้น
Architecture
ประกอบไปด้วย 2 ส่วนคือ Client-side กับ Server-side
ส่วนของ Client-Side
ปกติเวลาเราการเรียกใช้งาน command line ผ่าน docker-cli หรือ kubectl เบื้องหลังคือการไป request ไปยัง REST API ของ kubernetes และจะถูก route ไปยัง API ที่เป็น Compose API Server อีกที
ซึ่งใน compose on kubernetes มี custom API 2 ตัวในการสร้าง stack service คือ
- v1beta1 — จะทำการสร้าง stack ขึ้นมาโดยผ่านการ upload compose file (อนาคตจะถูก deprecate) ซึ่งจะใช้อยู่ใน Docker Enterprise 2.0
- v1beta2 — จะสร้าง stack โดยการรับโครงสร้าง struct ที่ serialize จาก compose file มาแล้ว
ซึ่งข้อมูลของ stack จะถูกเก็บไว้ใน etcd ในลักษณะ key-value
ส่วนของ Server-Side
ในส่วนของ server-side ประกอบไปด้วย 2 ส่วน
- Compose API server
ส่วนที่ custom API แยกออกมาจาก k8s API โดยมีทำหน้าที่เก็บ logic ที่ใช้ในการ store และจัดการข้อมูลของ stack - Compose Controller
ส่วน Controller จะทำหน้าที่ดึงข้อมูล stack มาจาก API Server จากนั้นแปลงให้กลายเป็น kubernetes components โดยจะเรียกใช้งานผ่าน K8S API Server ตัวหลักอีกที
Mapping Between Swarm Stack Service and Kubernetes Component
สำหรับคนที่เป็นสาย Swarm ยังไม่คุ้นชินกับ Kubernetes Component มากนัก เราจะพาไปดูว่าพอใช้ compose file แล้ว kubernetes จะมีโครงสร้างเหมือนหรือต่างกับ swarm อย่างไรบ้าง
1 Swarm Service เมื่อถูกแปลงเป็น Kubernetes จะแบ่งออกเป็น 2 ส่วน
- ส่วนของการ deploy และ scale container
- ส่วนของการจัดการ network ทั้ง internal และ external
ส่วนของการ deploy และ scale container
pod deployment
ใน kubernetes จะไม่ได้จัดการ container แต่ละตัวแยกออกจากกัน แต่จะทำการจัดการ container หลายๆ ตัวที่เป็นกลุ่มๆ เดียวกัน โดยเราเรียกว่า Pod ซึ่ง Pod จะถูก deploy หรือ scale ได้โดยใช้ controller ต่างๆ ที่เรียกใช้ผ่าน docker-cli หรือ kubectl
แต่ถ้าในกรณีที่ swarm มีการ config ให้ใช้ deploy mode เป็น global
compose on k8s จะใช้ DaemonSet ในการจัดการ pods ทั้งหลาย
version: "3.6"
services:
worker:
image: dockersamples/examplevotingapp_worker
deploy:
mode: global
กรณีที่มีการ Mount volume ออกมาภายนอก Container ใน k8s จะ map ไปสร้าง StatefulSet โดย Volume มีอยู่ด้วยกัน 2 แบบ
- PersistentVolumeClaim เก็บไฟล์ไว้ใน Volume ที่ถูกสร้างขึ้นมา
- Host bind mount จะถูกเก็บลง host แต่ต้องระบุแบบ absolute path
version: "3.6"
services:
mysql:
volumes:
- db-data:/var/lib/mysql
volumes:
db-data:
ซึ่งทั้ง DaemonSet, StatefulSet จะถูกใช้ร่วมกับ Deployment
Secret
ใน swarm secret จะถูกเก็บในรูปแบบของ key value แต่ใน k8s จะมีการกำหนดชื่อให้กับ secret แล้วจึง access ข้อมูลผ่านชื่อนั้นอีกที
เช่น หากเรากำหนดใน docker-compose.yml ให้มี secrets ชื่อ mysecret เวลาถูกแปลงเป็น k8s จะเก็บ secret นี้ไว้ในชื่อ my_secret โดยมี key ชื่อ my_secret แล้ว value จะเป็น content ทั้งหมดของ file อีกที
version: "3.6"
services:
web:
image: nginx:alpine
secrets:
- mysecret
- myexternalsecret
secrets:
mysecret:
file: ./my_secret.txt
myexternalsecret:
external: true
กรณีที่เป็น external secret จะต้องถูกสร้าง manual ผ่าน kubectl อีกที โดยชื่อจะต้องตรงกับ secret name ที่ระบุไว้ใน compose file
echo -n 'external secret' > ./file
kubectl create secret generic myexternalsecret --from-file=./file
secret "myexternalsecret" created
Network
Internal Network
ในส่วนของ k8s จะไม่ได้มีการกำหนด network แบบ swarm ทุก pods ของ k8s ถ้าอยู่ใน namespace เดียวกันคือสามารถเชื่อมต่อหากันได้หมด โดยของใน stack เดียวกันจะอยู่ใน namespace เดียวกัน
กรณีต้องการเชื่อมต่อหากันข้าม stack จะต้องสร้าง HeadlessService ของแต่ละ stack ขึ้นมาเพื่อใช้คุยกัน
External Network
version: "3.6"
services:
web:
image: nginx:alpine
ports:
- target: 80
published: 8080
protocol: tcp
mode: host
หากเราทำการ pusblish port ออกไปยังภายนอก k8s จะสร้าง LoadBalancer service ให้ โดยใส่ suffix ตามด้วย -published
และ NodePort กับ ClusterIP service ด้วย
กรณีที่ Cluster ไม่มี LoadBalancer NodePort จะถูกสร้างแทนด้วยการใช้ port ในการ publish port แต่จำเป็นต้องมีการระบุ port range ที่สามารถใช้ได้