# K8s 部署策略 [TOC] --- ## Recap: K8s Deployment ---- ### strategy - `RollingUpdate` - `Recreate`. ---- ![image](https://hackmd.io/_uploads/BJOYZF-ta.png =800x) ---- ### **MaxSurge** and **MaxUnavailable** ![image](https://hackmd.io/_uploads/r1IfZtbta.png) --- ## 部署側略 ---- ### 情境 ---- ㄟ我 IG 首頁怎麼跟妳不一樣?! ---- 有人不喜歡有人喜歡 ---- 哭啊我 Uber Eats 進不去 ---- (應該) 都沒人喜歡? ![image](https://hackmd.io/_uploads/B1XVP3cYa.png) ---- ### 一個好的部署策略 一個好的部署策略應該是要具備盡可能的 - 讓服務不中斷 - 戰略性的試試市場水溫 - 回退到歷史版本的能力 ---- ![image](https://hackmd.io/_uploads/BJOYZF-ta.png =800x) ---- ### 1. 重建部署 (Recreate) 重建部署是一個成本偏沈重的方式 簡單來說它會將舊版本完全下線後才開始上線新版本,這意味著你的服務將會有一段停機時間依賴於應用下線跟啟動的耗時。 ---- #### 優點: - 便於設定。 - 線上只會同時運行一種版本。 - 部署過程中不會造成主機額外負擔。 ---- #### 缺點: - 對使用者影響大,預期的停機時間取決於下線時間和啟動服務的耗時。 ---- ### 2. 滾動部署 (Rolling-update) 滾動部署策略就是指容器會如同水漸漸地往傾斜的地方聚集一樣的更新版本,能緩慢平和的釋出新版本。 如同水流的流速快慢一樣,滾動部署也能通過調整下列引述來調整部署穩定性以及速率: ---- #### 參數 - 最大執行數:可以同時釋出的服務數目。 - 最大峰值:升級過程中最多可以比原先設定所多出的服務數量。 - 最大不可用數:最多可以有幾個服務處在無法服務的狀態。 ---- #### 優點: 相較於藍綠部署更加節省資源。 便於設定,服務不中斷。 ---- #### 缺點: - 釋出與回滾耗時,想想當我們有 100 個服務,每次需要花五分鐘更新其中 10 個,當更新到第 80 個時發現錯誤需要緊急回滾的情況? - 部署期間新舊兩版服務都會同時在線上運作,無法控制流量且噴錯時除錯困難高。 ---- ### 3. 藍綠部署 (Blue / Green) 相較於滾動更新,藍綠部署則是會先將新版本的服務完整的開啟,並且在新版本滿足上線條件的測試後,才將流量在負載均衡層從舊版本切換到新版本。 ---- #### 優點: - 實時釋出、回滾。 - 避免新舊版本衝突,整個過程同時只會有一個版本存在。 - 服務不中斷。 ---- #### 缺點: - 部署完成前需要雙倍的資源要求所增加的開銷及成本。有時新版本通過不了測試時,舊版本將持續運行到新版本通過為止。 - 當切換到新版本的瞬間,如果有未處理完成的業務將會是比較麻煩的問題。 ---- ### 4. 金絲雀部署 (Canary) 金絲雀部署,與藍綠部署不同的是,它不是非黑即白的部署方式,所以又稱為灰度部署。 灰度部署是指在黑與白之間,能夠平滑過度的一種部署方式。我們能夠緩慢的將新版本先推廣到一小部分的使用者,驗證沒有問題後才完成部署,以降低生產環境引入新功能帶來的風險 ---- #### 使用情境 例如將 90% 的請求導向舊版本,10% 的請求轉向新版本。這種部署大多用於缺少可靠測試或者對新版本穩定性缺乏信心的情況下 ---- #### 金絲雀??? 金絲雀部署的命名來自於 17 世紀的礦井工人發現金絲雀對瓦斯這種氣體非常敏感,哪怕是只有及其微量的瓦斯,金絲雀也會停止歌唱率先比人類出現不良反應,所以工人每次下井時都會帶上一隻金絲雀作為危險狀況下的救命符 ---- #### 優點: - 方便除錯以及監控。 - 只向一小部分使用者釋出。 - 快速回滾、快速迭代。 ---- #### 缺點: - 完整釋出期漫長。 - 只能適用於相容迭代的方式,如果是大版本不相容就沒辦法使用這種方式了 ---- ### 5. A / B 測試 (A / B Testing) A / B 測試實際上是一種基於統計信息而非部署策略來製定業務決策的技術,與業務結合非常緊密。但是它們也是相關的,也可以使用金絲雀發布來實現。 除了基於權重在版本之間進行流量控制之外,A / B 測試還可以基於一些其他參數(比如**Cookie**、**User Agent**、**地區**等等)來精確定位給定的用戶群,該技術廣泛用於測試一些功能特性的效果,然後按照效果來進行確定。 ---- #### 假設檢定 假設檢驗是用來判斷樣本與樣本,樣本與總體之間的差異是由抽樣誤差引起還是本質差別造成的統計推斷方法,其基本原理是先對總體特徵做出某種假設,然後通過抽樣研究的統計推理,對此假設應該被拒絕還是接受做出判斷,假設檢驗使用了一種類似反證法的推理,通過A/B Test,我們要驗證的其實是一對假設,原假設和備擇假設。 假設檢驗的基本思路是“小概率事件在少量實驗中是幾乎不可能出現的”,如果在實驗中出現了小概率事件,那麼我們就可以推斷原假設是錯誤的,從而推斷備擇假設可能是正確的。 ---- 蛤你在說什麼? ---- 等等好像看過類似的情境ㄟ ---- 若天堂M紫布製作成功率$p=5%$,則: 虛無假設 $H_0:p=0.05$ 對立假設 $H_1:p<0.05$ 顯著水準$=0.05$ ---- 《第一次直播4/175》 丁特實抽機率 $\hat{p}=4/175$ 求得 $P-value=0.049779$ 《第二次直播7/300》 丁特實抽機率 $\hat{p}=7/300$ 求得 $P-value=0.017045$ 《總計11/475》 丁特實抽機率$\hat{p} =11/475$ 求得 $P-value=0.003637$ ---- 三次計算結果的 $P-value$ 分別為 $0.049779、0.017045、0.003637$ 當 $P-value < 0.05$ (顯著水準),就必需拒絕 $H_0$ 接受 $H_1$ *** 即「拒絕製作成功機率=5%的假設,接受製作成功機率<5%的假設」 而這三次的數值均小於顯著水準 代表不論哪個情況 ---- 天堂M的紫布製作成功機率皆顯著低於5% ---- #### 終於知道為什麼有些人會在限動說: 為什麼我的便利貼不見ㄌ, 可是我看都好好的ㄚ ---- A / B 測試是線上同時執行多個不同版本的服務,這些服務更多的是使用者側的體驗不同,比如**頁面佈局**、**按鈕顏色**,**互動方式**等,通常底層業務邏輯還是一樣的,也就是通常說的換湯不換藥。諸如 Google Analysis 等網站分析工具服務通常也可以搭配**自家負載均衡器**實現 A / B 測試。 ---- #### 優點: - 多版本並行執行 - 完全控制流量分佈 ---- ### 缺點: - 需要更全面的負載均衡(通常由雲端服務實現) - 難以定位辨別(要額外紀錄) ---- ### 6. 影子部署 (Shadow) 影子部署是指在原有版本旁完整運行新版本,並且將流入原有版本的請求同時分發到新版本,得以實現在更新之前就模擬正式產品環境的運作情況,直到滿足上線條件後才將進入點轉往新版本並關閉舊版本。 ---- #### 優點: - 可以直接對正式環境流量進行效能測試而不影響使用者。 - 直到應用穩定且達到上線條件時才釋出。 ---- #### 缺點: - 與藍綠部署一樣需要雙倍的資源請求。 - 配置複雜,容易出現預期外的情況。 --- ## 要怎麼做到ㄚ ---- ### 1. 重建部署 [deployment strategy](https://hackmd.io/@peter12345678/S1mRGUZYT#/2/8) ---- ### 2. 滾動部署 [deployment strategy](https://hackmd.io/@peter12345678/S1mRGUZYT#/2/8) ---- ### 3. 藍綠部署 1. 啟動一個原有的 v1 版本服務並且用 Service 的 label selector 指向該 v1 版本服務管理對外端口。 2. 啟動並且等待我們的 v2 版本完全就緒,此時新舊兩個版本處於同時存在的狀態。 3. 將 Service 的 label selector 從 v1 版本指向到 v2 版本。 4. 確保終止了舊的 v1 版本。 ---- #### V1 and V2 Deployment ---- 開兩個版本的 Deployment ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: foo-deployment labels: app: my-app spec: replicas: 3 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: apps/v1 kind: Deployment metadata: name: bar-deployment labels: app: my-app spec: replicas: 3 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 ``` ---- ### Service Version Transition ---- 先開一個綁 V1 deployment 的 service ```yaml! apiVersion: v1 kind: Service metadata: name: blue-green-service spec: selector: app: my-app version: v1 type: LoadBalancer ports: - protocol: TCP port: 8080 targetPort: 8080 ``` ---- #### Perform Transition ---- 把 label 從 v1 改成 v2 !!! ```diff apiVersion: v1 kind: Service metadata: name: blue-green-service spec: selector: app: my-app - version: v1 + version: v2 type: LoadBalancer ports: - protocol: TCP port: 8080 targetPort: 8080 ``` ---- #### Delete old version Deployment ```shell! kubectl delete deployment bar-deployment ``` ---- ### 4. 金絲雀部署 ---- 哭了這要怎麼用ㄚ ---- ㄟ流量 ---- ㄟˋ Ingress ---- #### Nginx Ingress 在 Kubernetes 中的使用 Ingress 在 Kubernetes 集群中基於七層的 HTTP 和 HTTPS 協議進行轉發,允許通過域名和路徑對訪問進行更細粒度的劃分。作為 Kubernetes 中的一種獨立資源,Ingress 通過創建來定義外部訪問流量的轉發規則,並由 Ingress Controller 負責將流量分配到一個或多個 Service。 ---- 可四ㄌㄟ Ingress要怎麼弄金絲雀? ---- 感覺[上一份簡報](https://hackmd.io/@peter12345678/S1mRGUZYT#/3/6) annotations 好像沒什麼提到 ![image](https://hackmd.io/_uploads/H1gPQ6cYT.png =400x) ---- #### Nginx Ingress 的外部流量切分策略 Nginx Ingress 提供基於 **Header**、**Cookie*、**權重**的三種外部流量切分策略 ---- 1. **`nginx.ingress.kubernetes.io/canary`** - 設定為 `true` 時,將被視為 Canary Ingress,用於流量切分 2. **`nginx.ingress.kubernetes.io/canary-by-header-value`** - 通知 Ingress 如有與 Header 設定值匹配的請求 Header,則轉導流量到 Canary Ingress ---- 3. **`nginx.ingress.kubernetes.io/canary-by-header-pattern`** - 功能類似於 `canary-by-header-value`,可以用 regex 配對 - 注意:若有 `canary-by-header-value` 設定,則此 Annotation 的功能將被忽略 4. **`nginx.ingress.kubernetes.io/canary-by-cookie`** - 通知 Ingress 如有與 Cookie 設定值匹配的請求 Cookie,則轉導流量到 Canary Ingress - 設定為 `always` 時,所有流量將被轉導 ---- 5. **`nginx.ingress.kubernetes.io/canary-weight`** - 數值為基於 0 到 100 的整數,表示有多少百分比的流量將被轉導到 Canary Ingress ---- #### 優先級 優先級由高到低為: 1. `canary-by-header` 2. `canary-by-cookie` 3. `canary-weight` ---- 來看看實際怎麼做 ---- #### V1 and V2 Deployments ---- 開兩個版本的 Deployment ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: foo-deployment labels: app: my-app spec: replicas: 3 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: apps/v1 kind: Deployment metadata: name: bar-deployment labels: app: my-app spec: replicas: 3 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 ``` ---- #### V1 and V2 Services ---- 開兩個版本的 Service 去綁 V1 and V2 deployment ```yaml! apiVersion: v1 kind: Service metadata: name: foo-service spec: selector: app: my-app version: v1 type: NodePort ports: - protocol: TCP port: 8080 targetPort: 8080 --- apiVersion: v1 kind: Service metadata: name: bar-service spec: selector: app: my-app version: v2 type: NodePort ports: - protocol: TCP port: 8080 targetPort: 8080 ``` ---- #### Ingress Transition ---- 先跑一個 Ingress serve V1 的 Service ---- ```yaml! apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-ingress spec: ingressClassName: nginx defaultBackend: service: name: foo-service port: number: 8080 ``` ---- #### 開始金絲雀 ---- 加入一個 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 ``` 設定將 10% 比重的請求分流到 bar-service 這個 v2 版本的服務 ---- #### and then? ---- 將 Ingress 設定為新版本的 v2 並且刪除過渡使用的 Canary Ingress 以及 v1 版本資源即可。 ---- ### 5. A / B 測試 ---- 剛剛有提到這其實是基於統計的策略, 並沒有固定的做法 ---- 但是我統計不太好 ---- 所以我們來嘗試讓中國用戶連不到我們的新服務吧~ ---- ```yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: nginx.ingress.kubernetes.io/canary: "true" nginx.ingress.kubernetes.io/canary-by-header: "X-Country" nginx.ingress.kubernetes.io/canary-by-header-pattern: "^(?!China$).*" name: canary-ingress spec: ingressClassName: nginx defaultBackend: service: name: bar-service port: number: 8080 ``` --- ## Thank you for participating! --- ## Reference - [x] 1. [Kubernetes 的組件 ](https://ithelp.ithome.com.tw/articles/10287576) - [x] 2. [實戰做一個 Pod](https://ithelp.ithome.com.tw/articles/10288199) - [x] 3. [實戰做一個 Service](https://ithelp.ithome.com.tw/articles/10288389) - [x] 4. [實戰做一個 Deployment](https://ithelp.ithome.com.tw/articles/10288602) - [x] 5. [Ingress](https://ithelp.ithome.com.tw/articles/10288843) - [x] 6. [部署策略](https://ithelp.ithome.com.tw/articles/10289496) - [x] [Grayscale](https://ithelp.ithome.com.tw/articles/10290317) - [x] [Canry](https://ithelp.ithome.com.tw/articles/10290852)
{"contributors":"[{\"id\":\"60f87ada-c8bc-4f5d-9b91-2a0d3103440d\",\"add\":11572,\"del\":1547}]","title":"K8s 第三週 [部署策略]-[Deployment, Service, Ingress]","description":"type: slide"}
    319 views