You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于GitHub Actions与Docker Compose实现DigitalOcean单Droplet自动化部署的最优方案咨询

使用GitHub Actions + Docker Compose 自动化部署到DigitalOcean单VM的最佳实践

针对你从单容器切换到多容器(API+Nginx)、要在单个DigitalOcean Droplet上实现自动化部署的需求,咱们先聊聊你列出的两个选项的优劣,再给出更适合生产环境的具体方案。

选项分析

选项1:远程服务器拉取代码直接构建

  • 优势:不需要维护GHCR镜像,流程简单直接;适合资源极其有限的小项目,省掉了本地构建推送镜像的网络开销。
  • 劣势:服务器得额外安装Git、语言环境(比如Node/Python)等构建依赖,增加了环境复杂度;构建过程会占用服务器CPU/内存,可能影响线上服务的稳定性;每次部署都要拉取代码,依赖网络稳定性。

选项2:GHCR构建镜像,远程用Compose拉取部署

  • 优势:构建过程在GitHub的服务器上完成,完全不占用Droplet资源;镜像复用性强,后续其他环境部署也能直接用;服务器只需要Docker和Docker Compose,环境更轻量化;部署速度更快,只需要拉取现成镜像而非重新构建。
  • 劣势:需要配置多镜像的构建推送逻辑,步骤比选项1多一点;要处理GHCR的登录权限问题。

推荐方案:GHCR构建镜像 + 远程执行Docker Compose部署

我更推荐选项2,因为它符合CI/CD“构建一次、部署多次”的最佳实践,能让你的部署流程更稳定、服务器环境更干净。下面是具体的配置步骤和示例:

步骤1:准备生产版Docker Compose文件

首先把你的Compose文件改成从GHCR拉取镜像(而非本地构建),命名为docker-compose.prod.yml

version: "3"
services:
  api:
    image: ghcr.io/${{ github.repository }}/api:latest
    networks:
      api-network:
        aliases:
          - api-net
  nginx:
    image: ghcr.io/${{ github.repository }}/nginx:latest
    ports:
      - "80:80"
      - "443:443"
    networks:
      api-network:
        aliases:
          - nginx-net
    depends_on:
      - api
networks:
  api-network:

步骤2:更新GitHub Actions工作流

修改原有的工作流,支持构建多个镜像推送到GHCR,再把生产版Compose文件传到服务器执行部署:

on:
  push:
    branches:
      - main
    paths:
      - "backend/**"
      - ".github/workflows/**"
      - "docker-compose.prod.yml"

jobs:
  build_and_push_images:
    name: Build & Push API/Nginx to GHCR
    runs-on: ubuntu-latest
    steps:
      - name: 拉取代码仓库
        uses: actions/checkout@v4

      - name: 配置Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: 登录GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.CR_PAT }}

      # 构建并推送API镜像
      - name: 构建推送API镜像
        uses: docker/build-push-action@v5
        with:
          context: ./backend/api
          push: true
          tags: ghcr.io/${{ github.repository }}/api:latest

      # 构建并推送Nginx镜像
      - name: 构建推送Nginx镜像
        uses: docker/build-push-action@v5
        with:
          context: ./backend/nginx
          push: true
          tags: ghcr.io/${{ github.repository }}/nginx:latest

  deploy_to_droplet:
    name: 部署到DigitalOcean Droplet
    runs-on: ubuntu-latest
    needs: build_and_push_images
    steps:
      - name: 拉取代码仓库(获取Compose文件)
        uses: actions/checkout@v4

      - name: 上传生产版Compose文件到服务器
        uses: appleboy/scp-action@v0.1.7
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USERNAME }}
          key: ${{ secrets.PRIVATE_KEY }}
          port: ${{ secrets.PORT }}
          source: "docker-compose.prod.yml"
          target: "/home/${{ secrets.USERNAME }}/your-project-dir"

      - name: SSH连接执行部署命令
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USERNAME }}
          key: ${{ secrets.PRIVATE_KEY }}
          port: ${{ secrets.PORT }}
          script: |
            cd /home/${{ secrets.USERNAME }}/your-project-dir
            # 安全登录GHCR(避免明文输出密码)
            echo "${{ secrets.CR_PAT }}" | docker login ghcr.io -u ${{ github.repository_owner }} --password-stdin
            # 拉取最新镜像并重启服务
            docker-compose -f docker-compose.prod.yml pull
            docker-compose -f docker-compose.prod.yml up -d
            # 清理无用镜像释放空间
            docker system prune -f

关键细节说明

  1. 多镜像区分:用ghcr.io/<用户名>/<仓库名>/<服务名>:latest的标签格式,把API和Nginx镜像分开,避免混淆。
  2. Compose文件传递:通过scp-action把生产版Compose文件传到服务器指定目录,确保部署时用的是最新配置。
  3. 安全登录GHCR:用管道方式传递token,避免在日志中明文输出密码;如果服务器已经持久化了GHCR登录(比如把token存在~/.docker/config.json),可以省略登录步骤。
  4. 部署命令优化:用pull + up -d代替down + up,能减少服务中断时间;prune -f自动清理无用镜像,避免服务器磁盘被占满。

额外优化建议

  • 版本标签:别只用latest,可以把Git commit hash作为标签(比如tags: ghcr.io/${{ github.repository }}/api:${{ github.sha }}),这样能快速追溯部署版本,回滚也更方便。
  • 环境变量管理:把敏感配置(比如API密钥、数据库连接串)存在DigitalOcean的环境变量里,或者用Docker Secrets,别硬编码在Compose文件中。
  • 健康检查:在Compose文件中为服务添加healthcheck配置,确保服务启动成功后再对外提供服务。
  • 服务器备份:定期用DigitalOcean的快照功能备份Droplet,避免数据丢失。

备选方案:如果偏好远程构建(选项1)

如果你的项目资源确实极其有限,不想维护GHCR镜像,也可以用选项1,工作流配置大概是这样:

on:
  push:
    branches:
      - main
    paths:
      - "backend/**"
      - ".github/workflows/**"
      - "docker-compose.yml"

jobs:
  deploy_to_droplet:
    name: 部署到DigitalOcean Droplet
    runs-on: ubuntu-latest
    steps:
      - name: SSH连接执行部署
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USERNAME }}
          key: ${{ secrets.PRIVATE_KEY }}
          port: ${{ secrets.PORT }}
          script: |
            cd /home/${{ secrets.USERNAME }}/your-project-dir
            # 拉取最新代码
            git pull origin main
            # 停止旧服务、构建新镜像、启动新服务
            docker-compose down
            docker-compose build --no-cache
            docker-compose up -d
            # 清理资源
            docker system prune -f

注意:这个方案要求服务器已经安装了Git、Docker、Docker Compose,并且已经克隆了你的代码仓库。


内容的提问来源于stack exchange,提问作者bitsmyth

火山引擎 最新活动