基于Docker的轻量级CI/CD实践:Drone与Gogs的自动化部署指南
1. 为什么小团队需要一套轻量级的CI/CD方案?
如果你在一个小团队或者个人开发者,我猜你一定遇到过这样的场景:每次代码改完,要手动登录服务器,拉取最新代码,然后执行构建命令,最后重启服务。这套流程走下来,少说也得十几分钟,而且特别容易出错,比如忘了执行某个命令,或者服务器环境不一致导致构建失败。更头疼的是,如果项目稍微复杂点,有前后端分离,那部署起来就更是一场“灾难”。
我刚开始带小团队的时候,就是这么过来的。直到有一次,半夜上线一个紧急修复,因为手滑敲错了一个命令,导致服务挂了半小时,那次教训让我下定决心,必须把自动化部署搞起来。但一看主流的方案,比如 Jenkins + GitLab,好家伙,光安装配置就得折腾一两天,对服务器资源的要求也不低,对于我们这种只有一两台低配云服务器的团队来说,实在是有点“杀鸡用牛刀”的感觉。
后来我发现了 Docker + Drone + Gogs 这个组合,简直是为小团队量身定做的“瑞士军刀”。它的核心思路非常清晰:用 Gogs 搭建一个私有的、像 GitHub 一样的代码仓库,轻巧到令人发指;用 Drone 作为持续集成引擎,它完全基于 Docker 容器,每个构建步骤都在一个干净的容器里运行,环境隔离做得非常好;而 Docker 本身,则是这一切的基石,它保证了从开发到测试再到生产,环境的高度一致性。
这套方案最大的优势就是 “轻” 。这里的“轻”体现在三个方面:一是资源占用轻,全部跑在容器里,不污染宿主机,不用时几乎不占资源;二是学习成本轻,配置是声明式的,写在 .drone.yml 文件里,一目了然,比 Jenkins 那些复杂的界面配置友好太多;三是维护成本轻,所有组件都用 Docker 运行,备份、迁移、升级都是一条命令的事。我自己用这套方案稳定运行了两年多,服务了好几个中小型项目,从来没出过大问题。下面,我就手把手带你从零开始,搭建这套属于你自己的自动化流水线。
2. 搭建基石:用Docker快速部署Gogs代码仓库
2.1 为什么选择Gogs而不是GitLab?
在搭建私有Git服务时,GitLab无疑是功能最强大的,但它同时也是个“资源怪兽”。默认安装后,内存占用轻松超过2GB,这对于很多预算有限的个人开发者或小团队来说,是难以承受的。Gogs 的诞生就是为了解决这个问题,它的目标就是做一个极易搭建、资源占用极低的 Git 服务。我实测下来,一个刚启动的 Gogs 容器,内存占用不到 100MB,这对于我们在单台服务器上部署全套 CI/CD 环境来说,简直是福音。
Gogs 是用 Go 语言写的,这意味着它只有一个独立的二进制文件,没有复杂的运行时依赖。它提供了 Git 仓库管理、Issue 跟踪、Pull Request 等核心功能,对于小团队协作来说完全够用。而且它是国人开发,中文文档和支持都比较友好。
2.2 一步一步用Docker拉起Gogs
假设你已经在服务器上安装好了 Docker 和 Docker Compose,我们这里直接用 docker run 命令来启动,这样更直观。我强烈建议你把所有容器的数据都挂载到宿主机的特定目录,比如 /opt/docker-data 下,这样以后备份和迁移会非常方便。
首先,我们创建一个目录来存放 Gogs 的数据:
sudo mkdir -p /opt/docker-data/gogs
然后,执行下面的命令来启动 Gogs 容器。我来逐行解释一下这些参数:
docker run -d \
--name=gogs \
--restart=always \
-p 10022:22 \
-p 13000:3000 \
-v /opt/docker-data/gogs:/data \
gogs/gogs:0.12.0
-d:让容器在后台运行。--name=gogs:给容器起个名字,方便管理。--restart=always:设置容器总是自动重启,即使服务器重启了,服务也能自己拉起来,非常省心。-p 10022:22:将容器内的 SSH 端口(22)映射到宿主机的 10022 端口。这样你就可以用ssh -p 10022 git@你的服务器IP来克隆和推送代码了。注意:如果你的服务器22端口已经被占用(比如系统本身的SSH),这个映射就非常有必要。-p 13000:3000:将容器内的 Web 服务端口(3000)映射到宿主机的 13000 端口。等下我们就要通过这个端口访问 Gogs 的网页界面。-v /opt/docker-data/gogs:/data:这是最关键的一步,把容器内的/data目录(Gogs 存放所有数据库、仓库、配置的地方)挂载到宿主机的/opt/docker-data/gogs目录。这样即使容器被删除,你的代码仓库数据也完好无损。gogs/gogs:0.12.0:指定使用的镜像和版本。这里我用了相对稳定的 0.12.0 版本,新版本可能会有一些变动,老版本教程更丰富,适合入门。
执行完命令后,用 docker ps 看看容器是不是跑起来了。然后打开浏览器,访问 http://你的服务器IP:13000,你应该就能看到 Gogs 的首次安装配置页面了。
2.3 初始化配置与创建第一个仓库
第一次访问 Gogs,它会引导你进行初始化配置。大部分设置保持默认即可,但有几点需要特别关注:
- 数据库类型:选择 SQLite3。对于小团队和个人项目,SQLite3 完全足够,它就是一个文件,管理起来比 MySQL 简单无数倍,备份就是复制一个文件的事。
- 应用基本设置:
- 域名:填写你的服务器 IP 或域名。
- SSH 端口:这里填 10022,就是我们上面映射的宿主机端口。
- HTTP 端口:填 3000(容器内端口),这个不用改。
- 应用 URL:填写
http://你的服务器IP:13000(即外部访问的地址)。
- 管理员账户设置:这里设置第一个用户,这个用户会自动成为系统管理员。账号密码一定要记好。
点击“立即安装”,稍等片刻,就会跳转到登录页面。用刚才设置的管理员账号登录,你就进入了 Gogs 的主界面。
接下来,我们创建第一个测试仓库。点击右上角的 “+” 号,选择 “新建仓库”。
- 仓库名称:填
my-test-project。 - 介绍:(可选)写个简单的介绍。
- 仓库类型:选择“私有”。
- 其他选项保持默认,直接点击“创建仓库”。
创建成功后,页面会显示一些 Git 命令提示。这里我们可以用 HTTP 的方式克隆,因为配置了 SSH 的话还需要添加公钥,我们为了快速开始,先用 HTTP。在服务器上(或者你的本地开发机),可以执行:
git clone http://你的服务器IP:13000/管理员用户名/my-test-project.git
输入你的 Gogs 账号密码,就能把空仓库克隆下来了。然后在这个目录里初始化一个简单的项目,比如一个 README.md 文件,提交并推送上去:
cd my-test-project
echo "# My Test Project" > README.md
git add .
git commit -m "Initial commit"
git push origin master
至此,你的私有 Git 仓库就完全准备好了。它运行在一个独立的 Docker 容器里,数据安全地保存在宿主机上,你可以像使用 GitHub 一样使用它,但一切都在你的掌控之中。
3. 构建引擎:部署Drone Server与Runner
3.1 理解Drone的核心组件:Server与Runner
Gogs 帮我们解决了代码存哪里的问题,接下来就需要一个“监工”,每当有代码推送时,自动触发一系列构建、测试、部署任务。这个“监工”就是 Drone。
Drone 的架构非常清晰,分为两个主要部分:
- Drone Server:这是大脑和指挥中心。它提供一个 Web 管理界面,负责与 Gogs 等代码仓库通信(通过 Webhook),接收代码推送事件,然后根据仓库里的
.drone.yml配置文件,生成流水线任务,并分派给 Runner 去执行。 - Drone Runner:这是干活的工人。它负责具体执行流水线中的每一个步骤。Runner 有多种类型,最常用的是 Docker Runner,它能为流水线的每一步都创建一个全新的、隔离的 Docker 容器来运行命令,这保证了每次构建环境的纯净性。
你可以把 Server 和 Runner 安装在同一台机器上,也可以分开部署。对于小团队,放在一起是最简单的。接下来我们就分别安装它们。
3.2 安装与配置Drone Server
首先,创建 Drone Server 的数据目录:
sudo mkdir -p /opt/docker-data/drone-server
然后,运行以下命令启动 Drone Server 容器。这里的环境变量比较多,我挨个解释:
docker run -d \
--name=drone-server \
--restart=always \
-p 20080:80 \
-v /opt/docker-data/drone-server:/data \
-e DRONE_AGENTS_ENABLED=true \
-e DRONE_GOGS_SERVER=http://你的服务器IP:13000 \
-e DRONE_SERVER_HOST=你的服务器IP:20080 \
-e DRONE_SERVER_PROTO=http \
-e DRONE_USER_CREATE=username:你的Gogs管理员账号,admin:true \
-e DRONE_RPC_SECRET=你自己生成的一个强密钥 \
drone/drone:2.6.0
关键环境变量解析:
DRONE_GOGS_SERVER:告诉 Drone,你的 Gogs 服务地址在哪里。DRONE_SERVER_HOST和DRONE_SERVER_PROTO:告诉 Drone,它自己的外部访问地址是什么。Gogs 需要通过这个地址来回调 Drone。DRONE_USER_CREATE:初始化一个 Drone 的管理员用户,这里的username一定要和你 Gogs 的管理员账号一致,这样 Drone 才能正确关联用户权限。DRONE_RPC_SECRET:这是 最重要的一个安全配置。它是 Drone Server 和 Runner 之间通信的共享密钥,用于验证 Runner 的身份。务必使用一个复杂的随机字符串,你可以用openssl rand -hex 16命令来生成一个。这个密钥在下一步配置 Runner 时还要用到。
启动后,访问 http://你的服务器IP:20080,你应该能看到 Drone 的登录页面,并且可以用你的 Gogs 管理员账号直接登录(因为我们已经关联了)。这说明 Server 端基本配置成功了。
3.3 安装与配置Drone Docker Runner
Runner 是真正执行任务的角色。我们使用官方的 drone-runner-docker,它能够基于 Docker 执行流水线步骤。
运行以下命令启动 Runner:
docker run -d \
--name=drone-runner \
--restart=always \
-v /var/run/docker.sock:/var/run/docker.sock \
-e DRONE_RPC_PROTO=http \
-e DRONE_RPC_HOST=你的服务器IP:20080 \
-e DRONE_RPC_SECRET=这里填上面生成的同一个密钥 \
-e DRONE_RUNNER_CAPACITY=2 \
-e DRONE_RUNNER_NAME=my-first-runner \
drone/drone-runner-docker:1.4.0
关键参数解析:
-v /var/run/docker.sock:/var/run/docker.sock:这是 至关重要 的一步。它将宿主机的 Docker 守护进程套接字挂载到 Runner 容器内部,使得 Runner 容器能够在宿主机上创建和管理其他容器(即流水线步骤容器)。没有这个权限,Runner 就无法工作。DRONE_RPC_HOST和DRONE_RPC_SECRET:必须和 Server 的配置对应上,这样 Runner 才能成功连接到 Server 并领取任务。DRONE_RUNNER_CAPACITY:这个 Runner 可以同时执行多少个流水线任务。根据你服务器的 CPU 和内存情况设置,小项目设为 2 就够用了。
启动后,可以通过 docker logs -f drone-runner 查看 Runner 的日志。如果看到类似 successfully pinged the remote server 的日志,恭喜你,Runner 已经成功连接上 Server 了。
现在,你的 CI/CD 引擎就全部就位了。Drone Server 是控制台,Drone Runner 是执行器,它们都通过 Docker 容器运行,互不干扰,管理起来异常方便。
4. 打通任督二脉:连接Gogs与Drone
4.1 在Drone中同步并激活Gogs仓库
用 Gogs 管理员账号登录 Drone 的 Web 界面 (http://你的服务器IP:20080)。登录后,你会看到一个仓库列表,这其实是 Drone 从 Gogs 那里同步过来的。
找到你之前创建的 my-test-project 仓库。在它的最右边,会有一个 “未激活” 的状态按钮。点击它,Drone 会引导你进入仓库的配置页面。这里我们暂时不需要修改任何设置,直接滚动到页面底部,点击 “激活” 按钮。
这个“激活”操作,实际上做了两件重要的事:
- Drone 会在你的 Gogs 仓库中设置一个 Webhook(网络钩子)。这样,以后这个仓库有任何推送事件(比如
git push),Gogs 都会自动发送一个 HTTP 请求通知 Drone Server。 - Drone 会为这个仓库在它的数据库里创建一个记录,用于存储该仓库的构建历史、密钥等配置信息。
激活成功后,仓库的状态会变成 “已激活”。你可以点击仓库名字,进入它的构建历史页面,当然现在还是空的。
4.2 配置仓库密钥(Secret):安全地传递敏感信息
在自动化部署中,我们经常需要用到一些敏感信息,比如连接服务器的 SSH 密码、私钥,或者 Docker 仓库的密码等。绝对不能把这些信息直接写在代码仓库的 .drone.yml 文件里!
Drone 提供了一个非常安全的功能:密钥(Secrets)。你可以把这些敏感信息以密钥的形式存储在 Drone Server 上,然后在 .drone.yml 文件中通过 from_secret 引用它们。这样,敏感信息既不会泄露在代码中,又能被流水线安全地使用。
进入你刚激活的 my-test-project 仓库的设置页面(在仓库页面上方有 “Settings” 选项卡)。找到 “Secrets” 子菜单。
假设我们的部署脚本需要登录到一台 IP 为 192.168.1.100 的服务器,用户是 deploy,密码是 your_secure_password。我们就来创建两个密钥:
- 点击 “New Secret”。
- Name: 输入
ssh_user - Value: 输入
deploy - 点击 “Create”
- Name: 输入
- 再次点击 “New Secret”。
- Name: 输入
ssh_pwd - Value: 输入
your_secure_password - 点击 “Create”
- Name: 输入
这样,我们就有了两个密钥。在后面的 .drone.yml 文件里,我们就可以用 from_secret: ssh_user 和 from_secret: ssh_pwd 来获取这些值了。Drone 会在执行流水线时,自动将它们注入到运行环境中,而不会在日志中明文显示,大大提升了安全性。
5. 编写流水线蓝图:.drone.yml文件详解
5.1 .drone.yml文件的结构与核心概念
一切就绪,现在到了最核心的部分:定义自动化流程。这个流程就写在项目根目录的一个名为 .drone.yml 的文件里。Drone 在收到代码推送通知后,会去仓库里找这个文件,并按照里面定义的步骤依次执行。
一个最简单的 .drone.yml 文件结构如下:
kind: pipeline # 固定,表示这是一个流水线定义
type: docker # 固定,表示使用 Docker Runner 来执行
name: default # 给这个流水线起个名字
steps: # 步骤列表,流水线的核心
- name: greet # 第一个步骤的名字
image: alpine:latest # 这个步骤在哪个Docker镜像里执行
commands: # 在这个镜像容器里要执行的命令
- echo "Hello, Drone!"
这个流水线只做一件事:在一个 alpine 镜像的容器里,打印一句 “Hello, Drone!”。当你把包含这个文件的代码推送到 Gogs 后,Drone 就会自动启动一个流水线,并看到这个步骤执行成功。
5.2 实战:一个完整的Spring Boot项目CI/CD流水线
光打印“Hello World”可不行,我们来点实际的。假设我们有一个标准的 Spring Boot 项目,使用 Maven 构建,最终产出是一个可执行的 JAR 包。我们的目标是:代码推送到 master 分支后,自动完成以下步骤:
- 拉取代码。
- 用 Maven 进行编译和打包。
- 通过 SSH 连接到测试服务器,上传 JAR 包并重启服务。
下面是一个功能完整的 .drone.yml 配置,我加了详细注释:
kind: pipeline
type: docker
name: build-and-deploy
# 触发器:指定在什么情况下运行这个流水线
trigger:
branch:
- master # 只有推送到 master 分支时才触发
event:
- push # 只有 push 事件触发,忽略 tag、pull_request 等
# 步骤定义
steps:
# 步骤1:克隆代码
- name: clone-code
image: alpine/git:latest # 使用一个包含git工具的镜像
commands:
- git clone ${DRONE_GIT_HTTP_URL} . # DRONE_GIT_HTTP_URL是Drone注入的环境变量,代表仓库地址
- git checkout ${DRONE_COMMIT_SHA} # 切换到本次推送的具体提交
# 步骤2:Maven构建
- name: maven-build
image: maven:3.8.5-openjdk-11 # 使用官方Maven镜像,自带JDK11
volumes: # 挂载卷,用于在步骤间共享数据(比如Maven本地仓库缓存)
- name: maven-cache
path: /root/.m2 # 将缓存目录挂载到卷上,加速后续构建
commands:
- mvn clean package -DskipTests # 执行Maven打包,跳过测试(根据需求可去掉-DskipTests)
when: # 条件执行:只有上一步成功,才执行这一步
status: [ success ]
# 步骤3:部署到服务器
- name: deploy-to-server
image: appleboy/drone-ssh:latest # 一个专门用于SSH操作的Drone插件镜像
settings:
host: 192.168.1.100 # 目标服务器的IP
username:
from_secret: ssh_user # 从Drone密钥中读取用户名
password:
from_secret: ssh_pwd # 从Drone密钥中读取密码
port: 22
script:
# 停止当前运行的服务(假设服务通过systemd管理,名为myapp)
- sudo systemctl stop myapp.service || true # “|| true”防止服务不存在时报错导致步骤失败
# 备份旧的JAR包(可选,好习惯)
- cd /opt/myapp
- cp myapp.jar myapp.jar.backup.$(date +%Y%m%d%H%M%S) || true
# 上传新构建的JAR包
# Drone的工作目录默认是 /drone/src,构建好的jar一般在target/下
- scp -o StrictHostKeyChecking=no ${PWD}/target/*.jar deploy@192.168.1.100:/opt/myapp/myapp.jar
# 启动服务
- sudo systemctl start myapp.service
# 检查服务状态
- sudo systemctl status myapp.service --no-pager
when:
status: [ success ]
# 定义卷,用于步骤间共享
volumes:
- name: maven-cache
host:
path: /tmp/drone-maven-cache # 在宿主机上创建一个目录来持久化Maven缓存
这个配置文件定义了一个清晰的“克隆 -> 构建 -> 部署”流水线。volumes 部分将宿主机的 /tmp/drone-maven-cache 目录挂载为名为 maven-cache 的卷,并在 maven-build 步骤中挂载到容器的 /root/.m2。这样,Maven 下载的依赖包就可以在多次构建之间共享,极大地提升了构建速度。
5.3 进阶技巧:多模块项目与前端项目部署
对于更复杂的场景,配置思路是一样的,只是步骤更多样。比如一个后端多模块项目,你可能需要在 maven-build 步骤中依次进入各个子模块进行构建。或者,你有一个前后端分离项目,流水线需要先构建后端,再构建前端(比如用 Node.js),最后将前端静态文件部署到 Nginx 目录。
一个构建 Vue.js 前端项目的步骤示例:
- name: build-frontend
image: node:16-alpine
commands:
- npm install --registry=https://registry.npmmirror.com # 使用国内镜像加速
- npm run build # 执行构建,生成dist目录
when:
status: [ success ]
然后,你可以使用 drone-scp 或 drone-rsync 插件镜像,将 dist 目录下的文件同步到服务器的 Nginx 静态目录。关键在于,每个步骤都用一个最适合的 Docker 镜像来提供纯净、一致的环境,这正是基于 Docker 的 CI/CD 魅力所在。
6. 触发与监控:完成首次自动化部署
6.1 推送代码,触发自动化流水线
现在,将我们精心编写的 .drone.yml 文件添加到你的 my-test-project 仓库中,提交并推送到 Gogs 的 master 分支。
# 在你的项目目录中
git add .drone.yml
git commit -m "feat: add drone ci/cd pipeline configuration"
git push origin master
推送完成的一瞬间,魔法就开始了。你可以立即刷新 Drone 的 Web 界面 (http://你的服务器IP:20080),点击进入 my-test-project 仓库。你应该会看到一条新的构建记录正在产生,状态可能是 “Pending”(等待中)、“Running”(运行中)。
点击这条构建记录,你可以看到流水线执行的实时日志!你会看到 clone-code 步骤开始拉取代码,接着 maven-build 步骤启动 Maven 容器并开始下载依赖、编译打包。如果一切顺利,最后 deploy-to-server 步骤会通过 SSH 连接到你的服务器,执行部署脚本。
6.2 排查常见问题与优化建议
第一次运行很可能不会一帆风顺,别担心,这很正常。查看日志是排查问题的唯一途径。这里有几个我踩过的坑和解决思路:
-
构建失败:
mvn: not found- 原因:
maven-build步骤指定的镜像不对,或者镜像里没有 Maven。 - 解决:确保使用官方镜像如
maven:3.8.5-openjdk-11。在本地可以用docker run --rm maven:3.8.5-openjdk-11 mvn -v测试一下。
- 原因:
-
SSH部署失败:Permission denied (password).
- 原因:服务器 SSH 密码错误,或者该用户不允许密码登录。
- 解决:
- 检查 Drone 中设置的
ssh_pwd密钥是否正确。 - 更推荐的方式是使用 SSH 密钥对 替代密码。在 Drone 中创建一个名为
ssh_key的密钥,值为服务器的私钥内容。然后在.drone.yml的 SSH 插件设置中,使用key: from_secret: ssh_key来配置。
- 检查 Drone 中设置的
-
流水线卡在 Pending 状态
- 原因:没有可用的 Runner,或者 Runner 没有成功连接到 Server。
- 解决:执行
docker logs drone-runner查看 Runner 日志,确认它是否成功pinged the remote server。检查 Server 和 Runner 配置中的DRONE_RPC_SECRET是否完全一致。
-
Maven构建速度慢
- 优化:这就是我们配置
volumes挂载 Maven 缓存卷的目的。第一次构建会慢,因为要下载所有依赖。第二次及以后的构建,依赖包直接从宿主机缓存读取,速度会快非常多。
- 优化:这就是我们配置
当你在 Drone 界面上看到所有步骤都打上了绿色的勾,并且最终状态显示为 “Success” 时,那份成就感是无与伦比的。这意味着从今往后,你每次完成功能开发,只需要简单地执行 git push,剩下的编译、测试、部署工作都会自动、可靠地完成。你可以把时间更多地花在写代码上,而不是重复的运维操作上。
这套基于 Docker、Drone 和 Gogs 的轻量级 CI/CD 方案,我已经在多个生产环境中使用了很长时间。它的稳定性超乎我的预期,几乎不需要维护。对于小团队来说,在项目初期就引入这样一套自动化流程,不仅能规范开发部署流程,更能为未来的项目增长打下坚实的技术基础。当你习惯了这种“推送即部署”的流畅体验后,就再也回不去了。
更多推荐
所有评论(0)