久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

kubernetes中rolling update機制的示例分析

171次閱讀
沒有評論

共計 7982 個字符,預計需要花費 20 分鐘才能閱讀完成。

這篇文章給大家分享的是有關 kubernetes 中 rolling update 機制的示例分析的內容。丸趣 TV 小編覺得挺實用的,因此分享給大家做個參考,一起跟隨丸趣 TV 小編過來看看吧。

##0. 命令行和依賴的基礎知識

Synopsis

Perform a rolling update of the given ReplicationController.
Replaces the specified controller with new controller, updating one pod at a time to use the
new PodTemplate. The new-controller.json must specify the same namespace as the
existing controller and overwrite at least one (common) label in its replicaSelector.

kubectl rolling-update OLD_CONTROLLER_NAME -f NEW_CONTROLLER_SPEC

Examples

// Update pods of frontend-v1 using new controller data in frontend-v2.json.
$ kubectl rolling-update frontend-v1 -f frontend-v2.json
// Update pods of frontend-v1 using JSON data passed into stdin.
$ cat frontend-v2.json | kubectl rolling-update frontend-v1 -f -

ReplicationController,簡稱 rc,是 kubernet 體系中某一種類型 pod 的集合,rc 有一個關鍵參數叫做 replicas,也是就是 pod 的數量。

那么 rc 有什么用呢?這是為了解決在集群上一堆 pod 中有些如果掛了,那么就在別的宿主機上把容器啟動起來,并讓業務流量導入到正確啟動的 pod 上。也就是說,rc 保證了集群服務的可用性,當你有很多個服務啟動在一個集群中,你需要用程序去監控這些服務的運行狀況,并動態保證服務可用。

rc 和 pod 的對應關系是怎么樣的?rc 通過 selector 來選擇一些 pod 作為他的控制范圍。只要 pod 的標簽(label)符合 seletor,則屬于這個 rc,下面是 pod 和 rc 的示例。

xx-controller.json

  spec :{
  replicas :1,
  selector :{
  name : redis ,
  role : master 
 },

xx-pod.json

  labels : {
  name :  redis 
 },

kubernetes 被我們簡稱為 k8s,如果對其中的基礎概念有興趣可以看這篇

##1.kubctl 入口

/cmd/kubectl/kubctl.go

func main() {runtime.GOMAXPROCS(runtime.NumCPU())
 cmd := cmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, os.Stdout, os.Stderr)
 if err := cmd.Execute(); err != nil {os.Exit(1)
}

##2. 實際調用

源代碼在 pkg 包內,/pkg/kubectl/cmd/cmd.go,每個子命令都實現統一的接口,rollingupdate 這行是:

cmds.AddCommand(NewCmdRollingUpdate(f, out))

這個函數的實現在:/pkg/kubectl/cmd/rollingupdate.go

func NewCmdRollingUpdate(f *cmdutil.Factory, out io.Writer) *cobra.Command {
 cmd :=  cobra.Command{
 Use:  rolling-update OLD_CONTROLLER_NAME -f NEW_CONTROLLER_SPEC ,
 // rollingupdate is deprecated.
 Aliases: []string{ rollingupdate},
 Short:  Perform a rolling update of the given ReplicationController. ,
 Long: rollingUpdate_long,
 Example: rollingUpdate_example,
 Run: func(cmd *cobra.Command, args []string) {err := RunRollingUpdate(f, out, cmd, args)
 cmdutil.CheckErr(err)
}

可以看到實際調用時的執行函數是 RunRollingUpdate,算是進入正題了

func RunRollingUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error {mapper, typer := f.Object()
 // TODO: use resource.Builder instead
 obj, err := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()).
 NamespaceParam(cmdNamespace).RequireNamespace().
 FilenameParam(filename).
 Do().
 Object()
 if err != nil {
 return err
 newRc, ok := obj.(*api.ReplicationController)
 if !ok {return cmdutil.UsageError(cmd,  %s does not specify a valid ReplicationController , filename)
 }

這是建立一個新的 rc 的代碼,其中 resource 是 kubneter 所有資源 (pod,service,rc) 的基類。可以看到新的 rc 從 json 參數文件中獲取所有信息,然后轉義為 ReplicationController 這個類。

if oldName == newName {
 return cmdutil.UsageError(cmd,  %s cannot have the same name as the existing ReplicationController %s ,
 filename, oldName)
 var hasLabel bool
 for key, oldValue := range oldRc.Spec.Selector {if newValue, ok := newRc.Spec.Selector[key]; ok   newValue != oldValue {
 hasLabel = true
 break
 if !hasLabel {
 return cmdutil.UsageError(cmd,  %s must specify a matching key with non-equal value in Selector for %s ,
 filename, oldName)
 }

這里可以看到,對于新的 rc 和舊的 rc,有 2 項限制,一個是新舊名字需要不同,另一個是 rc 的 selector 中需要至少有一項的值不一樣。

updater := kubectl.NewRollingUpdater(newRc.Namespace, client)
 // fetch rc
 oldRc, err := client.ReplicationControllers(newRc.Namespace).Get(oldName)
 if err != nil {
 return err
 err = updater.Update(out, oldRc, newRc, period, interval, timeout)
 if err != nil {return err}

在做 rolling update 的時候,有兩個條件限制,一個是新的 rc 的名字需要和舊的不一樣,第二是至少有個一個標簽的值不一樣。其中 namespace 是 k8s 用來做多租戶資源隔離的,可以先忽略不計。

##3. 數據結構和實現

這段代碼出現了 NewRollingUpdater,是在上一層的 /pkg/kubectl/rollingupdate.go 這個文件中,更加接近主體了

// RollingUpdater provides methods for updating replicated pods in a predictable,
// fault-tolerant way.
type RollingUpdater struct {
 // Client interface for creating and updating controllers
 c client.Interface
 // Namespace for resources
 ns string
}

可以看到這里的 RollingUpdater 里面是一個 k8s 的 client 的結構來向 api server 發送命令

func (r *RollingUpdater) Update(out io.Writer, oldRc, newRc *api.ReplicationController, updatePeriod, interval, timeout time.Duration) error {
 oldName := oldRc.ObjectMeta.Name
 newName := newRc.ObjectMeta.Name
 retry :=  RetryParams{interval, timeout}
 waitForReplicas :=  RetryParams{interval, timeout}
 if newRc.Spec.Replicas  = 0 {return fmt.Errorf( Invalid controller spec for %s; required:   0 replicas, actual: %s\n , newName, newRc.Spec)
 desired := newRc.Spec.Replicas
 sourceId := fmt.Sprintf(%s:%s , oldName, oldRc.ObjectMeta.UID)
 // look for existing newRc, incase this update was previously started but interrupted
 rc, existing, err := r.getExistingNewRc(sourceId, newName)
 if existing {fmt.Fprintf(out,  Continuing update with existing controller %s.\n , newName)
 if err != nil {
 return err
 replicas := rc.ObjectMeta.Annotations[desiredReplicasAnnotation]
 desired, err = strconv.Atoi(replicas)
 if err != nil {
 return fmt.Errorf( Unable to parse annotation for %s: %s=%s ,
 newName, desiredReplicasAnnotation, replicas)
 newRc = rc
 } else {fmt.Fprintf(out,  Creating %s\n , newName)
 if newRc.ObjectMeta.Annotations == nil {newRc.ObjectMeta.Annotations = map[string]string{}
 newRc.ObjectMeta.Annotations[desiredReplicasAnnotation] = fmt.Sprintf(%d , desired)
 newRc.ObjectMeta.Annotations[sourceIdAnnotation] = sourceId
 newRc.Spec.Replicas = 0
 newRc, err = r.c.ReplicationControllers(r.ns).Create(newRc)
 if err != nil {
 return err
 // +1, -1 on oldRc, newRc until newRc has desired number of replicas or oldRc has 0 replicas
 for newRc.Spec.Replicas   desired   oldRc.Spec.Replicas != 0 {
 newRc.Spec.Replicas += 1
 oldRc.Spec.Replicas -= 1
 fmt.Printf( At beginning of loop: %s replicas: %d, %s replicas: %d\n ,
 oldName, oldRc.Spec.Replicas,
 newName, newRc.Spec.Replicas)
 fmt.Fprintf(out,  Updating %s replicas: %d, %s replicas: %d\n ,
 oldName, oldRc.Spec.Replicas,
 newName, newRc.Spec.Replicas)
 newRc, err = r.resizeAndWait(newRc, retry, waitForReplicas)
 if err != nil {
 return err
 time.Sleep(updatePeriod)
 oldRc, err = r.resizeAndWait(oldRc, retry, waitForReplicas)
 if err != nil {
 return err
 fmt.Printf( At end of loop: %s replicas: %d, %s replicas: %d\n ,
 oldName, oldRc.Spec.Replicas,
 newName, newRc.Spec.Replicas)
 // delete remaining replicas on oldRc
 if oldRc.Spec.Replicas != 0 {
 fmt.Fprintf(out,  Stopping %s replicas: %d -  %d\n ,
 oldName, oldRc.Spec.Replicas, 0)
 oldRc.Spec.Replicas = 0
 oldRc, err = r.resizeAndWait(oldRc, retry, waitForReplicas)
 // oldRc, err = r.resizeAndWait(oldRc, interval, timeout)
 if err != nil {
 return err
 // add remaining replicas on newRc
 if newRc.Spec.Replicas != desired {
 fmt.Fprintf(out,  Resizing %s replicas: %d -  %d\n ,
 newName, newRc.Spec.Replicas, desired)
 newRc.Spec.Replicas = desired
 newRc, err = r.resizeAndWait(newRc, retry, waitForReplicas)
 if err != nil {
 return err
 // Clean up annotations
 if newRc, err = r.c.ReplicationControllers(r.ns).Get(newName); err != nil {
 return err
 delete(newRc.ObjectMeta.Annotations, sourceIdAnnotation)
 delete(newRc.ObjectMeta.Annotations, desiredReplicasAnnotation)
 newRc, err = r.updateAndWait(newRc, interval, timeout)
 if err != nil {
 return err
 // delete old rc
 fmt.Fprintf(out,  Update succeeded. Deleting %s\n , oldName)
 return r.c.ReplicationControllers(r.ns).Delete(oldName)
}

這段代碼很長,但做的事情很簡單:

如果新的 rc 沒有被創建,就先創一下,如果已經創建了(在上次的 rolling_update 中創建了但超時了)

用幾個循環,把新的 rc 的 replicas 增加上去,舊的 rc 的 replicas 降低下來,主要調用的函數是 resizeAndWait 和 updateAndWait

##4. 底層調用

接上一節的 resizeAndWait,代碼在 /pkg/kubectl/resize.go,這里的具體代碼就不貼了 其余的所有調用都發生 /pkg/client 這個目錄下,這是一個 http/json 的 client,主要功能就是向 api-server 發送請求 整體來說,上面的 wait 的實現都是比較土的,就是發一個 update 請求過去,后面輪詢的調用 get 來檢測狀態是否符合最終需要的狀態。

##5. 總結

先說一下這三個時間參數的作用:

update-period:新 rc 增加一個 pod 后,等待這個 period,然后從舊 rc 縮減一個 pod poll-interval:這個函數名來源于 linux 上的 poll 調用,就是每過一個 poll-interval,向服務端發起請求,直到這個請求成功或者報失敗 timeout:總操作的超時時間

rolling update 主要是客戶端這邊實現的,分析完了,但還是有一些未知的問題,例如:

api-server, cadvisor, kubelet, proxy, etcd 這些服務端組件是怎么交互的?怎么保證在服務一直可用的情況下增減 pod?

是否有可能在 pod 增減的時候插入自己的一些代碼或者過程?因為我們目前的架構中沒有使用 k8s 的 proxy,需要自己去調用負載均衡的系統給這些 pod 導流量

對于具體的 pod,我們怎么去做內部程序的健康檢查?在業務不可用的情況下向 k8s 系統發送消息,干掉這個 pod,在別的機器上創建新的來替代。

感謝各位的閱讀!關于“kubernetes 中 rolling update 機制的示例分析”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

正文完
 
丸趣
版權聲明:本站原創文章,由 丸趣 2023-08-16發表,共計7982字。
轉載說明:除特殊說明外本站除技術相關以外文章皆由網絡搜集發布,轉載請注明出處。
評論(沒有評論)
主站蜘蛛池模板: 吴江市| 大安市| 三门峡市| 定南县| 时尚| 汽车| 疏附县| 成都市| 长寿区| 双城市| 沙湾县| 蓬安县| 公主岭市| 紫阳县| 荥阳市| 米脂县| 榆树市| 六枝特区| 寿阳县| 巴彦县| 南京市| 含山县| 永德县| 柯坪县| 广河县| 涞水县| 通州市| 农安县| 定襄县| 南开区| 南宫市| 黔东| 元阳县| 灵宝市| 伊春市| 台东县| 辉县市| 汪清县| 五华县| 外汇| 龙川县|