實驗的環境:
1、已經搭建好k8s環境的3臺 centos 7.9機器。1個master,2個worker節點。 k8s版本爲1.20.4
2、在k8s裏安裝好 ingress nginx,安裝教程請看
實戰
一、創建一個常規的應用
1、將鏡像文件 openresty.tar.gz傳入 2個workder節點,docker load 安裝鏡像;這個鏡像相當於nginx的一個高級版本。
2、創建1個常規的應用,包括 Deployment,Service,Ingress資源。
v1.yaml
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-v1 spec: replicas: 1 selector: matchLabels: app: nginx version: v1 template: metadata: labels: app: nginx version: v1 spec: containers: - name: nginx image: "openresty/openresty:centos" imagePullPolicy: IfNotPresent 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: v1 kind: Service metadata: name: nginx-v1 spec: type: ClusterIP ports: - port: 80 protocol: TCP name: http selector: app: nginx version: v1
v1-ingress.yaml
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: nginx annotations: kubernetes.io/ingress.class: nginx spec: rules: - host: canary.example.com http: paths: - backend: serviceName: nginx-v1 servicePort: 80 path: /
從yaml文件中,我們可以看到,創建了一個應用,標籤 app爲nginx,version爲v1 .
只返回一句話 "nginx-v1" 。訪問的域名爲 :canary.example.com
kubectl apply -f 執行這2個yaml文件,等待1分鐘左右。
3、訪問驗證一下:
$ curl -H "Host: canary.example.com" http://EXTERNAL-IP # EXTERNAL-IP 替換爲Nginx Ingress 自身對外暴露的 IP
我的實驗環境爲:
curl -H "Host: canary.example.com" http://192.168.24.21
返回 nginx-v1
4、創建一個灰度版本(金絲雀版本)的資源 ,包 Deploy,Service
v2.yaml
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-v2 spec: replicas: 1 selector: matchLabels: app: nginx version: v2 template: metadata: labels: app: nginx version: v2 spec: containers: - name: nginx image: "openresty/openresty:centos" imagePullPolicy: IfNotPresent 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") '; } } } --- apiVersion: v1 kind: Service metadata: name: nginx-v2 spec: type: ClusterIP ports: - port: 80 protocol: TCP name: http selector: app: nginx version: v2
從yaml文件中,我們可以看到,創建了一個應用,標籤 app爲nginx,version爲v2.
只返回一句話 "nginx-v2" 。
執行v2.yaml
二、基於 Header 的流量切分
1、創建 Canary Ingress,指定 v2 版本的後端服務,且加上一些 annotation,實現僅將帶有名爲Region 且值爲 cd 或 sz 的請求頭的請求轉發給當前 Canary Ingress,模擬灰度新版本給成都和深圳地域的用戶:
v2-ingress.yaml
apiVersion: extensions/v1beta1 kind: Ingress metadata: annotations: kubernetes.io/ingress.class: nginx nginx.ingress.kubernetes.io/canary: "true" nginx.ingress.kubernetes.io/canary-by-header: "Region" nginx.ingress.kubernetes.io/canary-by-header-pattern: "cd|sz" name: nginx-canary spec: rules: - host: canary.example.com http: paths: - backend: serviceName: nginx-v2 servicePort: 80 path: /
這個與v1.ingress.yaml文件相比,host域名是相同的,serviceName指向 nginx-v2版本。加了一些註解。
執行這個yaml文件。
kubectl apply -f v2-ingress.yaml
2、測試訪問:
$ curl -H "Host: canary.example.com" -H "Region: cd" http://EXTERNAL-IP #EXTERNAL-IP 替換爲 Nginx Ingress 自身對外暴露的 IP
[root@k8s-master ingress-nginx]# curl -H "Host: canary.example.com" -H "Region: bj" http://192.168.24.21 nginx-v1 [root@k8s-master ingress-nginx]# curl -H "Host: canary.example.com" -H "Region: cd" http://192.168.24.21 nginx-v2
可以看到,只有 header Region 爲 cd 或 sz 的請求才由 v2 版本服務響應。
三、基於 Cookie 的流量切分:
1、與前面 Header 類似,不過使用 Cookie 就無法自定義 value 了,這裏以模擬灰度成都地域用戶爲例,僅將帶有名爲 user_from_cd 的 cookie 的請求轉發給當前 Canary Ingress 。先刪除前面基於 Header 的流量切分的 Canary Ingress,然後創建下面新的 Canary Ingress:
kubectl delete -f v2-ingress.yaml
2、vim v1-cookie.yaml
apiVersion: extensions/v1beta1 kind: Ingress metadata: annotations: kubernetes.io/ingress.class: nginx nginx.ingress.kubernetes.io/canary: "true" nginx.ingress.kubernetes.io/canary-by-cookie: "user_from_cd" name: nginx-canary spec: rules: - host: canary.example.com http: paths: - backend: serviceName: nginx-v2 servicePort: 80 path: /
kubectl apply -f v1-cookie.yaml
3、驗證
[root@k8s-master ingress-nginx]# curl -s -H "Host: canary.example.com" --cookie "user_from_cd=always" http://192.168.24.21 nginx-v2 [root@k8s-master ingress-nginx]# curl -s -H "Host: canary.example.com" --cookie "user_from_cd=111" http://192.168.24.21 nginx-v1
[root@k8s-master ingress-nginx]# curl -s -H "Host: canary.example.com" http://192.168.24.21 nginx-v1
可以看到,只有 cookie user_from_cd 爲 always 的請求才由 v2 版本的服務響應。
四、基於服務權重的流量切分
1、基於服務權重的 Canary Ingress 就簡單了,直接定義需要導入的流量比例,這裏以導入 10% 流量到 v2 版本爲例 (如果有,先刪除之前的 Canary Ingress):
kubectl delete -f v1-cookie.yaml
2、vim v1-weight.yaml
apiVersion: extensions/v1beta1 kind: Ingress metadata: annotations: kubernetes.io/ingress.class: nginx nginx.ingress.kubernetes.io/canary: "true" nginx.ingress.kubernetes.io/canary-weight: "10" name: nginx-canary spec: rules: - host: canary.example.com http: paths: - backend: serviceName: nginx-v2 servicePort: 80 path: /
kubectl apply -f v1-weight.yaml
3、驗證:
for i in {1..10}; do curl -H "Host: canary.example.com" http://192.168.24.21; done;
[root@k8s-master ingress-nginx]# for i in {1..10}; do curl -H "Host: canary.example.com" http://192.168.24.21; done; nginx-v1 nginx-v1 nginx-v1 nginx-v1 nginx-v1 nginx-v1 nginx-v2 nginx-v1 nginx-v1 nginx-v1
可以看到,大概只有十分之一的機率由 v2 版本的服務響應,符合 10% 服務權重的設置。