ข้ามไปยังเนื้อหา
กลับไปหน้าบทความ
การพัฒนาซอฟต์แวร์

Docker กับ Kubernetes ต่างกันอย่างไร เมื่อไหร่ต้องใช้ Kubernetes

Docker ใช้ build image และรัน container บนเครื่องหนึ่งเครื่อง ส่วน Kubernetes ดูแล container บนหลายเครื่อง ทั้งจัดวาง Pod รักษาจำนวน replica และเปิด Service

เริ่มต้น5 นาทีในการอ่าน

ในบทความนี้

ตอบสั้น ๆ ก่อนครับ Docker คือเครื่องมือสำหรับ build image และรัน container บนเครื่องที่มี Docker daemon ส่วน Kubernetes คือระบบที่ดูแล container บนหลายเครื่องพร้อมกัน คอยเลือกว่าจะรันที่เครื่องไหน รักษาจำนวนให้ครบตามที่สั่ง และสร้างใหม่เมื่อตัวเดิมหายไป

สองตัวนี้จึงไม่ได้แข่งกันโดยตรง image ที่ build ด้วย Docker ใช้กับ container runtime ที่ Kubernetes รองรับได้ทุกตัว คำถามที่ควรถามจริง ๆ คือระบบของเรา "ต้องการ Kubernetes แล้วหรือยัง" ครับ

Docker build image แล้วรัน container บนเครื่องเดียว ส่วน Kubernetes รักษา Deployment ไว้ที่ 3 replica และสร้าง Pod ใหม่แทนตัวที่หายไปเมื่อ node 2 เป็น NotReady

Docker ทำอะไร

Docker เป็นแพลตฟอร์มสำหรับแพ็กและรันแอปใน container ทำงานแบบ client-server คือคำสั่ง docker ส่ง request ผ่าน REST API ไปที่ Docker daemon (dockerd) ซึ่งเป็นตัว build, รัน และแจกจ่าย container จริง ๆ บน Linux Docker ใช้ namespace ของ kernel เพื่อแยกพื้นที่ทำงานของแต่ละ container

งานประจำวันของ Docker มีไม่กี่คำสั่ง:

docker build -t app:1 .          # Dockerfile -> image
docker run -d -p 8080:80 app:1   # image -> container
docker push registry.example.com/app:1

image กับ container ต่างกันอย่างไร

image คือแม่แบบแบบอ่านอย่างเดียวที่มีทุกอย่างสำหรับสร้าง container แต่ละคำสั่งใน Dockerfile สร้าง layer หนึ่งชั้น และเมื่อแก้ Dockerfile แล้ว build ใหม่ จะ build ใหม่เฉพาะ layer ที่เปลี่ยน

container คือ instance ที่รันอยู่ของ image ตอน docker run Docker จะเพิ่ม filesystem แบบอ่านเขียนได้เป็นชั้นสุดท้าย และเมื่อลบ container ทิ้ง สิ่งที่เปลี่ยนไปซึ่งไม่ได้เก็บใน persistent storage จะหายไปด้วย ข้อมูลที่ต้องเก็บจึงต้องอยู่ใน volume ครับ

รูปแบบของ image ไม่ได้เป็นของ Docker คนเดียว Open Container Initiative (OCI) กำหนด image-spec ไว้ว่า image ประกอบด้วย manifest, image index (ไม่บังคับ), ชุด filesystem layer และ configuration เป้าหมายคือให้เครื่องมือต่างเจ้า build, ส่ง และรัน image เดียวกันได้

Kubernetes ทำอะไรที่ Docker ไม่ได้ทำ

Kubernetes คือแพลตฟอร์มสำหรับจัดการ workload และ service ที่อยู่ใน container บน cluster ที่มีcontrol plane และ worker node หนึ่งเครื่องขึ้นไป เราไม่ได้สั่ง "รัน container นี้" ทีละตัว แต่เขียนสถานะที่ต้องการ (desired state) แล้วให้ระบบทำให้เป็นจริง เอกสารของ Kubernetes อธิบายว่ามันประกอบด้วย control process หลายตัวที่คอยขับสถานะปัจจุบันให้เข้าใกล้สถานะที่ต้องการอยู่ตลอด

Pod

Pod คือหน่วยเล็กที่สุดที่ deploy ได้ใน Kubernetes เป็นกลุ่ม container หนึ่งตัวขึ้นไปที่ใช้ storage และ network ร่วมกัน แบบที่เจอบ่อยที่สุดคือ หนึ่ง container ต่อหนึ่ง Pod และโดยทั่วไปเราไม่สร้าง Pod ตรง ๆ แต่สร้างผ่าน workload resource อย่าง Deployment

Deployment

Deployment ใช้ระบุว่าต้องการ Pod หน้าตาแบบไหนกี่ตัว แล้ว Deployment controller จะเปลี่ยนสถานะจริงให้เป็นสถานะที่ต้องการในอัตราที่ควบคุมได้ ตัวอย่างสั้น ๆ:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
spec:
  replicas: 3
  selector:
    matchLabels: { app: app }
  template:
    metadata:
      labels: { app: app }
    spec:
      containers:
        - name: app
          image: registry.example.com/app:1
          ports:
            - containerPort: 80

เวลาเปลี่ยน image เป็นเวอร์ชันใหม่ Deployment จะ rolling update โดยค่าเริ่มต้นให้ Pod ไม่พร้อมได้สูงสุด 25% และเกินจำนวนได้สูงสุด 25% และย้อนกลับไป revision ก่อนหน้าได้ถ้าเวอร์ชันใหม่มีปัญหา

Service

Pod ถูกสร้างและลบตลอดเวลา IP ของมันจึงเปลี่ยนไปเรื่อย ๆ Service แก้ปัญหานี้โดยเปิดกลุ่ม Pod ออกสู่ network ผ่าน selector และได้ IP ของตัวเองที่เรียกว่า cluster IP controller ของ Service จะคอยหา Pod ที่ตรงกับ selector แล้วอัปเดตรายการ endpoint ให้ ฝั่งที่เรียกจึงไม่ต้องรู้ว่าตอนนี้มี Pod ไหนบ้าง

Scheduler

kube-scheduler เลือก node ให้ Pod ที่ยังไม่มี node เป็นสองขั้น คือ filtering แล้ว scoring ขั้นแรกตัด node ที่ resource ไม่พอสำหรับ request ของ Pod ออก ขั้นที่สองให้คะแนน node ที่เหลือแล้วเลือกตัวที่ได้คะแนนสูงสุด ถ้าไม่มี node ไหนผ่านเลย Pod จะค้างอยู่ในสถานะ unscheduled จนกว่าจะวางได้

Self-healing

Kubernetes restart container ที่ล้ม แทนที่ container และ kill container ที่ไม่ตอบ health check ที่เรากำหนด และไม่ส่ง traffic ไปหาจนกว่ามันจะพร้อม ถ้า node ทั้งเครื่องล่ม controller จะสร้าง Pod ใหม่มาแทน แล้ว scheduler วาง Pod นั้นบน node ที่ยังปกติ

สิ่งที่ Kubernetes ไม่ทำก็ควรรู้ด้วยครับ มันไม่ build แอปและไม่ deploy source code และไม่มี database, message bus หรือระบบ logging ให้ในตัว ส่วนนั้นยังต้องเลือกและติดตั้งเอง

Kubernetes ยังใช้ Docker อยู่หรือไม่

ใช้ image ที่ Docker build ได้ แต่ Kubernetes ไม่ได้คุยกับ Docker Engine โดยตรงแล้วครับ Kubernetes คุยกับ runtime ผ่าน CRI (Container Runtime Interface) และ Docker Engine ไม่ได้ implement CRI โปรเจกต์จึงมีโค้ดเชื่อมที่ชื่อ dockershim ไว้ชั่วคราว และนำออกใน Kubernetes v1.24

ผลในทางปฏิบัติมีสามข้อ

ใช้ Docker Compose บนเครื่องเดียวพอเมื่อไหร่

ผมคิดว่าพอ ถ้าระบบรันบนเครื่องเดียวได้ และยอมรับได้ว่าถ้าเครื่องนั้นล่ม ระบบจะหยุดจนกว่าจะกู้เครื่องกลับมาครับ

Docker Compose ให้เขียน service, network, volume, config และ secret ไว้ใน compose.yaml ไฟล์เดียว แล้ว docker compose up จะสร้าง network และ volume ที่ต้องใช้ และเริ่มทุก service ส่วนการ restart เมื่อ container ล้ม Docker มี restart policy อย่าง on-failure, always และ unless-stopped (Start containers automatically) ซึ่งตั้งใน Compose ได้ด้วย restart: (Compose services)

services:
  web:
    image: registry.example.com/app:1
    ports: ["8080:80"]
    restart: unless-stopped
    depends_on: [worker]
  worker:
    image: registry.example.com/worker:1
    restart: unless-stopped

ข้อจำกัดคือ restart policy ทำงานโดย Docker daemon บนเครื่องนั้น ถ้าทั้งเครื่องหาย ก็ไม่มีอะไรย้าย container ไปเครื่องอื่น นี่คือจุดที่ Kubernetes เริ่มคุ้มกับความซับซ้อนที่เพิ่มขึ้น

ลองเช็กจากคำถามเหล่านี้ครับ

คำถาม ถ้าตอบว่า "ใช่"
ระบบต้องรันต่อได้แม้เครื่องหนึ่งเครื่องล่ม ต้องมีหลาย node และตัวที่ย้าย workload ให้
ต้อง deploy เวอร์ชันใหม่โดยไม่หยุดบริการ และย้อนกลับได้ด้วยคำสั่งเดียว Deployment มี rolling update และ rollback ให้
มีหลาย service ที่ต้องเพิ่มลดจำนวนตามโหลด Kubernetes ทำ horizontal scaling ได้
มีทีมหรือคนที่ดูแล cluster ได้ ถ้าไม่มี ต้นทุนการดูแลอาจมากกว่าประโยชน์

ถ้าทุกข้อตอบว่า "ไม่" Compose บนเครื่องเดียวพร้อม backup ที่ดีมักพอครับ

ควรเริ่มเรียนจากอะไร

เริ่มจาก Docker ก่อนครับ เพราะ image และ container คือสิ่งที่ Kubernetes เอาไปรันต่อ ลำดับที่ผมแนะนำคือ

  1. เขียน Dockerfile, build image และรัน container ให้คล่อง
  2. ใช้ Docker Compose รันแอปที่มีหลาย service บนเครื่องตัวเอง
  3. ลอง Kubernetes ในเครื่อง Docker Desktop มี Kubernetes มาให้แล้ว เขียน Deployment และ Service ของแอปเดิม แล้วลองลบ Pod ทิ้งเพื่อดูว่า controller สร้างตัวใหม่อย่างไร

ข้อสามคือจุดที่ความต่างของสองเครื่องมือนี้ชัดที่สุด Docker รันตามคำสั่งที่เราพิมพ์ ส่วน Kubernetes เทียบสิ่งที่รันอยู่กับสิ่งที่เราเขียนไว้ แล้วแก้ให้ตรงกันเองครับ

แหล่งอ้างอิง

อ่านต่อ