使用 Drone CI 自动部署服务
编辑前言
平时写了几个服务自己用,在此之前一直都是手工方式部署,也就是本地打包、上传服务器、停止原有服务、运行新的服务,整个流程虽然就几个步骤,但是上传等待、每次手动敲命令行都是很麻烦的一件事,本着能自动就不要手动的原则,就决定搞一套CI/CD流程自动完成打包、部署的工作。看了网上介绍的一些CI平台,结合自己服务器性能有限、需求比较简单的情况下,最终选择了Drone。
整个流程涉及到的框架及工具:
- Nginx作为代理服务器;
- SpringBoot;
- Gradle作为构建工具;
安装Drone
Drone可以与多个Git平台进行搭配使用,包括Github、GitLab、Gitea、Gogs等,这里以Github为例。
创建OAuth Application
Drone搭配Github使用首先需要创建Github OAuth Application,在Github的Settings - Developer settings - OAuth Apps中创建新应用。注意Authorization callback URL
一定要是http(s)://域名/login
的格式,示例如下:
创建完成后需要记住ClientId
与ClientSecret
,在后面需要用到;
安装Drone与Drone-Runner
配置完成后正式安装Drone。Drone需要使用Docker安装,使用如下命令:
# 拉取镜像
docker pull drone/drone:2
# 运行
docker run \
--volume=/var/lib/drone:/data \
--env=DRONE_USER_CREATE=username:${GITHUB_USERNAME},admin:true \
--env=DRONE_GITHUB_CLIENT_ID=${CLIENT_ID} \
--env=DRONE_GITHUB_CLIENT_SECRET=${CLIENT_SECRET} \
--env=DRONE_SERVER_HOST=${URL} \
--env=DRONE_SERVER_PROTO=http \
--env=DRONE_RPC_SECRET=d1dcf4f192da23plpm25k126zh87otv0 \
--publish=7000:80 \
--restart=always \
--detach=true \
--name=drone \
drone/drone:2
DRONE_USER_CREATE
是管理员用户名,username
后面跟Github的用户名,如果没有设置就无法对仓库进行高级设置;DRONE_GITHUB_CLIENT_ID
是创建OAuth Application后的ClientId;DRONE_GITHUB_CLIENT_SECRET
是创建OAuth Application后的ClientSecret;DRONE_SERVER_HOST
需要与创建OAuth Application时的Authorization callback URL的域名相同(无/login
path);DRONE_SERVER_PROTO
需要与创建OAuth Application时的Authorization callback URL的proto相同;DRONE_RPC_SECRET
将在后面配置Drone-Runner时使用,作为Drone与Drone-Runner通信的“凭证”,可以用openssl rand -hex 16
生成一个;--publish
设置Docker的端口映射规则,这里将Drone的80端口映射到主机的7000端口;
接着安装Drone-Runner,命令如下:
# 拉取镜像
docker pull drone/drone-runner-docker:1
# 运行
docker run --detach \
--volume=/var/run/docker.sock:/var/run/docker.sock \
--env=DRONE_RPC_PROTO=http \
--env=DRONE_RPC_HOST=drone \
--env=DRONE_RPC_SECRET=d1dcf4f192da23plpm25k126zh87otv0 \
--env=DRONE_RUNNER_CAPACITY=2 \
--env=DRONE_RUNNER_NAME=drone-runner \
--publish=7001:3000 \
--restart=always \
--name=runner \
--link drone:drone \
drone/drone-runner-docker:1
需要注意DRONE_RPC_SECRET
需要与运行Drone时的配置保持一致;此外还要注意这里的DRONE_RPC_PROTO
, DRONE_RPC_HOST
以及--link
,由于我们这里的Drone与Drone-Runner在同一台主机上运行,需要它们之间可以相互通信,因此采用了--link
选项,使得Runner的DRONE_RPC_HOST
能够访问到Drone;
上面的Runner叫做Docker Runner,是Drone-Runner最常用的一种,主要为在无状态容器中编译及测试的代码提供了优化。除了Docker Runner以外,还有Kubernetes Runner, Exec Runner, SSH Runner等,由于与本文内容无关,不在此详细介绍
运行后,使用docker logs runner
命令查看日志,如果有successfully pinged the remote server信息,代表Runner运行成功。
配置Nginx
Nginx的配置比较简单,只需要在Nginx的配置文件中添加以下内容,然后重启Nginx即可:
# /etc/nginx/nginx.conf
server {
listen 80;
# URL与Authorization callback URL保持一致,如drone.test.com
server_name ${URL};
location / {
# 与Docker配置的端口号保持一致
proxy_pass http://localhost:7000;
proxy_set_header HOST $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 100m;
}
}
重启Nginx:
systemctl restart nginx
对于比较新的Nginx版本,对于站点的配置可能在sites-enabled目录下,这时候在/etc/nginx/sites-enabled
目录下创建drone.conf文件,然后将上面的配置复制到drone.conf文件中即可;
完成配置后,访问Drone URL,登录Github账号,针对仓库进行配置。
配置Repository
在Drone后台首页,可以看到Github账号下的所有仓库,点击我们想要启用CI的仓库,进入Settings Tab下,点击Activate Repository
后,可以看到如下配置页面:
我们需要将Trusted
开关打开,以保证后续pipeline中的命令可以正常执行。此外还有Timeout和Configuration等配置项,Timeout指定Pipeline脚本超时时间,而Configuration则对应了脚本名称。
如果Settings中没有这么多配置项,可能是运行Drone时没有设置Github用户名或者用户名不对
Settings Tab下还有Secrets
等配置,可以在Sercets
中新建secret,比如密码等,其值可以在后续的pipeline中获取到。
然后切换到Builds Tab下,点击右上角的NEW BUILD按钮,在弹出的对话框中输入目标分支名,然后点击Create。当目标分支有新的commit时,Drone会自动根据脚本运行Pipeline。
配置Pipeline
完成配置仓库后,我们需要手工编写Drone脚本,告诉Drone当有新提交时需要做什么。对于大部分服务,都需要 构建 - 部署 步骤。我们分开进行介绍。
构建任务
在需要开启CI的仓库的根目录下,创建.drone.yml
文件,输入以下内容:
kind: pipeline
type: docker
name: MyService
steps:
- name: build
image: gradle:jdk11
volumes:
- name: service-root
path: /app/build
- name: gradle-cache
path: /root/.gradle
commands:
- ./gradlew bootJar
- cp /drone/src/app-api/build/libs/*.jar /app/build/my-service-latest.jar
volumes:
- name: service-root
host:
path: /root/service
- name: gradle-cache
host:
path: /root/.gradle
kind
代表脚本的类型,这里指定为pipeline脚本,除了pipeline脚本还有secret和signature脚本;type
定义pipeline的类型,与runner类型,除了Docker外,还有Kubernetes, Exec, SSH等;name
为pipeline的名称;steps
定义了pipeline的步骤,其由多个如上面形式的步骤组成:name
为此步骤的命名;image
指定了这次步骤所依赖的Docker镜像;volumes
将Docker的路径与主机路径进行映射,以能在文件系统上相互访问;commands
为此步骤需要运行的命令;
volumes
挂载了主机的卷,我们可以在steps
的volumes
中直接使用这里定义的卷名称;
在上面的脚本中,我们进行了如下的操作:
- 定义pipeline名称为
MyService
; - 接着在steps中,定义了名为
build
的步骤,指定使用gradle:jdk11
作为此步骤使用的镜像; - 在volumes中,将主机的
/root/service
路径与Docker容器内的/app/build
路径以及主机的/root/.gradle
路径与Docker容器内的/root/.gradle
路径做了映射,这样便可以直接访问主机指定目录下的文件系统; - 在commands中,首先使用
gradlew
命令打包SpringBoot程序,然后将打包后的Jar文件复制到/app/build
目录下,由于我们将/app/build
路径与主机的/root/service
做了映射,因此打包后的Jar文件会被直接复制到主机的/root/service
目录下;
在复制命令中,cp的源目录是/drone/src/…,/drone/src是Drone的默认工作目录,代码会直接被clone到此目录下,可以在.drone.yml中通过workspace:path的方式进行配置
完成后,我们将.drone.yml
进行提交并推送到Github上,打开Drone后台,可以看到Drone已经自动开始了pipeline任务:
Drone默认为pipeline增加clone step,并在steps之前运行,特殊配置我们会在下文进行介绍
经过如上的操作,我们便完成了构建任务;
部署任务
由于Drone运行在Docker中,无法直接访问主机,因此部署任务需要SSH来“绕行”。
仍然是在.drone.yml
文件中,在steps下增加以下内容:
steps:
- name: build
# ...
- name: deploy
image: appleboy/drone-ssh
settings:
host: 123.12.34.56
username: root
password:
from_secret: ssh_password
port: 22
command_timeout: 5m
script:
- cd /root/service
- mv my-service.jar my-service.backup.jar
- mv my-service-latest.jar my-service.jar
- chmod +x run.sh
- ./run.sh
when:
event:
- promote
image
指定此步骤需要使用SSH镜像;settings
针对SSH进行配置:host
指定主机IP地址;username
SSH用户名;password
SSH密码,这里我们使用from_secret
的方式从Secrets中取值(需要在仓库的Settings配置);port
SSH端口号;command_timeout
远程操作命令超时时间;script
是需要SSH运行的脚本,类比于steps的commands;
when
指定了此步骤运行的时机,event=promote
指当构建任务完成后,手动在这次pipeline的后台点击右上角三个点 - Promote时运行;
在script
中,首先将当前目录切换至构建任务目标Jar包所在目录,然后执行一些重命名操作,并将运行脚本修改为execuable,最后运行脚本;
以运行Jar包为例,脚本内容如下:
#!/bin/sh
originalPid=$(ps x | grep "my-service" | grep -v grep | awk '{print $1}')
if [[ -n "$originalPid" ]]
then
echo "Running my-service PID=$originalPid"
echo "Stop Running my-service..."
kill $originalPid
else
echo "No Running my-service Found..."
fi
newPid=$(nohup java -jar my-service.jar >/dev/null 2>&1 &)
echo "Start my-service PID=$newPid"
以上便是构建和部署任务的相关配置,下面我们介绍一些针对Pipeline的其他配置。
Clone配置
上文中讲到Drone会自动执行Clone步骤,如果我们需要某些自定义配置,也可以在脚本中进行声明:
kind: pipeline
type: docker
name: default
clone:
depth: 50
retries: 3
depth
同Git的--depth
tag;retries
失败的重试次数,默认不会重试;
Drone默认clone不会fetch tag,如果需要可以通过以下配置:
steps:
- name: fetch
image: alpine/git
commands:
- git fetch --tags
同样,Drone也不会拉取submodules,如需要,则使用以下配置:
steps:
- name: submodules
image: alpine/git
commands:
- git submodule update --init --recursive
如果仍需要比较复杂的Git逻辑,可以直接禁用Drone的clone,自己定义Git逻辑:
clone:
disable: true
steps:
- name: clone
image: alpine/git
commands:
- git clone https://github.com/octocat/hello-world.git .
- git checkout $DRONE_COMMIT
DRONE_COMMIT
是触发此次pipeline的commit id;
Trigger配置
如果想要pipeline在某些特殊的情景下运行,可以通过Drone提供的trigger进行配置:
steps:
- name: build
# ...
trigger:
branch:
- master
include:
- master
- feature/*
exclude:
- alpha/*
ref:
- refs/heads/master
- refs/heads/**
- refs/pull/*/head
event:
- cron
- custom
- push
- pull_request
- tag
- promote
- rollback
branch
当某个pull request以此分支作为目标分支时触发;ref
基于Git引用有更新时触发;event
当遇到某个事件时触发;
ref
与event
都可以使用include与exclude
参考文档
- 0
- 0
-
分享