手把手教你在 TKE 叢集中實現簡單的藍綠髮布和灰度釋出
阿新 • • 發佈:2020-09-25
## 概述
如何在騰訊雲 Kubernetes 叢集實現藍綠髮布和灰度釋出?通常要向叢集額外部署其它開源工具來實現,比如 Nginx Ingress,Traefik 等,或者讓業務上 Service Mesh(服務網格),利用服務網格的能力來實現。這些方案多多少少都是需要一點點門檻的,如果藍綠髮布或灰度釋出的需求不復雜,同時不希望讓叢集引入更多的元件或複雜的用法,可以考慮使用本文的簡單方案,利用 Kubernetes 原生的特性以及騰訊雲 TKE/EKS 叢集自帶的 LB 外掛實現簡單的藍綠髮布和灰度釋出。
> **注**: 本文適用產品範圍: TKE 叢集、EKS 叢集 (彈性叢集)
## 原理介紹
我們通常使用 Deployment、StatefulSet 等 Kubernetes 自帶的工作負載來部署業務,每個工作負載都管理一組 Pod,以 Deployment 為例:
![img](https://img2020.cnblogs.com/other/2041406/202009/2041406-20200925175954169-483726354.png)
通常還會為每個工作負載建立對應的 Service,Service 通過 selector 來匹配後端 Pod,其它服務或者外部通過訪問 Service 即可訪問到後端 Pod 提供的服務。要對外暴露可以直接將 Service 型別設定為 LoadBalancer,LB 外掛會自動為其建立 CLB (騰訊雲負載均衡器) 作為流量入口。
如何實現藍綠髮布?以 Deployment 為例,叢集中部署兩個不同版本的 Deployment,它們的 Pod 擁有共同的 label,但有一個 label 的值不同,用於區分不同的版本,Service 使用 selector 選中了其中一個版本的 Deployment 的 Pod,通過修改 Service 的 selector 中決定 服務版本的 label 的值來改變 Service 後端對應的 Deployment,實現讓服務從一個版本直接切換到另一個版本,即藍綠髮布:
![img](https://img2020.cnblogs.com/other/2041406/202009/2041406-20200925175954392-1105736612.png)
如何實現灰度釋出?雖然我們通常會為每個工作負載都建立一個 Service,但 Kubernetes 並沒有限制 Service 一定要與工作負載一一對應,因為 Service 是通過 selector 來匹配後端 Pod 的,只要不同工作負載的 Pod 都能被相同 selector 選中,就可以實現一個 Service 對應多個版本的工作負載的效果,調整不同版本工作負載的副本數就相當於調整不同版本服務的權重,實現灰度釋出:
![img](https://img2020.cnblogs.com/other/2041406/202009/2041406-20200925175954578-223329541.png)
## 使用 YAML 建立資源
本文的示例將使用 yaml 的方式部署工作負載和建立 Service,有兩種操作方式。
方式一:在 TKE 或 EKS 控制檯右上角點選 `YAML 建立資源`,然後將本文示例的 yaml 貼上進去:
![img](https://img2020.cnblogs.com/other/2041406/202009/2041406-20200925175954790-1702202229.png)
方式二:將示例的 yaml 儲存成檔案,然後使用 kubectl 指定 yaml 檔案來建立,如: `kubectl apply -f xx.yaml` 。
## 部署多版本工作負載
要實現藍綠髮布或灰度釋出,首先我們需要在叢集中部署多個版本的工作負載,這裡以簡單的 nginx 為例,部署第一個版本:
```
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-v1
spec:
replicas: 3
selector:
matchLabels:
app: nginx
version: v1
template:
metadata:
labels:
app: nginx
version: v1
spec:
containers:
- name: nginx
image: "openresty/openresty:centos"
ports:
- name: http
protocol: TCP
containerPort: 80
volumeMounts:
- mountPath: /usr/local/openresty/nginx/conf/nginx.conf
name: config
subPath: nginx.conf
volumes:
- name: config
configMap:
name: nginx-v1
---
apiVersion: v1
kind: ConfigMap
metadata:
labels:
app: nginx
version: v1
name: nginx-v1
data:
nginx.conf: |-
worker_processes 1;
events {
accept_mutex on;
multi_accept on;
use epoll;
worker_connections 1024;
}
http {
ignore_invalid_headers off;
server {
listen 80;
location / {
access_by_lua '
local header_str = ngx.say("nginx-v1")
';
}
}
}
```
再部署第二個版本:
```
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-v2
spec:
replicas: 3
selector:
matchLabels:
app: nginx
version: v2
template:
metadata:
labels:
app: nginx
version: v2
spec:
containers:
- name: nginx
image: "openresty/openresty:centos"
ports:
- name: http
protocol: TCP
containerPort: 80
volumeMounts:
- mountPath: /usr/local/openresty/nginx/conf/nginx.conf
name: config
subPath: nginx.conf
volumes:
- name: config
configMap:
name: nginx-v2
---
apiVersion: v1
kind: ConfigMap
metadata:
labels:
app: nginx
version: v2
name: nginx-v2
data:
nginx.conf: |-
worker_processes 1;
events {
accept_mutex on;
multi_accept on;
use epoll;
worker_connections 1024;
}
http {
ignore_invalid_headers off;
server {
listen 80;
location / {
access_by_lua '
local header_str = ngx.say("nginx-v2")
';
}
}
}
```
可以在控制檯看到部署的情況:
![img](https://img2020.cnblogs.com/other/2041406/202009/2041406-20200925175954957-1965552765.png)
## 實現藍綠髮布
為我們部署的 Deployment 建立 LoadBalancer 型別的 Service 對外暴露服務,指定使用 v1 版本的服務:
```
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
type: LoadBalancer
ports:
- port: 80
protocol: TCP
name: http
selector:
app: nginx
version: v1
```
測試訪問:
```
$ for i in {1..10}; do curl EXTERNAL-IP; done; # 替換 EXTERNAL-IP 為 Service 的 CLB IP 地址
nginx-v1
nginx-v1
nginx-v1
nginx-v1
nginx-v1
nginx-v1
nginx-v1
nginx-v1
nginx-v1
nginx-v1
```
全是 v1 版本的響應,現在我們切到 v2 版本,修改 Service 的 selector,讓它選中 v2 版本的服務,如果在控制檯改,先找到對應 Service,點選 `編輯YAML`:
![img](https://img2020.cnblogs.com/other/2041406/202009/2041406-20200925175955555-1446839510.png)
修改 selector 部分:
```
selector:
app: nginx
version: v2
```
或者也可以直接用 kubectl 修改:
```
kubectl patch service nginx -p '{"spec":{"selector":{"version":"v2"}}}'
```
再次測試訪問:
```
$ for i in {1..10}; do curl EXTERNAL-IP; done; # 替換 EXTERNAL-IP 為 Service 的 CLB IP 地址
nginx-v2
nginx-v2
nginx-v2
nginx-v2
nginx-v2
nginx-v2
nginx-v2
nginx-v2
nginx-v2
nginx-v2
```
全是 v2 版本的響應,成功實現了藍綠髮布。
## 實現灰度釋出
相比藍綠髮布,我們為不給 Service 指定使用 v1 版本的服務,從 selector 中刪除 `version` 標籤,讓 Service 同時選中兩個版本的 Deployment 的 Pod:
```
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
type: LoadBalancer
ports:
- port: 80
protocol: TCP
name: http
selector:
app: nginx
```
測試訪問:
```
$ for i in {1..10}; do curl EXTERNAL-IP; done; # 替換 EXTERNAL-IP 為 Service 的 CLB IP 地址
nginx-v1
nginx-v1
nginx-v2
nginx-v2
nginx-v2
nginx-v1
nginx-v1
nginx-v1
nginx-v2
nginx-v2
```
可以看到,一半是 v1 版本的響應,另一半是 v2 版本的響應。現在我們來調節 v1 和 v2 版本的 Deployment 的副本,將 v1 版本調至 1 個副本,v2 版本調至 4 個副本。
可以通過控制檯操作:
![img](https://img2020.cnblogs.com/other/2041406/202009/2041406-20200925175956187-1725336464.png)
也可以通過 kubectl 操作:
```
kubectl scale deployment/nginx-v1 --replicas=1
kubectl scale deployment/nginx-v2 --replicas=4
```
然後再次進行訪問測試:
```
$ for i in {1..10}; do curl EXTERNAL-IP; done; # 替換 EXTERNAL-IP 為 Service 的 CLB IP 地址nginx-v2nginx-v1nginx-v2nginx-v2nginx-v2nginx-v2nginx-v1nginx-v2nginx-v2nginx-v2$ for i in {1..10}; do curl EXTERNAL-IP; done; # 替換 EXTERNAL-IP 為 Service 的 CLB IP 地址
nginx-v2
nginx-v1
nginx-v2
nginx-v2
nginx-v2
nginx-v2
nginx-v1
nginx-v2
nginx-v2
nginx-v2
```
可以看到,10 次訪問中只有 2 次返回了 v1 版本,v1 與 v2 的響應比例與其副本數比例一致,為 1:4,通過控制不同版本服務的副本數就實現了灰度釋出。
## 總結
本文我們介紹瞭如何在有限的條件下在 Kubernetes 叢集中實現簡單的藍綠髮布與灰度釋出,對於一些簡單的釋出需求場景可以考慮使用這種方案。
>【騰訊雲原生】雲說新品、雲研新術、雲遊新活、雲賞資訊,掃碼關注同名公眾號,及時獲取更多幹貨!!
![](https://img2020.cnblogs.com/other/2041406/202009/2041406-20200925175956664-450980