## Drone
基于 `Docker` 的 `CI/CD` 工具 `Drone` 所有编译、测试的流程都在 `Docker` 容器中进行。
开发者只需在项目中包含 `.drone.yml` 文件,将代码推送到 git 仓库,`Drone` 就能够自动化的进行编译、测试、发布。
本小节以 `GitHub` + `Drone` 来演示 `Drone` 的工作流程。当然在实际开发过程中,你的代码也许不在 GitHub 托管,那么你可以尝试使用 `Gogs` + `Drone` 来进行 `CI/CD`。
### 要求
- 拥有公网 IP、域名 (如果你不满足要求,可以尝试在本地使用 Gogs + Drone)
- 域名 SSL 证书 (目前国内有很多云服务商提供免费证书)
- 熟悉 `Docker` 以及 `Docker Compose`
- 熟悉 `Git` 基本命令
- 对 `CI/CD` 有一定了解
### 新建 GitHub 应用
登录 GitHub,在 <https://github.com/settings/applications/new> 新建一个应用。
![](https://img.kancloud.cn/1d/cb/1dcbae3f2458a312d1a5da327ee58099.png)
接下来查看这个应用的详情,记录 `Client ID` 和 `Client Secret`,之后配置 Drone 会用到。
### 配置 Drone
我们通过使用 `Docker Compose` 来启动 `Drone`,编写 `docker-compose.yml` 文件。
```
<pre class="prettyprint"><ol class="linenums"><li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln">version</span><span class="pun">:</span><span class="pln"> </span><span class="str">'3'</span></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln">services</span><span class="pun">:</span></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> drone</span><span class="pun">-</span><span class="pln">server</span><span class="pun">:</span></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> image</span><span class="pun">:</span><span class="pln"> drone</span><span class="pun">/</span><span class="pln">drone</span><span class="pun">:</span><span class="lit">0.8</span><span class="pun">-</span><span class="pln">alpine</span></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> ports</span><span class="pun">:</span></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="lit">443</span><span class="pun">:</span><span class="lit">443</span></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> </span><span class="com"># - "${PRO_PUBLIC_IP}:8000:8000"</span></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> volumes</span><span class="pun">:</span></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> </span><span class="pun">-</span><span class="pln"> drone</span><span class="pun">-</span><span class="pln">data</span><span class="pun">:</span><span class="str">/var/</span><span class="pln">lib</span><span class="pun">/</span><span class="pln">drone</span><span class="pun">/:</span><span class="pln">rw</span></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> </span><span class="pun">-</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">SSL_PATH</span><span class="pun">}:</span><span class="str">/etc/</span><span class="pln">certs</span><span class="pun">:</span><span class="pln">rw</span></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> restart</span><span class="pun">:</span><span class="pln"> always</span></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> environment</span><span class="pun">:</span></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> </span><span class="pun">-</span><span class="pln"> DRONE_SECRET</span><span class="pun">=</span><span class="pln">drone</span></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> </span><span class="pun">-</span><span class="pln"> DRONE_OPEN</span><span class="pun">=</span><span class="kwd">false</span></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> </span><span class="pun">-</span><span class="pln"> DRONE_ADMIN</span><span class="pun">=</span><span class="pln">$</span><span class="pun">{</span><span class="pln">GITHUB_SERNAME</span><span class="pun">}</span></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> </span><span class="pun">-</span><span class="pln"> DRONE_HOST</span><span class="pun">=</span><span class="pln">$</span><span class="pun">{</span><span class="pln">DRONE_HOST</span><span class="pun">}</span></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> </span><span class="pun">-</span><span class="pln"> DRONE_GITHUB</span><span class="pun">=</span><span class="kwd">true</span></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> </span><span class="pun">-</span><span class="pln"> DRONE_GITHUB_CLIENT</span><span class="pun">=</span><span class="pln">$</span><span class="pun">{</span><span class="pln">DRONE_GITHUB_CLIENT</span><span class="pun">}</span></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> </span><span class="pun">-</span><span class="pln"> DRONE_GITHUB_SECRET</span><span class="pun">=</span><span class="pln">$</span><span class="pun">{</span><span class="pln">DRONE_GITHUB_SECRET</span><span class="pun">}</span></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> </span><span class="pun">-</span><span class="pln"> DRONE_SERVER_CERT</span><span class="pun">=</span><span class="str">/etc/</span><span class="pln">certs</span><span class="pun">/</span><span class="pln">drone</span><span class="pun">.</span><span class="pln">domain</span><span class="pun">.</span><span class="pln">com</span><span class="pun">.</span><span class="pln">crt</span></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> </span><span class="pun">-</span><span class="pln"> DRONE_SERVER_KEY</span><span class="pun">=</span><span class="str">/etc/</span><span class="pln">certs</span><span class="pun">/</span><span class="pln">drone</span><span class="pun">.</span><span class="pln">domain</span><span class="pun">.</span><span class="pln">com</span><span class="pun">.</span><span class="pln">key</span></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> drone</span><span class="pun">-</span><span class="pln">agent</span><span class="pun">:</span></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> image</span><span class="pun">:</span><span class="pln"> drone</span><span class="pun">/</span><span class="pln">agent</span><span class="pun">:</span><span class="lit">0.8</span><span class="pun">-</span><span class="pln">alpine</span></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> restart</span><span class="pun">:</span><span class="pln"> always</span></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> depends_on</span><span class="pun">:</span></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> </span><span class="pun">-</span><span class="pln"> drone</span><span class="pun">-</span><span class="pln">server</span></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> volumes</span><span class="pun">:</span></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="str">/var/</span><span class="pln">run</span><span class="pun">/</span><span class="pln">docker</span><span class="pun">.</span><span class="pln">sock</span><span class="pun">:</span><span class="str">/var/</span><span class="pln">run</span><span class="pun">/</span><span class="pln">docker</span><span class="pun">.</span><span class="pln">sock</span><span class="pun">:</span><span class="pln">rw</span></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> environment</span><span class="pun">:</span></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> </span><span class="pun">-</span><span class="pln"> DRONE_SECRET</span><span class="pun">=</span><span class="pln">drone</span></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> </span><span class="pun">-</span><span class="pln"> DRONE_SERVER</span><span class="pun">=</span><span class="pln">drone</span><span class="pun">-</span><span class="pln">server</span><span class="pun">:</span><span class="lit">9000</span></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> dns</span><span class="pun">:</span><span class="pln"> </span><span class="lit">114.114</span><span class="pun">.</span><span class="lit">114.114</span></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln">volumes</span><span class="pun">:</span></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> drone</span><span class="pun">-</span><span class="pln">data</span><span class="pun">:</span></code></li>
</ol>
```
替换 `${SSL_PATH}` 为你网站的 SSL 证书路径。
替换 `${GITHUB_SERNAME}` 为你 GitHub 的用户名,该用户将成为 Drone 的管理员。
替换 `${DRONE_HOST}` 为你部署 Drone 的域名。
替换 `${DRONE_GITHUB_CLIENT}` 为你 GitHub 应用的 `Client ID`
替换 `${DRONE_GITHUB_SECRET}` 为你 GitHub 应用的 `Client Secret`
*注意:* 如果你的服务器占用了 `443` 端口,请配置 Nginx 代理,这里不再赘述。
#### 启动 Drone
```
<pre class="prettyprint"><ol class="linenums"><li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln">$ docker</span><span class="pun">-</span><span class="pln">compose up </span><span class="pun">-</span><span class="pln">d</span></code></li></ol>
```
### Drone 关联项目
在 Github 新建一个名为 `drone-demo` 的仓库。
打开我们已经部署好的 Drone 网站,使用 GitHub 账号登录,在界面中关联刚刚新建的 `drone-demo` 仓库。
### 编写项目源代码
在本机初始化一个 git 仓库
```
<pre class="prettyprint"><ol class="linenums"><li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln">$ mkdir drone</span><span class="pun">-</span><span class="pln">demo</span></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln">$ cd drone</span><span class="pun">-</span><span class="pln">demo</span></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln">$ git init</span></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln">$ git remote add origin git@github</span><span class="pun">.</span><span class="pln">com</span><span class="pun">:</span><span class="pln">username</span><span class="pun">/</span><span class="pln">drone</span><span class="pun">-</span><span class="pln">demo</span><span class="pun">.</span><span class="pln">git</span></code></li>
</ol>
```
这里以一个简单的 `Go` 程序为例,该程序输出 `Hello World!`
编写 `app.go` 文件
```
<pre class="prettyprint"><ol class="linenums"><li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="kwd">package</span><span class="pln"> main</span></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="kwd">import</span><span class="pln"> </span><span class="str">"fmt"</span></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln">func main</span><span class="pun">(){</span><span class="pln"> </span></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> fmt</span><span class="pun">.</span><span class="typ">Printf</span><span class="pun">(</span><span class="str">"Hello World!"</span><span class="pun">);</span></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pun">}</span></code></li>
</ol>
```
编写 `.drone.yml` 文件
```
<pre class="prettyprint"><ol class="linenums"><li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln">workspace</span><span class="pun">:</span></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> </span><span class="kwd">base</span><span class="pun">:</span><span class="pln"> </span><span class="str">/srv/</span><span class="pln">drone</span><span class="pun">-</span><span class="pln">demo</span></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> path</span><span class="pun">:</span><span class="pln"> </span><span class="pun">.</span></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln">pipeline</span><span class="pun">:</span></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> build</span><span class="pun">:</span></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> image</span><span class="pun">:</span><span class="pln"> golang</span><span class="pun">:</span><span class="pln">alpine</span></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> </span><span class="com"># pull: true</span></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> environment</span><span class="pun">:</span></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> </span><span class="pun">-</span><span class="pln"> KEY</span><span class="pun">=</span><span class="pln">VALUE</span></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> secrets</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">key1</span><span class="pun">,</span><span class="pln"> key2</span><span class="pun">]</span></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> commands</span><span class="pun">:</span></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> </span><span class="pun">-</span><span class="pln"> echo $$KEY</span></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> </span><span class="pun">-</span><span class="pln"> pwd</span></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> </span><span class="pun">-</span><span class="pln"> ls</span></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> </span><span class="pun">-</span><span class="pln"> CGO_ENABLED</span><span class="pun">=</span><span class="lit">0</span><span class="pln"> GOOS</span><span class="pun">=</span><span class="pln">linux go build </span><span class="pun">-</span><span class="pln">a </span><span class="pun">-</span><span class="pln">installsuffix cgo </span><span class="pun">-</span><span class="pln">o app </span><span class="pun">.</span></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="pun">./</span><span class="pln">app</span></code></li>
</ol>
```
`workspace` 指明 git 源代码克隆的目标路径,本例中 git 源代码将被克隆到 golang 容器中的 `/srv/drone-demo` 目录中。
`pipeline` 指明构建所需的 Docker 镜像,环境变量,编译指令等。
现在目录结构如下
```
<pre class="prettyprint"><ol class="linenums"><li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pun">.</span></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pun">├──</span><span class="pln"> </span><span class="pun">.</span><span class="pln">drone</span><span class="pun">.</span><span class="pln">yml</span></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pun">└──</span><span class="pln"> app</span><span class="pun">.</span><span class="pln">go</span></code></li>
</ol>
```
### 推送项目源代码到 GitHub
```
<pre class="prettyprint"><ol class="linenums"><li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln">$ git add </span><span class="pun">.</span></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln">$ git commit </span><span class="pun">-</span><span class="pln">m </span><span class="str">"test drone ci"</span></code></li>
<li class="l1"><code class="pcalibre10 pcalibre11 pcalibre9"></code></li>
<li class="l"><code class="pcalibre10 pcalibre11 pcalibre9"><span class="pln">$ git push origin master</span></code></li>
</ol>
```
### 查看项目构建过程及结果
打开我们部署好的 `Drone` 网站,即可看到构建结果。
![](https://img.kancloud.cn/06/58/0658109d193575eeb6c1c54c541cdc60.png)
当然我们也可以把构建结果上传到 GitHub,Docker Registry,云服务商提供的对象存储,或者生产环境中。
本书 GitBook 也使用 Drone 进行 CI/CD,具体配置信息请查看本书根目录 [`.drone.yml`](https://github.com/yeasy/docker_practice/blob/master/.drone.yml) 文件。
## 参考链接
- [Drone Github](https://github.com/drone/drone)
- [Drone 文档](http://docs.drone.io/)
- 致谢
- 目录
- 01. 前言
- 02. 修订记录
- 03. 如何贡献
- 04. Docker 简介
- 什么是 Docker
- 为什么要用 Docker
- 05. 基本概念
- 镜像
- 容器
- 仓库
- 06. 安装 Docker
- Ubuntu
- Debian
- CentOS
- Raspberry Pi
- macOS
- Windows PC
- 镜像加速器
- 07.使用镜像
- 获取镜像
- 列出镜像
- 删除本地镜像
- 利用 commit 理解镜像构成
- 使用 Dockerfile 定制镜像
- Dockerfile 指令详解
- COPY 复制文件
- ADD 更高级的复制文件
- CMD 容器启动命令
- ENTRYPOINT 入口点
- ENV 设置环境变量
- ARG 构建参数
- VOLUME 定义匿名卷
- EXPOSE 暴露端口
- WORKDIR 指定工作目录
- USER 指定当前用户
- HEALTHCHECK 健康检查
- ONBUILD 为他人作嫁衣裳
- 参考文档
- Dockerfile 多阶段构建
- 其它制作镜像的方式
- 实现原理
- 08. 操作容器
- 启动
- 守护态运行
- 终止
- 进入容器
- 导出和导入
- 删除
- 09. 访问仓库
- Docker Hub
- 私有仓库
- 私有仓库高级配置
- 10. 数据管理
- 数据卷
- 监听主机目录
- 11. 使用网络
- 外部访问容器
- 容器互联
- 配置 DNS
- 12. 高级网络配置
- 快速配置指南
- 容器访问控制
- 配置 docker0 网桥
- 自定义网桥
- 工具和示例
- 编辑网络配置文件
- 实例:创建一个点到点连接
- 映射容器端口到宿主主机的实现
- 13. Docker 三剑客之 Compose 项目
- 简介
- 安装与卸载
- 使用
- 命令说明
- Compose 模板文件
- 实战 Django
- 实战 Rails
- 实战 WordPress
- 14. Docker 三剑客之 Machine 项目
- 安装
- 使用
- 15. Docker 三剑客之 Docker Swarm
- 16. Swarm mode
- 基本概念
- 创建 Swarm 集群
- 部署服务
- 使用 compose 文件
- 管理敏感数据
- 管理配置信息
- 17. 安全
- 内核命名空间
- 控制组
- 服务端防护
- 内核能力机制
- 其它安全特性
- 总结
- 18. 底层实现
- 基本架构
- 命名空间
- 控制组
- 联合文件系统
- 容器格式
- 网络
- 19. Etcd 项目
- 简介
- 安装
- 集群
- 使用 etcdctl
- 使用 etcdctl v2
- 20. CoreOS 项目
- 简介
- 工具
- 快速搭建 CoreOS 集群
- 21. Kubernetes 项目
- 简介
- 快速上手
- 基本概念
- kubectl 使用
- 架构设计
- 22. Mesos - 优秀的集群资源调度平台
- Mesos 简介
- 安装与使用
- 原理与架构
- Mesos 配置项解析
- 日志与监控
- 常见应用框架
- 本章小结
- 23. 容器与云计算
- 简介
- 亚马逊云
- 腾讯云
- 阿里云
- 小结
- 24. 实战案例-操作系统
- Busybox
- Alpine
- Debian Ubuntu
- CentOS Fedora
- 本章小结
- 25. 实战案例-CI-CD
- Drone
- 26. Docker 开源项目
- LinuxKit
- 27. 附录
- 附录一:常见问题总结
- 附录二:热门镜像介绍
- Ubuntu
- CentOS
- MySQL
- MongoDB
- Redis
- Nginx
- WordPress
- Node.js
- 附录三:Docker 命令查询
- 附录四:Dockerfile 最佳实践
- 附录五:资源链接
- 附录六:Docker 中文资源