昨天學了 Label 和 Selector,今天來學 Deployment。
Day 04 我們直接跑了裸 Pod,但它有個根本問題:裸 Pod 刪掉就沒了,不會自動重建。實際部署服務的時候沒有人直接用裸 Pod,用的是 Deployment。
裸 Pod 有兩個致命問題:
問題一:掛了不會自動重建
Pod 被刪掉或 Node 故障,這個 Pod 就消失了。沒有任何機制會幫你補回來。
問題二:沒辦法管理多個副本
你想跑 3 個 todo-api Pod 來分攤流量,裸 Pod 沒有副本的概念,你只能手動建三個名字不同的 Pod 自己管理。
Deployment 解決了這兩個問題。
Deployment 是一個上層資源,你告訴它「我要幾個什麼樣的 Pod」,它負責把這些 Pod 建起來,並持續確保數量和狀態符合你的描述。
Deployment
│
└── 管理 ReplicaSet
│
├── Pod 1
├── Pod 2
└── Pod 3
apiVersion: apps/v1
kind: Deployment
metadata:
name: todo-api
# ┌─── ① Deployment spec:控制副本數、選擇器 ───────────────────────────────┐
spec:
replicas: 2 # 要幾個 Pod 副本
selector:
matchLabels:
app: todo-api # 管理有這個 Label 的 Pod
template: # Pod 的模板
metadata:
labels:
app: todo-api # Pod 會帶這個 Label
# ┌─── ② Pod spec:定義容器內容 ──────────────────────────────────┐
spec:
containers:
- name: api
image: yourname/todo-app-api:v1.0.0
ports:
- containerPort: 8000
env:
- name: DATABASE_URL
value: "mysql+pymysql://user:password@mysql-service:3306/tododb"
# └────────────────────────────────────────────────────────────────┘
# └───────────────────────────────────────────────────────────────────────┘
replicas:要維持幾個 Pod。設成 2,Deployment 就會確保隨時都有 2 個 Pod 在跑,少了自動補,多了自動刪。selector.matchLabels:Deployment 用這個 Selector 找它管理的 Pod。要跟 template.metadata.labels 對得上。 (就是昨天學的 Label 和 Selector 在 Deployment 裡的實際應用 )template:Pod 的模板。Deployment 建立新 Pod 的時候就是照這個模板複製的。你會發現 template 底下的結構跟裸 Pod 的 YAML 很像,只是少了 apiVersion 和 kind。apiVersion: apps/v1:Deployment 屬於 apps 這個 API group,跟 Pod 的 v1 不同。今天目標:把 todo-api 和 todo-frontend 改用 Deployment 部署,並親眼驗證 Pod 刪掉之後會自動補回來。
/Todo-App/k8s目錄,建立 todo-api-deployment.yaml:apiVersion: apps/v1
kind: Deployment
metadata:
name: todo-api
spec:
replicas: 2
selector:
matchLabels:
app: todo-api
template:
metadata:
labels:
app: todo-api
tier: backend
spec:
containers:
- name: api
image: yourname/todo-app-api:v1.0.0
ports:
- containerPort: 8000
env:
- name: DATABASE_URL
value: "mysql+pymysql://user:password@mysql-service:3306/tododb"
kubectl apply -f todo-api-deployment.yaml
kubectl get deployments

2/2 代表 2 個 Pod 都準備好了kubectl get pods

Pod 名稱格式是 <deployment名稱>-<replicaset hash>-<pod hash>,這是 Deployment 自動生成的。
# 刪掉其中一個 Pod(名稱換成你自己的)
kubectl delete pod todo-api-69bd9b578-gkwp4
# 立刻看 Pod 列表
kubectl get pods

Deployment 發現少了一個 Pod,立刻建了一個新的補上。這就是 Day 02 說的「自癒」在實際運作的樣子。
# 擴充到 4 個副本
kubectl scale deployment todo-api --replicas=4
# 確認
kubectl get pods
也可以直接修改 YAML 的 replicas 欄位,再 kubectl apply 一次,效果一樣。
todo-frontend-deployment.yaml:apiVersion: apps/v1
kind: Deployment
metadata:
name: todo-frontend
spec:
replicas: 2
selector:
matchLabels:
app: todo-frontend
template:
metadata:
labels:
app: todo-frontend
tier: frontend
spec:
containers:
- name: frontend
image: yourname/todo-frontend:v1.0.0
ports:
- containerPort: 80
kubectl apply -f todo-frontend-deployment.yaml
kubectl get deployments

如果你照著操作,會發現 todo-frontend 的 Pod 一直重啟並顯示 Error:
kubectl logs todo-frontend-5c8466594d-4vjvw
# nginx: [emerg] host not found in upstream "todo-api-service"
這是預期中的行為,不是你的設定有問題。
原因是 frontend 的 nginx 設定裡有 proxy_pass 指向 todo-api-service,nginx 啟動時會嘗試解析這個 DNS 名稱,但 Service 還沒建立,解析失敗就 crash 了。
等到 Day 08 建立 Service 之後,todo-api-service 的 DNS 記錄就會存在,frontend 就能正常啟動。目前先忽略這個錯誤,繼續往下學就好。
# 看 Deployment 詳細狀態
kubectl describe deployment todo-api
# 確認 Deployment 是否完成更新
kubectl rollout status deployment todo-api
# 看 Deployment 歷史版本
kubectl rollout history deployment todo-api
# 刪除 Deployment(連帶刪掉底下的 Pod)
kubectl delete deployment todo-api
今天學了 Deployment,它是 K8s 裡最常用的部署方式:
kubectl scale 或修改 replicas,立即生效目前 todo-api 和 todo-frontend 各自跑著 2 個 Pod,但它們之間還不能互相溝通,外面也連不進來。明天先學 Rolling Update,了解如何在不停機的情況下更新服務版本,Day 08 再用 Service 把這些 Pod 串起來。