最近有个小需求,我们公司写文档是通过slate来的。每一次文档编写完成之后都需要执行bundle exec middleman build --clean生成静态文件。然后再使用rsync来进行上传操作。虽然只有2步。但是做的次数多了难免也让人觉得厌烦。萌生了自动化处理的想法。比较粗暴的方式有。每隔几分钟去检测一次git版本库是否有更新(以前本人真这样实现过),自己写个小web程序。设置webhook。接收到webhook再拉取进行操作(我也实现过,目前一切运行良好)。现在尝试下使用可持续集成的套路去实现。

说一下第三种方案相对前两种方案的特点。

  • 集中化管理。第一二种方案都需要将脚本放在对应的应用机器上面。第三种不需要,它集中化了。所有的webhook都在可持续集成平台处理。由平台再来操控应用。而且每一次触发操作都有详尽的日志。集中在一起比较容易看出哪里出了什么问题
  • 不需要在每一台应用机处理webhook(处理意味着每个应用机都需要绑定一个域名),可能和上面的集中化管理的优点是一个意思吧
  • 对于一个良好的架构。可持续集成平台比零散的脚本无疑更合适
  • 算是一个缺点。单独写脚本独立处理webhook相对可持续集成更容易

Drone配置

docker-compose我都不大用了,直接用docker stack deploy替代。后者相对前者实际没有什么区别。或者说前者是因为docker网络配置欠发达的产物。docker-compose适合单机部署,docker-swarm适合多主机。开启docker-swarm只需要docker swarm init大概就可以了,更详细的知识请查看官方文档

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
version: '3'

services:
drone-server:
image: drone/drone:0.7
ports:
- 82:8000
volumes:
- /data/github_drone:/var/lib/drone/
environment:
- DRONE_OPEN=false
- DRONE_ADMIN=ficapy
- DRONE_GITLAB=true
- DRONE_GITHUB_CLIENT=xxxxxxxxx
- DRONE_GITHUB_SECRET=xxxxxxxxxx
- DRONE_SECRET=xxxpwdxxx

drone-agent:
image: drone/drone:0.7
command: agent
depends_on:
- drone-server
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- DRONE_SERVER=ws://drone-server:8000/ws/broker
- DRONE_SECRET=xxxpwdxxx

然后执行docker stack deploy --compose-file=drone.yml drone,整个程序就运行起来了。此处是监听81端口。从配置文件中也可以猜一个大概。了解下drone的流程。service主要用来响应浏览器端的请求。接收到请求后使用websocket和agent通信用来下发任务获取结果。agent通过/var/run/docker.sock来和docker daemon进行通信。执行创建容器。执行命令等一系列过程。

有几点需要注意的

  • 最好指定管理员用户DRONE_ADMIN,因为有些操作需要admin权限
  • DRONE_OPEN设置为true的时候所有的用户都能授权后使用你的Drone进行集成测试。这往往是你不愿意的,个人的诉求往往是私有的
  • github和gitlab在一个drone不能同时使用,所以我部署了两套,使用了不同的域名
  • 起码了解OAuth流程和webhook的过程,才能更好的理解整套流程
  • 其他事宜可以查看Drone的安装文档

之所以监听81端口,是因为我使用的Caddy当做前置web服务器。配置如下Caddyfile

1
2
3
4
5
6
drone.ficapy.com {
proxy / dockerhost:81 {
websocket
transparent
}
}

对应的docker-compose文件如下

1
2
3
4
5
6
7
8
9
10
11
12
13
version: "3"

services:
Caddy:
image: "abiosoft/caddy"
volumes:
- /root/caddy/Caddyfile:/etc/Caddyfile
- /root/.caddy:/root/.caddy
ports:
- "80:80"
- "443:443"
extra_hosts:
- "dockerhost:${dockerhost}"

此处使用了环境变量dockerhost,我将它写入了zshrc配置文件中export dockerhost="172.17.0.1",不难看出只是为了转发请求, 和drone一样。执行docker stack deploy --compose-file=caddy_compose.yml caddy启动

持续集成部署

当使用Drone,并授权之后。Drone会自动给你的项目加上webhook地址。我们在项目根目录放置.drone.yml文件后会使用docker自动构建

实现本文开头说的需求.drone.yml文件如下

1
2
3
4
5
6
7
8
9
10
11
12
pipeline:
build:
image: ruby:2.3
commands:
- bundle config mirror.https://rubygems.org https://gems.ruby-china.org
- bundle install
- apt-get update && apt-get install nodejs rsync -y && bundle exec middleman build --clean
- rsync -r --rsh='ssh -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -i /data/id_rsa -p 22' build/* user@host:~/docs --progress --delete
volumes:
- /data:/data
when:
branch: master

在使用之前。我现在/data目录使用ssh-agent生成了密匙对。在目标主机上放上公钥,让我们能够ssh连接远端主机。然后容器创建的时候将密匙挂载到容器目录(此处需要管理员权限将项目设置为可信)。然后就顺理成章了。搭建环境,生成静态文件,rsync同步就完成了。