如今的项目或者个人项目中,大家的代码怎么部署呢?公司一般都有完整的持续集成以及持续交付平台,对于小公司可能也有各自搭建了一些,比如jenkins,以及gitlab集成的gitlab-ci等等,这些都可以完成我们部署的工作甚至是测试集成等等一系列流水化工作。

但是,即使如此,我依旧相信,很多公司或者前端开发者根本不太关注什么持续交付、持续集成等等。至于在部署这一块,不少走的还是本地npm run build+ssh+sftp手动模式。当然我之前也一直这么干,为此还借助开源项目自己写了一个开源脚本@dllcnx/auto-deploy供团队使用(注意:此脚本密码模式因为要知道服务器用户密码且部署目录你的账户要有权限,虽然针对密码做了个加密,但是…)。

那么,我就想提交一下代码,其它啥都不想管。所以为什么不试试Drone呢?

Drone是一个现代持续集成平台,它使繁忙的团队能够使用强大的云原生管道引擎自动执行其构建、测试和发布工作流程。

所以Drone的功能可不仅仅是部署,只是我针对这个需求可以使用它,所以一定不要被我的文章局限住,此文只是抛砖引玉。上篇docker服务系列的文章,我也介绍了搭建了一个git服务仓库06.搭建一个自己的私有仓库-Gitea,所以这篇文章也就以gitea仓库为例,来达到代码推送,由drone去自动编译部署,其它如Github、Gitlab、Gitee、Bitbucket以及gitea/Gogs可以看各自文档。

为什么不用jenkins呢?因为原来在公司就在用它,自己个人的代码就想试试其它的,并且jenkins从界面到配置,再到内存占用,我个人都是不太喜欢的,当然不是说jenkins不好,反而借助插件等等,它的能力是很强大的。

官方是这么说的: Drone是一个容器原生持续集成系统,旨在成为老旧Jenkins安装的自助服务替代品。

一、准备

创建 Gitea OAuth 应用程序。Client ID 和 Client Secret 用于授权访问 Gitea 资源。setting->Applications->create Applications

  1. 填入名称和回调地址(其实就是后边drone部署的访问地址)

注意: Client Secret只显示一次,确认关闭后就不再显示了,如果忘了就只能重置。

image-20240204202815216

  1. 创建共享秘钥

创建一个共享密钥,以验证运行器与中央无人机服务器之间的通信。

您可以使用 openssl 生成共享密钥:

1
openssl rand -hex 16     // 796f8245fdba973216a5493b4c0b7f83

二、安装Drone服务

完整的drone服务由Drone和drone-runner构成。

drone服务启动并运行后,需要安装runner来执行生成管道。有关详细的安装说明,请参阅运行器安装文档。drone-runner轮询服务器以查找要执行的工作负载。有不同类型的运行器针对不同的用例和运行时环境进行了优化。您可以安装一个或多个一个或多个类型的流道。这一块我们采用drone-runner即可,它是一个通用运行器。

Docker runner是在临时 Docker 容器中执行管道步骤的守护程序。您可以安装单个 Docker runner,也可以在多台计算机上安装 Docker runner以创建自己的构建集群。

docker原生部署
  1. 拉取镜像
1
2
docker pull drone/drone:latest
docker pull drone/drone-runner-docker:latest
  1. 启动drone服务
1
2
3
4
5
6
7
8
9
10
11
12
docker run --detach=true --volume=/docker/drone:/data --env=DRONE_GITEA_SERVER=https://dllcnx.com:30000 \
--env=DRONE_GITEA_CLIENT_ID=06310187-3064-4cf8-92f3-4008dfaa810b \
--env=DRONE_GITEA_CLIENT_SECRET=gto_c4ytebo2jmorn3bihhyy6phbtet34hl6sbhomhk77x7x2iuv2hva \
--env=DRONE_RPC_SECRET=796f8245fdba973216a5493b4c0b7f83 \
--env=DRONE_SERVER_HOST=192.168.64.1:37078 \
--env=DRONE_SERVER_PROTO=http \
--env=DRONE_USER_CREATE=username:test,admin:true \
--publish=37078:80 \
--publish=443:443 \
--restart=always \
--name=drone \
drone/drone:latest
  • DRONE_GITEA_CLIENT_ID: 必需的字符串,值提供您的 Gitea oauth 客户端 ID。

  • DRONE_GITEA_CLIENT_SECRET:必需的字符串,提供 Gitea oauth 客户端密码。

  • DRONE_GITEA_SERVER:必填字符串,提供您的 Gitea 服务器地址。

  • DRONE_GIT_ALWAYS_AUTH: 可选的布尔值,将 Drone 配置为在克隆公共存储库时进行身份验证。

  • DRONE_RPC_SECRET: 必需的字符串,提供在上一步中生成的共享密钥。这用于验证服务器和运行器之间的 rpc 连接。必须为服务器和运行器提供相同的密钥值。

  • DRONE_SERVER_HOST:必需的字符串,提供外部主机名或 IP 地址。如果使用 IP 地址,则可以包含端口。例如。drone.company.com

  • DRONE_SERVER_PROTO: 必需的字符串,提供外部协议方案。此值应设置为 http 或 https。如果配置 ssl 或 acme,则此字段默认为 https。

  • DRONE_USER_CREATE: 可选,应在启动时创建的可选用户帐户。用于为系统提供一个管理帐户。它可以是一个真实的帐户(说白了就是第一步用哪个git仓库的用户,这边用哪个即可),也可以是一个机器帐户。

image-20240204194436664

  1. 启动drone-runner服务
1
2
3
4
5
6
7
8
9
10
11
docker run --detach \
--volume=/var/run/docker.sock:/var/run/docker.sock \
--env=DRONE_RPC_PROTO=http \
--env=DRONE_RPC_HOST=192.168.64.1:37078 \
--env=DRONE_RPC_SECRET=796f8245fdba973216a5493b4c0b7f83 \
--env=DRONE_RUNNER_CAPACITY=2 \
--env=DRONE_RUNNER_NAME=drone-runner \
--publish=33000:3000 \
--restart=always \
--name=runner \
drone/drone-runner-docker:latest
  • DRONE_RPC_HOST: 提供 Drone 服务器的主机名(和可选端口)。运行器连接到主机地址的服务器,以接收要执行的管道。
  • DRONE_RPC_PROTO: 提供用于连接到 Drone 服务器的协议。该值必须是 http 或 https。
  • DRONE_RPC_SECRET: 提供用于向 Drone 服务器进行身份验证的共享密钥。这必须与 Drone 服务器配置中定义的密钥匹配。

image-20240204195848989

  1. 查看日志并验证运行器是否已成功与 Drone 服务器建立连接。

    1
    docker logs runneer

image-20240204201800066

注意:这边有一个坑,大家发现我上边因为本机部署drone,但是运行器的DRONE_RPC_HOST和服务的DRONE_SERVER_HOST都使用的ip地址,因为localhost和127.0.0.1会因为地址特殊性连接失败。

docker-compose部署

还是推荐docker-compose部署,清楚明了。

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
# docker-compose.yml
version: '3'
services:
drone-server:
container_name: drone-server
# 构建所使用的镜像
image: drone/drone:latest
# 映射容器内80端口到宿主机的37079端口,如果我们开启https就需要使用443端口了
ports:
- 37078:80
- 443:443
# 映射容器内/data目录到宿主机的/data/drone目录
volumes:
- /docker/drone:/data
# 容器随docker自动启动
restart: always
environment:
# Gitea 服务器地址
- DRONE_GITEA_SERVER=https://dllcnx.com:30000
# Gitea OAuth2客户端ID
- DRONE_GITEA_CLIENT_ID=06310187-3064-4cf8-92f3-4008dfaa810b
# Gitea OAuth2客户端密钥
- DRONE_GITEA_CLIENT_SECRET=gto_c4ytebo2jmorn3bihhyy6phbtet34hl6sbhomhk77x7x2iuv2hva
# drone的共享密钥
- DRONE_RPC_SECRET=796f8245fdba973216a5493b4c0b7f83
# drone的主机名
- DRONE_SERVER_HOST=192.168.64.1:37078
# 外部协议方案
- DRONE_SERVER_PROTO=http
# 用户
- DRONE_USER_CREATE=username:test,admin:true
drone-runner:
image: drone/drone-runner-docker:latest
container_name: drone-runner
environment:
# 外部协议方案
- DRONE_RPC_PROTO=http
# drone的主机名
- DRONE_RPC_HOST=192.168.64.1:37078
# drone的共享密钥,与drone服务的DRONE_RPC_SECRET一致
- DRONE_RPC_SECRET=796f8245fdba973216a5493b4c0b7f83
- DRONE_RUNNER_CAPACITY=2
- DRONE_RUNNER_NAME=drone-runner
restart: always
volumes:
- /var/run/docker.sock:/var/run/docker.sock
ports:
# 端口
- 33000:3000

三、授权连接

浏览器访问192.168.64.1:37078就可以打开drone的web界面了。点击continue连接git仓库。

image-20240204202316949

注意:这块也有一个坑,我们第一步在gitea创建应用时,填了个回调地址,我在drone访问地址后边加了个/login,如果不加,gitea无法正常连接,其它我暂时没验证过。
image-20240204203110579
验证成功后可以添加一个用户,但是需要注意,只有我们部署时DRONE_USER_CREATE指定的用户才是管理员,他可以管理其它用户,而且我们使用的Gitea账户其实已经存在了。
image-20240204222354718
进入后我们其实就可以看到我们仓库的代码了。
image-20240204222703984
如果我们通过管理员账户进来的,其实只要是gitea授权进来的,就可以看到左下角设置按钮。
image-20240204223847121

四、使用

我这边只以自动部署为例,其它需求可以自行扩展扩展。dronejenkins配置的方式这一块就有了差异,jenkins之前我都是在其web页面进行配置编译流程,管道等等(可能我还用到其更多功能)。而drone只需要在web界面激活自己要操作的仓库,然后在项目根目录添加.drone.yml配置文件,编写流程即可。

  1. 我本地创建了一个vue项目,并且上传gitea仓库成功。

image-20240205091246488

  1. 进入drone的web界面,右上角Sync同步一下项目,就可以看到我的仓库了。进入Activate repository。

image-20240205091449877

  1. 配置参数,此次只需要激活setting-General-Trusted选项存储即可。

激活成功后,我们去gitea仓库也能看到其实给gitea的webhooks添加了条触发钩子。
image-20240205101922640

  1. 在项目中创建.drone.yml配置文件,进行流程配置。所有的执行环境都是以docker驱动的,比如node,ssh,scp等等。更多插件可以访问Drone Plugins具体看看

我这边只是一个示例,用node镜像编译,用drone-scp进行文件传递,方法其实可以千奇百怪,流程步骤也可以自己添加,删除以及调整。比如下面的流程没备份,我们可以在步骤2之前加一个备份流程,可以自己去找对应的镜像按文档添加即可。

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
# 项目的.drone.yml 文件
kind: pipeline # 定义一个管道
type: docker # 定义管道类型
name: default # 定义管道名称


volumes:
- name: node_modules # 数据卷名称
# Host Volume, 挂载到宿主机上的卷轴
host:
# 宿主机的绝对路径,因为是前端项目,每次重新npm install会因为网络或者版本有很大风险
# 所以这一步相当于将node_modules缓存到宿主机某个目录下
path: /home/drone/cache/test/node_modules


steps:
# 步骤1,编译项目
- name: build-project # 步骤名称
image: node:18.12.1 # 使用镜像和版本
volumes: # 当前步骤使用(挂载)的卷轴
- name: node_modules # 数据卷名称,上边volumes的真实宿主机名称
# 容器内的绝对路径,drone启动时,都会先执行clone代码操作,将对应仓库代码拉取到/drone/src目录下
# 所以相当于你的代码目录是/drone/src,然后把node_modules映射也映射进这个目录即可
path: /drone/src/node_modules
# 执行的命令
commands:
- pwd # 查看当前目录 `/drone/src`
- ls -alt
- npm install # 安装依赖
- npm run build # 执行构建指令
- ls -alt
when:
event: push #触发构建的操作,有push, merge等等,我这边是push操作触发,其它可以具体研究文档

# 步骤2,编译完就会自动执行这个第二个步骤,这一步是为了把编译好的文件部署到位置
# 这边我采用了ssh连接,然后传递的模式,这里就用到了敏感信息的变量,连接服务器后把dist文件夹上传到对应服务器的/www下
# 这一块方法肯定很多,如果在同一个服务器,利用上一步的直接挂载个目录,把代码直接编译进挂载目录或者移入挂载目录
- name: deploy-project
image: appleboy/drone-scp:latest
settings:
host:
from_secret: uhost # Secrets定义的敏感变量,服务器地址
username:
from_secret: uusername # Secrets定义的敏感变量,服务器地址
password:
from_secret: upassword # Secrets定义的敏感变量,服务器密码
port:
from_secret: uport # Secrets定义的敏感变量,服务器访问端口
target: /www # 部署到服务器目录
source: ./dist/* # 需要传递的目录
when:
event: push

可以看到,我们上传代码就可自动触发构建,第一步clone默认操作,第二步第三步就是我们定义的步骤1和步骤2。第一次npm install可能会有点慢,因为没缓存的原因。

还有一个我自己用的流程案例,比如我利用vuepress写笔记,但是我就想把文章目录独立出来,不要vuepress的配置代码,node_modules,等等混在一起,因为配置模块等等定义好一般很少动,只需要每次更新文章即可。我就把两个拆分外部配置和内部文章目录拆分成两个项目。

我的想法是把pages独立成一个分支,每次激活流程后先拉取架子代码,然后再里面在拉取文章代码,然后合二唯一后编译即可。我为了自己控制代码,就禁用了drone的自动拉取代码,完全由自己控制。

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
# 项目的.drone.yml 文件
kind: pipeline # 定义一个管道
type: docker # 定义管道类型
name: default # 定义管道名称


volumes:
- name: node_modules # 数据卷名称
# Host Volume, 挂载到宿主机上的卷轴
host:
# 宿主机的绝对路径
path: /home/drone/cache/dllcnx-notes-vuepress/node_modules


steps:
- name: clone
image: drone/git # 使用镜像和版本
# 执行的命令
commands:
- git clone https://xxx/xxx/dllcnx-notes-vuepress.git
when:
event: push
- name: clone-dllcnx-note-vuepress # 步骤名称
image: drone/git # 使用镜像和版本
# 执行的命令
commands:
- cd dllcnx-notes-vuepress
- git clone -b pages https://xxx/xxx/dllcnx-notes-vuepress.git pages
- pwd # 查看当前目录 `/drone/src`
# - ls -alt
when:
event: push
- name: build-project # 步骤名称
image: node:18.12.1 # 使用镜像和版本
volumes: # 当前步骤使用(挂载)的卷轴
- name: node_modules # 数据卷名称
path: /drone/src/dllcnx-notes-vuepress/node_modules # 容器内的绝对路径
# 执行的命令
commands:
- cd dllcnx-notes-vuepress
- pwd # 查看当前目录 `/drone/src`
- ls -alt
- npm install # 安装依赖
- npm run docs:build # 执行构建指令
- ls -alt
when:
event: push

- name: deploy-project
image: appleboy/drone-scp:latest
settings:
host:
from_secret: uhost
username:
from_secret: uusername
password:
from_secret: upassword
port:
from_secret: uport
target: /docker/www
source: ./dllcnx-notes-vuepress/dist/*
when:
event: push
clone:
disable: true

五、未来

本来完结撒花了,但是我因为自己个人一直使用drone,这次写文章时想重新走一遍流程,才发现drone官方被Harness收购了,所以下一代产品也有了大的变革。

官方这么说:

我们最近宣布了Drone 的下一个主要版本——更名为 Gitness。通过此更改,我们将添加源代码管理功能,其中包括对代码托管、拉取请求、代码审查等基本功能的支持。

这是 Drone 的重大演变,从持续集成到成熟的开发人员平台。如需了解更多信息,请访问 gitness.com

其实按我简单理解,把代码仓库也集成进去了,自己直接完成了Gitea+Drone。

那么还能继续用Drone吗?

是的。Gitness 是 Drone 的下一个主要版本,但是,Gitness 仍处于测试阶段并正在积极开发中。您应该继续使用最新的稳定版本的 Drone 进行生产安装,直到 Gitness 的稳定版本可用。

我们如果想用drone,会被迫使用 Gitness 进行代码托管吗?

不。我们计划为所有主要代码托管提供商(包括 GitHub、GitLab、Bitbucket、Bitbucket Server、Gitea、Gogs 和 Azure Devops)提供支持 Gitness 管道。

还有其它具体问题,可以看看官方答疑Gitness 与 Drone

六、后续

问题1:私有仓库无法拉取代码

因为drone默认不验证账号密码,如果我们需要拉取私有仓库代码,打开验证配置,并填入git账号与密码配置

解决方案:

1
2
3
4
# docker-compose.yml文件中drone-server环境变量里面开启:
- DRONE_GIT_ALWAYS_AUTH=true
- DRONE_GIT_USERNAME=部署账户的用户名
- DRONE_GIT_PASSWORD=部署账户的密码

但是,如果你关闭了官方clone选项,然后采用插件,例如我第二个案例,可能这个方式就失效了,可以直接采用http配置方式或者ssh方式拉取代码。

1
2
# http带账号密码拉取方式
git clone http://username:password@gitea.dllcnx.com/jukaifeng/xxx.git