这次我们看一个比较有意思的开源项目组合:Gitea + Harbor + Drone + Docker + Nginx。名字里有 drone,但别往机场那则新闻上联想,这里是 DevOps 里的 Drone CI/CD。它解决的问题很具体:代码推到 Gitea 之后,自动触发构建、跑测试、打镜像,再推到 Harbor 私有仓库,最后通过 Nginx 对外提供访问入口。整套流程下来就是一个能落地的内网持续集成方案。
先说结论:Drone 的核心优势不在功能堆砌,而是“管道即代码”。你只需要在仓库里放一个.drone.yml,它就能按你定义的步骤去执行构建任务。配合 Gitea 做代码托管、Harbor 做镜像仓库、Docker 跑运行环境、Nginx 做反向代理,这套架构非常适合中小团队或内网环境快速搭建。本文不会只讲概念,会按“环境准备 -> 安装部署 -> 流水线测试 -> API 调用 -> 性能观察 -> 问题排查”的顺序,给你一套可以直接复制的落地流程。
1. 核心能力速览
| 能力项 | 说明 |
|---|---|
| 项目类型 | 开源 CI/CD 持续集成工具,Drone 分为 Server 与 Runner 两部分 |
| 代码托管 | 原生支持 Gitea,也可对接 GitHub、GitLab、Bitbucket 等 |
| 管道定义 | 通过仓库内.drone.yml声明式配置,支持 step、service、pipeline 概念 |
| 镜像仓库 | 可对接 Harbor、Docker Registry、阿里云 ACR 等,通过 Docker 插件推送 |
| 反向代理 | 推荐 Nginx 终结 HTTPS,再转发到 Drone Server 与 Runner API |
| 启动方式 | Docker Compose 编排,适合单机或内网服务器部署 |
| 接口能力 | 提供 REST API 与 Drone CLI,可用于查询仓库、触发构建、查看日志 |
| 批量任务 | 支持多仓库、多分支自动触发,也可通过 API 外部批量调度 |
| 并发能力 | Runner 默认按容器并发执行,可配置并发数与资源限制 |
| 适合场景 | 内网代码托管、私有镜像仓库、自动构建发布、多环境交付 |
从材料看,这套技术栈的典型使用路径是:开发者在 Gitea 提交代码,Webhook 通知 Drone Server,Server 将任务分发给 Runner,Runner 启动 Docker 容器执行构建命令,最后把产物推送到 Harbor。整个过程无需人工登录服务器敲命令,CI 的“持续”二字在这里体现得最明显。
实际部署时,你不需要关心 Gitea、Harbor、Drone 三者之间的深层依赖关系,它们各自是独立服务,通过 Webhook、API、Docker Socket 互相通信。重点要理解的是网络路径:Gitea 需要能访问到 Drone Server 的 Webhook 地址,Drone Runner 需要能访问到 Harbor 或镜像仓库,Nginx 需要把对应域名或路径转发到正确容器。只要这条链路通了,整套系统就能跑起来。
2. 适用场景与使用边界
这套组合最适合三类团队。
第一类是内网开发团队。代码不想放到公网 Git 平台,镜像也不想推到公共仓库,但又要享受自动构建的便利。Gitea 加上 Harbor 正好覆盖代码和镜像两端的私有化需求。
第二类是中小项目或创业团队。没有专职 DevOps 工程师,但又需要一套能自动构建、自动打标签、自动推送的流水线。Drone 的.drone.yml比 Jenkins 的界面配置更容易用 Git 管理,也比 GitLab CI 少了很多组件。
第三类是已经在用 Docker 和 Nginx 做部署的团队。因为 Drone 的 Runner 本身就是以容器方式运行,构建环境也通过容器隔离,整个技术栈的一致性很高,不需要额外学习 K8s 或复杂编排工具。
需要强调使用边界。第一,不要把 Drone Server 暴露到公网而且不做访问控制,Gitea 和 Drone 之间的 Webhook 必须用密钥保护。第二,Runner 在执行管道时会挂载 Docker Socket,这意味着管道内容器拥有较高的宿主权限,不能随便让不可信的人往 Gitea 仓库提交代码。第三,Harbor 只存放你拥有合法授权或可以合法使用的镜像,项目中涉及第三方基础镜像、商业软件镜像时,要注意许可证和分发限制。第四,如果你把 Drone 集成到生产环境,所有流水线步骤必须有日志留存,镜像 tag 要可追溯,不能只图方便而跳过验证环节。
这套方案不是一个完整的发布系统。你可以把它理解为“构建 + 推送”的自动化工具,但部署到哪台服务器、如何平滑升级、如何回滚,这些还需要 Jenkins、Ansible、K8s 或其他工具配合。不要在文章里过度拔高它的能力边界。
3. 环境准备与前置条件
部署前先确认基础设施。以下清单适用于大多数单机内网部署场景,具体版本和路径以实际环境为准。
3.1 操作系统与 Docker
推荐使用 Ubuntu 22.04 LTS 或 Debian 12 这类长期支持系统。Docker 安装方式不固定,但建议使用官方源安装 Docker Engine 和 Docker Compose 插件。安装完成后执行以下命令验证:
docker version docker compose version只要这两个命令都能输出版本号,说明 Docker 环境可用了。
3.2 网络与域名规划
建议为每个服务规划独立的域名或子路径,避免端口混淆。常见规划如下:
| 服务 | 推荐域名 | 后端端口 |
|---|---|---|
| Gitea | git.example.com | 3000 |
| Harbor | harbor.example.com | 80 / 443 |
| Drone Server | ci.example.com | 80 / 443 |
| Drone Runner | 不需要对外暴露 | 3000(仅内网) |
如果暂时没有域名,也可以直接用 IP + 端口访问,但后续配置 Drone 的DRONE_SERVER_HOST时,必须把外部访问地址写对,否则 Webhook 回调会失败。
3.3 Gitea 与 Harbor 准备
Gitea 和 Harbor 可以先通过各自官方文档部署好。这里不展开完整安装过程,但有一个关键点:Gitea 需要提前创建好一个 OAuth2 应用,用于和 Drone 做单点登录集成。在 Gitea 的用户设置 -> 应用 -> 管理 OAuth2 应用中,创建一个新的 OAuth2 应用,回调地址填http://ci.example.com/login或https://ci.example.com/login,注意和后续 Drone 配置中的协议保持一致。
Harbor 需要确认两点:一是当前仓库策略是否开放匿名拉取,如果私有项目需要认证,管道中要配置 Docker 登录凭据;二是如果 Drone Runner 和 Harbor 在同一台机器,建议在/etc/docker/daemon.json中把 Harbor 地址加入insecure-registries,否则推送 https 证书不受信任的 Harbor 时会报错。
4. 安装部署与启动方式
部署方式推荐 Docker Compose,运维成本最低。这里给出一套单机部署示例,参数需要根据实际环境替换。
4.1 创建 Drone Server 与 Runner 的 Compose 文件
新建/opt/drone/docker-compose.yml,内容如下:
version: "3" services: drone-server: image: drone/drone:2 container_name: drone-server ports: - "8080:80" environment: - DRONE_GITEA_SERVER=http://git.example.com - DRONE_GITEA_CLIENT_ID=${DRONE_GITEA_CLIENT_ID} - DRONE_GITEA_CLIENT_SECRET=${DRONE_GITEA_CLIENT_SECRET} - DRONE_RPC_SECRET=${DRONE_RPC_SECRET} - DRONE_SERVER_HOST=ci.example.com - DRONE_SERVER_PROTO=https - DRONE_USER_CREATE=username:your_admin_username,admin:true volumes: - /var/lib/drone:/data restart: always drone-runner: image: drone/drone-runner-docker:1 container_name: drone-runner ports: - "3000:3000" environment: - DRONE_RPC_PROTO=http - DRONE_RPC_HOST=drone-server - DRONE_RPC_SECRET=${DRONE_RPC_SECRET} - DRONE_RUNNER_CAPACITY=2 - DRONE_RUNNER_NAME=docker-runner volumes: - /var/run/docker.sock:/var/run/docker.sock depends_on: - drone-server restart: always需要创建.env文件,存放密钥:
DRONE_GITEA_CLIENT_ID=your_client_id DRONE_GITEA_CLIENT_SECRET=your_client_secret DRONE_RPC_SECRET=$(openssl rand -hex 32)启动命令:
cd /opt/drone docker compose up -d启动后观察日志:
docker compose logs -f drone-server docker compose logs -f drone-runner如果看到 Runner 成功注册到 Server,说明通信正常。这里要注意DRONE_RPC_SECRET必须一致,否则 Runner 无法连接 Server。
4.2 Nginx 反向代理配置
Nginx 配置中,需要将ci.example.com转发到 Drone Server 的 8080 端口。一个最小可用的配置如下:
server { listen 80; server_name ci.example.com; client_max_body_size 100m; location / { 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; proxy_pass http://127.0.0.1:8080; } }实际生产环境建议再加上 HTTPS 证书。注意 Drone 的DRONE_SERVER_PROTO必须和 Nginx 对外协议一致,如果 Nginx 只做 HTTP,那DRONE_SERVER_PROTO填http;如果做了 HTTPS,就填https。这个参数影响 Gitea Webhook 的回调地址和登录跳转地址,写错会导致登录后回调异常。
5. 功能测试与效果验证
部署完成后,需要验证整套链路是否真的能自动构建。下面给出一套可执行的验证流程。
5.1 验证 Drone 与 Gitea 集成
访问http://ci.example.com,使用 Gitea 账号登录。登录后进入 Drone 界面,选择需要激活的仓库,点击“Activate Repository”。这一步会触发以下几件事:Drone 在 Gitea 中创建 Webhook,Gitea 后续的 push、tag、merge 事件都会通知 Drone。
如果点击激活后没反应,先确认 Gitea 的 OAuth2 回调地址是否填了http://ci.example.com/login,再确认 Nginx 能正常转发到 Drone Server 的 8080 端口。
5.2 编写.drone.yml流水线
激活仓库后,在仓库根目录创建.drone.yml。下面是一个完整的示例,包含构建和推送镜像到 Harbor 两个步骤:
kind: pipeline type: docker name: build-and-push steps: - name: build image: golang:1.21 commands: - go build -o myapp . - go test ./... - name: docker-build-push image: plugins/docker settings: registry: harbor.example.com repo: harbor.example.com/library/myapp tags: ${DRONE_COMMIT_SHA} username: from_secret: harbor_username password: from_secret: harbor_password将文件提交到 Gitea:
git add .drone.yml git commit -m "add drone pipeline" git push origin main推送后,Gitea 会触发 Webhook,Drone Server 会创建一次构建记录。你可以在 Drone 界面看到流水线状态,点击构建步骤可以查看实时日志。
5.3 判断构建是否成功
成功标准有三条:第一,Drone 界面中流水线状态为绿色;第二,构建日志里面能看到go test通过;第三,Harbor 仓库中出现 tag 为 commit SHA 的镜像,例如harbor.example.com/library/myapp:abc123...。
如果镜像没有出现在 Harbor 中,先查看 Docker 推送步骤的日志,重点排查以下问题:
dial tcp: lookup harbor.example.com失败:Runner 容器无法解析 Harbor 域名,检查 Docker DNS 配置。x509: certificate signed by unknown authority:Harbor 的 https 证书未受信任,把 Harbor 地址加入 Docker daemon 的insecure-registries。denied: requested access to the resource is denied:Harbor 用户名或密码错误,检查 Drone secrets 是否配置正确。
5.4 验证 Tag 触发与多分支
团队实践中,主干分支和 tag 往往需要不同处理。可以在.drone.yml中增加条件触发:
trigger: event: - push - tag也可以按分支区分构建参数。例如只在main分支上推送镜像,其他分支只执行测试:
steps: - name: test image: golang:1.21 commands: - go test ./... - name: build image: golang:1.21 commands: - go build -o myapp . when: branch: - main通过when条件可以控制步骤在哪些分支、哪些事件下执行。这是 Drone 流水线非常实用的功能。
6. 接口 API 与批量任务
6.1 Drone REST API 概览
Drone 提供 REST API,可以通过 token 访问。常用接口包括:
| 接口 | 方法 | 说明 |
|---|---|---|
/api/repos | GET | 列出当前用户所有仓库 |
/api/repos/{owner}/{repo} | GET | 获取仓库详情 |
/api/repos/{owner}/{repo}/builds | GET | 获取构建列表 |
/api/repos/{owner}/{repo}/builds/{build} | POST | 重启某个构建 |
/api/repos/{owner}/{repo}/builds | POST | 手动触发构建 |
调用 API 需要先获取 token。登录 Drone Web 界面后,在用户设置页面可以复制 Personal Access Token。拿到 token 后,在请求头中带上Authorization: Bearer <token>。
6.2 使用 curl 触发构建
手动触发某个仓库的构建,可以使用如下命令:
curl -X POST \ -H "Authorization: Bearer ${DRONE_TOKEN}" \ "http://ci.example.com/api/repos/{owner}/{repo}/builds"查询构建列表:
curl -H "Authorization: Bearer ${DRONE_TOKEN}" \ "http://ci.example.com/api/repos/{owner}/{repo}/builds"6.3 使用 Drone CLI 管理多仓库
Drone 官方提供了命令行工具,适合在脚本中批量操作。首先安装 CLI:
curl -L https://github.com/drone/drone-cli/releases/latest/download/drone_linux_amd64.tar.gz | tar -xz sudo install drone /usr/local/bin/配置环境变量:
export DRONE_SERVER=http://ci.example.com export DRONE_TOKEN=your_personal_access_token查看仓库列表:
drone repo ls查看构建日志:
drone build logs {owner}/{repo} {build_number}6.4 批量触发仓库构建
如果需要在一批仓库统一的版本上重新构建,可以写一个 shell 脚本,读取仓库列表,逐个触发:
#!/bin/bash DRONE_SERVER=http://ci.example.com DRONE_TOKEN=your_personal_access_token REPOS=$(curl -s -H "Authorization: Bearer ${DRONE_TOKEN}" ${DRONE_SERVER}/api/repos | jq -r '.[] | .slug') for repo in ${REPOS}; do echo "trigger build for ${repo}" curl -X POST -H "Authorization: Bearer ${DRONE_TOKEN}" "${DRONE_SERVER}/api/repos/${repo}/builds" sleep 2 done这里用jq解析 JSON,实际使用前需要确认是否安装。批量触发后,建议在脚本中增加检查机制:每 30 秒查询一次构建状态,避免某个仓库构建失败后无感知。
这里要说明的是,Drone 自身有并发队列,批量触发大量任务时,Runner 的DRONE_RUNNER_CAPACITY决定了同时执行的构建数。如果一次触发 50 个仓库,而 Runner 并发数只有 2,那么任务会排队,这不算故障,只是吞吐量受限于 Runner 资源。
7. 资源占用与性能观察
7.1 容器资源监控
Drone Server、Runner、Gitea、Harbor、Nginx 都是容器运行,监控资源占用的方式是docker stats:
docker stats drone-server drone-runner从实际部署经验看,Drone Server 本身很轻,单机运行通常几百 MB 内存以内。真正吃资源的是 Runner 启动的构建容器,以及 Harbor 中镜像的存储和推送过程。如果 Runner 并发数过高,会导致单机 CPU 和内存飙升,甚至影响其他服务。更稳妥的做法是在 Compose 文件中为 Runner 设置资源限制:
services: drone-runner: deploy: resources: limits: cpus: "4" memory: 4G7.2 显存与性能的类比理解
如果你之前玩过 AI 相关的一键包,会习惯看显存占用。Drone 这类 CI 工具不涉及 GPU,关注点是 CPU、内存、磁盘 I/O 和网络带宽。每一次构建启动一个容器,会拉取基础镜像并创建多个层,所以磁盘和网络会成为主要瓶颈。建议把/var/lib/docker放到 SSD 上,避免构建速度被机械盘拖慢。
7.3 如何提升构建吞吐量
如果团队内构建任务很多,可以考虑两个方向:
第一,增加 Runner 数量。一台机器跑多个 Runner 实例,或多台机器各跑一个 Runner,通过同一个 RPC Secret 连接到 Drone Server。这样任务会被分发到不同机器执行。
第二,使用构建缓存。Drone 社区有缓存插件,可以将依赖目录打包上传到对象存储或本机缓存。比如 Go 项目的GOPATH、Node 项目的node_modules都可以缓存,避免每次构建重新下载全部依赖。配置示例如下:
steps: - name: restore-cache image: meltwater/drone-cache settings: backend: filesystem cache_key: volume archive_format: gzip mount: - /go/pkg/mod当然,缓存插件会依赖外部存储或共享目录,使用前需要确认版本兼容性。
8. 常见问题与排查方法
以下排查表来自常见部署场景,不同版本可能有个别参数差异,但排查思路通用。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| Gitea 登录 Drone 后提示回调失败 | OAuth2 回调地址与DRONE_SERVER_HOST不一致 | 对比 Gitea 应用配置和 Drone 环境变量 | 将回调地址改为当前外部访问地址,协议保持一致 |
| 推送代码后 Drone 没有自动构建 | Webhook 未创建,或 Gitea 无法访问 Drone | 在 Gitea 仓库 Webhook 设置中检查投递记录 | 重新激活仓库,确认 Drone 地址可从 Gitea 访问 |
| 构建卡在 pending 状态 | Runner 未启动或 RPC Secret 不一致 | 查看 Runner 日志 | 检查DRONE_RPC_HOST、DRONE_RPC_PROTO、DRONE_RPC_SECRET |
| 构建容器无法访问代码仓库 | 仓库为私有,未配置凭据 | 查看构建日志中 git clone 的报错 | Drone 通过 Gitea 集成自动 clone,确认仓库已激活且账号有权限 |
| 推送镜像到 Harbor 失败认证失败 | Harbor 用户名或密码不正确 | 检查 Drone secrets | 重新在 Drone 仓库设置中添加harbor_username和harbor_password |
| Harbor 证书不受信任 | 使用自签 https 证书 | 查看推送日志是否出现 x509 错误 | 将 Harbor 地址加入insecure-registries,或配置可信证书 |
| Nginx 访问 Drone 返回 502 | Drone Server 容器没有启动或端口错误 | 检查docker compose ps和 Nginx 日志 | 确认 proxy_pass 指向正确端口 |
| 多个 Runner 同时挂载同一个 Docker Socket | 并发构建导致资源竞争 | 观察构建卡顿和失败时间点 | 降低DRONE_RUNNER_CAPACITY,或横向扩展 Runner 机器 |
.drone.yml语法错误导致构建不启动 | YAML 缩进或字段错误 | 在 Drone 界面查看构建状态和错误信息 | 使用 Drone 文档示例对比,注意kind、type、steps层级 |
9. 最佳实践与使用建议
这套组合的工程化落地,建议按以下几条来约束。
第一,第一次跑通时永远使用最小配置。不要一上来就写复杂的多阶段构建,先用一条echo "hello drone"命令的.drone.yml验证链路,确认自动触发和日志显示正常后,再逐步加入编译、测试、镜像推送步骤。这样可以快速定位问题是出在链路还是出在流水线本身。
第二,密钥统一走 Drone Secrets。不要在.drone.yml中明文写 Harbor 密码、云平台 AccessKey、SSH 私钥。Drone 的 Secrets 机制可以在仓库或全局级别配置,然后在管道中通过from_secret引用。生产环境更应该限制 Secrets 的可见范围,避免所有仓库共享全部密钥。
第三,镜像 tag 规范必须提前定好。建议使用 commit SHA 作为唯一 tag,例如myapp:abc123def,同时通过latest、v1.2.3这样的标签辅助发布。不要把 tag 只写成latest,否则无法定位线上跑的是哪一个版本。
第四,日志和状态要有留存。Drone Server 的日志默认在容器内,如果构建失败需要回溯,建议把/var/lib/drone挂载到持久化磁盘,并定期备份。更大的团队可以接 Prometheus 和 Loki,把构建指标和日志集中管理,但这不是首次部署的必选项。
第五,批量任务要设计重试和告警。通过 API 批量触发构建时,不要一股脑全部发过去,控制并发数,增加失败重试和状态检查。Gitea 和 Drone 的 Webhook 传递是有时间窗口的,网络抖动可能导致事件丢失,关键发布场景最好保留手动触发入口。
第六,涉及外部依赖时先确认授权边界。Harbor 中如果有从第三方拉取再转存的基础镜像或商用组件,要确认许可证允许分发和内部使用。构建过程中从公网拉取依赖包时,也要留意依赖的许可证变化,避免合规风险。
10. 总结与下一步
把 Gitea、Harbor、Drone、Docker、Nginx 组合在一起,得到的是一套轻量但完整的持续集成交付链路。最值得尝试的点是 Drone 的“管道即代码”模式,仓库里提交一个.drone.yml,构建流程就跟着代码走,不需要在某个中心化界面里拖拽配置。
最先应该验证的功能是“提交代码自动触发构建”。只要这一步通了,后面的镜像推送、多分支条件、API 批量调度都可以在这个基础上叠加。最容易踩的坑有两个:一是 Gitea OAuth2 回调地址和 Drone 外部访问地址不一致,导致登录失败;二是 Runner 的 RPC Secret 配置不一致,导致构建全部卡在 pending。部署时先把这两个点检查一遍,能省下大量排查时间。
后续可以扩展的方向包括:在流水线中加入单元测试和静态检查,把构建结果通知到企业微信或钉钉,通过 Drone 的 API 接入自己的发布平台,或者把 Runner 横向扩展到多台机器提升构建吞吐。对于还想继续深入的同学,建议先去读 Drone 官方文档中关于kind: pipeline的完整定义,把trigger、when、depends_on这些条件组合用熟,你会发现这套系统的表达能力比想象中强很多。