基于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
关键细节说明
- 多镜像区分:用
ghcr.io/<用户名>/<仓库名>/<服务名>:latest的标签格式,把API和Nginx镜像分开,避免混淆。 - Compose文件传递:通过
scp-action把生产版Compose文件传到服务器指定目录,确保部署时用的是最新配置。 - 安全登录GHCR:用管道方式传递token,避免在日志中明文输出密码;如果服务器已经持久化了GHCR登录(比如把token存在
~/.docker/config.json),可以省略登录步骤。 - 部署命令优化:用
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




