使用Gitea及Drone搭建轻量持续集成服务

8/15/2022 DroneGiteaPostgreSQLJenkinsDevOpsCI/CD持续集成

# 1. 前言

持续集成和构建的工具有很多,比如著名的 Jenkins、Github Action 和 Gitlab CI/CD。但是这些工具面对私人项目不是要收费就是占用大量服务器资源,作为个人开发者的私人项目如果想要使用并不友好。那么开源免费的 Drone CI 是个不错选择,它不但非常轻量,而且十分强大。并可以结合私有代码仓库自动编译、构建服务,几行脚本即可实现自动化部署。本文主要讲述怎么在 VPS 里从零开始搭建一个基于 Gitea + Drone CI 的持续集成系统。

# 1.1 需求背景

持续部署是能以自动化方式,频繁而且持续性的,将软件部署到生产环境,使软件产品能够快速迭代。

在之前部署 web 项目时,都是手动进行部署:

拉取代码 ---> 编译项目 ---> 打包镜像 ---> 推送镜像仓库 ---> 服务器拉取新镜像 ---> 停止和移除旧容器 ---> 启动新容器

这一整套部署步骤枯燥又费时。持续部署就是使用工具自动处理整套步骤,代码在提交之后自动执行整套流程将项目部署到生产环境,省去繁琐的人工操作。

持续部署整套流程本质上是一个极其简单的东西,可以拆解为两个阶段:

  • 打包阶段: 拉取代码 ---> 编译项目 ---> 镜像打包 ---> 推送镜像仓库

  • 部署阶段: SSH 连接服务器 ---> 拉取新镜像 ---> 停止和移除旧容器 ---> 启动新容器

未使用自动化部署工具时,整套流程也可以使用shell脚本实现半自动化。而所谓的持续部署工具本质上做的也是这么一件事,只是提供了更强大更丰富的功能。

# 1.2 基本概念

# 1.2.1 DevOps简介

DevOps 一词的来自于 Development 和 Operations 的组合,突出重视软件开发人员和运维人员的沟通合作,通过自动化流程来使得软件构建、测试、发布更加快捷、频繁和可靠。DevOps 其实包含了三个部分:开发、测试和运维。它强调的是高效组织团队之间如何通过自动化的工具协作和沟通来完成软件的生命周期管理,从而更快、更频繁地交付更稳定的软件。

# 1.2.2 CI/CD简介

CI(Continuous integration,中文意思是持续集成)强调开发人员提交了新代码之后,立刻进行构建、测试。根据测试结果,我们可以确定新代码和原有代码能否正确地集成在一起。持续集成的好处在于,每次代码的小幅变更,就能看到运行结果,从而不断累积小的变更,而不是在开发周期结束时,一下子合并一大块代码。

CI

CD(Continuous Delivery, 持续交付)是在持续集成的基础上,将集成后的代码部署到类生产环境中。如果代码没有问题,可以继续手动部署到生产环境。下图反映的是CI/CD 的大概工作模式。

CI及CD

# 1.3 介绍Gitea与PostgreSQL

# 1.3.1 Gitea简介

Gitea (opens new window) 是一个开源社区驱动的轻量级代码托管解决方案,后端采用 Go 编写,采用 MIT 许可证。 Gitea 的首要目标是创建一个极易安装,运行非常快速,安装和使用体验良好的自建 Git 服务。采用 Go 作为后端语言,只要生成一个可执行程序即可。并且他还支持跨平台,支持 Linux, macOS 和 Windows 以及各种架构,除了 x86,amd64,还包括 ARM 和 PowerPC。

Gitea简介

# 1.3.2 PostgreSQL简介

PostgreSQL (opens new window) 是一款高级的企业级开源关系数据库,支持 SQL(关系型)和 JSON(非关系型)查询。它是一个高度稳定的数据库管理系统,依托 20 多年的社区发展,造就了其高水平的故障恢复能力、完整性和正确性。

# 1.4 介绍Drone与Jenkins

# 1.4.1 Drone简介

Drone (opens new window) 是一款基于 Docker 的 CI/CD 工具,所有编译、测试、发布的流程都在 Docker 容器中进行。

开发者只需在项目中包含 .drone.yml 文件,将代码推送到 git 仓库,Drone 就能够自动化的进行编译、测试、发布。

Drone应用由 Server(服务器)和 Runner(执行器)两种服务构成,前者主要负责管理和展示,后者主要负责执行操作。

drone简介

# 1.4.2 Jenkins简介

Jenkins (opens new window) 是一个开源项目,基于Java开发的持续集成工具,用于监控持续重复的工作,旨在提供一个开放易用的软件平台,使软件的持续集成变成可能。

Jenkins的主要功能是将项目中重复执行的工作自动化的执行。具体功能大概有:

  • 软件的持续构建和测试,Jenkins提供了一个系统,使开发人员可以很容易的将改变集成到工程中。自动化的,持续的构建有利于提高开发效率。
  • 项目源代码修改的检测,jenkins能够从项目的CVS生成最近修改的集合列表,且不会增加CVS Repository的负载。
  • 分布式构建,Jenkins可以将工程构建到多台机器,更好地利用硬件资源,节省时间。

Jenkins代码开源,用户可以自己编写插件,所以jenkins可以实现很多定制的功能。

# 1.4.3 Drone与Jenkins的对比

Drone 还有很多其它的优势:Drone 使用 Go 语言写成,其内存占用仅是 Jenkins 的十分之一;Drone 的插件虽然相比 Jenkins 较少,但全部都是 Docker 容器,可以统一在配置文件中调用。

Jenkins Drone
运行环境 Worker 系统环境 可选 Docker 容器,Worker 系统环境,DigitalOcean 云服务器
配置文件 专用语言 Jenkinsfile 通用语言 YAML/Jsonnet
插件数量 多,1836个 少,102个
插件配置 主要在网页端,只有少量能通过配置文件 统一在配置文件中
开发语言 Java Go
内存占用 多,1GB左右 少,100MB左右

# 2. 搭建前的环境准备

以下我将采用Nginx+Docker部署的方式进行搭建,VPS系统用的是Debian 11 x86_64。VPS的购买及配置、域名解析、Docker及Docker Compose的概念及使用...这些基本的就不再赘述了,如果不会的话见我的其他博客:VPS基本部署环境的搭建与配置 (opens new window)Docker容器化及项目环境管理 (opens new window)

# 2.1 Docker及Docker Compose环境搭建

# 2.1.1 Docker环境搭建

$ apt-get update -y && apt-get install curl -y  # 安装curl
$ curl https://get.docker.com | sh -   # 安装docker
$ sudo systemctl start docker  # 启动docker服务
$ docker version # 查看docker版本(客户端要与服务端一致)
1
2
3
4

# 2.1.2 Docker Compose环境搭建

// 下载安装docker-compose,最新版见:https://github.com/docker/compose/releases
$ sudo curl -L https://github.com/docker/compose/releases/download/1.29.2/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose       
// 赋予docker-compose执行权限
$ sudo chmod +x /usr/local/bin/docker-compose
// 查看docker-compose版本号,验证是否安装成功
$ docker-compose --version
1
2
3
4
5
6

# 2.2 使用OneinStack搭建Nginx并进行配置

# 2.2.1 OneinStack简介

项目简介:OneinStack是一套部署开发环境的脚本工具,它的部署方式是纯原生部署,我主要使用里面的Nginx,用来辅助生成配置。

项目地址:https://github.com/oneinstack/oneinstack (opens new window)

# 2.2.2 使用OneinStack搭建Nginx服务

$ wget -c http://mirrors.linuxeye.com/oneinstack-full.tar.gz && tar xzf oneinstack-full.tar.gz && ./oneinstack/install.sh --nginx_optio
1

注:OneinStack采用编译安装的方式搭建Nginx服务,速度比较慢,请耐心等待,常用的默认目录有:

Nginx安装目录: /usr/local/nginx
Nginx配置目录:/usr/local/nginx/conf
网站数据目录:/data/wwwroot/域名
1
2
3

# 2.2.3 创建网站开启HTTPS

OneinStack 内置了 acme.sh,它会自动帮你申请 SSL 证书。

$ cd ./oneinstack
$ ./vhost.sh
1
2

配置过程的参考示例如下(示例域名为www.demo.com):

#######################################################################
#       OneinStack for CentOS/RedHat 7+ Debian 8+ and Ubuntu 16+      #
#       For more information please visit https://oneinstack.com      #
#######################################################################

What Are You Doing?
        1. Use HTTP Only
        2. Use your own SSL Certificate and Key
        3. Use Let's Encrypt to Create SSL Certificate and Key
        q. Exit
Please input the correct option: 2 【选择2】(使用了Cloudfare CDN的话,可以选择自签名证书)

Please input domain(example: www.example.com): www.demo.com 【输入域名】
domain=www.demo.com

Please input the directory for the domain:www.demo.com :
(Default directory: /data/wwwroot/www.demo.com): 【回车,使用默认配置】
Virtual Host Directory=/data/wwwroot/www.demo.com

Create Virtul Host directory......
set permissions of Virtual Host directory......

Do you want to add more domain name? [y/n]: n  【输入n,不添加其他域名了】

Do you want to redirect all HTTP requests to HTTPS? [y/n]: y  【输入y,强制HTTPS】

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.

Country Name (2 letter code) [CN]: 【回车】

State or Province Name (full name) [Shanghai]: 【回车】

Locality Name (eg, city) [Shanghai]: 【回车】

Organization Name (eg, company) [Example Inc.]: 【回车】

Organizational Unit Name (eg, section) [IT Dept.]: 【回车】

Do you want to add hotlink protection? [y/n]: n 【输入n,不需要防盗链保护】

Allow Rewrite rule? [y/n]: y 【输入y,允许重写规则】

Please input the rewrite of programme :
wordpress,opencart,magento2,drupal,joomla,codeigniter,laravel
thinkphp,pathinfo,discuz,typecho,ecshop,nextcloud,zblog,whmcs rewrite was exist.
(Default rewrite: other): 【回车】
You choose rewrite=other

Allow Nginx/Tengine/OpenResty access_log? [y/n]: y  【输入y,允许日志记录】
You access log file=/data/wwwlogs/www.demo.com_nginx.log

#######################################################################
#       OneinStack for CentOS/RedHat 7+ Debian 8+ and Ubuntu 16+      #
#       For more information please visit https://oneinstack.com      #
#######################################################################
Your domain:                  www.demo.com
Virtualhost conf:             /usr/local/nginx/conf/vhost/www.demo.com.conf
Directory of:                 /data/wwwroot/www.demo.com
Rewrite rule:                 /usr/local/nginx/conf/rewrite/other.conf
Self-signed SSL Certificate:  /usr/local/nginx/conf/ssl/www.demo.com.crt
SSL Private Key:              /usr/local/nginx/conf/ssl/www.demo.com.key
SSL CSR File:                 /usr/local/nginx/conf/ssl/www.demo.com.csr
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

配置完成后,修改一下配置文件

$ vim /usr/local/nginx/conf/vhost/www.demo.com.conf
1

server模块删除内容:

  location ~ [^/]\.php(/|$) {
    #fastcgi_pass remote_php_ip:9000;
    fastcgi_pass unix:/dev/shm/php-cgi.sock;
    fastcgi_index index.php;
    include fastcgi.conf;
  }

  location /.well-known {
    allow all;
  }
1
2
3
4
5
6
7
8
9
10

server模块新增内容:

location ^~ /.well-known/acme-challenge/ {
  default_type "text/plain";
  allow all;
  root /data/wwwroot/www.demo.com/;  # 把路径换一下
}
1
2
3
4
5

检查并重载Nginx配置

$ nginx -t
$ nginx -s reload
1
2

注:使用nginx -t命令可以检查Nginx配置,若出现如下内容则说明配置正确。

nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
1
2

之后把资源文件放置到 /data/wwwroot/www.demo.com 目录即可,打开Chrome浏览器即可访问(如果出现403则需要给这个目录添加一下权限)。

# 2.2.4 配置反向代理

如果需要配置反向代理的话(比如Docker部署的服务),流程同上,只是修改Nginx配置文件处有所不同。

server模块删除内容:

  location ~ [^/]\.php(/|$) {
    #fastcgi_pass remote_php_ip:9000;
    fastcgi_pass unix:/dev/shm/php-cgi.sock;
    fastcgi_index index.php;
    include fastcgi.conf;
  }
  location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|flv|mp4|ico)$ {
    expires 30d;
    access_log off;
  }
  location ~ .*\.(js|css)?$ {
    expires 7d;
    access_log off;
  }
    location /.well-known {
    allow all;
  }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

server模块新增内容:

location / {
  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;
  proxy_pass http://demo;  # 与下面 upstream 的名称一致即可
}
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|flv|mp4|ico)$ {
  proxy_pass http://demo;  # 与下面 upstream 的名称一致即可
  expires 30d;
  access_log off;
}
location ~ .*\.(js|css)?$ {
  proxy_pass http://demo;   # 与下面 upstream 的名称一致即可
  expires 7d;
  access_log off;
}
location ^~ /.well-known/acme-challenge/ {
  default_type "text/plain";
  allow all;
  root /data/wwwroot/www.demo.com/;  # 把路径换一下
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

与server模块同级新增内容:

upstream demo {
  server 127.0.0.1:8888;  # 8888换成自己的反向代理端口号
}
1
2
3

# 2.3 生成并设置 SSH KEY

Step1:生成SSH KEY

$ ssh-keygen -t rsa -C "[email protected]"     
$ ls -l /root/.ssh
$ cat /root/.ssh/id_rsa.pub
$ cat /root/.ssh/id_rsa
1
2
3
4

生成SSH-KEY

Step2:设置SSH KEY

Gitea:登录Gitea,找到右上角的图标,找到设置,再选中 SSH/GPG 密钥选项卡,增加密钥,然后密钥名称随便填,再把刚才 id_rsa.pub 里面的内容复制到密钥内容框里面,最后点击增加密钥,这样就完成了SSH Key 的加密。

Github:登录 Github ,找到右上角的图标,打开点进里面的 Settings ,再选中里面的 SSH and GPG KEYS ,点击右上角的 New SSH key,然后 Title 里面随便填,再把刚才 id_rsa.pub 里面的内容复制到 Title 下面的 Key 内容框里面,最后点击Add SSH key,这样就完成了SSH Key 的加密。

# 3. 搭建PostgreSQL及Gitea服务

企业级最流行的私有代码仓库是Gitlab, 但Gitlab的资源占用太大了,于是选择了轻量级的Gitea(Gitea内存占用情况如下)。Gitea 支持 MySQL、PostgreSQL、SQLite3 等多种数据库,我这里选择了PostgreSQL数据库。

Gitea内存占用

# 3.1 拉取镜像并创建容器

$ mkdir /root/gitea && cd /root/gitea
$ vim docker-compose.yml
1
2

写入 docker-compose.yml 的配置(注意连接信息和挂载路径改成自己的)

version: '3.9'

# 创建自定义网络
networks:
  gitea_network:
    name: gitea_network
    driver: bridge

services:
  ## 数据库服务
  db:
    image: postgres:latest
    container_name: gitea_db
    restart: always
    networks:
      - gitea_network # 加入到gitea_network网络
    ports:
      - 3003:5432
    environment:
      - POSTGRES_USER=gitea # PGSQL默认用户
      - POSTGRES_PASSWORD=my_pgsql_password # PGSQL默认密码
      - POSTGRES_DB=giteadb # PGSQL默认数据库
    volumes:
      - /root/gitea/db:/var/lib/postgresql/data
  #gitea服务
  server:
    image: gitea/gitea:latest
    container_name: gitea_server
    restart: always
    networks:
      - gitea_network # 加入到gitea_network网络
    ports:
      - '3000:3000' # HTTP服务端口
      - '3002:22' # SSH服务器端口
    environment:
      - USER_UID=1000 # 运行容器使用的 UID  UID和GID是用于匿名数据卷挂载,
      - USER_GID=1000 # 运行容器使用的 GID
      - APP_NAME=gitea
      - PROTOCOL=http # 服务使用的访问协议
      - HTTP_PORT=3000 # HTTP 侦听端口 默认为3000
      - SSH_PORT=22 # 克隆 URL 中显示的 SSH 端口
      - DOMAIN=ip:3000 # UI显示的 HTTP克隆URL
      - LANDING_PAGE=explore
      - ROOT_URL=http://ip:3000 # 服务器的对外 URL
      - DB_TYPE=postgres # 数据库类型
      - DB_HOST=db # 数据库连接地址, 使用network形式连接, serverName或者containerName
      - DB_NAME=giteadb # 数据库名称
      - DB_USER=gitea # 数据库连接用户
      - DB_PASSWD=my_pgsql_password # 数据库连接密码

      - DISABLE_REGISTRATION=true # 禁用用户注册,启用后只允许管理员添加用户
      - SHOW_REGISTRATION_BUTTON=false # 是否显示注册按钮
      - REQUIRE_SIGNIN_VIEW=true # 是否所有页面都必须登录后才可访问

    volumes:
      - /root/gitea/server/data:/data
      - /root/gitea/server/conf:/data/gitea/conf
      - /root/gitea/server/config:/etc/config
      - /root/gitea/server/timezone:/etc/timezone:ro
      - /root/gitea/server/localtime:/etc/localtime:ro
    depends_on:
      - db
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

注:我这里出于安全性的考虑,将 REQUIRE_SIGNIN_VIEW 设置为了 true,所有页面都必须登录后才可访问,如果想公开访问,就把它设置成false。

然后拉取镜像并创建容器即可。

$ docker-compose up -d     // 拉取镜像并创建容器
1

# 3.2 Gitea的初始化设置

使用Chrome浏览器打开http://ip:3000路径,会显示Gitea初始化的基本设置界面。向导页面会带入 environment 中填写的设置,很多信息不需要进行设置。

其中我们只需要设置管理员账号设置即可(如果不设置管理员,默认第一个注册用户将自动成为管理员,但是在禁止注册的情况下必须设置管理员)。

Gitea初始化设置

# 3.3 设置反向代理并开启HTTPS

通用的创建网站开启HTTPS并配置反向代理见本文2.2节。

这时我们使用https://域名打开Gitea时,会显示如下错误,我们需要修改一下Gitea的配置

Gitea反向代理错误

配置文件是容器内的 /data/gitea/conf/app.ini,先前我们已经挂载出来了,只需要修改此配置文件中 ROOT_URL、DOMAIN、 SSH_DOMAIN配置,随后重启Gitea容器即可。

DOMAIN           = 域名
SSH_DOMAIN       = 域名
ROOT_URL         = https://域名/
1
2
3

然后 Nginx 有一个上传文件大小限制,上传大于限制大小的文件会报错,我们把Nginx配置文件里的client_max_body_size属性调大即可,然后重载Nginx配置。

$ vim /usr/local/nginx/conf/nginx.conf  // 修改client_max_body_size属性
$ nginx -s reload
1
2

配置完成之后,我们便可使用https://域名访问我们的Gitea服务了。

# 3.4 生成并设置SSH KEY

Git 生成并设置 SSH KEY 见本文2.3节。

# 3.5 创建一个仓库并推送项目

Gitea与Github、Gitlab的使用基本一致,这里就不赘述了,如果遇到问题的话可以看我的另一篇博客:Git及Github相关生态的介绍与使用 (opens new window)

Gitea私有仓库

# 3.6 创建OAuth2密钥

登录Gitea后,点击右上角头像选择设置,然后选择应用,在最下面创建OAuth2应用,填写名称(填drone)和重定向URI(填随后搭建的drone服务地址的/login路由,https://域名/login),点击创建应用,会出现“客户端ID、客户端密钥”,这两个信息好好记下来,后面部署Drone会用到,最后点击保存。

创建OAuth2密钥

# 3.7 将一个项目同时推送多个Git远程仓库

需求情景:本文只介绍Drone搭配Gitea的使用情形,公司内可能用的是别的Git远程仓库,而且不会给你管理权限。这时,我们可以将一个项目同时推送到多个远程仓库,通过以下方式来实现(前提是共用同一个ssh-key)。无论公司用什么Git远程仓库,都只是将代码附带推送一份而已,持续集成还是走自己搭建的Gitea。

$ git remote set-url --add origin https://[email protected]:user/test-project.git      // 另一个远程仓库的地址(https的那个)
1

其实质是改动了.git目录下的config文件,产生了多个推送url,如果哪个远程仓库不想推送了,就删掉它即可。

[remote "origin"]
	url = https://gitea.your.domain/user/test-project.git
	fetch = +refs/heads/*:refs/remotes/origin/*
	url = [email protected]:user/test-project.git
1
2
3
4

# 4. 搭建Drone服务

Drone 数据存储默认使用 sqlite 数据库,并且提供支持 postgres 和 mysql,官方文档中强烈建议使用 postgres 而非 mysql,某些操作在mysql未得到优化。

见:https://docs.drone.io/server/storage/database/ (opens new window),我这里使用的是本文 3.1 节搭建的 PostgreSQL 服务。

# 4.1 拉取镜像并创建容器

$ docker network create drone_network
$ docker network connect drone_network gitea_db
$ mkdir /root/drone && cd /root/drone
$ vim docker-compose.yml
1
2
3
4

写入 docker-compose.yml 的配置(注意这里面有很多信息要改成自己的)

version: '3.9'

# 创建自定义网络
networks:
  drone_network:
    name: drone_network
    driver: bridge
    
services:
  # Drone Server 服务
  server:
    image: drone/drone:2.8.0 # 目前drone最新版本为 2.8.0
    container_name: drone_server
    restart: always
    networks:
      - drone_network # 加入到drone_network网络
    ports:
      - '7929:80'
    environment:
      - DRONE_SERVER_PROTO=https # 访问协议,创建webHooks和重定向
      - DRONE_SERVER_HOST=drone域名 # 主机名称,创建webHooks和重定向
      - DRONE_RPC_SECRET=your_drone_rpc_secret # 与 drone runner 通讯的密钥
      - DRONE_USER_CREATE=username:your_gitea_user,admin:true # 管理员账户
      - DRONE_DATABASE_DRIVER=postgres # 数据库类型
      - DRONE_DATABASE_DATASOURCE=postgres://postgres_user:postgres_password@ip:3003/postgres?sslmode=disable # 数据库连接
      - DRONE_GIT_ALWAYS_AUTH=true # 使用 oauth 身份验证信息拉取代码
      - DRONE_GITEA_SERVER=https://gitea域名 # gitea服务器地址
      - DRONE_GITEA_CLIENT_ID=your_gitea_id # gitea 客户端 id
      - DRONE_GITEA_CLIENT_SECRET=your_gitea_key # gitea 客户端 密钥
      - DRONE_GITEA_SKIP_VERIFY=false # 禁用 gitea 链接时 tls 验证
    volumes:
      - /root/drone/server:/data
      - /var/run/docker.sock:/var/run/docker.sock

  # Drone Docker Runner
  runner:
    image: drone/drone-runner-docker:1.8.0 # 目前drone-runner-docker最新版本为 1.8.0
    container_name: drone_runner
    restart: always
    networks:
      - drone_network # 加入到drone_network网络
    ports:
      - '7930:3000'
    environment:
      - DRONE_RUNNER_NAME=docker-runner
      - DRONE_RUNNER_CAPACITY=10 # 限制runner可执行的并发管道数量
      - DRONE_RPC_PROTO=http # 访问drone server 协议
      - DRONE_RPC_HOST=server # 访问drone server 服务器地址
      - DRONE_RPC_SECRET=your_drone_rpc_secret # 与 drone server 通讯的密钥
      - DRONE_UI_USERNAME=your_drone_runner_user # 设置 Drone Runner 的 UI 用户账号
      - DRONE_UI_PASSWORD=your_drone_runner_password # 设置 Drone Runner 的 UI 用户密码
    volumes:
      - '/var/run/docker.sock:/var/run/docker.sock'
    depends_on:
      - server
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

部署 Drone 的 environment 属性有些麻烦,在此简单介绍下其中的某些属性。

Server 部分:

  • DRONE_SERVER_PROTO 、 DRONE_SERVER_HOST:这两个属性是设置 Webhook 重定向 URL 的访问协议和主机名称。

  • DRONE_RPC_SECRET:此属性是设置 Server 与 Runner 之间通讯的密钥,二者必须设置相同的密钥值才允许通信。

  • DRONE_USER_CREATE:此属性是设置 Drone 管理员【注意:username 必须设置为OAuth用户(Gitea 的用户名),否则不具有管理员权限】

  • DRONE_DATABASE_DRIVER 、 DRONE_DATABASE_DATASOURCE:这两个属性是设置数据库类型和数据库连接,具体连接配置可以参考 官方文档 (opens new window)

    DRONE_DATABASE_DATASOURCE=postgres://root:[email protected]:5432/postgres?sslmode=disable
    
    1
  • DRONE_GIT_ALWAYS_AUTH:此属性是设置 OAuth 登录用户进行拉取代码,默认情况下 OAuth 只作用于登录操作。但存储库设置为私有时,需要登录用户才允许拉取代码,此时需要将此属性设置为 true,默认值为 false。

  • DRONE_GITEA_SERVER 、 DRONE_GITEA_CLIENT_ID 、DRONE_GITEA_CLIENT_SECRET:这几个属性是设置 Gitea 地址和 OAuth ID、OAuth 密钥。

  • DRONE_GITEA_SKIP_VERIFY:默认值为 false,此属性是设置禁用 Gitea 的 TLS 验证,此属性为false时,当 Gitea 使用 HTTPS 协议但证书有问题,会出现授权验证失败,报 x509 错误。当存储库使用 HTTPS 协议但没有证书情况下,此属性设置设置为 true 跳过 TLS 验证。

Runner 部分:

  • DRONE_RUNNER_CAPACITY:此属性是设置 Runner(执行器)并发管道数量,默认值为 2。
  • DRONE_RPC_PROTO 、DRONE_RPC_HOST:这两个属性设置通信 Server(服务器)的协议和主机名。
  • DRONE_UI_USERNAME、DRONE_UI_PASSWORD:Runner(执行器)也具有 UI 展示页面,可以查看当前执行器的执行信息。这两个属性是设置 UI 展示页面的用户名称和密码。

然后拉取镜像并创建容器即可。

$ docker-compose up -d     // 拉取镜像并创建容器
1

# 4.2 设置反向代理并开启HTTPS

通用的创建网站开启HTTPS并配置反向代理见本文2.2节。这里只需要反向代理端口号为7929的服务端即可(7030端口是执行器端)。

# 4.3 Drone的初始化设置

使用Chrome浏览器打开https://域名路径(即服务端),会显示Drone初始化界面。

Drone初始化界面

点击Continue会自动跳转到Gitea的授权页面(如果提示授权失败,则需要检查 DRONE_SERVER_PROTO及DRONE_SERVER_HOST、 DRONE_GITEA_CLIENT_ID及DRONE_GITEA_CLIENT_SECRET 以及 Gitea的OAuth2地址配置是否正确)

Gitea的授权页面

这时候我们就正式进入了Drone的管理界面,这里会同步我们的Gitea仓库,如果没有的话,点击一下SYNC按钮,可以主动请求同步。

Drone同步Gitea仓库

# 4.4 激活仓库并测试执行

点击我们的Gitea仓库,未激活情况下进入当前项目会跳转到 settings 页面,当前页面具有一个激活按钮,点击就可以激活此存储库,出现如下界面:

drone激活仓库

此时 Settings 页面就会出现很多设置,下面对其进行简要介绍:

  • Protected:此属性设置是否要验证配置文件(.drone.yml)中的签名,开启后签名验证错误则不允许构建。
  • Trusted:此属性设置是否允许使用挂载权限。
  • Auto cancel pushes、Auto cancel running:这两个属性是优化操作的属性。开启这两个属性,当执行构建任务时,会自动取消之前未执行完毕的构建任务。当合并多个 commit 时,这个属性具有很好的效果。
  • Timeout:此属性是设置构建任务执行的超时时间。
  • Configuration:此属性是设置配置文件文件,默认为 .drone.yml,这个一般不需要改动。
  • Secrets:此属性是用来设置敏感属性的。编写配置时,有些敏感数据需要隐藏,如账号密码,这些属性可以配置 Secrets 使用。

这里我们直接点击 SAVE CHANGES,然后在项目根目录创建 .drone.yml 文件,内容如下:

kind: pipeline # 定义一个管道
type: docker # 当前管道的类型
name: test # 当前管道的名称
steps: # 定义管道的执行步骤
  - name: test # 步骤名称
    image: node:latest # 当前步骤使用的镜像
    commands: # 当前步骤执行的命令
      - echo 测试drone执行
1
2
3
4
5
6
7
8

然后提交并推送到我们的Gitea仓库,这时它就会自动进行部署,我们可以在Builds界面查看部署记录。

注:如果因为特殊情况(比如卡死在某个阶段)在构建时想要取消构建,可以点击右上角的红色Cancel按钮,终止构建。

测试drone执行

# 5. 配置及使用Drone服务

# 5.1 Drone 管道机制

Drone 中引入了管道(Pipeline)机制,管道相当于一个流程,管道中可以执行多个步骤(Step)。步骤就是使用插件(Plugin)配置的操作。与Runner(执行器)相同的是,管道也支持多种类型,用于适配不同运行环境,当然某些类型可以使用容器化代替统一管理。

Drone 也是使用 YML 语法作配置文件,在配置文件可以同时配置多个管道。默认情况下多个管道是并行执行,这也是 Drone 的强大功能之一:分布式管道系统。

可以简单的理解为,.drone.yml 配置文件相当于一个 .sh 文件,部署操作配置在这个文件中,交给 Drone 引擎执行。

# 5.2 使用Drone持续集成Springboot项目

# 5.2.1 编写持续集成的配置文件

需要在项目根目录新增3个文件,内容如下,其中.drone.yml需要修改成自己配置的地方比较多也容易出错,Dockerfile和run.sh微调一下即可。

.drone.yml(该文件所有需要修改的地方已经用中文中括号标注了)

kind: pipeline # 定义对象类型,还有secret和signature两种类型
type: docker # 定义流水线类型,还有kubernetes、exec、ssh等类型
name: yoyo-admin # 定义流水线名称 【改成项目名即可】

steps: # 定义流水线执行步骤,这些步骤将顺序执行
  - name: package # 流水线名称
    image: maven:3-jdk-8 # 定义创建容器的Docker镜像
    volumes: # 将容器内目录挂载到宿主机,仓库需要开启Trusted设置
      - name: maven-cache
        path: /root/.m2 # 将maven下载依赖的目录挂载出来,防止重复下载
      - name: maven-build
        path: /app/build # 将应用打包好的jar和执行脚本挂载出来
    commands: # 定义在Docker容器中执行的shell命令
      - mvn clean package # 应用打包命令
      - cp web_manage/target/web_manage-0.0.1.jar /app/build/web_manage-0.0.1.jar  #【改一下jar包名即可】
      - cp Dockerfile /app/build/Dockerfile
      - cp run.sh /app/build/run.sh

  - name: build-start
    image: appleboy/drone-ssh # SSH工具镜像
    settings:
      host: 111.111.111.111 # 远程连接地址 【改一下服务器连接地址】
      username: root # 远程连接账号 【改一下服务器连接用户名】
      password:
        from_secret: ssh_password # 从Secret中读取SSH密码
      port: 22 # 远程连接端口  【改一下服务器连接端口】
      command_timeout: 5m # 远程执行命令超时时间
      script:
        - cd /root/drone_volumes/drone_build/yoyo-admin # 进入宿主机构建目录 【改一下宿主机构建目录】
        - chmod +x run.sh # 更改为可执行脚本
        - ./run.sh # 运行脚本打包应用镜像并运行

volumes: # 定义流水线挂载目录,用于共享数据
  - name: maven-build
    host:
      path: /root/drone_volumes/drone_build/yoyo-admin  #【改一下宿主机构建目录】
  - name: maven-cache
    host:
      path: /root/drone_volumes/drone_maven_resp  # 【改一下宿主机maven依赖目录】
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

Dockerfile

# 基于java8镜像创建新镜像
FROM java:8
# 将jar包添加到容器中并更名为app.jar
COPY web_manage-0.0.1.jar /web_manage-0.0.1.jar
# 后台运行jar包
ENTRYPOINT ["nohup","java","-jar","web_manage-0.0.1.jar","&"]
1
2
3
4
5
6

run.sh

#!/bin/bash

echo =======暂停旧容器=======
docker stop `docker ps -a | grep yoyo-admin | awk '{print $1}' `
echo =======删除旧容器和镜像=======
docker rm -f `docker ps -a | grep yoyo-admin | awk '{print $1}' `
docker rmi `docker images | grep yoyo-admin-image | awk '{print $3}' `
echo =======开始构建新镜像=======
cd /root/drone_volumes/drone_build/yoyo-admin
docker build -t yoyo-admin-image:latest .
echo =======查看镜像列表=======
docker images
echo =======开始部署应用=======
docker run -d --name yoyo-admin -p 8081:8081 yoyo-admin-image:latest
docker update yoyo-admin --restart=always
docker ps
echo =======部署成功=======
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 5.2.2 修改项目的 Drone 设置

设置项目允许使用挂载权限,Settings——General——Project Settings——开启Trusted

drone设置允许使用挂载权限

由于服务器密码非常敏感,因此没有写死在 .drone.yml 配置文件里,这里设置成了 from_secret: ssh_password,下面我们就要在Drone里设置这个值。

设置Secrets:Settings——Secret——NEW SECRET——输入Name和Value(name设置为ssh_password,设置为服务器连接密码)然后保存即可

Drone设置Secret

# 5.2.3 推送代码查看持续集成的效果

将代码推送到 Gitea,推送成功后 Drone 会自动以Docker的方式开始构建项目。我们可以在Builds模块查看构建日志,如果出错了,就根据日志找到出错原因重新提交,下图我是已经构建成功了。

Drone成功部署项目

构建成功后,我访问了Swagger接口文档并测试了登录接口,没有问题,说明我的Springboot持续集成已经部署成功了。

# 5.3 使用Drone持续集成完整的前后端分离项目

以下将以 Springboot + Angular 前后端分离的项目为例,将二者打到一个docker里,简化后的项目结构如下:

.
├── ng-zorro-admin
│    ├── angular.json
│    ├── package.json
│    └── src
├──  web_manage
│    ├── pom.xml
│    └── src
├──  drone_config
│    ├── Dockerfile
│    ├── config
│    │   ├── application-dev.properties
│    │   ├── application-prod.properties
|    │   └── application.properties
│    ├── nginx.conf
│    ├── proxy.conf
│    ├── yoyo_web.conf
│    ├── run.sh
│    └── start_web.sh
├──  pom.xml
└──  .drone.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 5.3.1 编写持续集成的配置文件

.drone.yml

kind: pipeline # 定义对象类型,还有secret和signature两种类型
type: docker # 定义流水线类型,还有kubernetes、exec、ssh等类型
name: yoyo-web # 定义流水线名称

steps: # 定义流水线执行步骤,这些步骤将顺序执行
  - name: backend-build # 流水线名称
    image: maven:3-jdk-8 # 定义创建容器的Docker镜像
    volumes: # 将容器内目录挂载到宿主机,仓库需要开启Trusted设置
      - name: maven-cache
        path: /root/.m2 # 将maven下载依赖的目录挂载出来,防止重复下载
      - name: yoyo-web-build
        path: /app/build # 将应用打包好的jar和执行脚本挂载出来
    commands: # 定义在Docker容器中执行的shell命令
      - mvn clean package # 应用打包命令
      - cp web_manage/target/web_manage-0.0.1.jar /app/build/web_manage-0.0.1.jar
      - cp drone_config/Dockerfile /app/build/Dockerfile
      - cp drone_config/nginx.conf /app/build/nginx.conf
      - cp drone_config/proxy.conf /app/build/proxy.conf
      - cp drone_config/run.sh /app/build/run.sh
      - cp drone_config/start_web.sh /app/build/start_web.sh
      - cp drone_config/yoyo_web.conf /app/build/yoyo_web.conf
      - cp -r drone_config/config /app/build

  - name: frontend-build # 流水线名称
    image: node:18 # 定义创建容器的Docker镜像
    volumes: # 将容器内目录挂载到宿主机,仓库需要开启Trusted设置
      - name: ng-zorro-admin-node
        path: /drone/src/ng-zorro-admin/node_modules # 将npm下载依赖的目录挂载出来,防止重复下载
      - name: yoyo-web-build
        path: /app/build # 将打包好的dist包和执行脚本挂载出来
    commands: # 定义在Docker容器中执行的shell命令
      - cd ng-zorro-admin && pwd
      - npm install -g @angular/cli   # 安装Angular CLI
      - npm install   # 安装依赖
      - ng build --base-href ./  # 应用打包命令
      - cp -r dist /app/build

  - name: build-start
    image: appleboy/drone-ssh # SSH工具镜像
    settings:
      host: 111.111.111.111 # 远程连接地址 
      username: root # 远程连接账号 
      password:
        from_secret: ssh_password # 从Secret中读取SSH密码
      port: 22 # 远程连接端口 
      command_timeout: 5m # 远程执行命令超时时间
      script:
        - cd /root/drone_volumes/drone_build/yoyo-web # 进入宿主机构建目录 
        - chmod +x run.sh # 更改为可执行脚本
        - ./run.sh # 运行脚本打包应用镜像并运行
        
volumes: # 定义流水线挂载目录,用于共享数据
  - name: yoyo-web-build
    host:
      path: /root/drone_volumes/drone_build/yoyo-web
  - name: maven-cache
    host:
      path: /root/drone_volumes/drone_maven_resp
  - name: ng-zorro-admin-node
    host:
      path: /root/drone_volumes/drone_node_modules/ng-zorro-admin
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

/drone_config/Dockerfile

# 设置基础镜像
FROM nginx

# 安装常用命令
RUN apt-get update
RUN apt-get install -y wget       # 安装wget
RUN apt-get install vim -y        # 安装vim
RUN apt-get install -y psmisc     # 安装ps

# 设置工作目录
RUN mkdir /storage
WORKDIR /storage

# 安装java8环境
RUN mkdir /usr/local/java
# 方式一:下载jdk并解压到指定目录(适用于网速快的情况,需要提前安装wget)
RUN wget https://mirrors.huaweicloud.com/java/jdk/8u202-b08/jdk-8u202-linux-x64.tar.gz
RUN tar zxvf jdk-8u202-linux-x64.tar.gz -C /usr/local/java && rm -f jdk-8u202-linux-x64.tar.gz
# 方式二:将本地jdk复制到内部目录并自动解压(适用于网速慢的情况,提前下载好)
# ADD jdk-8u202-linux-x64.tar.gz /usr/local/java
# RUN rm -f jdk-8u202-linux-x64.tar.gz
RUN ln -s /usr/local/java/jdk1.8.0_202 /usr/local/java/jdk
ENV JAVA_HOME /usr/local/java/jdk
ENV JRE_HOME ${JAVA_HOME}/jre
ENV CLASSPATH .:${JAVA_HOME}/lib:${JRE_HOME}/lib
ENV PATH ${JAVA_HOME}/bin:$PATH

# 放置前端代码及nginx配置
ADD dist/ /storage/web_code
COPY nginx.conf /etc/nginx/nginx.conf
COPY yoyo_web.conf /etc/nginx/conf.d/yoyo_web.conf
COPY proxy.conf /etc/nginx

# 放置后端代码及包外配置
COPY web_manage-0.0.1.jar /storage
COPY config /storage

# 放置启动脚本并授予权限
COPY start_web.sh /storage/start_web.sh
RUN chmod u+x /storage/start_web.sh

# 容器服务自启
ENTRYPOINT ["/storage/start_web.sh"]
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

注意事项:

  • ENTRYPOINT 里的配置会覆盖父镜像的启动命令,因此这里需要启动 Nginx 和 Jar 的两个命令。若用&&连接的话只会执行前面的那一个,因此这里将两个启动命令都写进一个Shell脚本里。

    关于CMD和ENTRYPOINT有一点需要特别注意:如果一个Dockerfile中有多个CMD或ENTRYPOINT,只有最后一个会生效,前面其他的都会被覆盖。
    
    1
  • 前端包我这里采用的是将 zip 通过shell脚本解压后再拷贝进容器的方式,如果采用 tar.gz 格式,ADD命令会自动对其进行解压(其他压缩格式不可以)。

    ADD dist.tar.gz /storage/web_code
    
    1

/drone_config/nginx.conf

user  root;
worker_processes  auto;

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}
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

/drone_config/proxy.conf

proxy_connect_timeout 900s;
proxy_send_timeout 900;
proxy_read_timeout 900;
proxy_buffer_size 32k;
proxy_buffers 4 64k;
proxy_busy_buffers_size 128k;
proxy_redirect off;
proxy_hide_header Vary;
proxy_set_header Accept-Encoding '';
proxy_set_header Referer $http_referer;
proxy_set_header Cookie $http_cookie;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

/drone_config/yoyo_web.conf

upstream dev_yoyo_web {
        server 127.0.0.1:8081 weight=1 max_fails=1 fail_timeout=10s;
}
server {
    listen       82;
    server_name  127.0.0.1;
    location / {
        gzip on;
        gzip_vary on;
	      gzip_min_length 1k;
	      gzip_buffers 16 16k;
        gzip_http_version 1.1;
        gzip_comp_level 9;
        gzip_types text/plain application/javascript application/x-javascript text/css text/xml text/javascript application/json;
        root  /storage/web_code;
	      index index.html;
	      try_files $uri $uri/ /index.html?$query_string;
    }

    location ~* ^(/login|/logout|/api/|/auth/) {
        proxy_pass http://dev_yoyo_web; 
        client_max_body_size    48m;
        include proxy.conf;
    }
}
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

/drone_config/start_web.sh

#!/bin/bash

/docker-entrypoint.sh nginx -g 'daemon off;' &
java -jar /storage/web_manage-0.0.1.jar --spring.profiles.active=prod
1
2
3
4

注意:前面的服务一定要在后台运行,即后面加个&,最后一个服务要以前台运行。否则,全部以前台运行的话,只有第一个服务会启动;全部以后台运行的话,当最后一个服务执行完成后,容器就退出了。

/drone_config/run.sh

#!/bin/bash

echo =======暂停旧容器=======
docker stop `docker ps -a | grep yoyo-web | awk '{print $1}' `
echo =======删除旧容器和镜像=======
docker rm -f `docker ps -a | grep yoyo-web | awk '{print $1}' `
docker rmi `docker images | grep yoyo-web-image | awk '{print $3}' `
echo =======开始构建新镜像=======
cd /root/drone_volumes/drone_build/yoyo-web
base_path=$(cd `dirname $0`; pwd)
uploads_path="${base_path}/uploads"
mkdir ${uploads_path}
docker build -t yoyo-web-image:latest .
echo =======查看镜像列表=======
docker images
echo =======开始部署应用=======
docker run -itd --name yoyo-web -h yoyo-web -v ${uploads_path}:/storage/web_code/uploads -p 8082:82 -p 8081:8081 yoyo-web-image:latest
docker update yoyo-web --restart=always
docker ps
echo =======部署成功=======
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

如果没有配置好自启动,也可以在Shell脚本里加上在容器外执行容器内命令的方式启动,但这种方式重启容器后就又需要手动开启了,因此不推荐使用。

docker exec -itd `docker ps |grep yoyo-web |awk '{print $1}'` /bin/bash -c 'java -jar -Duser.timezone=GMT+8 /storage/web_manage-0.0.1.jar > /storage/web_manage-0.0.1.log 2>&1'
docker exec -it `docker ps |grep yoyo-web |awk '{print $1}'` /bin/bash -c 'tail -fn 100 /storage/web_manage-0.0.1.log'
1
2

注意:docker exec -it 这里必须不带上d,否则看不到输出结果。

# 5.3.2 修改项目的 Drone 设置

同 5.2.2 节

# 5.3.3 推送代码查看持续集成的效果

同5.2.3节,本示例没有下图中的doc-build步骤,其他的与之一致。

Drone成功部署前后端分离项目

# 6. 参考资料

[1] 私有化轻量级持续集成部署方案--04-私有代码仓库服务-Gitea from 莫问今朝 (opens new window)

[2] 私有化轻量级持续集成部署方案--05-持续部署服务-Drone(上)from 莫问今朝 (opens new window)

[3] 私有化轻量级持续集成部署方案--05-持续部署服务-Drone(下)from 莫问今朝 (opens new window)

[4] Drone-比Jenkins更轻量化的持续集成部署工具 from young-q (opens new window)

[5] 用轻量级开源工具 Drone 完成小团队的 CI/CD from 51CTO (opens new window)

[6] Drone CI/CD系列(三)——java语言(Springboot)之配置.drone.yml文件 from juinjonn (opens new window)

[7] Gogs私有git仓库 + Drone构建CI/CD平台 from 练涛 (opens new window)

[8] 再见 Jenkins !几行脚本搞定自动化部署,这款神器有点厉害!from 稀土掘金 (opens new window)

[9] Drone+Gitlab 一条龙服务,直接起飞 — 从介绍->部署->配置->写.drone.yml流水线+常见的报错解决 from 阿里云 (opens new window)

[10] 基于gitlab的drone的搭建和使用 from CSDN (opens new window)

[11] Docker容器及其内应用自启动解决方案 from CSDN (opens new window)

[12] 同步gitea仓库到github from Wrpota (opens new window)

[13] docker 利用CMD或者ENTRYPOINT命令同时启动多个服务 from CSDN (opens new window)

[14] Docker之docker run参数覆盖Dockerfile中CMD命令以及CMD与ENTRYPOINT的区别 from CSDN (opens new window)

Last Updated: 11/25/2023, 8:58:08 PM