實現云容器引擎的灰度發布,常規做法需要在集群中部署如Nginx Ingress或Traefik這樣的開源工具,或者依賴服務網格的功能。這些方案在操作上可能較為復雜,對于只需簡單灰度發布且不希望引入過多額外插件或復雜流程的用戶來說,可以考慮利用Kubernetes自帶的特性來達成目標。這樣,我們不僅能實現簡單的灰度發布和藍綠發布,還能保持系統的簡潔和高效。
原理介紹
用戶在進行業務部署時,通常會選擇利用Kubernetes中的無狀態負載Deployment和有狀態負載StatefulSet等對象,這些對象各自負責管理一組Pod。以Deployment為例,示意圖如下:
在Kubernetes中,為了使得工作負載能夠被外部訪問,用戶通常會為每個工作負載創建一個對應的Service。這個Service通過selector機制來匹配后端Pod,從而建立起訪問路徑。無論是集群內部的其他服務還是集群外部的客戶端,只需訪問這個Service,就能間接訪問到后端Pod所提供的服務。若希望將服務對外暴露,用戶只需將Service的類型設置為LoadBalancer,此時,一個彈性負載均衡器(ELB)將作為流量入口,負責將外部請求轉發到后端Pod。
灰度發布原理
以Deployment為例,按照上述的方式每個Deployment創建一個Service,Service通過selector匹配后端Pod,通過Service最終訪問到業務Pod。使不同Deployment的Pod被同一Service的selector選中,即表示同一Service可以訪問不同Deployement的Pod。調整不同版本Deployment的副本數,即可調整路由到不同版本負載的流量比例,實現灰度發布。示意圖如下:
藍綠發布原理
以Deployment為例,假設在集群中已經部署了兩個不同版本的Deployment,這些Pod都擁有一些共同的標簽,但其中有一個標簽的值是不同的,這個值用于區分它們的版本。Service在選擇后端Pod時,會依據這些標簽。如果想要更改Service后端對應的Pod,即實現服務從一個版本切換到另一個版本,我們只需修改Service的selector中那個用于區分版本的標簽的值。這樣,Service就會自動將流量轉發到新版本的Pod上,從而實現了版本的平滑切換。示意圖如下:
示例說明
前提條件
已上傳測試鏡像至容器鏡像服務,鏡像包含v1和v2兩個版本。
資源創建方式
本文提供以下兩種方式使用YAML部署Deployment和Service:
- 方式1:在無狀態工作負載頁面,單擊上方的“新增YAML”,再將本文示例的YAML文件內容輸入編輯窗中。
- 方式2:將本文的示例YAML保存為文件,再使用kubectl指定YAML文件進行創建。例如:kubectl create -f xxx.yaml。
步驟1:部署兩個版本的服務
在集群中部署兩個版本的服務。
1、創建第一個版本的Deployment,名稱以app-v1為例。YAML示例如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-v1
spec:
replicas: 2
selector:
matchLabels:
app: myapp
version: v1
template:
metadata:
labels:
app: myapp
version: v1
spec:
containers:
- image: {repository_url}/myapp:v1
name: container
resources:
limits:
cpu: 100m
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
2、創建第二個版本的Deployment,名稱以app-v2為例。YAML示例如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-v2
spec:
replicas: 2
selector:
matchLabels:
app: myapp
version: v2
template:
metadata:
labels:
app: myapp
version: v2
spec:
containers:
- image: {repository_url}/myapp:v2
name: container
resources:
limits:
cpu: 100m
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
您可以登錄云容器引擎控制臺查看部署情況。
步驟2:實現灰度發布
1、為了將部署的Deployment服務暴露給外部訪問,我們為其創建一個類型為LoadBalancer的Service。在這個Service的配置中,不會在其selector中指定特定的版本標簽,這樣做的目的是讓Service能夠同時選中并暴露兩個不同版本Deployment的Pod。通過這種方式,外部流量可以通過這個Service訪問到這兩個版本的Pod,從而實現了服務的對外暴露和版本共存。YAML示例如下:
apiVersion: v1
kind: Service
metadata:
annotations:
service.beta.kubernetes.io/ctyun-loadbalancer-id: 586c97da-a47c-467c-a615-bd25a20de39c # ELB實例的ID,請替換為實際取值
name: app-service
spec:
ports:
- name: service0
port: 80
protocol: TCP
targetPort: 80
selector: # selector中不包含version信息
app: myapp
type: LoadBalancer # 類型為LoadBalancer
2、沒有內置的機制來按權重分配流量,因此流量可能會隨機分配到這兩個版本的Pod上。要驗證這一點,你可以多次訪問Service并觀察響應是否來自不同的版本。訪問若干次,結果一半為v1的響應,一半為v2的響應。
3、通過控制臺或kubectl方式調整Deployment的副本數,將v1版本調至4個副本,v2版本調至1個副本。
kubectl scale deployment/app-v1 --replicas=4
kubectl scale deployment/app-v2 --replicas=1
4、再次訪問若干次,結果中v1與v2版本的響應比例與其副本數比例一致,為4:1。通過控制不同版本服務的副本數就實現了灰度發布。
步驟3:實現藍綠發布
1、為部署的Deployment創建LoadBalancer類型的Service對外暴露服務,指定使用v1版本的服務。YAML示例如下:
apiVersion: v1
kind: Service
metadata:
annotations:
service.beta.kubernetes.io/ctyun-loadbalancer-id: 586c97da-a47c-467c-a615-bd25a20de39c # ELB實例的ID,請替換為實際取值
name: app-service
spec:
ports:
- name: service0
port: 80
protocol: TCP
targetPort: 80
selector: # selector中指定version為v1
app: myapp
version: v1
type: LoadBalancer # 類型為LoadBalancer
2、訪問若干次,結果均為v1版本的響應。
3、通過控制臺或kubectl方式修改Service的selector,使其選中v2版本的服務。
kubectl patch service nginx -p '{"spec":{"selector":{"version":"v2"}}}'
4、再次訪問若干次,均為v2版本的響應,成功實現了藍綠發布。