做者 | 子白(阿里雲開發工程師)、溪恆(阿里雲技術專家)html
<關注阿里巴巴雲原生公衆號,回覆 排查 便可下載電子書>node
《深刻淺出 Kubernetes》一書共聚集 12 篇技術文章,幫助你一次搞懂 6 個核心原理,吃透基礎理論,一次學會 6 個典型問題的華麗操做!nginx
Kubernetes 集羣中,業務一般採用 Deployment + LoadBalancer 類型 Service 的方式對外提供服務,其典型部署架構如圖 1 所示。這種架構部署和運維都十分簡單方便,可是在應用更新或者升級時可能會存在服務中斷,引起線上問題。今天咱們來詳細分析下這種架構爲什麼在更新應用時會發生服務中斷以及如何避免服務中斷。git
圖1 業務部署圖github
Deployment 滾動更新時會先建立新 pod,等待新 pod running 後再刪除舊 pod。後端
圖 2 服務中斷示意圖api
中斷緣由:Pod running 後被加入到 Endpoint 後端,容器服務監控到 Endpoint 變動後將 Node 加入到 SLB 後端。此時請求從 SLB 轉發到 Pod 中,可是 Pod 業務代碼還未初始化完畢,沒法處理請求,致使服務中斷,如圖 2 所示。
解決方法:爲 pod 配置就緒檢測,等待業務代碼初始化完畢後後再將 node 加入到 SLB 後端。bash
在刪除舊 pod 過程當中須要對多個對象(如 Endpoint、ipvs/iptables、SLB)進行狀態同步,而且這些同步操做是異步執行的,總體同步流程如圖 3 所示。網絡
圖 3 Deployment 更新時序圖架構
中斷緣由:上述 一、二、三、4步驟同時進行,所以有可能存在 Pod 收到 SIGTERM 信號而且中止工做後,還未從 Endpoints 中移除的狀況。此時,請求從 slb 轉發到 pod 中,而 Pod 已經中止工做,所以會出現服務中斷,如圖 4 所示。
圖 4 服務中斷示意圖
解決方法:爲 pod 配置 preStop Hook,使 Pod 收到 SIGTERM 時 sleep 一段時間而不是馬上中止工做,從而確保從 SLB 轉發的流量還能夠繼續被 Pod 處理。
中斷緣由:當 pod 變爲 termintaing 狀態時,會從全部 service 的 endpoint 中移除該 pod。kube-proxy 會清理對應的 iptables/ipvs 條目。而容器服務 watch 到 endpoint 變化後,會調用 slb openapi 移除後端,此操做會耗費幾秒。因爲這兩個操做是同時進行,所以有可能存在節點上的 iptables/ipvs 條目已經被清理,可是節點還未從 slb 移除的狀況。此時,流量從 slb 流入,而節點上已經沒有對應的 iptables/ipvs 規則致使服務中斷,如圖 5 所示。
圖 5 服務中斷示意圖
解決方法:
圖 6 Cluster 模式請求轉發示意圖
圖 7 Local 模式原地升級時請求轉發示意圖
圖 8 ENI 模式請求轉發示意圖
圖 9 服務中斷示意圖
中斷緣由:容器服務監控到 Endpoints 變化後,會將 Node 從 slb 後端移除。當節點從 slb 後端移除後,SLB 對於繼續發往該節點的長鏈接會直接斷開,致使服務中斷。
解決方法:爲 SLB 設置長連接優雅中斷(依賴具體雲廠商)。
避免服務中斷能夠從 Pod 和 Service 兩類資源入手,接下來將針對上述中斷緣由介紹相應的配置方法。
apiVersion: v1 kind: Pod metadata: name: nginx namespace: default spec: containers: - name: nginx image: nginx # 存活檢測 livenessProbe: failureThreshold: 3 initialDelaySeconds: 30 periodSeconds: 30 successThreshold: 1 tcpSocket: port: 5084 timeoutSeconds: 1 # 就緒檢測 readinessProbe: failureThreshold: 3 initialDelaySeconds: 30 periodSeconds: 30 successThreshold: 1 tcpSocket: port: 5084 timeoutSeconds: 1 # 優雅退出 lifecycle: preStop: exec: command: - sleep - 30 terminationGracePeriodSeconds: 60
注意:須要合理設置就緒檢測(readinessProbe)的探測頻率、延時時間、不健康閾值等數據,部分應用啓動時間自己較長,若是設置的時間太短,會致使 POD 反覆重啓。
apiVersion: v1 kind: Service metadata: name: nginx namespace: default spec: externalTrafficPolicy: Cluster ports: - port: 80 protocol: TCP targetPort: 80 selector: run: nginx type: LoadBalancer
容器服務會將集羣中全部節點掛載到 SLB 的後端(使用 BackendLabel 標籤配置後端的除外),所以會快速消耗 SLB quota。SLB 限制了每一個 ECS 上可以掛載的 SLB 的個數,默認值爲 50,當 quota 消耗完後會致使沒法建立新的監聽及 SLB。
Cluster 模式下,若是當前節點沒有業務 pod 會將請求轉發給其餘 Node。在跨節點轉發時須要作 NAT,所以會丟失源 IP。
apiVersion: v1 kind: Service metadata: name: nginx namespace: default spec: externalTrafficPolicy: Local ports: - port: 80 protocol: TCP targetPort: 80 selector: run: nginx type: LoadBalancer # 須要儘量的讓每一個節點在更新的過程當中有至少一個的Running的Pod # 經過修改UpdateStrategy和利用nodeAffinity儘量的保證在原地rolling update # * UpdateStrategy能夠設置Max Unavailable爲0,保證有新的Pod啓動後才中止以前的pod # * 先對固定的幾個節點打上label用來調度 # * 使用nodeAffinity+和超過相關node數量的replicas數量保證儘量在原地建新的Pod # 例如: apiVersion: apps/v1 kind: Deployment ...... strategy: rollingUpdate: maxSurge: 50% maxUnavailable: 0% type: RollingUpdate ...... affinity: nodeAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 1 preference: matchExpressions: - key: deploy operator: In values: - nginx
容器服務默認會將 Service 對應的 Pod 所在的節點加入到 SLB 後端,所以 SLB quota 消耗較慢。Local 模式下請求直接轉發到 pod 所在 node,不存在跨節點轉發,所以能夠保留源 IP 地址。Local 模式下能夠經過原地升級的方式避免服務中斷,yaml 文件如上。
apiVersion: v1 kind: Service metadata: annotations: service.beta.kubernetes.io/backend-type: "eni" name: nginx spec: ports: - name: http port: 30080 protocol: TCP targetPort: 80 selector: app: nginx type: LoadBalancer
Terway 網絡模式下,經過設置 service.beta.kubernetes.io/backend-type:
"eni" annotation 能夠建立 ENI 模式的 SLB。ENI 模式下,pod會直接掛載到 SLB 後端,不通過 kube-proxy,所以不存在服務中斷的問題。請求直接轉發到 pod,所以能夠保留源 IP 地址。
三種 svc 模式對好比下表所示。
圖 10 Service 對比
選用 ENI 模式的 svc + 設定 Pod 優雅終止 + 就緒檢測。
爲了更多開發者可以享受到 Serverless 帶來的紅利,這一次,咱們集結了 10+ 位阿里巴巴 Serverless 領域技術專家,打造出最適合開發者入門的 Serverless 公開課,讓你即學即用,輕鬆擁抱雲計算的新範式——Serverless。
點擊便可免費觀看課程:https://developer.aliyun.com/learning/roadmap/serverless
「 阿里巴巴雲原生關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,作最懂雲原生開發者的公衆號。」