Drone對私人鏡像倉庫的支援方式,以及源碼改造最佳化

來源:互聯網
上載者:User

Drone介紹

Drone是新一代的CI/CD工具,基於pipeline+docker模式,可以非常靈活的支撐很多業務情境,目前,Done最新為0.8.6版本,在github上,已經斬獲15K高星star。

Drone和gitlab結合,可以在項目中設定 .drone.yml 檔案來定製你需要執行的各種各樣的流程,比如,代碼拉取、鏡像構建推送、PHP composer 包管理、Golang構建、訊息通知、自動部署、自動化測試等等。外掛程式化的支援,以及外掛程式的開發和使用模式,使得Drone的擴充性非常靈活。

目前來說,Drone官方外掛程式倉庫已經提供了很多外掛程式來擴充Drone的功能,而實現一套外掛程式也非常簡單。基本上,靠著 編寫pipeline設定檔(.drone.yml) 的靈活編寫+外掛程式模式,足夠應付無限的情境。個人覺得,相比Jenkins(其實Jenkins也出了一個基於docker、k8s的新一代工具:Jenkins X),Drone靈活簡單多了。


Drone對私人鏡像倉庫的支援

在我們的實際使用Drone過程中,有可能需要私人鏡像倉庫的支援的話,以下面的pipeline為例:

clone:  git:    image: xxx.com/plugins/drone-plugin-gitpipeline:  build:    image: xxx.com/octocat/hello-image  push_image:    image: xxx.com/plugins/docker    repo: xxx.com/xxx/test

從這個例子中,可以看到,這個pipeline分為3個步驟

①:git步驟通過image對應的鏡像拉取代碼。

②:build步驟,通過對應的image鏡像,執行代碼構建操作。

③:push_image操作,通過image對應的鏡像,完成鏡像的構建,以及鏡像推送到 repo對應的鏡像倉庫上。

這個過程中,假設我們的私人鏡像倉庫地址是 xxx.com,且這個鏡像倉庫有許可權校正,那麼這個Pipeline中,有2個地方涉及到私人鏡像倉庫的許可權處理:

①:pipeline裡,image對應的3個鏡像,需要從私人鏡像倉庫拉取。

②:最後一個步驟是鏡像構建和鏡像推送操作,鏡像構建其實就是基於具體項目裡的Dockerfile,而這個Dockerfile的基礎鏡像,有可能也是一個私人鏡像。另外,docker push也可能是推送到私人的鏡像倉庫。


官方方案

從上,要解決這2個方面對私人鏡像倉庫的需求。Drone本身提供了一套解決方案了。

第一,“pipeline裡,image對應的鏡像,需要從私人鏡像倉庫拉取”,這個解決方案,Drone提供了2種:

①:為每個image,通過 drone 用戶端工具,設定鏡像倉庫token

drone secrets add \  --image=octocat/hello-image \  octocat/hello-world REGISTRY_USERNAME octocat

這種方案,比較麻煩,好處是,.drone.yml 不需要做任何改動

②:直接把pipeline裡需要私人鏡像倉庫的image,在.drone.yml中,寫好認證資訊,比如寫為這樣:

clone:  git:    image: xxx.com/plugins/drone-plugin-git    auth_config:      username: octocat      password: password      email: octocat@github.compipeline:  build:    image: xxx.com/octocat/hello-image    auth_config:      username: octocat      password: password      email: octocat@github.com  push_image:    image: xxx.com/plugins/docker    repo: xxx.com/xxx/test    auth_config:      username: octocat      password: password      email: octocat@github.com

這種方案,不需要操作用戶端工具,但需要每個項目的 .drone.yml 的pipeline配置裡,都明文寫好。

第二、如何解決鏡像FROM私人倉庫,以及鏡像推送到私人倉庫的問題

Drone其實提供了 plugins/docker 外掛程式,來做鏡像的構建和推送。解決此問題的單子,其實是靠此外掛程式來做的,此外掛程式,解決此問題,同樣需要你配置pipeline的時候,明確寫明認證資訊(最後三行)

clone:  git:    image: xxx.com/plugins/drone-plugin-git    auth_config:      username: octocat      password: password      email: octocat@github.compipeline:  build:    image: xxx.com/octocat/hello-image    auth_config:      username: octocat      password: password      email: octocat@github.com  push_image:    image: xxx.com/plugins/docker    repo: xxx.com/xxx/test    auth_config:      username: octocat      password: password      email: octocat@github.com    username: octocat    password: password    email: octocat@github.com


改造的期望效果

我們可以看到,Drone,靈活就靈活在 pipeline 的配置上,但也因此,很多東西,都會寫在 pipeline的設定檔 .drone.yml 裡,明文暴露,這樣不夠優雅,我們可以嘗試對源碼進行一定的改造,用來更好的支援對私人鏡像倉庫的支援,但又不暴露認證資訊。

首先,我期望的pipeline配置效果是這樣的:

clone:  git:    image: xxx.com/plugins/drone-plugin-git    auth_config:      innerid: xxx.compipeline:  build:    image: xxx.com/octocat/hello-image    auth_config:      innerid: xxx.com  push_image:    image: xxx.com/plugins/docker    repo: xxx.com/xxx/test    auth_config:      innerid: xxx.com    auth_config_innerid: xxx.com

我們對比之前的.drone.yml,發現,裡邊少了明文暴露的私人鏡像倉庫認證資訊,多了一個 innerid,這個innerid,其實就是私人鏡像倉庫的標識,這個標識,我們就定為私人鏡像倉庫的網域名稱。

我的目的是,讓 Drone 程式,可以通過識別 innerid,來自動從其他地方(比如drone-server的環境變數)擷取對應的私人鏡像倉庫的認證資訊,這樣一來,.drone.yml,就不用在寫明文認證資訊了。

我們要改造2個項目,第一個是 drone 項目,第二個是 plugins/docker 這個外掛程式。


源碼改造

改造 drone 項目(用來支援pipeline中image使用私人鏡像倉庫)

1、增加一個自訂的環境變數,讓 drone server 知道,有這個環境變數且有值的話,這個值,就是我們配置的所有私人鏡像倉庫的認證資訊集合了

①:更改 cmd/drone-server/server.go 檔案,增加 配置的環境變數名:REGISTRY_AUTH_INNER_CONFIG

//pipeline的authconfig預設配置cli.StringFlag{    EnvVar: "REGISTRY_AUTH_INNER_CONFIG",    Name:   "registry-auth-inner-config",    Usage:  "private docker registry authentication username",    Value:  "",},

②:還是更改 cmd/drone-server/server.go 檔案,更改 server 函數,增加剛剛環境變數的使用,目的是解析這個字串,把它解析成golang的map類型

    //初始化全域registry認證資料    registryInnerAuthConfig := c.String("registry-auth-inner-config")    if registryInnerAuthConfig != "" {        authinfo := droneserver.DecodeToMap(registryInnerAuthConfig)        for k, v := range authinfo {            registry := compiler.Registry{}            if e := json.Unmarshal([]byte(v), &registry); e == nil {                droneserver.GlobalRegistryAuthConfig[k] = registry            }        }    }

這裡邊,有一個將字串,轉換為map的過程,使用的是自訂函數:DecodeToMap,這個函數的定義是這樣的

//將string解碼為mapfunc DecodeToMap(s string) map[string]string {    b := new(bytes.Buffer)    var decodedMap map[string]string    if data, err := base64.StdEncoding.DecodeString(s); err == nil {        b.Write(data)        d := gob.NewDecoder(b)        // Decoding the serialized data        err = d.Decode(&decodedMap)    }    return decodedMap}//將map編碼為stringfunc EncodeMapToString(m map[string]string) string {    b := new(bytes.Buffer)    e := gob.NewEncoder(b)    // Encoding the map    err := e.Encode(m)    if err == nil {        return base64.StdEncoding.EncodeToString(b.Bytes())    } else {        return ""    }}

③:更改 server/hook.go 檔案,這個檔案的開始部分,需要一個包引用,以及一個變數

//import下面的包"github.com/cncd/pipeline/pipeline/frontend/yaml/compiler"//全域的私人倉庫認證資訊,這個資料,從環境變數裡取(從環境變數,解析為map,key為私人鏡像倉庫標識,比如 hub.mfwdev.com,value為認證資訊json)var GlobalRegistryAuthConfig = make(map[string]compiler.Registry)

④:我們的核心,就是要把環境變數裡配置的編碼後的字串,解析成上面的變數,也就是一個 string 轉 map 的過程。還是更改 上面的檔案:

        //找到下面的這行代碼        parsed, err := yaml.ParseString(y)        //在上面行代碼下面,補充下面代碼        //處理私人鏡像倉庫認證資訊問題        if parsed != nil && err == nil {            if parsed.Pipeline.Containers != nil && len(parsed.Pipeline.Containers) > 0 {                for _, c := range parsed.Pipeline.Containers {                    if c.AuthConfig.Innerid != "" && GlobalRegistryAuthConfig != nil {                        if registrytemp, ok := GlobalRegistryAuthConfig[c.AuthConfig.Innerid]; ok {                            c.AuthConfig.Username = registrytemp.Username                            c.AuthConfig.Password = registrytemp.Password                            c.AuthConfig.Email = registrytemp.Email                        }                    }                }            }        }

⑤:好了,改定義我們的innerid資料類型了,變更檔:

vendor/github.com/cncd/pipeline/pipeline/frontend/yaml/container.go

type (    // AuthConfig defines registry authentication credentials.    AuthConfig struct {        //私人鏡像倉庫標識,比如:hub.xxx.com,有了這個標識,就可以結合環境變數,取出來這個標識對應的認證資訊,取代下面的三個欄位的內容        Innerid  string        Username string        Password string        Email    string

改造 plugins/docker 外掛程式(用來改造 docker build、docker push使用私人鏡像倉庫)

我們需要建立一個項目,複製 plugins/docker 作為我們自己的項目,來進行改造,通 drone 項目一樣

我們為什麼,既要改動 drone,又要改動 plugins/docker 項目呢?

這是因為,drone的改造,drone其實是server端,而 plugins/docker,僅僅是pipeline的一個步驟,我們可以讓drone,把這個認證資訊,傳遞給 plugins/docker。所以,回過頭,我們還需要再次改造一下 drone 項目,將 drone的環境變數的認證資訊,傳遞給 plugins/docker,這樣一來,認證資訊,傳遞給 plugins/docker,就不要 plugins/docker再去做什麼從外部擷取認證資訊的事兒了

①:改一下 drone項目,檔案:server/hook.go,在 hook函數中,設定環境變數的部分,增加下面一行

    //全域的registry認證資訊    envs["REGISTRY_AUTH_INNER_CONFIG"] = os.Getenv("REGISTRY_AUTH_INNER_CONFIG")

然後,回到 plugins/docker 項目來。

②:更改 cmd/drone-docker/main.go 檔案,定義新類型結構體

type Registry struct {    // mfwupdate 是否內部驗證(pipeline的docker認證,其實是需要在pipeline的yml中配置的,不好的地方是,會匯出暴露認證資訊    // 這裡提供一個 Innerid 表示,優先走內部驗證,也就是,通過drone-server的環境變數中取認證username、password、email資訊)    Innerid  string    Hostname string    Username string    Password string    Email    string    Token    string}

再同樣的檔案中,增加取環境變數的部分

        //use PLUGIN_AUTH_CONFIG_INNERID first,compared to PLUGIN_USERNAME、PLUGIN_PASSWORD、PLUGIN_EMAIL,做docker login        cli.StringFlag{            Name:   "auth_config_innerid",            Usage:  "use auth_config_innerid first,compared to username、password、email for docker login",            EnvVar: "PLUGIN_AUTH_CONFIG_INNERID",        },    }

還是這個檔案,增加對 這個環境變數的處理

    //mfwupdate    // login to the Docker registry    authConfigInnerId := c.String("auth_config_innerid")    registryAuthInnerInfo := os.Getenv("REGISTRY_AUTH_INNER_CONFIG")    if registryAuthInnerInfo != "" && authConfigInnerId != "" {        authinfo := DecodeStringToMap(registryAuthInnerInfo)        for k, v := range authinfo {            registry := Registry{}            if e := json.Unmarshal([]byte(v), &registry); e == nil {                if k == authConfigInnerId {                    plugin.Login.Email = registry.Email                    plugin.Login.Username = registry.Username                    plugin.Login.Password = registry.Password                    plugin.Login.Registry = authConfigInnerId                }            }        }    }

至此,改造完畢。


參考:

http://readme.drone.io/0.5/

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.