เบื้องหลัง Compose on Kubernetes มีอะไรอยู่ในนั้น

Apipol Sukgler
Red Crane
Published in
3 min readMay 6, 2019

หลังจากที่เราได้นำเสนอกันไปในบทความแรกแล้ว ถึง 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 ส่วน

  1. Compose API server
    ส่วนที่ custom API แยกออกมาจาก k8s API โดยมีทำหน้าที่เก็บ logic ที่ใช้ในการ store และจัดการข้อมูลของ stack
  2. 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 ที่สามารถใช้ได้

--

--

Apipol Sukgler
Red Crane

Full-Stacked Developer. Let’s share to the world :)