ข้ามไปยังเนื้อหา

Deployment และ ReplicaSet

ReplicaSet ทำให้จำนวน Pod ที่ตรงกับ label selector คงที่ตามที่กำหนดอยู่ตลอดเวลา ส่วน Deployment จัดการ ReplicaSet ให้คุณ เพื่อทำ rolling update แบบ declarative ที่มี version และมี rollback history ให้ด้วย

หน้าที่ของ ReplicaSet แคบมาก คือรับ Pod template กับ label selector มา แล้วทำให้มี Pod ที่ตรงกับ selector นั้นอยู่พอดี replicas ตัวเสมอ ถ้า Pod ที่ตรง selector หายไป ReplicaSet จะ create ตัวใหม่มาแทน ถ้ามีเกินก็จะลบส่วนเกินทิ้ง ReplicaSet ยังสามารถ adopt Pod ที่มีอยู่แล้วซึ่งตัวเองไม่ได้ create ขึ้นมาได้ด้วย ตราบใดที่ label ของ Pod นั้นตรงกับ selector

apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: web-rs
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: myregistry/web:1.0.0
ports:
- containerPort: 8080

ในการใช้งานจริงคุณแทบไม่เขียน ReplicaSet ตรง ๆ แบบนี้เอง แต่จะ create Deployment แล้ว Deployment จะ create และเป็นเจ้าของ ReplicaSet ให้แทน

Deployment ห่อ ReplicaSet ไว้พร้อม rollout strategy, revision history และการ rollback เมื่อคุณเปลี่ยน Pod template ใน Deployment (image tag ใหม่ หรือแก้ env var) Deployment จะ create ReplicaSet ใหม่ ที่ใช้ template นั้น แล้วค่อย ๆ ย้าย replica จาก ReplicaSet เก่าไปยังตัวใหม่

apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3
selector:
matchLabels:
app: web
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: myregistry/web:1.1.0
ports:
- containerPort: 8080

chain ความเป็นเจ้าของจะเป็น Deployment → ReplicaSet → Pod เสมอ Deployment เป็นเจ้าของ ReplicaSet หนึ่งตัวหรือมากกว่านั้น (ตัวปัจจุบัน บวกกับตัวเก่าที่เก็บไว้เพื่อ rollback history) และ ReplicaSet แต่ละตัวก็เป็นเจ้าของ Pod ของตัวเอง

flowchart LR
  dep["Deployment web"] --> rsOld["ReplicaSet web-abc123 (old): 3 -> 0"]
  dep --> rsNew["ReplicaSet web-def456 (new): 0 -> 3"]
  rsOld --> p1["Pod (terminating)"]
  rsNew --> p2["Pod (running)"]
  rsNew --> p3["Pod (running)"]
  rsNew --> p4["Pod (running)"]
A Deployment shifts replicas from an old ReplicaSet to a new one during a rollout

maxUnavailable จำกัดว่า replica ที่ต้องการมีได้กี่ตัวที่ unavailable พร้อมกันระหว่าง rollout และ maxSurge จำกัดว่ายอม Pod เกินจากจำนวนที่ต้องการได้กี่ตัวชั่วคราวระหว่างที่ตัวใหม่กำลังขึ้น สองค่านี้รวมกันปรับ trade-off ระหว่างความเร็วของ rollout กับ capacity ที่ใช้งานได้ ค่าด้านบนยอมให้ Pod ดับได้หนึ่งตัวและมี Pod เกินได้หนึ่งตัวพร้อมกัน สำหรับ Deployment ที่มี 3 replica

อีก strategy หนึ่งคือ Recreate ซึ่ง terminate Pod เก่าทุกตัวก่อนที่จะ create ตัวใหม่ตัวไหนเลย

spec:
strategy:
type: Recreate

Recreate ทำให้เกิด outage สั้น ๆ จึงสงวนไว้ใช้กับกรณีที่ version เก่ากับใหม่รันคู่กันจริง ๆ ไม่ได้ เช่น application ที่ทนไม่ได้ถ้ามี schema สอง version ยิง database เดียวกันพร้อมกัน

Terminal window
# Trigger a rollout by changing the Pod template (e.g. a new image), then watch it
kubectl set image deployment/web web=myregistry/web:1.2.0
kubectl rollout status deployment/web
# See every revision this Deployment has kept
kubectl rollout history deployment/web
# Roll back to the previous revision
kubectl rollout undo deployment/web
# Scale independently of a rollout
kubectl scale deployment/web --replicas=5
ใช้งานปกติ คุณแก้ object ไหนตรง ๆ เพื่อเปลี่ยนสิ่งที่กำลังรันอยู่ ระหว่าง Deployment กับ ReplicaSet
ระหว่าง RollingUpdate maxUnavailable กับ maxSurge แต่ละตัวคุมอะไร
จะ rollback Deployment กลับไป revision ก่อนหน้าที่ยังทำงานได้ปกติยังไง
ทำไมถึงเลือกใช้ strategy Recreate แทน RollingUpdate