Drone CI是一个基于容器的轻量级的本地持续交付平台,可以将下面的流程自动化
graph LR
Commit --> Build --> Unit-Tests --> Integraion-Tests --> Review --> Staging --> Production
安装
官方推荐Gitea和Drone分别在不同的实例里,不推荐将Gitea和Drone通过docker-compose安装在一个实例里
这里需要先介绍以下Drone的工作模式,Drone提供Web UI界面,并将需要执行的工作分配给drone runner(也可以说是 drone agent),drone runner可以不和Server安装在同一个服务器,你可以在自己的电脑上安装Runnner
- Server: 用于验证,配置repository、用户、密钥、接受webhooks,分发任务
- Runnner: 用于接受构建任务,并实际执行
例如:将Server安装在树莓派上,将Runnner安装在电脑上
drone runner有许多不同种类用来最优化执行不同的运行环境。分别有
数据库
Drone需要数据库来持续化储存,默认Drone使用sqlite(不需要额外配置)
Drone推荐使用Postgres而不是MySQL,因为Drone专门为Postgres进行了优化 - 官网
下面先给出Postgres的配置,将在最后给出完整的配置
docker-compose.yml
:
1 2 3 4 5 6 7 8 9 10 11 12 13
| postgres: image: postgres:latest restart: unless-stopped container_name: drone-postgres env_file: - ./postgres.env environment: - POSTGRES_DB={数据库名} - POSTGRES_USER={数据库用户名} volumes: - ./postgres/:/var/lib/postgresql/data:rw networks: - default
|
postgres.env
:
1
| POSTGRES_PASSWORD={数据库密码}
|
Drone Server
我们是基于Gitea进行配置的,基于不同的版本控制服务其环境变量的配置部分也不同,其中Gitea需要配置以下部分
Gitea OAuth
我们还需要现在Gitea里面配置OAuth2
验证回调连接必须完全和你的Drone服务器链接一致(包括scheme和host)
记下Client ID和Client Secret下面需要用到
RPC Secret
还需要设置RPC连接密码,Runner会使用这个密码来连接Server,可以使用openssl生成密码
1 2
| > openssl rand -hex 16 bea26a2221fd8090ea38720fc445eca6
|
Drone Server的docker-compose.yml
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 34 35 36 37 38 39 40 41 42 43
| drone-server: image: drone/drone:latest container_name: drone env_file: - ./drone.env - ./drone-rpc.env environment: - DRONE_DATABASE_DRIVER=postgres - DRONE_SERVER_HOST={你的域名:www.drone.com} - DRONE_SERVER_PROTO=https - DRONE_TLS_AUTOCERT=false - DRONE_GITEA_SKIP_VERIFY=false - DRONE_GITEA_SERVER={https://你的git服务器} - DRONE_GIT_ALWAYS_AUTH=false - VIRTUAL_HOST={你的域名:www.drone.com} - LETSENCRYPT_HOST={你的域名:www.drone.com} - LETSENCRYPT_EMAIL={你的邮箱:[email protected]} volumes: - ./drone:/var/lib/drone/:rw - /var/run/docker.sock:/var/run/docker.sock networks: - nginx-proxy - default depends_on: - postgres restart: unless-stopped
networks: nginx-proxy: external: name: nginx-proxy
|
drone.env
:
1 2 3 4 5
| DRONE_USER_CREATE=username:{你的用户名},machine:false,admin:true,token:{你的密码} DRONE_GITEA_CLIENT_ID={Gitea的Client ID} DRONE_GITEA_CLIENT_SECRET={Gitea的Client Secret} DRONE_DATABASE_DATASOURCE=postgres://{数据库用户名}:{数据库密码}@postgres:5432/{数据库名}?sslmode=disable DRONE_DATABASE_SECRET={用于数据库数据加密储存:自行设置}
|
drone-rpc.env
:
1
| DRONE_RPC_SECRET={RPC Secret}
|
Drone Runner
当我们完成Server的安装以后,我们就需要Runner来执行pipelines
Runner可以安装在Linux和Windows上,但是都需要使用Docker进行运行,至少需要配置以下3项:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| drone-agent: image: drone/drone-runner-docker:1 container_name: drone-agent networks: - default volumes: - /var/run/docker.sock:/var/run/docker.sock:rw env_file: - ./drone-rpc.env environment: - DRONE_RPC_HOST=drone-server - DRONE_RPC_PROTO=http - DRONE_RUNNER_CAPACITY=2 - DRONE_RUNNER_NAME={Runner自定义名字} depends_on: - drone-server restart: unless-stopped
|
部署
完整配置文件
Drone的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
| --- version: "3" services: postgres: image: postgres:latest restart: unless-stopped container_name: drone-postgres env_file: - ./postgres.env environment: - POSTGRES_DB={数据库名} - POSTGRES_USER={数据库用户名} volumes: - ./postgres/:/var/lib/postgresql/data:rw networks: - default drone-server: image: drone/drone:latest container_name: drone env_file: - ./drone.env - ~/docker/drone/drone-rpc.env environment: - DRONE_DATABASE_DRIVER=postgres - DRONE_SERVER_HOST={你的域名:www.drone.com} - DRONE_SERVER_PROTO=https - DRONE_TLS_AUTOCERT=false - DRONE_GITEA_SKIP_VERIFY=false - DRONE_GITEA_SERVER={https://你的git服务器网址} - DRONE_GIT_ALWAYS_AUTH=false - VIRTUAL_HOST={你的域名:www.drone.com} - LETSENCRYPT_HOST={你的域名:www.drone.com} - LETSENCRYPT_EMAIL={你的邮箱:[email protected]} volumes: - ./drone:/var/lib/drone/:rw - /var/run/docker.sock:/var/run/docker.sock networks: - nginx-proxy - default depends_on: - postgres restart: unless-stopped
drone-agent: image: drone/drone-runner-docker:1 container_name: drone-agent networks: - default volumes: - /var/run/docker.sock:/var/run/docker.sock:rw env_file: - ~/docker/drone/drone-rpc.env environment: - DRONE_RPC_HOST=drone-server - DRONE_RPC_PROTO=http - DRONE_RUNNER_CAPACITY=2 - DRONE_RUNNER_NAME={Runner自定义名字} depends_on: - drone-server restart: unless-stopped
networks: nginx-proxy: external: name: nginx-proxy
|
postgres.env
:
1
| POSTGRES_PASSWORD={数据库密码}
|
drone.env
:
1 2 3 4 5
| DRONE_USER_CREATE=username:{你的用户名},machine:false,admin:true,token:{你的密码} DRONE_GITEA_CLIENT_ID={Gitea的Client ID} DRONE_GITEA_CLIENT_SECRET={Gitea的Client Secret} DRONE_DATABASE_DATASOURCE=postgres://{数据库用户名}:{数据库密码}@postgres:5432/{数据库名}?sslmode=disable DRONE_DATABASE_SECRET={用于数据库数据加密储存:自行设置}
|
drone-rpc.env
:
1
| DRONE_RPC_SECRET={RPC Secret}
|
nginx-proxy的docker-compose-nginx-proxy.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
| --- version: "3" services: nginx-proxy: image: jwilder/nginx-proxy:alpine container_name: nginx-proxy labels: com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy: true ports: - "80:80" - "443:443" volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - ./nginx-proxy/conf.d/:/etc/nginx/conf.d/ - ./nginx-proxy/conf/proxy.conf:/etc/nginx/proxy.conf - ./nginx-proxy/conf/nginx.conf:/etc/nginx/nginx.conf - ./nginx-proxy/certs/:/etc/nginx/certs/:ro - ./nginx-proxy/vhost.d/:/etc/nginx/vhost.d/ - ./nginx-proxy/dhparam/:/etc/nginx/dhparam/:rw - ./nginx-proxy/html/:/usr/share/nginx/html networks: - nginx-proxy restart: unless-stopped
letsencrypt-nginx-proxy-companion: image: jrcs/letsencrypt-nginx-proxy-companion container_name: nginx-letsencrypt volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - ./nginx-proxy/conf.d/:/etc/nginx/conf.d/ - ./nginx-proxy/certs/:/etc/nginx/certs:rw - ./nginx-proxy/vhost.d/:/etc/nginx/vhost.d - ./nginx-proxy/html/:/usr/share/nginx/html restart: unless-stopped depends_on: - nginx-proxy
networks: nginx-proxy: external: name: nginx-proxy
|
请自行注意文件结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| Drone ├── drone ├── drone.env ├── drone-rpc.env ├── postgres [error opening dir] └── postgres.env
nginx-proxy ├── certs ├── conf ├── conf.d ├── dhparam ├── html └── vhost.d
|
然后运行
1 2
| > docker-compose -p drone -f docker-compose-drone.yml up -d > docker-compose -p nginx-proxy -f docker-compose-nginx-proxy.yml up -d
|
即可启动Drone
运行后的配置
运行成功后,访问Drone的域名会自动跳转到Gitea进行验证,需要注意的是检测链接里面的回调链接的scheme(https还是http)需要和Gitea里面OAuth设置的一致,否则会验证失败。
验证成功后就会自动进入Drone的主界面,在网页上Drone本身不需要进行配置,进入则代表部署成功
Pipelines
Pipelines帮助我们自动化程序交付过程,比如:编译,测试,发布到生产环境
Pipelines通过源代码库的变动触发从而执行,一次提交后将触发Drone的webhook运行对于的pipeline
同时还有其他触发器,比如:定时触发器
Pipelines通过在Git项目根目录下放置.drone.yml
进行配置,使用yaml语法
Drone支持多种Pipelines,每个都对不同的使用场景和运行环境进行了优化:
Docker Pipelines
这里只对Docker Pipelines进行说明,可以先看一个例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| --- kind: pipeline type: docker name: default
steps: - name: greeting image: golang:1.12 commands: - go build - go test
- name: frontend image: node commands: - npm install - npm test
|
kind
和type
定义一个Docker pipeline
1 2 3
| --- kind: pipeline type: docker
|
Triggers
当我们push代码到库、或拉取、或创建一个tag,我们的版本控制服务将自动发起一个webhook给Drone然后触发Triggers运行,我们可以使用Triggers来限制pipeline的执行,总共有以下几种方式
- Branch
- Event
- Reference
- Repository
- Status
- Target
- Cron
By Branch
1 2 3
| trigger: branch: - master
|
可以使用include
和exclude
语法
1 2 3 4 5 6
| trigger: branch: include: - master exclude: - feature/*
|
By Event
可以通过不同事件来触发trigger
1 2 3 4 5 6 7 8 9
| trigger: event: - cron - custom - push - pull_request - tag - promote - rollback
|
同样支持include
和exclude
语法
利用platform
来配置支持的操作系统、系统构架,默认为Linux amd64
1 2 3 4 5 6 7
| kind: pipeline type: docker name: default
platform: os: linux arch: amd64
|
Workspace
Drone会自动创建一个临时的volume作为workspace,在这里将clone我们的源码库,同时workspace将作为我们每一个步骤的工作目录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| kind: pipeline type: docker name: default
workspace: path: /drone/src
steps: - name: backend image: golang:latest commands: - go get - go test
- name: frontend image: node:latest commands: - npm install - npm run tests
|
等价于
1 2 3
| > docker volume create my-named-volume > docker run --volume=my-named-volume:/drone/src golang > docker run --volume=my-named-volume:/drone/src node
|
Steps
steps
部分定义一系列的指令,这些指令将作为Entrypoint
在Docker容器里面的项目根目录(workspace
)下执行
如果任何一个指令返回一个不是0的代码,则pipeline将失败并退出,同时整个pipeline状态将被标记为失败,剩下的流程将被跳过。
需要注意的是每一个step
都是在不同容器里面执行的
其他Docker相关配置
Hexo自动交付
至于为什么要设置hexo的自动交付,因为设备很多,每个设备环境可能多少有些不同,管理起来非常的麻烦。而且随着blog的字数越来越多,每一次编译都要非常的久,在8GB内存的笔记本上经常直接爆内存,导致失败,所以干脆都转移到服务器上进行generate,然后deploy,每次只需要git push,后面的事情都是自动的,省时省力。
由于使用了next主题所以需要先pull主题,推荐自己创建一个next主题的库,然后在本地库新增一个远程分支,每次推送到你自己的远程分支里面,这样就可以和next官方的分支分可互不影响,也不会影响更新,同时配置文件也都同步。
流程:需要去Drone的网站激活你的blog库,同时我们使用drillster/drone-rsync
插件进行同步数据,所以需要设置添加secret rsync_key
为你的私钥。
.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
| kind: pipeline type: docker name: default
steps: - name: clone-theme image: alpine/git commands: - git clone {你自己的next库的git链接} themes/next
- name: build image: node:latest commands: - npm install -g hexo - npm install - npm run build - name: deploy image: drillster/drone-rsync environment: RSYNC_KEY: from_secret: rsync_key settings: user: {用户名} hosts: - {部署服务器IP} port: 22 source: ./public target: {你的web目录} delete: true when: branch: master
trigger: event: - push
|
这样设置好了后,每次git push后就会自动按照环境、然后generate,最后deploy了,所有的过程都可以在Drone后台看到。
添加缓存
当你上面成功后你会发现,虽然git都是clone的自己的服务器的,但是npm install还是得从网上托啊,不说浪费资源的问题啥的了,每次托也慢,所以有必要加上缓存。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| - name: restore-cache image: drillster/drone-volume-cache volumes: - name: cache path: /cache settings: restore: true mount: - ./node_modules
- name: rebuild-cache image: drillster/drone-volume-cache volumes: - name: cache path: /cache settings: rebuild: true mount: - ./node_modules
volumes: - name: cache host: path: /tmp/cache
|
restore
:标志让插件把host的文件复制到build环境里面,所以这需要在pipeline最开始声明
rebuild
:标志让插件把build环境的文件复制到host里面,所以这需要在pipeline最后声明
于是最后.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
| kind: pipeline type: docker name: default
steps: - name: restore-cache image: drillster/drone-volume-cache volumes: - name: cache path: /cache settings: restore: true mount: - ./node_modules
- name: clone-theme image: alpine/git commands: - git clone {你自己的next库的git链接} themes/next
- name: build image: node:latest commands: - npm install -g hexo - npm install - npm audit fix - npm run build - name: deploy image: drillster/drone-rsync environment: RSYNC_KEY: from_secret: rsync_key settings: user: {用户名} hosts: - {部署服务器IP} port: 22 source: ./public target: {你的web目录} delete: true when: branch: master
- name: rebuild-cache image: drillster/drone-volume-cache volumes: - name: cache path: /cache settings: rebuild: true mount: - ./node_modules
trigger: event: - push
|
更新时间
通过git clone
会把文件修改时间都变为现在的时间,所以我们需要找个方法解决,比较好的方法是使用git的commit的时间替代修改时间
我们选择git-restore-mtime
来修复更新时间,放在generate
之前就行了
1 2 3 4 5 6 7
| - name: git-restore-time image: ubuntu:18.04 commands: - apt-get update - apt-get install -y bash python git - apt-get install -y git-restore-mtime - /usr/lib/git-core/git-restore-mtime --commit-time --work-tree . --git-dir ./.git
|
参考