CI/CD初探 (Drone+Docker)

将使用 docker + docker compose + drone + git 实现项目自动部署

使用docker可以让drone实现任意语言程序的构建与部署

分工

  • docker: 整洁迅速的部署方案
  • docker-compose: 组网和简单容器编排
  • drone: go语言写的基于docker的CI框架

流程预览

  • 代码提交到git
  • git 通知drone应用
  • 触发drone服务器运行项目下的.drone.yml
    • 如果没有配置clone步骤, drone会运行默认的clone步骤, 并clone到workspace设置下的path
    • 依次运行pipeline下的步骤, 通常是 test -> build -> publish -> ssh连接到运行服务容器的服务器 并重启容器, 下面会依次说明pipeline各步骤.

安装Drone

此文基于Drone0.8版本安装, 已经过时, 请多多参考Drone官方安装文档.

官方使用docker-compose安装, 编写docker-compose.yml如下

docker-compose.yml

version: '2'

services:
  drone-server:
    image: drone/drone:latest

    ports:
      - 8000
      - 9000
    volumes:
      - /workspace/docker/volumes/drone:/var/lib/drone/
    restart: always
    environment:
      - DRONE_OPEN=true
      - DRONE_HOST=${DRONE_HOST}
      - DRONE_CODING=true
      - DRONE_CODING_CLIENT=${DRONE_CODING_CLIENT}
      - DRONE_CODING_URL=https://coding.net
      - DRONE_CODING_SECRET=${DRONE_CODING_SECRET}
      - DRONE_CODING_GIT_MACHINE=e.coding.net
      - DRONE_CODING_SCOPE=user,project,project:depot
      - DRONE_GITHUB_SKIP_VERIFY=false
      - DRONE_SECRET=${DRONE_SECRET}
      - DRONE_ADMIN=bysir
      - DRONE_CODING_GIT_USERNAME=${DRONE_CODING_GIT_USERNAME}
      - DRONE_CODING_GIT_PASSWORD=${DRONE_CODING_GIT_PASSWORD}

  drone-agent:
    image: drone/agent:latest

    command: agent
    restart: always
    depends_on:
      - drone-server
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - DRONE_SERVER=drone-server:9000
      - DRONE_SECRET=${DRONE_SECRET}
      - DRONE_MAX_PROCS=5

备注

字段 注释
DRONE_ADMIN 管理员, 名字是你版本控制的账号名, 添加了管理员的才可以管理用户
DRONE_OPEN 是否开启新用户注册
DRONE_GITHUB 使用github作为版本控制

在上面你会看到其中有${DRONE_HOST}的变量,这是docker-compose支持的语法, 文档

我们需要在同级目录下编写一个.env文件来写入这些变量

.env

# drone的安装服务器, 不需要端口
DRONE_HOST=http://40.90.200.130
# DRONE需要GITHUB作为应用授权, 这里填写授权账号密码, 详情看[官方文档](http://docs.drone.io/install-for-github/)
DRONE_GITHUB_CLIENT=xxxx
DRONE_GITHUB_SECRET=xxxx
# 用于drone服务器和agent之间的密钥, 随意填写
DRONE_SECRET=123456

现在运行docker-compose up就能运行drone了, 如果有报错就按报错提示来解决.

访问服务器 8000 端口就能看到drone的web页面了, 使用GITHUB的账号登陆即可, 登陆后drone会自动拉取项目目录, 点击项目就能开启对这个项目的监管.

编写项目

需要单独为每一个项目编写.drone.yml文件 用来表述在代码提交后需要执行哪些操作.

用一个简单的go项目说明

.drone.yml

workspace:
  base: /go
  path: src/github.com/bysir-zl/gokit_start

pipeline:
  build:
    image: golang:1.9
    commands:
      - pwd
      - go env
      - go version
      - go build

  publish:
      image: plugins/docker
      registry: https://index.docker.io/v1/ # 仓库
      repo: bysir/golang_base # docker仓库地址
      # mirror: https://docker.mirrors.ustc.edu.cn
      # 需要使用drone cli添加secrets: http://docs.drone.io/manage-secrets/
      secrets: [ docker_username, docker_password ]
      tags:
        - latest

  deploy:
      image: appleboy/drone-ssh
      host: 47.94.204.137
      username: root
      port: 22
      secrets: [ ssh_key ]
      script:
        - cd /root/app
        - docker-compose pull bysir/golang_base
        - docker-compose up

workspace

其中workspace指定pipeline的工作目录, 上例中我们会在buildpwd看到当前目录是/go/src/github.com/bysir-zl/gokit_start, 为什么我们需要指定到/go目录下, 因为在golang:1.9的镜像中, go_path就是/go, 我们要go build当然要在go_path下执行.

build

build步骤很简单只是go build, 你好奇为什么没有go get, 因为我将vendor目录也一起提交了, 推荐使用go官方依赖管理工具dep

docker在构建的时候都是以一个空白镜像golang:1.9作为基础的, 如果不提交vendor就需要每次构建都go get, 十分耗时. 当然还有办法就是提交一个已经按照好go包的基础镜像到registry里, 在build中的image就换成你提交的镜像. 相比之下更简单的方法就是提交vendor目录.

publish

publish步骤就有点复杂了, 使用到了plugins/docker插件, 这个插件是drone写的, 用于发布docker镜像. 它的作用就是构建一个镜像, 并push到registry.

我们需要配置的值有

  • registry: 仓库registry, 如hub.docker.com的registry地址是https://index.docker.io/v1/
  • repo: 在docker仓库下的项目名称
  • secrets: drone用于传递密钥的实现方式, 下面会详细介绍

plugins/docker插件中, 构建项目镜像是通过Dockerfile来的, 所以我们还需要在项目根编写一个Dockerfile
Dockerfile

FROM alpine:latest

COPY gokit_start /

WORKDIR /

ENTRYPOINT ["./gokit_start"]

其中ENTRYPOINT是容器启动后的运行入口, "./gokit_start"是示例项目build后的二进制文件

deply

发布流程就是通过SSH登陆上要部署程序的服务器pull下刚刚publish的镜像并启动.

登陆SSH就需要配置ssh_key或者ssh_password, 更多详情看appleboy/ssh这个插件的文档, 这里推荐使用ssh_key, 我们需要在drone的secrets添加一项ssh_key值为私钥, 然后我们将与之匹配的公钥放在服务器上.ssh/authorized_keys里, 这样就能使用ssh_key登陆上服务器并执行script.

测试

现在你的项目机构应该类似于


项目

现在你可以将你写的代码和配置文件一起提交到git, 然后你就能看到这个项目正在构建

你可以点击查看构建步骤


ps

终于跑起来了


我相信你没有我这么菜

2018/04/27 更新

最近需要使用coding作为版本控制软件

文档说drone-server在0.8版本之后是支持coding的, 但实际不然. 实际运行起来会报错:version control system not configured

找了很久的原因, 翻了很久源代码发现并没有问题, 直到发现这个Issue

I encounter this issue too.
When use drone from latest docker image, setting DRONE_CODING=true has no effect, result: "version control system not configured".
When build drone from git source code, It's works.

I found

git clone git@github.com:drone/drone-enterprise.git extras

go build -ldflags '-extldflags "-static" -X github.com/drone/drone/version.VersionDev=build.'${DRONE_BUILD_NUMBER} -o release/drone-server github.com/drone/drone/extras/cmd/drone-server
at .drone.sh,
You use drone-enterprise.git for extras to build the docker image, not the open source code.
Is that the problem?

所以我尝试使用git上源码重新编译一个drone-server使用, 修改仓库下.drone.sh如下:

#!/bin/sh

# clone the extras project.
set -e
set -x

# 由于要运行在alpine, 所以需要加GOOS=linux GOARCH=arm64 CGO_ENABLED=0
# 由于drone-server使用到了sqlite3, sqlite3使用到了gcc, 所以需要-extldflags "-static"
# -ldflags "-extldflags -static" at the end makes sure C code is statically linked so resulting binary truly has no dependencies even for C code.
go build -ldflags '-extldflags "-static" -X github.com/drone/drone/version.VersionDev=build.'0.01 -o release/drone-server github.com/drone/drone/cmd/drone-server
GOOS=linux GOARCH=amd64 CGO_ENABLED=0         go build -ldflags '-X github.com/drone/drone/version.VersionDev=build.'0.01 -o release/drone-agent             github.com/drone/drone/cmd/drone-agent
GOOS=linux GOARCH=arm64 CGO_ENABLED=0         go build -ldflags '-X github.com/drone/drone/version.VersionDev=build.'0.01 -o release/linux/arm64/drone-agent github.com/drone/drone/cmd/drone-agent
GOOS=linux GOARCH=arm   CGO_ENABLED=0 GOARM=7 go build -ldflags '-X github.com/drone/drone/version.VersionDev=build.'0.01 -o release/linux/arm/drone-agent   github.com/drone/drone/cmd/drone-agent

重新编译, 打包镜像.

为了方便, 我将做好的镜像放在了共有仓库下:
bysir/drone-server (对于官方0.8.5的版本)

可以这样使用:

version: '2'

services:
  drone-server:
    image: bysir/drone-server

    ports:
      - 8082:8000
      - 9000
    volumes:
      - /workspace/docker/volumes/drone:/var/lib/drone/
    restart: always
    environment:
      - DRONE_OPEN=true
      - DRONE_HOST=${DRONE_HOST}
      - DRONE_CODING=true
      - DRONE_CODING_CLIENT=${DRONE_CODING_CLIENT}
      - DRONE_CODING_URL=https://zhuzi.coding.net
      - DRONE_CODING_SECRET=${DRONE_CODING_SECRET}
      - DRONE_CODING_GIT_MACHINE=e.coding.net
      - DRONE_CODING_SCOPE=user,project,project:depot
      - DRONE_GITHUB_SKIP_VERIFY=false
      - DRONE_SECRET=${DRONE_SECRET}
      - DRONE_ADMIN=bysir

  drone-agent:
    image: drone/agent

    command: agent
    restart: always
    depends_on:
      - drone-server
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - DRONE_SERVER=drone-server:9000
      - DRONE_SECRET=${DRONE_SECRET}

ps: 真是折腾... 不过终于好了

欢迎看我接下来的折腾:


扩展阅读: 私钥登录原理

为什么要生成一对秘钥将秘钥放在drone的ssh_key:
DroneService(A)想要登录Service(B)并执行命令, 在SSH登录认证的时候需要A用私钥签名一段信息发送到B, B收到请求后会使用.ssh/authorized_keys中的公钥依次验签, 如果验签成功则登录成功.

ps: 私钥的格式是-----BEGIN RSA PRIVATE KEY-----xxxxxxxxx-----END RSA PRIVATE KEY----- 全部放在web页面就行了, 如下

添加secrets

publish步骤有secrets选项, 其中docker_username和docker_password是由drone注入的, 所以我们需要在drone右上角添加secrets:


添加之后如下:

secrets实现原理:
drone有一个专门存储secrets的地方, 当pipeline中step需要secrets的时候, 会得到其值并设置为容器的环境变量

上例中drone会从数据库拿到docker_usernamedocker_password并赋值给环境变量DOCKER_USERNAMEDOCKER_PASSWORD, 在plugins/docker中会使用这两个环境变量去登陆你设置的registry : docker login -p $DOCKER_PASWORD -u $DOCKER_USERNAME $DOCKER_REGISTRY

扩展阅读:安装drone cli

可以使用drone的cli来添加secrets: manage-secrets

安装方法: CLI Installation

curl -L https://github.com/drone/drone-cli/releases/download/v0.8.3/drone_linux_amd64.tar.gz | tar zx
sudo install -t /usr/local/bin drone

CLI需要通过server的授权: CLI Authentication

export DRONE_SERVER=http://47.94.204.137:8080
export DRONE_TOKEN=eyJh33OiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZXh0IjoiYnlzaXItemwiLCJ0eXBlIjoidXNlciJ9.jLsJvi4PafTi-ffefe5-bYTD2N_xmIi9fg

之后就可以使用cli执行命令

添加secrets

drone secret add   -repository bysir-zl/gokit_start   -image plugins/docker   -name docker_username  -value bysir
drone secret add   -repository bysir-zl/gokit_start   -image plugins/docker   -name docker_password  -value 123456

删除secret

drone secret rm --repository bysir-zl/gokit_start --name docker_username

2020/5/14 更新

Coding.net改了API 导致Drone0.8无法获取到仓库的.drone.yml 文件, 相关补丁已发布到此仓库中https://github.com/zbysir/drone/tree/0.8.10-coding.

如果需要编译, 为了方便, 应该在linux上编译, 因为drone依赖了sqlite3, sqlite3依赖的cgg. 另外drone中的vendor缺失了一些依赖包, 你可以试着运行./drone.sh并且下载所缺失的包, 总之 折腾起来有些麻烦, 耐心.

或者 你可以直接使用我构建好的镜像来一键修复此问题: https://hub.docker.com/repository/docker/bysir/drone-server

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

推荐阅读更多精彩内容