日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

如何为Kubernetes实现原地升级

發布時間:2024/8/23 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如何为Kubernetes实现原地升级 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

作者 | 王思宇(酒祝) ?阿里云技術專家

參與阿里巴巴云原生文末留言互動,即有機會獲得贈書福利及作者答疑!

概念介紹

原地升級一詞中,“升級”不難理解,是將應用實例的版本由舊版替換為新版。那么如何結合 Kubernetes 環境來理解“原地”呢?

我們先來看看 K8s 原生 workload 的發布方式。這里假設我們需要部署一個應用,包括 foo、bar 兩個容器在 Pod 中。其中,foo 容器第一次部署時用的鏡像版本是 v1,我們需要將其升級為 v2 版本鏡像,該怎么做呢?

  • 如果這個應用使用 Deployment 部署,那么升級過程中 Deployment 會觸發新版本 ReplicaSet 創建 Pod,并刪除舊版本 Pod。如下圖所示:

在本次升級過程中,原 Pod 對象被刪除,一個新 Pod 對象被創建。新 Pod 被調度到另一個 Node 上,分配到一個新的 IP,并把 foo、bar 兩個容器在這個 Node 上重新拉取鏡像、啟動容器。

  • 如果這個應該使用 StatefulSet 部署,那么升級過程中 StatefulSet 會先刪除舊 Pod 對象,等刪除完成后用同樣的名字在創建一個新的 Pod 對象。如下圖所示:

值得注意的是,盡管新舊兩個 Pod 名字都叫 pod-0,但其實是兩個完全不同的 Pod 對象(uid也變了)。StatefulSet 等到原先的 pod-0 對象完全從 Kubernetes 集群中被刪除后,才會提交創建一個新的 pod-0 對象。而這個新的 Pod 也會被重新調度、分配IP、拉鏡像、啟動容器。

  • 而所謂原地升級模式,就是在應用升級過程中避免將整個 Pod 對象刪除、新建,而是基于原有的 Pod 對象升級其中某一個或多個容器的鏡像版本:

在原地升級的過程中,我們僅僅更新了原 Pod 對象中 foo 容器的 image 字段來觸發 foo 容器升級到新版本。而不管是 Pod 對象,還是 Node、IP 都沒有發生變化,甚至 foo 容器升級的過程中 bar 容器還一直處于運行狀態。

總結:這種只更新 Pod 中某一個或多個容器版本、而不影響整個 Pod 對象、其余容器的升級方式,被我們稱為 Kubernetes 中的原地升級。

收益分析

那么,我們為什么要在 Kubernetes 中引入這種原地升級的理念和設計呢?

首先,這種原地升級的模式極大地提升了應用發布的效率,根據非完全統計數據,在阿里環境下原地升級至少比完全重建升級提升了 80% 以上的發布速度。這其實很容易理解,原地升級為發布效率帶來了以下優化點:

  • 節省了調度的耗時,Pod 的位置、資源都不發生變化;
  • 節省了分配網絡的耗時,Pod 還使用原有的 IP;
  • 節省了分配、掛載遠程盤的耗時,Pod 還使用原有的 PV(且都是已經在 Node 上掛載好的);
  • 節省了大部分拉取鏡像的耗時,因為 Node 上已經存在了應用的舊鏡像,當拉取新版本鏡像時只需要下載很少的幾層 layer。
  • 其次,當我們升級 Pod 中一些 sidecar 容器(如采集日志、監控等)時,其實并不希望干擾到業務容器的運行。但面對這種場景,Deployment 或 StatefulSet 的升級都會將整個 Pod 重建,勢必會對業務造成一定的影響。而容器級別的原地升級變動的范圍非常可控,只會將需要升級的容器做重建,其余容器包括網絡、掛載盤都不會受到影響。

    最后,原地升級也為我們帶來了集群的穩定性和確定性。當一個 Kubernetes 集群中大量應用觸發重建 Pod 升級時,可能造成大規模的 Pod 飄移,以及對 Node 上一些低優先級的任務 Pod 造成反復的搶占遷移。這些大規模的 Pod 重建,本身會對 apiserver、scheduler、網絡/磁盤分配等中心組件造成較大的壓力,而這些組件的延遲也會給 Pod 重建帶來惡性循環。而采用原地升級后,整個升級過程只會涉及到 controller 對 Pod 對象的更新操作和 kubelet 重建對應的容器。

    技術背景

    在阿里巴巴內部,絕大部分電商應用在云原生環境都統一用原地升級的方式做發布,而這套支持原地升級的控制器就位于?OpenKruise?開源項目中。

    也就是說,阿里內部的云原生應用都是統一使用 OpenKruise 中的擴展 workload 做部署管理的,而并沒有采用原生 Deployment/StatefulSet 等。


    那么 OpenKruise 是如何實現原地升級能力的呢?在介紹原地升級實現原理之前,我們先來看一些原地升級功能所依賴的原生 Kubernetes 功能:

    背景 1:Kubelet 針對 Pod 容器的版本管理

    每個 Node 上的 Kubelet,會針對本機上所有 Pod.spec.containers 中的每個 container 計算一個 hash 值,并記錄到實際創建的容器中。

    如果我們修改了 Pod 中某個 container 的 image 字段,kubelet 會發現 container 的 hash 發生了變化、與機器上過去創建的容器 hash 不一致,而后 kubelet 就會把舊容器停掉,然后根據最新 Pod spec 中的 container 來創建新的容器。

    這個功能,其實就是針對單個 Pod 的原地升級的核心原理。

    背景 2:Pod 更新限制

    在原生 kube-apiserver 中,對 Pod 對象的更新請求有嚴格的?validation 校驗邏輯:

    // validate updateable fields: // 1. spec.containers[*].image // 2. spec.initContainers[*].image // 3. spec.activeDeadlineSeconds

    簡單來說,對于一個已經創建出來的 Pod,在 Pod Spec 中只允許修改 containers/initContainers 中的 image 字段,以及 activeDeadlineSeconds 字段。對 Pod Spec 中所有其他字段的更新,都會被 kube-apiserver 拒絕。

    背景 3:containerStatuses 上報

    kubelet 會在 pod.status 中上報 containerStatuses,對應 Pod 中所有容器的實際運行狀態:

    apiVersion: v1 kind: Pod spec:containers:- name: nginximage: nginx:latest status:containerStatuses:- name: nginximage: nginx:mainlineimageID: docker-pullable://nginx@sha256:2f68b99bc0d6d25d0c56876b924ec20418544ff28e1fb89a4c27679a40da811b

    絕大多數情況下,spec.containers[x].image 與 status.containerStatuses[x].image 兩個鏡像是一致的。

    但是也有上述這種情況,kubelet 上報的與 spec 中的 image 不一致(spec 中是 nginx:latest,但 status 中上報的是 nginx:mainline)。

    這是因為,kubelet 所上報的 image 其實是從 CRI 接口中拿到的容器對應的鏡像名。而如果 Node 機器上存在多個鏡像對應了一個 imageID,那么上報的可能是其中任意一個:

    $ docker images | grep nginx nginx latest 2622e6cca7eb 2 days ago 132MB nginx mainline 2622e6cca7eb 2 days ago

    因此,一個 Pod 中 spec 和 status 的 image 字段不一致,并不意味著宿主機上這個容器運行的鏡像版本和期望的不一致。

    背景 4:ReadinessGate 控制 Pod 是否 Ready

    在 Kubernetes 1.12 版本之前,一個 Pod 是否處于 Ready 狀態只是由 kubelet 根據容器狀態來判定:如果 Pod 中容器全部 ready,那么 Pod 就處于 Ready 狀態。

    但事實上,很多時候上層 operator 或用戶都需要能控制 Pod 是否 Ready 的能力。因此,Kubernetes 1.12 版本之后提供了一個 readinessGates 功能來滿足這個場景。如下:

    apiVersion: v1 kind: Pod spec:readinessGates:- conditionType: MyDemo status:conditions:- type: MyDemostatus: "True"- type: ContainersReadystatus: "True"- type: Readystatus: "True"

    目前 kubelet 判定一個 Pod 是否 Ready 的兩個前提條件:

  • Pod 中容器全部 Ready(其實對應了 ContainersReady condition 為 True);
  • 如果 pod.spec.readinessGates 中定義了一個或多個 conditionType,那么需要這些 conditionType 在 pod.status.conditions 中都有對應的 status: "true" 的狀態。
  • 只有滿足上述兩個前提,kubelet 才會上報 Ready condition 為 True。

    實現原理

    了解了上面的四個背景之后,接下來分析一下 OpenKruise 是如何在 Kubernetes 中實現原地升級的原理。

    1. 單個 Pod 如何原地升級?

    由“背景 1”可知,其實我們對一個存量 Pod 的 spec.containers[x] 中字段做修改,kubelet 會感知到這個 container 的 hash 發生了變化,隨即就會停掉對應的舊容器,并用新的 container 來拉鏡像、創建和啟動新容器。

    由“背景 2”可知,當前我們對一個存量 Pod 的 spec.containers[x] 中的修改,僅限于 image 字段。

    因此,得出第一個實現原理:**對于一個現有的 Pod 對象,我們能且只能修改其中的 spec.containers[x].image 字段,來觸發 Pod 中對應容器升級到一個新的 image。

    2. 如何判斷 Pod 原地升級成功?

    接下來的問題是,當我們修改了 Pod 中的 spec.containers[x].image 字段后,如何判斷 kubelet 已經將容器重建成功了呢?

    由“背景 3”可知,比較 spec 和 status 中的 image 字段是不靠譜的,因為很有可能 status 中上報的是 Node 上存在的另一個鏡像名(相同 imageID)。

    因此,得出第二個實現原理:判斷 Pod 原地升級是否成功,相對來說比較靠譜的辦法,是在原地升級前先將 status.containerStatuses[x].imageID 記錄下來。在更新了 spec 鏡像之后,如果觀察到 Pod 的 status.containerStatuses[x].imageID 變化了,我們就認為原地升級已經重建了容器。

    但這樣一來,我們對原地升級的 image 也有了一個要求:不能用 image 名字(tag)不同、但實際對應同一個 imageID 的鏡像來做原地升級,否則可能一直都被判斷為沒有升級成功(因為 status 中 imageID 不會變化)。

    當然,后續我們還可以繼續優化。OpenKruise 即將開源鏡像預熱的能力,會通過 DaemonSet 在每個 Node 上部署一個 NodeImage Pod。通過 NodeImage 上報我們可以得知 pod spec 中的 image 所對應的 imageID,然后和 pod status 中的 imageID 比較即可準確判斷原地升級是否成功。

    3. 如何確保原地升級過程中流量無損?

    在 Kubernetes 中,一個 Pod 是否 Ready 就代表了它是否可以提供服務。因此,像 Service 這類的流量入口都會通過判斷 Pod Ready 來選擇是否能將這個 Pod 加入 endpoints 端點中。

    由“背景 4”可知,從 Kubernetes 1.12+ 之后,operator/controller 這些組件也可以通過設置 readinessGates 和更新 pod.status.conditions 中的自定義 type 狀態,來控制 Pod 是否可用。

    因此,得出第三個實現原理:可以在 pod.spec.readinessGates 中定義一個叫 InPlaceUpdateReady 的 conditionType。

    在原地升級時:

  • 先將 pod.status.conditions 中的 InPlaceUpdateReady condition 設為 "False",這樣就會觸發 kubelet 將 Pod 上報為 NotReady,從而使流量組件(如 endpoint controller)將這個 Pod 從服務端點摘除;
  • 再更新 pod spec 中的 image 觸發原地升級。
  • 原地升級結束后,再將 InPlaceUpdateReady condition 設為 "True",使 Pod 重新回到 Ready 狀態。

    另外在原地升級的兩個步驟中,第一步將 Pod 改為 NotReady 后,流量組件異步 watch 到變化并摘除端點可能是需要一定時間的。因此我們也提供優雅原地升級的能力,即通過 gracePeriodSeconds 配置在修改 NotReady 狀態和真正更新 image 觸發原地升級兩個步驟之間的靜默期時間。

    4. 組合發布策略

    原地升級和 Pod 重建升級一樣,可以配合各種發布策略來執行:

    • partition:如果配置 partition 做灰度,那么只會將 replicas-partition 數量的 Pod 做原地升級;
    • maxUnavailable:如果配置 maxUnavailable,那么只會將滿足 unavailable 數量的 Pod 做原地升級;
    • maxSurge:如果配置 maxSurge 做彈性,那么當先擴出來 maxSurge 數量的 Pod 之后,存量的 Pod 仍然使用原地升級;
    • priority/scatter:如果配置了發布優先級/打散策略,會按照策略順序對 Pod 做原地升級。

    總結

    如上文所述,OpenKruise?結合 Kubernetes 原生提供的 kubelet 容器版本管理、readinessGates 等功能,實現了針對 Pod 的原地升級能力。

    而原地升級也為應用發布帶來大幅的效率、穩定性提升。值得關注的是,隨著集群、應用規模的增大,這種提升的收益越加明顯。正是這種原地升級能力,在近兩年幫助了阿里巴巴超大規模的應用容器平穩遷移到了基于 Kubernetes 的云原生環境,而原生 Deployment/StatefulSet 是完全無法在這種體量的環境下鋪開使用的。(歡迎加入釘釘交流群:23330762)

    -?贈書福利?-

    6 月 19 日 12:00 前在【阿里巴巴云原生公眾號】留言區提出你的疑問,精選留言點贊第 1 名將免費獲得此書,屆時我們還會請本文作者針對留言點贊前 5 名的問題進行答疑!

    課程推薦

    為了更多開發者能夠享受到 Serverless 帶來的紅利,這一次,我們集結了 10+ 位阿里巴巴 Serverless 領域技術專家,打造出最適合開發者入門的 Serverless 公開課,讓你即學即用,輕松擁抱云計算的新范式——Serverless。

    點擊即可免費觀看課程:https://developer.aliyun.com/learning/roadmap/serverless

    “阿里巴巴云原生關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦云原生流行技術趨勢、云原生大規模的落地實踐,做最懂云原生開發者的公眾號。”

    原文鏈接
    本文為云棲社區原創內容,未經允許不得轉載。

    創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

    總結

    以上是生活随笔為你收集整理的如何为Kubernetes实现原地升级的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。