Drone对私有镜像仓库的支持方式,以及源码改造优化

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-git

pipeline:
  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.com
pipeline:
  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.com
pipeline:
  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.com
pipeline:
  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解码为map
func 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编码为string
func 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/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • Docker — 云时代的程序分发方式 要说最近一年云计算业界有什么大事件?Google Compute Engi...
    ahohoho阅读 15,508评论 15 147
  • 将使用 docker + docker compose + drone + git 实现项目自动部署 使用dock...
    bysir阅读 15,207评论 14 14
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,596评论 18 139
  • 大概是一年前发现了这样一个叫做 Drone 的开源 ci,在逐渐的尝试过程中发现它的功能非常的强大,其 pipel...
    eisenxu阅读 6,673评论 1 9
  • 1. 熟练昨天的听译练习 week 18 D2 ; 经过练习之后,再次翻译关于 Multiculturalism ...
    齐大枣阅读 181评论 0 0