前面完成了基础设施搭建和测试,接下来就该将 Drone CI/CD 应用到项目中了。 这里涉及到的前几篇内容有:

简介

这里以一个 Golang 项目(Go-Web-App)为例,基于 Drone CI 服务完成其自动化部署工作流的配置和演示:主要步骤包含:

clone => test => build => image => deploy => notify

包含开发的完成流程说明:

  • 开发项目代码, 包括源码文件,.drone.yml 文件、Dockerfile 文件等配置文件和一系列执行脚本(*.sh)
  • 提交代码:pushpull requesttag 到代码仓库(Bitbucket Server), 通过 Webhook 触发 Drone 的 Pipeline
  • Drone 开始 Pipeline 的执行
    • Clone 代码至容器(Drone 默认执行)
    • 执行单元测试
    • 编译代码,构建生成可执行程序
    • 构建 Docker 镜像,并发布到 Docker Registry
    • 打包资源文件、执行脚本、配置文件等到远程测试服务器
    • 部署应用容器到测试环境
    • 结果通知

Note: Drone 引入了 pipeline 的概念,上述整个构建过程由多个 stage 组成,每一个 stage 都是在一个 Docker 容器内执行:

  • 各 stage 间可以通过共享宿主机的磁盘目录, 实现 build 阶段的数据共享和缓存基础数据, 实现加速下次 build 的目标
  • 各 stage 也可以共享宿主机的 docker 环境,实现共享宿主机的 docker image, 不用每次 build 都重新拉取 base image,减少 build 时间
  • 可以并发运行。多个 build 可以并发运行,单机并发数量由服务器 cpu 数决定

准备 Go-Web-App 演示项目

Go-Web-App Demo Project 简介:

  • 由 Golang 实现一组 RESTFul API
  • 由 Gorilla Mux 提供路由
  • 由 MongoDB 提供数据存储服务

准备 Go-Web-App 项目:

  • 创建源码根目录

    1
    2
    
    $ cd /home/workspace/code/bitbucket/
    $ mkdir goweb-app && cd goweb-app
    • 初始化工程
    1
    2
    3
    4
    5
    6
    7
    
    $ git init
    $ touch .gitignore README.md
    $ echo .idea >> .gitignore
    $ git add --all
    $ git commit -m "Initial Commit"
    $ git remote add origin http://admin@<_bitbucket_server_>.com:7990/scm/sof/goweb-app.git
    $ git push -u origin master
  • 添加源码文件

    1
    2
    3
    4
    
    $ mkdir api cicd www 
    #   - cicd:存放 Drone CI 服务的相关配置文件和脚本
    #   - www: 存放网站资源
    $ touch api/app.go api/model.go main.go main_test.go
    • 源码内容可以 Click Here 进行拷贝。

    • 初始化 go module 并运行项目

    1
    2
    3
    4
    5
    6
    
    $ go mod init go-web
    $ go run .
    go: finding github.com/gedex/inflector latest
    go: finding gopkg.in/mgo.v2 latest
    2018/10/31 16:03:14 mgo listen and serve on [mgo, localhost:27017]
    2018/10/31 16:03:14 api listen and serve on [:8090]
  • 项目创建完成后先提交代码到源码库

    1
    
    $ git add -A && git commit -m "initial project" && git push

    此时的项目结构为:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    goweb-app/
    |-- README.md
    |-- api
    |   |-- app.go
    |   `-- model.go
    |-- go.mod
    |-- go.sum
    |-- main.go
    `-- main_test.go
    
    1 directory, 7 files

配置 Drone CI/CD 服务

需要提前阅读的相关文档:

在项目 根目录 创建 Drone CI/CD 的配置文件:.drone.yml;然后跳转到 Drone Web UI:http://your_company_domain.com:8000,完成登陆后 Drone Server 会自动同步你的账号下面的项目列表(或者手动同步)。然后选择需要启用 Drone CI 的项目,点击右侧开关选项即可启用。如下图:

Drone UI: Repositories

最后,需要在 Drone Web UI 管理界面,找到 goweb-app 的设置选项,开启 Repo Hooks,如下图:

Drone Pipeline: 单元测试(test)

Go-Web-App 的接口单元测试依赖于 MongoDB 数据库服务,因此在配置 pipeline: test 前需要先配置好 services

  • 需要提前阅读的相关文档:

  • 此时 .drone.yml 配置如下:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    
    workspace:
    base: /go
    path: src/repo/goweb-app
    
    clone:    # Clone code from bitbucket
      git:
        image: plugins/git
        depth: 50
        tags: true
    
    pipeline:
    
      ping:   # Mgo-server for testing restful api
        image: mongo:latest
        commands:
          - sleep 3
          - mongo --host mgo --eval "{ping:1}"
    
      test:   # Golang unit testing
        image: golang:latest
        group: build
        environment:
          - BRC=${DRONE_COMMIT_BRANCH}
          - SHA=${DRONE_COMMIT_SHA:0:8}
          - EVN=${DRONE_BUILD_EVENT}
          - MSG=${DRONE_COMMIT_MESSAGE}
          - TAG=${DRONE_TAG}
          - MGO=mgo:27017
        commands:
          - cd cicd
          - /bin/sh test_api.sh $MGO $BRC $SHA $EVN $MSG $TAG
    
    services:  # Dependent service
      mgo:
        image: mongo:latest
    
    branches:  # Enable Drone CICD on: master & release/* branches
      include: [ master, release/* ]

    Note:

    • workspace 定义了所有工作流步骤共享的容器空间和目录
    • clone 部分是默认配置, 也可以自定义
    • Pipeline:ping stage 放在第一步是用来验证 mgo services 已经可以正常提供服务了
    • 命令 mongo --host mgo --eval "{ping:1} 的选项 --host 的值填写 services 部分配置的名称,即:mgo
    • 官网的 --eval "{ ping: 1 }" 会导致错误,需改成 --eval "{ping:1}"
    • ${DRONE_COMMIT_BRANCH} 等一系列预设变量可用来动态配置 pipelines
    • commands 部分如果内容过多或想让 .drone.yml 配置变得简洁时,可用脚本实现
    • services 部分用来定义服务应用容器,例如:database、cache、redis等
    • branches 部分可以用来限定 Drone CI 作用在那些分支上(这里只作用在:master 和 release/* 开头的所有分支上)
    • 添加脚本 test_api.sh
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    
    # 进入项目根目录,创建 cicd 目录用来存放 Drone 执行时的一些脚本和配置文件
    $ cd goweb-app && mkdir cicd
    
    # 创建 test_api.sh
    $ touch test_api.sh 
    
    # 脚本内容如下:
    
    #!/bin/sh
    #
    # Program: API 单元测试脚本
    # Authors: zhangzhe
    # History:
    #   2018-10-24:编写脚本实现
    #
    
    # Pre Set
    set -x
    
    # Set ENV
    export GO111MODULE=on                               # Go1.11.x 版本需要开启
    export http_proxy=http://10.0.0.121:1080
    export https_proxy=${http_proxy}
    
    # Set Var
    Src=../                                             # 程序源码
    DB=$1                                               # Database Server Address
    if [ -z $1 ]; then
        DB=mgo:27017
    fi
    
    # Golang Test
    echo Test Info: $@                                  # 打印构建信息, 脚本执行参数
                                                        # ${DRONE_COMMIT_BRANCH}
                                                        # ${DRONE_COMMIT_SHA:0:8}
                                                        # ${DRONE_BUILD_EVENT}
                                                        # ${DRONE_COMMIT_MESSAGE}
                                                        # ${DRONE_TAG}
    echo $PWD
    
    go mod tidy                                         # 解决依赖包
    go test -v --cover ${Src} --db_addr=${DB}           # 'mgo:27017' Drone 提供的数据库服务
                                                        # 'localhost:27017' 宿主机提供的数据库服务
  • Now, 提交代码进行测试(备注:Drone CI/CD 设定只在 master 和 以 release/ 开头的分支起作用)

    1
    
    $ git add -A && git commit -m "test drone cicd pipeline -> test stage" && git push
    • 构建结果如下图:

    Drone Pipeline: 构建 Go-Web-App(build)

    单元测试执行完成后,就可以进入 build 阶段来构建可执行程序了。

    • 此时的 .drone.yml 配置如下:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    
    workspace:
      base: /go
      path: src/repo/goweb-app
        
    clone:    # Clone code from bitbucket
      git:
        image: plugins/git
        depth: 50
        tags: true
        
    pipeline:
      ping:   # Mgo-server for testing restful api
        image: mongo:latest
        commands:
          - sleep 3
          - mongo --host mgo --eval "{ping:1}"
          
      test:   # Golang unit testing
        image: golang:latest
        group: build
        environment:
          - BRC=${DRONE_COMMIT_BRANCH}
          - SHA=${DRONE_COMMIT_SHA:0:8}
          - EVN=${DRONE_BUILD_EVENT}
          - MSG=${DRONE_COMMIT_MESSAGE}
          - TAG=${DRONE_TAG}
          - MGO=mgo:27017
        commands:
          - cd cicd
          - /bin/sh test_api.sh $MGO $BRC $SHA $EVN $MSG $TAG
        
        + build:  # Build go app and package source files
        +   image: golang:latest
        +   commands:
        +     - cd cicd
        +     - /bin/sh build_app.sh
        
    services:  # Dependent service
      mgo:
        image: mongo:latest
        
    branches:  # Enable Drone CICD on: master & release/* branches
      include: [ master, release/* ]
  • 构建可执行程序的具体步骤由脚本:build_app.sh 定义,内容如下:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    
    # 进入项目根目录,创建 cicd 目录用来存放 Drone 执行时的一些脚本和配置文件
    $ cd cicd
    
    # 创建 build_app.sh
    $ touch build_app.sh 
    
    # 脚本内容如下:
    
    #!/bin/sh
    #
    # Program: 构建 Go-App & 打包资源文件
    # Authors: zhangzhe
    # History:
    #   2018-10-24:编写脚本实现
    #
    
    # Pre Set
    set -x
    
    # Set ENV
    export GO111MODULE=on                               # Go1.11.x 版本需要开启
    export http_proxy=http://10.0.0.121:1080
    export https_proxy=${http_proxy}
    
    # Set Var
    Src=../                                             # 程序源码
    Out="app"
    Options="-a -installsuffix cgo -o"
    # PreSet='GOOS=linux GOARCH=amd64'                  # 构建变量设置
    
    # Build
    go version
    go env
    
    CGO_ENABLED=0 go build -o ${Out} ${Src}             # build
    
    # Package
    tar -zcvf release.tar.gz ../www ./*

    Note:

    • build_app.sh 脚本的最后一行用来打包资源文件(网站资源、配置文件和脚本等),该资源文件最终需要上传到远程服务器
    • 最后,在创建 www 目录用来测试静态页面:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    
    # 进入项目根目录,创建 www 目录
    $ cd goweb-app && mkdir www
    
    # 创建 index.html 静态页面
    $ touch www/index.html
    
    # 填入如下内容:
    <!DOCTYPE html>
    <html>
    <body>
    
    <h1>I'm zher.</h1>
    
    </body>
    </html>
  • Now, 提交代码进行测试(备注:Drone CI/CD 设定只在 master 和 以 release/ 开头的分支起作用)

    1
    
    $ git add -A && git commit -m "test drone cicd pipeline -> build stage" && git push
    • 构建结果如下图:

    Drone Pipeline: 发布 Docker 镜像(image)

    这里 Docker 镜像是发布到自建的 Docker Registry Server 的,其部署文档参考:Docker | Deploy Docker Registry and Web UI

    • 生成 Go-Web-App 的可执行文件后,就可以将其打包为 Docker Image 发布了,此时的 .drone.yml 配置如下:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    
    workspace:
      base: /go
      path: src/repo/goweb-app
    
    clone:    # Clone code from bitbucket
      git:
        image: plugins/git
        depth: 50
        tags: true
    
    pipeline:
    
      ping:   # Mgo-server for testing restful api
        image: mongo:latest
        commands:
          - sleep 3
          - mongo --host mgo --eval "{ping:1}"
    
      test:   # Golang unit testing
        image: golang:latest
        group: build
        environment:
          - BRC=${DRONE_COMMIT_BRANCH}
          - SHA=${DRONE_COMMIT_SHA:0:8}
          - EVN=${DRONE_BUILD_EVENT}
          - MSG=${DRONE_COMMIT_MESSAGE}
          - TAG=${DRONE_TAG}
          - MGO=mgo:27017
        commands:
          - cd cicd
          - /bin/sh test_api.sh $MGO $BRC $SHA $EVN $MSG $TAG
    
      build:  # Build go app and package source files
        image: golang:latest
        commands:
          - cd cicd
          - /bin/sh build_app.sh
    
        +  image:  # Build image and publish to private registry
        +    image: plugins/docker
        +    repo: repo.company.com:5000/go-app
        +    registry: repo.company.com:5000
        +    secrets: [ docker_username, docker_password ]
        +    context: ./cicd/
        +    dockerfile: ./cicd/Dockerfile
        +    default_tags: true
        +    when:
        +      event: tag
    
    services:  # Dependent service
      mgo:
        image: mongo:latest
    
    branches:  # Enable Drone CICD on: master & release/* branches
      include: [ master, release/* ]

    Note:

    • image 阶段使用了插件:plugins/docker 来构建 Docker Image 且将其发布到指定的远程 Docker Registry, 详细说明可以查阅其文档

    • image 阶段限制只在 tag 事件被触发式才执行 Docker Image 构建和发布

    • secrets 选项中的 docker_username & docker_password 是 Drone 注入的,我们需要在提前在 Drone Web 端先设置好这些 secrets,如下图:

  • 构建镜像的 Dockerfile 内容如下:

    1
    2
    3
    4
    5
    6
    7
    
    # 进入项目根目录,创建 cicd 目录用来存放 Drone 执行时的一些脚本和配置文件
    $ cd cicd
    
    # 创建 Dockerfile
    $ touch Dockerfile
    
    # 填入以下内容:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    
    # Program: Dockerfile for API Service
    # Build:
    #   docker build -t <image_name> .
    
    # 基础镜像
    # Usage: FROM image:tag
    FROM repo.company.com:5000/alpine:3.8
        
    # 设置环境变量
    # Usage: ENV <key=value> <key=value>
    ENV WorkDir=/home/api
    
    # 指定维护者信息
    # Usage: MAINTAINER name
    MAINTAINER <xxx@xxx.com>
    
    # 给镜像添加工作目录
    # Usage: RUN [命令]
    # RUN mkdir -p ${WorkDir}}
    # Set Time Zone
    RUN echo 'http://mirrors.ustc.edu.cn/alpine/v3.5/main' > /etc/apk/repositories \
        && echo 'http://mirrors.ustc.edu.cn/alpine/v3.5/community' >> /etc/apk/repositories \
        && apk update && apk add tzdata \
        && ln -sf /usr/share/zoneinfo/Asia/Shanghai/etc/localtime \
        && echo "Asia/Shanghai" > /etc/timezone
    
    # 设定默认工作路径
    # Usage: WORKDIR [path]
    WORKDIR ${WorkDir}
    
    # 复制本地应用程序文件[src]到Container的工作目录[dst]
    # Usage: COPY [src] [dst]
    COPY app ${WorkDir}/
    
    RUN chmod +x ${WorkDir}/app
            
    # 容器暴漏给外侧的端口号
    # Usage: EXPOSE port [port...]
    EXPOSE 80
    
    # 指定容器启动时执行的命令
    # Usage: CMD ["executable", "param1", "param2"]
    CMD ["./app"]

    Note:

    • Dockerfile 中的基础镜像使用的是 alpine, 这里将 alpine:3.8 上传到我们自建的 Docker Registry 以节省构建时间。如果使用 Docker Hub 中的镜像每次都要拉取一次,会很慢。
  • 上传构建 Go-Web-App 的基础镜像到我们自建的 Docker Private Registry:

    • 上传镜像到 Docker Registry 参考文章

      1
      2
      3
      4
      5
      
      $ docker pull alpine:3.8
      $ docker tag alpine:3.8 repo.company.com:5000/alpine:3.8
      $ docker push repo.company.com:5000/alpine:3.8
      
      # push 失败时,尝试重新登陆后继续 push 即可

      Push repo.company.com:5000/alpine:3.8 成功后,可以打开 Docker Registry UI (http://your_domain.com:5001) 即可看到刚刚提交的镜像信息。

  • Now, 提交代码进行测试(备注:Drone CI/CD 设定只在 master 和 以 release/ 开头的分支起作用)

    1
    2
    3
    4
    
    $ git add -A && git commit -m "test drone cicd pipeline -> publish image stage" && git push
    
    # 打 tag, 触发 when: [event: tag] 事件,即触发 publish_image 阶段执行
    $ git tag 0.0.1 -m "release 0.0.1" && git push origin 0.0.1
    • 构建结果如下图:

    Drone Pipeline: 上传静态资源和部署脚本(scp)

    要实现自动部署容器服务,光有镜像是不够的,因此,还需要将网站资源,配置文件等一些脚本打包发到远端服务器上.

    • 此时的 .drone.yml 配置如下:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    
    workspace:
      base: /go
      path: src/repo/goweb-app
    
    clone:    # Clone code from bitbucket
      git:
        image: plugins/git
        depth: 50
        tags: true
    
    pipeline:
    
      ping:   # Mgo-server for testing restful api
        image: mongo:latest
        commands:
          - sleep 3
          - mongo --host mgo --eval "{ping:1}"
    
      test:   # Golang unit testing
        image: golang:latest
        group: build
        environment:
          - BRC=${DRONE_COMMIT_BRANCH}
          - SHA=${DRONE_COMMIT_SHA:0:8}
          - EVN=${DRONE_BUILD_EVENT}
          - MSG=${DRONE_COMMIT_MESSAGE}
          - TAG=${DRONE_TAG}
          - MGO=mgo:27017
        commands:
          - cd cicd
          - /bin/sh test_api.sh $MGO $BRC $SHA $EVN $MSG $TAG
    
      build:  # Build go app and package source files
        image: golang:latest
        commands:
          - cd cicd
          - /bin/sh build_app.sh
    
      image:  # Build image and publish to private registry
        image: plugins/docker
        repo: repo.company.com:5000/go-app
        registry: repo.company.com:5000
        secrets: [ docker_username, docker_password ]
        context: ./cicd/
        dockerfile: ./cicd/Dockerfile
        default_tags: true
        when:
          event: tag
    
        +  scp:    # Upload resource and config files to remote server
        +    image: appleboy/drone-scp
        +    group: build
        +    host:
        +      - your_server_address
        +    username: root
        +    port: 22
        +    secrets: [ ssh_password ]
        +    target: /home/workspace/service/api-server # 远程服务器目录
        +    source: ./cicd/release.tar.gz              # 打包好的文件
        +    when:
        +      event: tag
    
    services:  # Dependent service
      mgo:
        image: mongo:latest
    
    branches:  # Enable Drone CICD on: master & release/* branches
      include: [ master, release/* ]

    Note:

    • 预设变量 ssh_password 也需要提前在 Drone Web 端设置好
  • 部署脚本 deploy_services.sh 内容如下:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    
    # 进入项目根目录,创建 cicd 目录用来存放 Drone 执行时的一些脚本和配置文件
    $ cd cicd
    
    # 创建部署脚本:deploy_services.sh
    $ touch deploy_services.sh
    
    # 填入如下内容:
    
    #!/bin/sh
    #
    # Program: 部署 Docker 容器服务
    # Authors: zhangzhe
    # History:
    #   2018-10-24:编写脚本实现
    #
    
    # Pre Set
    set -x
    
    # Set Var
    Tag=$1                                    # 读取 git tag (版本号:x.y.z)
    Commit=$2                                 # 读取 git commit 号(8位)
    
    if [ -z ${Tag} ]; then
        echo -e "Empty git tag given"
        exit 1
    fi
    
    ImageName=go-app:${Tag}                   # 镜像名
    Registry=develop.robot-qixing.com:5000    # 私有仓库地址
    BaseImage=${Registry}/${ImageName}        # 远程 Image 地址:docker pull ${BaseImage}
    ContainerName=api${Tag}
    
    # Reconfigure docker-compose.yml & Caddyfile
    sed -i "s/api(@tag)/${ContainerName}/g" docker-compose.yml  # 启动对应版本(Tag)的 API 容器
    sed -i "s/(@tag)/${Tag}/g" dockere-compose.yml              # 拉去对应版本(Tag)的 API 镜像
    sed -i "s/api(@tag)/${ContainerName}/g" Caddyfile           # Caddy 代理到对应版本的 API 容器服务
    
    # Pull image and run docker container
    docker pull ${BaseImage}
    docker-compose up -d
    docker ps
    docker-compose logs caddy
    docker-compose logs mgo
    docker logs api${Tag}

    配置 docker-compose.yml

    1
    2
    3
    
    $ cd cicd && touch docker-compose.yml
    
    # 填入如下内容
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    
    version: '3'
    services:
      caddy: # caddy proxy server
        container_name: caddy
        image: abiosoft/caddy:latest
        restart: always
        ports:
          - 80:80
          - 443:443
        volumes:
          - /etc/ssl/server-certs:/root/.caddy
          - ./Caddyfile:/etc/Caddyfile
          - ./www:/srv
        depends_on:
          - api
    
      api:  # api server
        container_name: api(@tag)
        image: repo.company.com:5000/go-app:(@tag)
        restart: always
        command: './app --port=80 --db_addr=mgo:27017'
        depends_on:
          - mgo
    
      mgo:  # mgo server
        container_name: mgo
        image: mongo:latest
        restart: always
        environment:
          MONGO_INITDB_ROOT_USERNAME: <__input_username__>
          MONGO_INITDB_ROOT_PASSWORD: <__input_password__>
        volumes:
          - ./mgo-data/db:/data/db
    • Caddyfile 配置内容如下:
    1
    2
    3
    
    $ cd cicd && touch Caddyfile.yml
    
    # 填入如下内容
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
    https://company-domain.com {
        gzip
        root /srv
        tls /root/.caddy/cert.crt /root/.caddy/cert.key
        timeouts none
    
        proxy /v0 api(@tag):80 {
            transparent
            websocket
            except static
        }
    
        cors /v0 {
            origin            *
            methods           POST,GET,PATCH,DELETE,OPTIONS
            allow_credentials false
            max_age           3600
            allowed_headers   content-type,Authorization
            exposed_headers   X-Something-Special,SomethingElse,Authorization
        }
    }

    Note:

    • Now, 提交代码进行测试(备注:Drone CI/CD 设定只在 master 和 以 release/ 开头的分支起作用)
    1
    2
    3
    4
    
    $ git add -A && git commit -m "test drone cicd pipeline -> scp stage" && git push
    
    # 打 tag, 触发 when: [event: tag] 事件,即触发 publish_image 阶段执行
    $ git tag 0.0.2 -m "release 0.0.2" && git push origin 0.0.2
  • 构建结果如下图:

Drone Pipeline: 服务部署(deploy)

一切准备好后,就可以执行容器服务部署了,主要包括:Caddy Server Container、Go Web Server Container、MongoDB Server Container,这一组应用容器服务通过 docker-compose 进行管理和组织。

  • 此时的 .drone.yml 配置如下:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    
    workspace:
      base: /go
      path: src/repo/goweb-app
        
    clone:    # Clone code from bitbucket
      git:
        image: plugins/git
        depth: 50
        tags: true
        
    pipeline:
        
      ping:   # Mgo-server for testing restful api
        image: mongo:latest
        commands:
          - sleep 3
          - mongo --host mgo --eval "{ping:1}"
        
      test:   # Golang unit testing
        image: golang:latest
        group: build
        environment:
          - BRC=${DRONE_COMMIT_BRANCH}
          - SHA=${DRONE_COMMIT_SHA:0:8}
          - EVN=${DRONE_BUILD_EVENT}
          - MSG=${DRONE_COMMIT_MESSAGE}
          - TAG=${DRONE_TAG}
          - MGO=mgo:27017
        commands:
          - cd cicd
          - /bin/sh test_api.sh $MGO $BRC $SHA $EVN $MSG $TAG
        
      build:  # Build go app and package source files
        image: golang:latest
        commands:
          - cd cicd
          - /bin/sh build_app.sh
        
      image:  # Build image and publish to private registry
        image: plugins/docker
        repo: repo.company.com:5000/go-app
        registry: repo.company.com:5000
        secrets: [ docker_username, docker_password ]
        context: ./cicd/
        dockerfile: ./cicd/Dockerfile
        default_tags: true
        when:
          event: tag
        
      scp:    # Upload resource and config files to remote server
        image: appleboy/drone-scp
        group: build
        host:
          - your_server_address
        username: root
        port: 22
        secrets: [ ssh_password ]
        target: /home/workspace/service/api-server # 远程服务器目录
        source: ./cicd/release.tar.gz              # 随镜像一起发布的文件
        when:
          event: tag
        
        +  deploy: # Deploy api-server, caddy-server, mgo-server
        +    image: appleboy/drone-ssh
        +    host:
        +      - your_server_address
        +    username: root
        +    port: 22
        +    secrets: [ ssh_password ]
        +    command_timeout: 60
        +    script:
        +      - cd /home/workspace/service/api-server/cicd
        +      - tar -zxvf release.tar.gz
        +      - ls -l
        +      - /bin/sh deploy_services.sh ${DRONE_TAG} ${DRONE_COMMIT_SHA:0:8}
        +    when:
        +      event: tag
        
    services:  # Dependent service
      mgo:
        image: mongo:latest
        
    branches:  # Enable Drone CICD on: master & release/* branches
      include: [ master, release/* ]

    Note:

    • Drone Plugins: appleboy/drone-ssh 用来连接到远程服务器
    • deploy_services.sh 是执行服务部署的脚本,需要接收 ${DRONE_TAG} 用来动态构建容器名称等
    • Now, 提交代码进行测试(备注:Drone CI/CD 设定只在 master 和 以 release/ 开头的分支起作用)
    1
    2
    3
    4
    
    $ git add -A && git commit -m "test drone cicd pipeline -> deploy stage" && git push
    
    # 打 tag, 触发 when: [event: tag] 事件,即触发 publish_image 阶段执行
    $ git tag 0.0.3 -m "release 0.0.3" && git push origin 0.0.3
  • 构建结果如下图:

Drone Pipeline: 结果通知(notify:hipchat)

为了确保能够及时获取 Drone CI 构建结果,需要选取一种合适的方式来接收通知,这里集成了我们在用的 hipchat.

  • 此时的 .drone.yml 配置如下:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    
    workspace:
      base: /go
      path: src/repo/goweb-app
        
    clone:    # Clone code from bitbucket
      git:
        image: plugins/git
        depth: 50
        tags: true
        
    pipeline:
        
      ping:   # Mgo-server for testing restful api
        image: mongo:latest
        commands:
          - sleep 3
          - mongo --host mgo --eval "{ping:1}"
        
      test:   # Golang unit testing
        image: golang:latest
        group: build
        environment:
          - BRC=${DRONE_COMMIT_BRANCH}
          - SHA=${DRONE_COMMIT_SHA:0:8}
          - EVN=${DRONE_BUILD_EVENT}
          - MSG=${DRONE_COMMIT_MESSAGE}
          - TAG=${DRONE_TAG}
          - MGO=mgo:27017
        commands:
          - cd cicd
          - /bin/sh test_api.sh $MGO $BRC $SHA $EVN $MSG $TAG
        
      build:  # Build go app and package source files
        image: golang:latest
        commands:
          - cd cicd
          - /bin/sh build_app.sh
        when:
          event: tag
        
      image:  # Build image and publish to private registry
        image: plugins/docker
        repo: repo.company.com:5000/go-app
        registry: repo.company.com:5000
        secrets: [ docker_username, docker_password ]
        context: ./cicd/
        dockerfile: ./cicd/Dockerfile
        default_tags: true
        when:
          event: tag
        
      scp:    # Upload resource and config files to remote server
        image: appleboy/drone-scp
        group: build
        host:
          - your_server_address
        username: root
        port: 22
        secrets: [ ssh_password ]
        target: /home/workspace/service/api-server # 远程服务器目录
        source: ./cicd/release.tar.gz              # 随镜像一起发布的文件
        when:
          event: tag
        
      deploy: # Deploy api-server, caddy-server, mgo-server
        image: appleboy/drone-ssh
        host:
          - your_server_address
        username: root
        port: 22
        secrets: [ ssh_password ]
        command_timeout: 60
        script:
          - cd /home/workspace/service/api-server/cicd
          - tar -zxvf release.tar.gz
          - ls -l
          - /bin/sh deploy_services.sh ${DRONE_TAG} ${DRONE_COMMIT_SHA:0:8}
        when:
          event: tag
        
        +  hipchat: # Notify
        +    image: jmccann/drone-hipchat
        +    url: https://hipchat.your_company_domain.com
        +    room: 19
        +    auth_token: tWVtsTFzsbkwSJO79JHcyb11TR8dLyEYAlicHviB
        +    from: Drone
        +    when:
        +      status: [ success, failure ]
        +    template: >
            <strong>{{ uppercasefirst build.status }}</strong> <a href=\"{{ system.link_url }}/{{     repo.owner }}/{{ repo.name }}/{{ build.number }}\">{{ repo.owner }}/{{ repo.name }}#{{     truncate build.commit 8 }}</a> ({{ build.branch }}) by {{ build.author }} in {{ duration     build.started_at build.finished_at }} </br> - {{ build.message }}
        
    services:  # Dependent service
      mgo:
        image: mongo:latest
        
    branches:  # Enable Drone CICD on: master & release/* branches
      include: [ master, release/* ]

    Note:

    • Now, 提交代码进行测试(备注:Drone CI/CD 设定只在 master 和 以 release/ 开头的分支起作用)
    1
    
    $ git add -A && git commit -m "test drone cicd pipeline -> notify stage" && git push
  • 构建结果如下图:

至此,一套完整的 Drone CI/CD Pipeline 流已配置完成。

最终完整的项目结构如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
goweb-app/
|-- README.md
|-- api
|   |-- app.go
|   `-- model.go
|-- cicd
|   |-- Caddyfile
|   |-- Dockerfile
|   |-- build_app.sh
|   |-- deploy_services.sh
|   |-- docker-compose.yml
|   `-- test_api.sh
|-- go.mod
|-- go.sum
|-- main.go
|-- main_test.go
`-- www
    `-- index.html

3 directories, 14 files

See Also

Thanks to the authors 🙂

FAQ

Q: go: golang.org/x/crypto@v0.0.0-20180904163835-0709b304e793: unrecognized import path “golang.org/x/crypto” (https fetch: Get https://golang.org/x/crypto?go-get=1: dial tcp 216.239.37.1:443: i/o timeout) go: golang.org/x/sys@v0.0.0-20180905080454-ebe1bf3edb33: unrecognized import path “golang.org/x/sys” (https fetch: Get https://golang.org/x/sys?go-get=1: dial tcp 216.239.37.1:443: i/o timeout) go: error loading module requirements

A: https://gocn.vip/question/567