自建 CI/CD 自动化流水线实战:Gitea + Drone 实现代码推送即部署

代码写好了,本地测试通过,然后怎么部署到服务…

代码写好了,本地测试通过,然后怎么部署到服务器?手动 SSH 登录、git pull、重启服务?一次两次还行,项目多了你就知道这活儿有多烦人。

今天这篇就是来解决这个问题的。咱们用 Gitea + Drone CI 走一套完整的自动化流水线:Gitea 做自建 Git 仓库,Drone 做 CI/CD 引擎,推到仓库自动触发构建、自动部署到服务器。全程容器化,不污染宿主机,配置一次以后只管 git push。

为什么选 Gitea + Drone?

市面上自建 CI/CD 的选择不少,简单列个对比:

  • GitLab CE:功能最全,但太重了。单是 Runner 的安装和管理就够喝一壶,内存起步 4GB,资源不够的机器别碰。
  • Jenkins:生态最大,但配置繁琐,插件依赖严重,界面风格还停留在 2015 年。
  • Woodpecker CI:轻量,但社区规模相对小,模板生态不够丰富。
  • Gitea + Drone:两个容器加起来不到 200MB 镜像大小,内存占用低,配置简单,够用。这是自部署场景下性价比最高的组合。

Gitea 不用多介绍,Go 写的高性能 Git 服务,一个二进制搞定,界面和 GitHub 几乎一致。Drone 也是 Go 写的 CI 引擎,用 YAML 定义流水线,和 Gitea 是「天作之合」——OAuth 直连,推送事件自动触发。

先上架构图(脑补版)

整个链路大概长这样:

你本地开发 → git push → Gitea 收到推送 → Webhook 通知 Drone
→ Drone 启动 Runner 容器 → 拉代码 → 执行构建/测试
→ 编译产物/构建镜像 → 部署到目标服务器

整个过程全自动,你唯一要做的就是 git push

第一步:用 Docker Compose 部署 Gitea + Drone

我习惯用 Docker Compose 统一管理,目录结构清晰,迁移也方便。先创建一个项目目录:

mkdir -p ~/ci-stack && cd ~/ci-stack
mkdir -p data/gitea data/drone data/drone-runner

然后写 compose.yml:

cat <<'EOF' > compose.yml
version: "3.8"

networks:
  ci-net:
    driver: bridge

services:
  gitea:
    image: gitea/gitea:latest
    container_name: gitea
    restart: unless-stopped
    networks:
      - ci-net
    ports:
      - "3000:3000"
      - "2222:22"   # Gitea SSH 端口,避免和主机 SSH 冲突
    volumes:
      - ./data/gitea:/data
    environment:
      - USER_UID=1000
      - USER_GID=1000
      - GITEA__database__DB_TYPE=sqlite3
      - GITEA__server__DOMAIN=git.你的域名.com
      - GITEA__server__SSH_DOMAIN=git.你的域名.com

  drone-server:
    image: drone/drone:latest
    container_name: drone-server
    restart: unless-stopped
    networks:
      - ci-net
    ports:
      - "3080:80"
    volumes:
      - ./data/drone:/data
    environment:
      - DRONE_GITEA_SERVER=http://gitea:3000
      - DRONE_GITEA_CLIENT_ID=      # 稍后在 Gitea 创建
      - DRONE_GITEA_CLIENT_SECRET=  # 稍后在 Gitea 创建
      - DRONE_RPC_SECRET=changeme-long-random-secret
      - DRONE_SERVER_HOST=ci.你的域名.com
      - DRONE_SERVER_PROTO=https
      - DRONE_USER_CREATE=username:你的Gitea用户名,admin:true

  drone-runner:
    image: drone/drone-runner-docker:latest
    container_name: drone-runner
    restart: unless-stopped
    networks:
      - ci-net
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./data/drone-runner:/data
    environment:
      - DRONE_RPC_PROTO=https
      - DRONE_RPC_HOST=drone-server
      - DRONE_RPC_SECRET=changeme-long-random-secret
      - DRONE_RUNNER_CAPACITY=2
      - DRONE_RUNNER_NAME=runner-1
EOF

几个关键点提醒:

  • DRONE_RPC_SECRET 必须是一个强随机字符串,server 和 runner 两边要一致
  • DRONE_RUNNER_CAPACITY=2 表示同时最多跑 2 个 Pipeline,根据你服务器资源调整
  • Runner 需要挂载宿主机的 Docker socket,因为它要动态创建容器来执行流水线

先别急着启动。我们需要先到 Gitea 里创建 OAuth 应用,拿到 client_id 和 client_secret 才能让 Drone 和 Gitea 握手。

第二步:启动 Gitea 并配置 OAuth

先启动 Gitea:

docker compose up -d gitea

浏览器打开 http://你的服务器IP:3000,首次访问会进入安装页面。按提示设置管理员账号(记下来!),数据库选 SQLite3 就够了,个人或小团队场景完全够用,没必要为了 MySQL 多开一个容器。

安装完成后,用管理员账号登录,去右上角头像 → 设置 → 应用 → 创建新 OAuth2 应用:

  • 应用名称:Drone CI
  • 重定向 URIhttps://ci.你的域名.com/login(注意这是 Drone 的回调地址,要和实际域名一致)

创建后会得到 Client IDClient Secret。把它俩填回到之前的 compose.yml 里,然后启动整套服务:

docker compose up -d

第三步:配置反向代理和自动 HTTPS

我强烈建议给 Gitea 和 Drone 配上域名和 HTTPS。用 Caddy 最省事,一行配置搞定自动证书:

git.你的域名.com {
  reverse_proxy gitea:3000
}

ci.你的域名.com {
  reverse_proxy drone-server:80
}

如果你用 Nginx,自己配 certbot 或 acme.sh 也一样,就是多几行。HTTPS 是刚需——Drone 的 OAuth 回调必须走 HTTPS,浏览器也认这个。

第四步:写一个实际的 Pipeline

现在整套 CI 环境已经跑起来了。登录 Drone 界面(https://ci.你的域名.com),用 Gitea 账号点一下「Authorize」,就能看到你的 Gitea 仓库列表了。激活一个仓库,Drone 就开始监听它的推送事件。

接下来,在你的项目根目录创建 .drone.yml,这就是流水线的定义文件。下面我给一个实际项目用的例子——一个 Node.js 后端服务的完整流水线:

---
kind: pipeline
type: docker
name: build-and-deploy

trigger:
  branch:
    - main
  event:
    - push

steps:
  - name: install-deps
    image: node:20-alpine
    commands:
      - npm ci --only=production
      - cp -r node_modules /tmp/node_modules

  - name: build
    image: node:20-alpine
    commands:
      - cp -r /tmp/node_modules ./node_modules
      - npm run build
      - cp -r dist /tmp/artifacts

  - name: docker-build
    image: plugins/docker
    settings:
      repo: registry.你的域名.com/myapp
      tags: latest
      dockerfile: Dockerfile
      registry: registry.你的域名.com
      username:
        from_secret: docker_username
      password:
        from_secret: docker_password
    when:
      branch: main

  - name: deploy
    image: alpine:latest
    commands:
      - apk add --no-cache openssh-client
      - mkdir -p ~/.ssh
      - echo "$SSH_KEY" > ~/.ssh/id_rsa
      - chmod 600 ~/.ssh/id_rsa
      - ssh -o StrictHostKeyChecking=no user@目标服务器 "
          docker pull registry.你的域名.com/myapp:latest &&
          docker compose up -d myapp
        "
    environment:
      SSH_KEY:
        from_secret: ssh_private_key
    when:
      branch: main

这个流水线做了四件事:

  • install-deps:拉依赖,注意我用 npm ci 而不是 npm install,因为它更严格、更快,而且保证 lockfile 一致。
  • build:构建产物,用 copy 的方式跨步骤传递数据,这是 Drone 的标准做法。
  • docker-build:用官方 Docker 插件构建镜像并推送到私有 registry。
  • deploy:SSH 到目标服务器,拉取新镜像并重启容器。

用到了几个要点:

敏感信息(Docker 仓库密码、SSH 私钥)通过 from_secret 注入,而不是直接写在 YAML 里。这些 secret 需要在 Drone 仓库设置里预先配置好——仓库 → Settings → Secrets,逐个添加。记住 secret 的名字要和 YAML 里的对应。

第五步:秘密管理与权限控制

Drone 的秘密管理有一个值得注意的点:secret 只在信任的仓库里才自动注入到所有步骤。如果仓库是「非信任」状态,secret 只会在 when 条件匹配的步骤里注入。这对安全来说是好事,但也意味着如果你的流水线里 secret 一直为空,先去检查仓库的 Trusted 开关。

另外还有几个安全建议:

  • SSH 密钥单独创建一个 部署专用用户,权限仅限于目标服务的目录和 Docker 操作,不要用 root
  • Runner 容器本身就挂载了 Docker socket,不要开放 Runner 到公网,只能内网访问
  • 定期轮换 Drone RPC secret 和 Gitea OAuth 密钥

第六步:调试与常见问题

流水线跑不起来?别慌,先看 Drone Runner 的日志:

docker logs drone-runner -f --tail 50

常见翻车场景:

  • Runner 连不上 Server:检查 DRONE_RPC_HOST 和 DRONE_RPC_SECRET 是不是一致,检查 Docker 网络
  • Webhook 没触发:到 Gitea 仓库设置 → Webhooks 里检查是否收到推送事件,注意 Drone 2.0 之后不再需要手动配置 Webhook,激活仓库时自动创建
  • Pipeline 一直 pending:一般是 Runner 的资源不够,DRONE_RUNNER_CAPACITY 调大或者检查宿主机磁盘/Docker 状态
  • Docker 构建失败:Runner 默认用宿主机的 Docker 构建,如果你的 Docker 版本太老可能不兼容,建议 Docker 版本不低于 24.x

总结

这一套 Gitea + Drone 组合,是我目前在用的 CI/CD 方案。从去年搭好到现在,三个项目跑了快一年,期间只因为升级 Drone 版本重启过一次。稳定、轻量、够用。

当然它也有短板——Pipeline 的 YAML 语法不如 GitHub Actions 丰富,日志输出没有 GitLab CI 那么漂亮,社区插件数量也有限。但它最大的优势是:你自己的服务器,你自己的代码,你自己说了算。没有 Action 分钟限制,没有 Runner 并发数限制,也不怕平台方哪天改政策。

最后给还在犹豫的朋友一句话:自动化流水线这个坑,越早入越值。刚开始配的时候会花一两个小时,但只要跑起来,后面省的是你每个迭代都重复的 SSH + docker compose up 那几十次操作。时间成本算一算,回本周期不到两周。

祝你的代码走上自动化流水线,从此告别手动部署。

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注