概述#
上一章用 Service 完成了藍綠部署,這次輪到本系列另一位主角 Ingress 上場,示範如何用它實作金絲雀部署(Canary Deployment)。在 Kubernetes 裡,Ingress 透過 hostname 與 pathname 處理七層流量,並由 Ingress Controller 將請求分配到一個或多個 Service。Kubernetes 官方維護的 Controller 為 Nginx Ingress,可以透過註解(Annotations)為不同部署情境提供細緻的流量控制。
Nginx Ingress 的金絲雀功能#
Nginx Ingress 提供三種基於 Header、Cookie、權重的流量切分策略,只要在註解中加上對應設定即可:
nginx.ingress.kubernetes.io/canary:值為true即視為 Canary Ingress,會與既有 Ingress 互相搭配進行流量切分。nginx.ingress.kubernetes.io/canary-by-header-value:當請求 Header 與設定值匹配時,將流量導向 Canary Ingress。nginx.ingress.kubernetes.io/canary-by-header-pattern:行為與上一項類似但支援正則表達式。注意:若同時設定了canary-by-header-value,本項會被忽略。nginx.ingress.kubernetes.io/canary-by-cookie:請求 Cookie 與設定值匹配時導向 Canary Ingress;設成always則導所有流量。nginx.ingress.kubernetes.io/canary-weight:以 0 至 100 的整數宣告要把多少百分比的流量導向 Canary Ingress。
優先級由高到低:canary-by-header → canary-by-cookie → canary-weight。
金絲雀部署的特色#
金絲雀部署不像藍綠部署那樣是非黑即白的切換,而是介於兩者之間,能平滑過渡到下一個版本。先把新版本推送給少量使用者,確認穩定後再擴大比例,藉此把新功能上線的風險壓到最低。這種「灰度發布(Canary Release)」的精神,對缺少完整測試或對新版本沒把握的場景特別實用。
落實步驟#
- 啟動 v1 版本,搭配 Ingress 對外提供服務。
- 部署 v2 版本,等待完全就緒。此時新舊兩版本並存於叢集中。
- 加入 Canary Ingress,並設定欲分流到 v2 的權重。
- 觀察一段時間後若條件成立,把主 Ingress 指向 v2,並刪除 Canary Ingress。
- 關閉舊的 v1 版本資源。
實戰練習#
1. 啟動 v1 與 Ingress#
# app-v1.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo-deployment
labels:
app: my-app
spec:
replicas: 1
selector:
matchLabels:
app: my-app
version: v1
template:
metadata:
labels:
app: my-app
version: v1
spec:
containers:
- name: foo
image: mikehsu0618/foo
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: foo-service
spec:
selector:
app: my-app
version: v1
type: NodePort
ports:
- protocol: TCP
port: 8080
targetPort: 8080# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
spec:
ingressClassName: nginx
defaultBackend:
service:
name: foo-service
port:
number: 8080kubectl apply -f app-v1.yaml,ingress.yaml連續打十次驗證對外只有 v1:
for i in {1..10}; do curl localhost; echo; done
# 預期會看到 10 筆 {"data":"Hello foo"}2. 部署 v2 但暫不接流量#
# app-v2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: bar-deployment
labels:
app: my-app
spec:
replicas: 1
selector:
matchLabels:
app: my-app
version: v2
template:
metadata:
labels:
app: my-app
version: v2
spec:
containers:
- name: bar
image: mikehsu0618/bar
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: bar-service
spec:
selector:
app: my-app
version: v2
type: NodePort
ports:
- protocol: TCP
port: 8080
targetPort: 8080kubectl apply -f app-v2.yaml再打一次驗證仍然只有 v1 接受外部請求:
for i in {1..10}; do curl localhost; echo; done
# 預期仍是 10 筆 {"data":"Hello foo"}3. 加入 Canary Ingress 進行權重分流#
# canary-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "10"
name: canary-ingress
spec:
ingressClassName: nginx
defaultBackend:
service:
name: bar-service
port:
number: 8080
canary-weight: "10"代表把 10% 流量分到bar-service,其餘 90% 留在foo-service。
kubectl apply -f canary-ingress.yaml再次發出十筆請求,可預期約 10% 會被導向 v2:
for i in {1..10}; do curl localhost; echo; done
# 大致上會看到一筆 {"data":"Hello bar"},其餘為 {"data":"Hello foo"}4. 完成切換#
確認 v2 表現穩定後,把主 Ingress 後端改為 bar-service,刪除 Canary Ingress 與舊的 foo-deployment、foo-service,整個金絲雀部署就告一段落。
小結#
進階部署策略不外乎組合 Deployment、Service、Pod、Ingress 這幾位老朋友,再以 Label、Selector、Annotation 控制流量。Nginx Ingress Controller 是個 LoadBalancer 層的小宇宙,深入研究會更有收穫;但更重要的是把基礎概念內化,遇到任何花式部署需求都能自己拼出對應的解法。
原文出處#
- GitHub:https://github.com/MikeHsu0618/2022-ithelp/tree/main/Day15
- iThome:https://ithelp.ithome.com.tw/articles/10290852