欢迎光临
我们一直在努力

在 VPS 上自建 Git 服务与 CI/CD 自动化部署流水线:Gitea + Drone CI 从零到生产环境完整实战

引言:为什么要在 VPS 上自建 Git + CI/CD?

Git仓库和CI/CD自动化流程概念图

对于很多个人开发者和小团队来说,GitHub、GitLab 等托管平台确实方便,但也有不少痛点:私有仓库有数量限制、Actions 免费额度不够用、代码托管在第三方总有隐私顾虑。更重要的是,当你维护一个自己的 VPS 上的项目时,每次改完代码还要 SSH 登录去拉取、构建、重启,这套流程重复几次就让人烦躁了。

好消息是,现在完全可以用一台配置不高的 VPS,自建一套媲美 GitHub 的完整开发工作流——代码托管、CI/CD 流水线、自动化部署一气呵成。本文将以 Gitea(轻量级 Git 服务)和 Drone CI(云原生的 CI/CD 引擎)为核心,从零搭建一套完整的自动化部署体系。

先看下最终效果:当你执行 git push 之后,Drone 自动检测到代码变更,拉取最新代码、运行测试、构建 Docker 镜像、SSH 到生产服务器部署——全程不需要人工介入。如果你用惯了 GitHub Actions,会发现 Drone 的体验几乎一模一样,但完全跑在你自己的硬件上。

方案选型:为什么是 Gitea + Drone CI?

自建 Git + CI/CD 的方案其实不少,我先帮你快速过一下主流的几个选项,方便你做判断:

方案 Git 服务 CI/CD 引擎 资源占用 上手难度
Gitea + Drone CI Gitea(Go 编写) Drone(Go 编写) 极低(1G 内存够用)
GitLab CE GitLab(Ruby + Go) 内置 CI/CD 高(4G 内存起步)
Gogs + Woodpecker Gogs(Go 编写) Woodpecker(Go) 极低
GitHub AE GitHub 私有实例 GitHub Actions 极高(企业级)

GitLab CE 虽然功能最全,但对资源要求太高,1G 内存的 VPS 跑起来非常吃力。Gogs 比 Gitea 还要轻量,不过社区活跃度和插件生态不如 Gitea。至于 GitHub AE,那是企业级产品,个人用户基本不用考虑。

Gitea 是目前个人开发者和小团队自建 Git 服务的最优选择——它用 Go 编写,二进制文件不到 100MB,跑起来内存占用大概 200-300MB。Drone CI 同样用 Go 编写,通过 Docker 容器执行流水线任务,资源隔离性好,配置方式和 GitHub Actions 高度相似,学习成本很低。

环境准备与 Docker 部署

Docker容器化部署示意图

假设你已经有一台 VPS,系统是 Ubuntu 22.04 LTS,内存至少 1GB。首先安装 Docker 和 Docker Compose:

# 安装 Docker
curl -fsSL https://get.docker.com | bash
sudo usermod -aG docker $USER
newgrp docker

# 安装 Docker Compose
sudo apt install -y docker-compose-plugin

# 验证安装
docker --version
docker compose version

接下来创建项目目录结构:

mkdir -p ~/gitea-drone/{data/gitea,data/drone,data/caddy}
cd ~/gitea-drone

这里我用 Caddy 作为反向代理——它自动处理 HTTPS 证书,比 Nginx 配置简单得多。如果你更喜欢 Nginx,也可以用 Nginx Proxy Manager 替代。

用 Docker Compose 编排所有服务

创建一个 docker-compose.yml 文件,把所有服务定义在这里:

version: '3.8'

services:
  # 1. Gitea - Git 服务
  gitea:
    image: gitea/gitea:latest
    container_name: gitea
    environment:
      - USER_UID=1000
      - USER_GID=1000
      - GITEA__database__DB_TYPE=sqlite3
      - GITEA__server__DOMAIN=git.yourdomain.com
      - GITEA__server__ROOT_URL=https://git.yourdomain.com
      - GITEA__server__SSH_PORT=2222
      - GITEA__server__SSH_LISTEN_PORT=22
    volumes:
      - ./data/gitea:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "2222:22"
    restart: unless-stopped

  # 2. Drone Server - CI/CD 控制面
  drone-server:
    image: drone/drone:latest
    container_name: drone-server
    environment:
      - DRONE_GITEA_SERVER=https://git.yourdomain.com
      - DRONE_GITEA_CLIENT_ID=your_client_id
      - DRONE_GITEA_CLIENT_SECRET=your_client_secret
      - DRONE_RPC_SECRET=your_shared_secret
      - DRONE_SERVER_HOST=ci.yourdomain.com
      - DRONE_SERVER_PROTO=https
      - DRONE_USER_CREATE=username:yourusername,admin:true
    volumes:
      - ./data/drone:/data
    restart: unless-stopped
    depends_on:
      - gitea

  # 3. Drone Runner - CI/CD 执行端
  drone-runner:
    image: drone/drone-runner-docker:latest
    container_name: drone-runner
    environment:
      - DRONE_RPC_PROTO=https
      - DRONE_RPC_HOST=ci.yourdomain.com
      - DRONE_RPC_SECRET=your_shared_secret
      - DRONE_RUNNER_CAPACITY=2
      - DRONE_RUNNER_NAME=runner-1
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    restart: unless-stopped
    depends_on:
      - drone-server

  # 4. Caddy - 反向代理 + 自动 HTTPS
  caddy:
    image: caddy:latest
    container_name: caddy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./data/caddy:/data
      - ./Caddyfile:/etc/caddy/Caddyfile
    restart: unless-stopped

这个编排文件有几个关键点值得说明:

  • Gitea 使用 SQLite 作为数据库——对于个人或小团队来说完全够用,不需要额外跑一个 MySQL 容器
  • Gitea 的 SSH 端口映射到宿主机的 2222 端口,避免和宿主机原有的 SSH 冲突
  • Drone Runner 挂载了 /var/run/docker.sock,这样它可以在容器内启动新的容器来执行 CI 任务
  • DRONE_RUNNER_CAPACITY=2 表示同时最多并行执行 2 个流水线任务

配置 Caddy 反向代理

Caddyfile 中定义路由规则:

git.yourdomain.com {
    reverse_proxy gitea:3000
}

ci.yourdomain.com {
    reverse_proxy drone-server:80
}

Caddy 会自动从 Let’s Encrypt 申请和续期 SSL 证书,无需手动配置。把 yourdomain.com 换成你自己的域名,并且确保两个子域名的 A 记录都指向你的 VPS IP。

初始化 Gitea 并创建 OAuth 应用

启动所有服务:

cd ~/gitea-drone
docker compose up -d

# 查看启动日志
docker compose logs -f

启动后,访问 https://git.yourdomain.com,首次访问会看到安装向导。填写以下信息:

  • 站点标题:随便填,比如 “My Git”
  • 服务器域名git.yourdomain.com
  • SSH 服务器端口2222(注意,这是给 Gitea 用户看到的 SSH 端口)
  • HTTP 监听端口3000(保持默认,因为 Caddy 会转发)
  • 数据库:选择 SQLite3(最简单,不需要额外配置)
  • 管理员账号:设置你的用户名和密码

安装完成后,登录 Gitea,然后创建一个 OAuth 应用供 Drone 使用:

进入 个人设置 → 应用 → 管理 OAuth2 应用,填写:

  • 应用名称:Drone CI
  • 重定向 URIhttps://ci.yourdomain.com/login

创建后会得到一个 Client IDClient Secret,把它们填回到之前的 docker-compose.yml 中的 DRONE_GITEA_CLIENT_IDDRONE_GITEA_CLIENT_SECRET。同时生成一个随机的 DRONE_RPC_SECRET(可以用 openssl rand -hex 16 生成)。

修改完成后,重新加载 compose 配置:

docker compose down
docker compose up -d

配置第一个 CI/CD 流水线

代码自动部署流水线示意图

现在基础设施已经就绪,我们来创建一个实际项目体验完整的 CI/CD 流程。

假设我们有一个 Node.js 的 Web 应用,项目根目录创建 .drone.yml

kind: pipeline
type: docker
name: default

trigger:
  branch:
    - main
    - master

steps:
  - name: install
    image: node:20-alpine
    commands:
      - npm ci

  - name: test
    image: node:20-alpine
    commands:
      - npm run test

  - name: build
    image: node:20-alpine
    commands:
      - npm run build

  - name: docker-build
    image: plugins/docker
    settings:
      registry: registry.yourdomain.com
      repo: yourusername/my-app
      tags: latest
      dockerfile: Dockerfile
    when:
      branch:
        - main

  - name: deploy
    image: appleboy/drone-scp
    settings:
      host: your-production-server.com
      username: deploy
      password:
        from_secret: ssh_password
      port: 22
      target: /opt/apps/my-app
      source: ./dist/
    when:
      branch:
        - main

这份流水线的执行逻辑很清晰:

  1. install:安装项目依赖
  2. test:运行测试
  3. build:构建项目
  4. docker-build:只有在 main 分支上,才构建 Docker 镜像
  5. deploy:通过 SCP 把构建产物部署到生产服务器

Drone 的流水线语法和 GitHub Actions 很相似,如果你用过 Actions,上手 Drone 基本不需要学习成本。每个 step 都是一个独立的 Docker 容器,互不干扰,环境隔离非常干净。

在 Drone 的管理界面中,你还需要为仓库配置 Secrets(密钥)。比如上面的 ssh_password,你需要去 Drone 的仓库设置里添加这个 secret,Drone 会在执行流水线时注入到容器中,但不会在日志中暴露明文。

高级配置:Webhook 自动触发与部署通知

Gitea 和 Drone 之间的集成是通过 Webhook 实现的。当你在 Gitea 中激活仓库的 Drone 插件后,Gitea 会自动在仓库中注册一个 Webhook——每次代码推送、PR 合并等事件发生时,Gitea 会通知 Drone 触发流水线。

你可以在 Gitea 仓库的 设置 → Webhooks 中看到自动创建的 Webhook。值得注意的一个优化是:你可以在 Webhook 的触发事件中只勾选 Push 事件Pull Request 事件,去掉不必要的分支/标签删除等事件,减少不必要的流水线触发。

我们还可以加上部署完成后的通知功能,把 CI 结果推送到手机或聊天工具:

# 在 .drone.yml 末尾添加通知步骤
- name: notify-telegram
  image: appleboy/drone-telegram
  settings:
    token:
      from_secret: telegram_token
    to:
      from_secret: telegram_chat_id
    message: >
      {{#success build.status}}
      ✅ 部署成功:{{ repo.name }} ({{ build.branch }})
      提交者:{{ build.author }}
      提交信息:{{ build.message }}
      构建耗时:{{since build.finished build.started}}
      {{else}}
      ❌ 部署失败:{{ repo.name }} ({{ build.branch }})
      请查看:{{ build.link }}
      {{/success}}

配置好 Telegram Bot 后,每次构建完成你都会在手机上收到通知——成功了告诉你部署了什么,失败了直接给链接去排查。这套通知机制配合之前提到的 Prometheus 监控,基本上可以做到出了问题 5 分钟内就发现并定位。

Drone 与 Docker Registry 集成

如果你构建 Docker 镜像并存放到远端仓库,需要一个私有的 Docker Registry。最简单的方案是用 Docker 官方 Registry 镜像自建:

# 在 docker-compose.yml 中添加
registry:
  image: registry:2
  container_name: registry
  environment:
    - REGISTRY_STORAGE_DELETE_ENABLED=true
  volumes:
    - ./data/registry:/var/lib/registry
  restart: unless-stopped

然后在 Caddyfile 中添加路由:

registry.yourdomain.com {
    reverse_proxy registry:5000
}

不过需要注意,自建 Registry 如果暴露在公网上,一定要加上认证或使用 Token 鉴权,不然任何人都可以往你的仓库 push 镜像。推荐的做法是只在内网使用,或者通过 VPN(如 WireGuard)连接后访问。

对于不想自建 Registry 的用户,也可以用 Docker Hub 的私有仓库——Drone 的 docker 插件原生支持 Docker Hub 认证,只要在 Secrets 中配置 docker_usernamedocker_password 即可。

安全加固要点

服务器安全加固示意图

把 Git 服务和 CI/CD 暴露在公网上,安全方面有几个必须要注意的点:

1. Gitea 的 SSH 端口不要使用默认的 22

上面我们在 compose 文件中已经将 Gitea 的 SSH 映射到了 2222 端口,这样可以避免与宿主机 SSH 服务的端口冲突。同时,在 Gitea 的配置中建议关闭密码登录,只允许 SSH Key 认证:

# Gitea 配置文件(在容器内 /data/gitea/conf/app.ini)
[service]
DISABLE_REGISTRATION = true
REQUIRE_SIGNIN_VIEW = true
ENABLE_NOTIFY_MAIL = true

2. Drone 的管理接口要限制访问

Drone 默认只在 /login 路径做 OAuth 认证,但管理 API 端点仍然需要保护。建议在 Caddy 层面添加 IP 白名单或基础认证:

# Caddyfile 中增加
@internal {
    remote_ip 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16
}

ci.yourdomain.com {
    reverse_proxy drone-server:80
    # 管理接口仅限内网 IP 访问
    handle_path /api/* {
        @internal_match @internal
        handle @internal_match {
            reverse_proxy drone-server:80
        }
        respond 403
    }
}

3. 定期备份数据

Gitea 的所有数据(仓库、数据库、附件)都存储在 ~/gitea-drone/data/gitea/ 目录下,Drone 的流水线配置存储在 ~/gitea-drone/data/drone/。设置一个 cron 定时任务:

# crontab -e 添加
0 3 * * * tar -czf /backups/gitea-drone-$(date +\%Y\%m\%d).tar.gz -C ~/gitea-drone data/
0 5 * * * find /backups/ -name "gitea-drone-*" -mtime +30 -delete

4. 利用 Docker 网络隔离

compose 文件中的所有服务默认在同一个 Docker 网络中,Gitea、Drone、Caddy 之间可以互相通信。但不要把其他业务容器放到这个网络里——让 CI/CD 基础设施保持独立的网络命名空间,可以降低被横向移动攻击的风险。

性能优化与资源调优

对于 1G 内存的 VPS,跑 Gitea + Drone Server + Drone Runner + Caddy 四个容器,再加上可能同时运行的 CI 任务容器,资源还是比较紧张的。这里分享几个调优技巧:

  • 限制 Drone Runner 的并行数DRONE_RUNNER_CAPACITY=1,一次只跑一个任务,防止内存被打满
  • 使用 SQLite 而非 MySQL:Gitea 默认用 SQLite,对个人用户完全够用,节省一个数据库容器的开销
  • 给 Docker 设置资源限制:在 /etc/docker/daemon.json 中限制 Docker 的全局资源
  • 启用 Gitea 的 Git GC:Gitea 后台会自动运行 git gc,但默认频率偏低,可以在配置中调高频率以控制仓库体积
  • 使用 Drone 的缓存插件:每次 CI 都重新 npm install 很浪费时间,配置缓存后每次构建能节省 30-60 秒
# 在 .drone.yml 中添加缓存步骤
- name: restore-cache
  image: meltwater/drone-cache
  settings:
    restore: true
    mount:
      - node_modules
    backend: "filesystem"
    cache_base: /cache

- name: rebuild-cache
  image: meltwater/drone-cache
  settings:
    rebuild: true
    mount:
      - node_modules
    backend: "filesystem"
    cache_base: /cache

这需要把宿主机的 /cache 目录挂载到 Drone Runner 容器中。加上缓存后,第二次及以后的构建速度会有明显提升。

总结与扩展思路

通过本文的实践,我们在一台普通的 VPS 上完成了以下部署:

  • Gitea:轻量级的 Git 代码托管服务
  • Drone CI:容器化的 CI/CD 引擎
  • Caddy:自动 HTTPS 的反向代理
  • Docker Registry:私有镜像仓库

整套系统加起来的内存占用大约在 500-600MB(空闲状态下),1GB 的 VPS 完全可以胜任。如果再结合之前介绍过的 Prometheus + Grafana 监控方案,你就有了一套从代码提交到部署监控的完整 DevOps 闭环。

如果还想进一步扩展,可以考虑以下几个方向:

  • 集成 SonarQube:在 CI 流程中加入代码质量检查
  • 使用 Kubernetes 替代 Docker:当服务数量增多后,迁移到 K3s 可以更方便地管理容器编排
  • 搭建 Drone 的 Runner 集群:多台机器注册到同一个 Drone Server,实现 CI 任务的水平扩展
  • 引入 ArgoCD:实现 GitOps 风格的声明式部署

最后想说的是,自建 CI/CD 不是为了替代 GitHub/GitLab,而是在你需要更多控制权、更好的隐私保护、或者不想被免费额度限制时,给你一个完全自主的选择。动手试试吧,从 git push 到自动部署,这种”一条龙”的完成感是很有成就感的。

【本站文章皆为原创,未经允许不得转载】:汤不热吧 » 在 VPS 上自建 Git 服务与 CI/CD 自动化部署流水线:Gitea + Drone CI 从零到生产环境完整实战
分享到: 更多 (0)