全場景效能平臺豬齒魚 Agent——helm元件升級

豬齒魚效能平臺發表於2021-10-26

此前文章豬齒魚 Agent——基於GitOps的雲原生持續交付模型介紹了Choerodon Agent基於helm2版本在豬齒魚平臺持續交付部署流水線中的作用以及實現原理。

現在最新helm版本已經到helm3,繼續使用helm2會面臨著如下問題:

  1. helm2版本使用的k8s api較老,不利於Choerodon Agent對k8s高版本進行支援
  2. helm2架構是client-server架構。其中tiller-pod需要高叢集許可權,不方便叢集中許可權管理。同時在Choerodon Agent這邊又屬於客戶端,出現問題後不方便進行除錯

因此Choerodon Agent需要支援helm3版本,同時遷移helm2版本下安裝的例項。

helm2與helm3區別

  1. tiller被刪除

如圖所示,helm2中部署Release需要經過tiller-pod,但是在helm3就直接通過kubeconfig部署例項

  1. helm2中Release是全域性資源,在helm3中Release儲存在各自的名稱空間
  2. Values支援JSON Schema校驗器,自動檢查所有輸入的變數格式
  3. 移除了用於本地臨時搭建Chart Repository的helm serve 命令
  4. helm install 不再預設生成一個Release的名稱,除非指定了–generate-name
  5. Helm Cli個別更名

    helm2到helm3的遷移

helm2到helm3的遷移包括如下步驟:

  • helm2的配置遷移
  • helm2的release遷移
  • 清除helm2的配置、release資料以及Tileer deployment

    使用helm-2to3進行資料遷移

安裝2to3外掛

helm3 plugin install https://github.com/helm/helm-2to3

外掛特性

支援功能:

  • 遷移helm2的配置
  • 遷移helm2的releases
  • 清除helm2的配置,rel ease 資料以及Tiller deployment

    遷移helm2的配置

首先需要遷移helm2的配置和資料資料夾,包括如下內容:

  • Chart starters
  • Repositories
  • Plugins

通過如下命令開始遷移:

helm3 2to3 move config

遷移helm2的例項

通過如下命令開始遷 移:

helm3 2to3 convert [ReleaseName]

清除helm2的資料

如果遷移完成後沒有出現錯誤,就可以通過 此條命令清楚helm2的資料,包括如下內容:

  • Configuration(Helm homedirectory)
  • v2release data
  • Tiller deployment

通過如下命令開始清楚資料:

helm3 2to3 cleanup

注意:如果執行清楚命令,所有被刪掉的 資料都不能恢復。所以沒必要的話,還是將以前的資料保留下來

Choerodon Agent的升級處理

helm2到helm3的變動非常大,所以Choerdon Agent呼叫helm也發生巨大變化。其中有兩部分需要進行修改

  1. helm客戶端獲取、安裝、升級、解除安裝例項需要重構
  2. 需要遷移helm2安裝的例項到helm3,不然升級後Choerodon Agent無法繼續管理以前的例項

    helm客戶端重構

在helm2的時候,Choerodon Agent直接將helm原始碼作為Choerodon Agent部分程式碼進行使用。而在helm3,直接對helm3的原始碼進行二次開發,然後通過依賴引用。這樣做的好處是將helm程式碼與Choerodon Agent程式碼解藕,有利於helm相關程式碼更新升級。

在Choerodon Agent裡面,安裝或升級例項會對Chart中的資源新增Choeordon Agent相關的label,比如choerodon.io/release、choeroodn.io/command等等,所以helm3的二次開發主要是新增資源label,以安裝(Install)操作舉例,其他操作(升級、刪除)大同小異。%E6%93%8D%E4%BD%9C%E4%B8%BE%E4%BE%8B%EF%BC%8C%E5%85%B6%E4%BB%96%E6%93%8D%E4%BD%9C(%E5%8D%87%E7%BA%A7%E3%80%81%E5%88%A0%E9%99%A4)%E5%A4%A7%E5%90%8C%E5%B0%8F%E5%BC%82%E3%80%82)

1. 修改模組名稱

進行二次開發,首先需要修改該專案的模組名稱,該步驟也是最麻煩的,因為修改後需要修改程式碼裡面所有的包引用路徑

如圖所示,go.mod檔案中的module進行如下修改

github.com/choerodon/helm => github.com/open-hand/helm

然後程式碼檔案中修改引用路徑

2. 加入新增label邏輯

通過斷點除錯,找到helm3安裝邏輯由open-hand-helm/pkg/action/install.go::Run()方法實現,在該方法中插入新增標籤步驟。下面省略不必要的程式碼

func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}, valsRaw string) (*release.Release, error) {
  // ···省略的步驟是helm對chart包進行校驗渲染,生成k8s物件,並儲存到resources變數中

    // 以下為修改的內容,遍歷resources物件,新增Label
    for _, r := range resources {
        err = action.AddLabel(i.ImagePullSecret, i.Command, i.AppServiceId, r, i.ChartVersion, i.ReleaseName, i.ChartName, i.AgentVersion, "", i.TestLabel, i.IsTest, false, nil)
        if err != nil {
            return nil, err
        }
    }
  // ···省略的步驟是helm將resources更新到叢集中
}

接下來看看open-hand-helm/pkg/agent/action/label.go::AddLabel()方法

func AddLabel(imagePullSecret []v1.LocalObjectReference,
    command int64,
    appServiceId int64,
    info *resource.Info,
    version, releaseName, chartName, agentVersion, testLabel, namespace string,
    isTest bool,
    isUpgrade bool,
    clientSet *kubernetes.Clientset) error {
    // 該方法內容比較多,不在這裡展示,具體可參考原始碼。其作用就是根據不同的資源型別新增不同的Label值
}

3. Choerodon Agent 引用二次開發的helm庫

參照helm3原始碼install命令的初始化方式,將分為以下幾個步驟

  1. 獲取helm配置資訊

    // 獲取helm的配置資訊
    func getCfg(namespace string) (*action.Configuration, *cli.EnvSettings) {
    settings := cli.New()
    settings.SetNamespace(namespace)
    actionConfig := &action.Configuration{}
    helmDriver := os.Getenv("HELM_DRIVER")
    if err := actionConfig.Init(settings.RESTClientGetter(), settings.Namespace(), helmDriver, debug); err != nil {
        log.Fatal(err)
    }
    return action config, settings
    }
    
    
  2. 建立Install操作物件

        installClient := action.NewInstall(
            cfg,
            chartPathOptions,
            request.Command,
            request.ImagePullSecrets,
            request.Namespace,
            request.ReleaseName,
            request.ChartName,
            request.ChartVersion,
            request.AppServiceId,
            envkube.AgentVersion,
            "",
            false)
    
    
  3. 校驗chart包並生成values值 `go ··· valueOpts := getValueOpts(request.Values) p := getter.All(envSettings) vals, err := valueOpts.MergeValues(p) if err != nil { return nil, err }

// Check chart dependencies to make sure all are present in /charts chartRequested, err := loader.Load(cp) if err != nil { return nil, err }

validInstallableChart, err := isChartInstallable(chartRequested) if !validInstallableChart { return nil, err } ···


4. 呼叫安裝方法,執行安裝命令

···
responseRelease, err := installClient.Run(chartRequested, vals, request.Values)
···


具體的邏輯可檢視[choerodon-cluster-agent/pkg/helm/helm.go::InstallRelease()](https://github.com/open-hand/choerodon-cluster-agent/blob/master/pkg/helm/helm.go#L172)

總結來說就是以下4個步驟:

獲取配置物件->生成操作物件->校驗chart包並生成values值->執行操作

## 對已安裝的Release進行遷移

對Release的遷移需要用到helm遷移工具,該工具是直接整合到Choerodon Agent專案程式碼中的。

在Choeordon Agent的啟動邏輯中,接收到的第一個命令是agent_init。該命令負責名稱空間的建立和監聽,因此Release遷移邏輯也就放到這一步操作中。

整個流程如下圖所示:

![](https://oscimg.oschina.net/oscnet/agent_helm-4.png)

1.  首先從[choerodon-cluster-agent/pkg/command/agent/agent.go::InitAgent()](https://github.com/open-hand/choerodon-cluster-agent/blob/master/pkg/command/agent/agent.go#L25)方法開始 ```go func InitAgent(opts \*commandutil.Opts, cmd \*model.Packet) (\[\]\*model.Packet, \*model.Packet) { // ···省略的步驟是處理初始化的引數

// 下面的程式碼開始對需要監聽的名稱空間進行初始化 for _, envPara := range agentInitOpts.Envs { nsList = append(nsList, envPara.Namespace) err := createNamespace(opts, envPara.Namespace, envPara.Releases) if err != nil { return nil, commandutil.NewResponseError(cmd.Key, cmd.Type, err) } }

// ···省略的步驟是開啟gitops監聽、controller監聽、以及返回叢集資訊 }
  1. choerodon-cluster-agent/pkg/command/agent/agent.go::createNamespace()開始初始化名稱空間

    func createNamespace(opts *commandutil.Opts, namespaceName string, releases []string) error {
     ns, err := opts.KubeClient.GetKubeClient().CoreV1().Namespaces().Get(namespaceName, metav1.GetOptions{})
     if err != nil {
         // 如果名稱空間不存在的話,則建立
         if errors.IsNotFound(err) {
             _, err = opts.KubeClient.GetKubeClient().CoreV1().Namespaces().Create(&corev1.Namespace{
                 ObjectMeta: metav1.ObjectMeta{
                     Name:   namespaceName,
                     Labels: map[string]string{model.HelmVersion: "helm3"},
                 },
             })
             return err
         }
         return err
     }
    
     labels := ns.Labels
     annotations := ns.Annotations
     // 如果名稱空間存在,則檢查labels標籤
     if _, ok := labels[model.HelmVersion]; !ok {
     // 開始遷移例項
         return update(opts, releases, namespaceName, labels, annotations)
     }
     return nil
    }
    
    
  2. choerodon-cluster-agent/pkg/command/agent/agent.go::update()中遷移例項 `go func update(opts *commandutil.Opts, releases []string, namespaceName string, labels, annotations map[string]string) error { releaseCount := len(releases) upgradeCount := 0

    // 此處不對choerodon名稱空間下的例項進行遷移處理 // 安裝agent的時候,會直接建立choerodon名稱空間而不打上 model.HelmVersion 標籤 // 然後使用者直接建立pv,會導致choerodon沒有標籤也納入環境管理(如果通過agent安裝了prometheus或者cert-manager就會出現問題) // 所以直接預設choeordon不需要進行helm遷移 if namespaceName != “choerodon” && releaseCount != 0 { for i := 0; i < releaseCount; i++ { getReleaseRequest := &helm.GetReleaseContentRequest{ ReleaseName: releases[i], Namespace: namespaceName, }

        // 檢視該例項是否helm3管理,如果是upgradeCount加1,如果不是,進行遷移操作然後再加1
        _, err := opts.HelmClient.GetRelease(getReleaseRequest)
        if err != nil {
            // 例項不存在有可能是例項未遷移,嘗試遷移操作
            if strings.Contains(err.Error(), helm.ErrReleaseNotFound) {
                helm2to3.RunConvert(releases[i])
                if opts.ClearHelmHistory {
                    helm2to3.RunCleanup(releases[i])
                }
                upgradeCount++
            }
        } else {
            // 例項存在表明例項被helm3管理,嘗試進行資料清理,然後upgradeCount加1
            if opts.ClearHelmHistory {
                helm2to3.RunCleanup(releases[i])
            }
            upgradeCount++
        }
    }
    
    if releaseCount != upgradeCount {
        return fmt.Errorf("env %s : failed to upgrade helm2 to helm3 ", namespaceName)
    }
    
    

    }

// 新增label if labels == nil { labels = make(map[string]string) }

labels[model.HelmVersion] = "helm3"
_, err := opts.KubeClient.GetKubeClient().CoreV1().Namespaces().Update(&corev1.Namespace{
    ObjectMeta: metav1.ObjectMeta{
        Name:        namespaceName,
        Labels:      labels,
        Annotations: annotations,
    },
})
return err

} ` 由此完成了Choerodon Agent對Release的遷移邏輯

常見問題解決

  1. 有時候Choerodon Agent重啟後,會出現啟動失敗的問題,檢視日誌有例項遷移失敗,tiller-pod不存在錯誤

該問題可能是例項遷移完成後,名稱空間的label新增失敗。解決辦法是手動給該名稱空間新增”choerodon.io/helm-version”:“helm3” 標籤,以此表示該名稱空間的例項遷移已完成,不需要再次遷移

  1. 修改資源後,部署失敗,且錯誤資訊提示為超時

首先在devops的環境層檢查三個commit值是否一致,有如下情況:

  • 前兩個commit不一致:說明devops在gitlab的webhook回撥處理上有問題,應該從gitlab的webhook執行記錄以及devops日誌進行排查
  • 前兩個一致,第三個落後於前兩個:說明devops同步相關gitops操作已完成,但是在Choerodon Agent同步gitops庫上出了問題。

這時先保留一份日誌,供開發人員查詢分析,然後kubectl -n choerodon delete [podName]刪除Choerodon Agent進行重啟。

如果重啟完成後,Choerodon Agent仍然沒有同步gitops庫,這時應該考慮該環境庫在gitlab的金鑰是否與devops資料庫中儲存的一致。先在本地驗證能否通過該金鑰拉取gitops庫。如果不行,重置該gitops庫的金鑰,最好的辦法就是刪掉該環境重新建立。如果可以,那麼說明Choerodon Agent與gitlab連線有問題,檢查gitlab的埠開發情況以及Choerodon Agent與外部網路的訪問連線情況

  • 三個commit都一致,但是例項部署失敗,且提示訪問chart倉庫超時:這個問題經常遇到。排查後發現是Choerodon Agent與Chart Museum網路連線有問題

本文由豬齒魚技術團隊原創,轉載請註明出處:豬齒魚官網

關於豬齒魚

豬齒魚Choerodon全場景效能平臺,提供體系化方法論和協作、測試、DevOps及容器工具,幫助企業拉通需求、設計、開發、部署、測試和運營流程,一站式提高管理效率和質量。從團隊協同到DevOps工具鏈、從平臺工具到體系化方法論,豬齒魚全面滿足協同管理與工程效率需求,貫穿端到端全流程,助力團隊效能更快更強更穩定。戳此處試用豬齒魚

相關文章