假設當前線上環境咱們已經有一套服務 app-old 對外提供 7 層服務,此時咱們修復了一些問題,須要灰度發佈上線一個新的版本 app-new,可是咱們又不但願簡單直接地將全部客戶端流量切換到新版本 app-new 中,而是但願僅僅切換 20% 的流量到新版本 app-new 中,待運行一段時間穩定,將全部的流量切換到 app-new 服務中後,再平滑地下線掉 app-old 服務。nginx
針對以上多種不一樣的應用發佈需求,K8S Ingress Controller 支持了多種流量切分方式:git
基於 Request Header 的流量切分,適用於灰度發佈以及 AB 測試場景github
基於 Cookie 的流量切分,適用於灰度發佈以及 AB 測試場景api
基於 Query Param 的流量切分,適用於灰度發佈以及 AB 測試場景session
基於服務權重的流量切分,適用於藍綠髮布場景app
如下測試基於服務權重的流量切分,也能夠將nginx.ingress.kubernetes.io/canary-weight: "30"
改成基於 header 的流量切分。curl
老版本程序 app-oldide
app-old.yaml
測試
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: app-old spec: replicas: 2 selector: matchLabels: run: app-old template: metadata: labels: run: app-old spec: containers: - image: zouhl/app:v2.1 imagePullPolicy: Always name: app-old ports: - containerPort: 80 protocol: TCP restartPolicy: Always --- apiVersion: v1 kind: Service metadata: name: app-old spec: ports: - port: 80 protocol: TCP targetPort: 80 selector: run: app-old sessionAffinity: None type: NodePor
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: app-old spec: replicas: 2 selector: matchLabels: run: app-old template: metadata: labels: run: app-old spec: containers: - image: zouhl/app:v2.1 imagePullPolicy: Always name: app-old ports: - containerPort: 80 protocol: TCP restartPolicy: Always --- apiVersion: v1 kind: Service metadata: name: app-old spec: ports: - port: 80 protocol: TCP targetPort: 80 selector: run: app-old sessionAffinity: None type: NodePor
ui
老版本的 ingress
app-v1.yaml
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: my-app labels: app: my-app annotations: kubernetes.io/ingress.class: nginx namespace: default spec: rules: - host: test.192.168.2.20.xip.io http: paths: - backend: serviceName: app-old servicePort: 80 path: /
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: my-app labels: app: my-app annotations: kubernetes.io/ingress.class: nginx namespace: default spec: rules: - host: test.192.168.2.20.xip.io http: paths: - backend: serviceName: app-old servicePort: 80 path: /
在 k8s 中建立
kubectl create -f app-old.yaml kubectl create -f app-v1.yaml
kubectl create -f app-old.yaml kubectl create -f app-v1.yaml
新版本app-new.yaml
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: app-new spec: replicas: 2 selector: matchLabels: run: app-new template: metadata: labels: run: app-new spec: containers: - image: zouhl/app:v2.2 imagePullPolicy: Always name: app-new ports: - containerPort: 80 protocol: TCP restartPolicy: Always --- apiVersion: v1 kind: Service metadata: name: app-new spec: ports: - port: 80 protocol: TCP targetPort: 80 selector: run: app-new sessionAffinity: None type: NodePort
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: app-new spec: replicas: 2 selector: matchLabels: run: app-new template: metadata: labels: run: app-new spec: containers: - image: zouhl/app:v2.2 imagePullPolicy: Always name: app-new ports: - containerPort: 80 protocol: TCP restartPolicy: Always --- apiVersion: v1 kind: Service metadata: name: app-new spec: ports: - port: 80 protocol: TCP targetPort: 80 selector: run: app-new sessionAffinity: None type: NodePort
新版本 canary-ingress
app-v2-canary.yaml
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: my-app-canary labels: app: my-app annotations: kubernetes.io/ingress.class: nginx nginx.ingress.kubernetes.io/canary: "true" nginx.ingress.kubernetes.io/canary-weight: "30" namespace: default spec: rules: - host: test.192.168.2.20.xip.io http: paths: - backend: serviceName: app-new servicePort: 80 path: /
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: my-app-canary labels: app: my-app annotations: kubernetes.io/ingress.class: nginx nginx.ingress.kubernetes.io/canary: "true" nginx.ingress.kubernetes.io/canary-weight: "30" namespace: default spec: rules: - host: test.192.168.2.20.xip.io http: paths: - backend: serviceName: app-new servicePort: 80 path: /
新版本 ingress yaml
app-v2.yaml
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: my-app labels: app: my-app annotations: kubernetes.io/ingress.class: nginx namespace: default spec: rules: - host: test.192.168.2.20.xip.io http: paths: - backend: serviceName: app-new servicePort: 80 path: /
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: my-app labels: app: my-app annotations: kubernetes.io/ingress.class: nginx namespace: default spec: rules: - host: test.192.168.2.20.xip.io http: paths: - backend: serviceName: app-new servicePort: 80 path: /
$ tree . ├── app-new.yaml ├── app-old.yaml ├── app-v1.yaml ├── app-v2-canary.yaml └── app-v2.yaml
$ tree . ├── app-new.yaml ├── app-old.yaml ├── app-v1.yaml ├── app-v2-canary.yaml └── app-v2.yaml
app-v1 已經發布了,如今灰度發佈第二版,權重爲 30%,nginx.ingress.kubernetes.io/canary-weight: "30"
,更多參數參考github
kubectl create -f app-new.yaml kubectl create -f app-v2-canary.yaml
kubectl create -f app-new.yaml kubectl create -f app-v2-canary.yaml
檢查
$ kubectl get ingresses.extensions NAME HOSTS ADDRESS PORTS AGE app-ingress www.example.com 80 109m my-app test.192.168.2.20.xip.io 80 25m my-app-canary test.192.168.2.20.xip.io 80 1s nginx-test nginx.192.168.2.20.xip.io 80 3h12m
$ kubectl get ingresses.extensions NAME HOSTS ADDRESS PORTS AGE app-ingress www.example.com 80 109m my-app test.192.168.2.20.xip.io 80 25m my-app-canary test.192.168.2.20.xip.io 80 1s nginx-test nginx.192.168.2.20.xip.io 80 3h12m
在後臺觀察,70% to v1,30% to v2
$ while sleep 0.5; do curl "test.192.168.2.20.xip.io";echo; done {"v2.2 hostname":"app-new-658dfc9c6b-lbmvr"} {"v2.2 hostname":"app-new-658dfc9c6b-qhwtg"} {"v1 hostname":"app-old-64fd44b699-4hvlb"} {"v1 hostname":"app-old-64fd44b699-zb58f"}
$ while sleep 0.5; do curl "test.192.168.2.20.xip.io";echo; done {"v2.2 hostname":"app-new-658dfc9c6b-lbmvr"} {"v2.2 hostname":"app-new-658dfc9c6b-qhwtg"} {"v1 hostname":"app-old-64fd44b699-4hvlb"} {"v1 hostname":"app-old-64fd44b699-zb58f"}
若是一切正常則能夠正式發佈
# delete the canary ingress kubectl delete -f app-v2-canary.yaml # set 100% traffic to v2 kubectl apply -f app-v2.yaml
# delete the canary ingress kubectl delete -f app-v2-canary.yaml # set 100% traffic to v2 kubectl apply -f app-v2.yaml
檢查 ingress
$ kubectl get ingresses.extensions NAME HOSTS ADDRESS PORTS AGE app-ingress www.example.com 80 109m my-app test.192.168.2.20.xip.io 80 25m nginx-test nginx.192.168.2.20.xip.io 80 3h13m $ while sleep 0.5; do curl "test.192.168.2.20.xip.io";echo; done {"v2.2 hostname":"app-new-658dfc9c6b-lbmvr"} {"v2.2 hostname":"app-new-658dfc9c6b-qhwtg"}
$ kubectl get ingresses.extensions NAME HOSTS ADDRESS PORTS AGE app-ingress www.example.com 80 109m my-app test.192.168.2.20.xip.io 80 25m nginx-test nginx.192.168.2.20.xip.io 80 3h13m $ while sleep 0.5; do curl "test.192.168.2.20.xip.io";echo; done {"v2.2 hostname":"app-new-658dfc9c6b-lbmvr"} {"v2.2 hostname":"app-new-658dfc9c6b-qhwtg"}