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

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

作者: 海角之南 | 来源:发表于2018-08-16 01:00 被阅读204次

    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/

    相关文章

      网友评论

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

          本文链接:https://www.haomeiwen.com/subject/mvigbftx.html