本站包含联盟链接——如果你通过链接注册,我们可能获得佣金。 Affiliate links.

使用 GitHub Actions 与 DigitalOcean 的 CI/CD 指南 2026

使用 GitHub Actions 与 DigitalOcean 的 CI/CD 指南 2026

将 GitHub Actions 与 DigitalOcean 集成可以在每次代码推送到 main 分支时自动构建、测试和部署,无需手动 SSH 进入服务器。本文介绍 CI/CD 概念,然后展示如何编写生产工作流以通过 SSH 部署到 Droplets 以及通过 doctl 部署到 App Platform,包括密钥管理和部署失败时的回滚规划。

什么是 CI/CD 以及为什么很重要

CI/CD 代表持续集成(Continuous Integration)和持续部署(Continuous Deployment),有些团队对后者使用"持续交付"(Continuous Delivery)这一术语。这是一种方法,其中代码更改通过预定义的步骤自动构建、测试和部署到生产环境,而不是要求开发者每次都手动 SSH 进入服务器、拉取代码并重启服务——这既耗时又容易出错。忘记某一步或在错误的环境中运行命令等错误是常见的人为失误。 GitHub Actions 是直接内置于 GitHub 的 CI/CD 系统,通过放置在存储库的 .github/workflows/ 文件夹中的 YAML 文件来运行,例如 .github/workflows/deploy.yml。当指定的事件发生时,该系统会自动运行工作流。例如 on: push: branches: [main] 意味着每次代码被推送或合并到 main 分支时,工作流都会被触发。GitHub Actions 的优势在于您不需要单独的 CI 服务器,公共存储库没有额外成本,私有存储库则根据您的 GitHub 计划包含免费的每月运行时间。 对于 DigitalOcean 用户,将 GitHub Actions 连接到基础设施有两种主要方式:通过 SSH 部署到由您自己管理的 Droplet(提供最大灵活性,因为您控制每一步),或部署到 App Platform,这是一个由 DigitalOcean 处理基础设施的 PaaS 服务。两种方法都可以使用相同的工作流——您只需交换最后的部署步骤。CI/CD 的真实价值不仅仅是速度,而是一致性:无论谁推送代码,每次部署都遵循相同的流程,减少了小团队中每个人都以不同方式部署而导致的问题,例如在重启服务之前忘记运行数据库迁移。

构建工作流以通过 SSH 部署到 Droplets

综合多次测试,通过 GitHub Actions 部署到 Droplet 使用与开发者手动 SSH 进入服务器相同的原理——工作流只是运行命令而已。最流行的方法使用现成的 appleboy/ssh-action action,它接受主机、用户名和私钥,然后在远程服务器上运行指定的命令。以下是一个示例工作流步骤: - uses: appleboy/ssh-action@v1 with: host: ${{ secrets.DROPLET_HOST }} username: deploy key: ${{ secrets.SSH_PRIVATE_KEY }} script: | cd /var/www/myapp git pull origin main docker compose up -d --build 通常,相同的文件在部署步骤之前还包含用于构建和测试的作业——使用 actions/checkout@v4 检出代码、安装依赖、运行测试——只有在所有测试通过后才进行到部署步骤。使用 needs: 使部署作业等待测试作业完成,防止有失败测试的代码到达生产环境。 对于运行容器化应用的 Droplets,考虑先构建映像并将其推送到 DigitalOcean Container Registry,然后让 Droplet 只拉取并运行映像,而不是在生产环境中构建。这样您的生产服务器就不需要安装完整的构建工具链,部署时间也更短,因为您只需交换映像而不是在实时机器上重新编译。 具有 1 GiB RAM / 1 vCPU 的入门 Droplet 在 $6/月处足以用于小到中型应用。对于泰国用户,建议使用 sgp1(新加坡)地区以获得与其他 DigitalOcean 地区相比最低的延迟。

构建工作流以部署到 App Platform

App Platform 已通过控制面板支持原生 GitHub 集成,只要您推送到配置的分支,就可以自动部署——无需工作流。但是,许多团队选择通过 GitHub Actions 控制部署,因为他们想在触发实际部署之前运行复杂的测试/lint/构建步骤,或者他们需要只部署 monorepo 中实际发生更改的特定文件夹。 主要工具是通过官方 GitHub Action digitalocean/action-doctl@v2 的 doctl,它在 runner 中安装 doctl 并使用存储在 secrets 中的个人访问令牌进行身份验证。以下是一个示例步骤: - uses: digitalocean/action-doctl@v2 with: token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }} - run: doctl apps create-deployment ${{ secrets.DO_APP_ID }} --wait 命令 doctl apps create-deployment <app-id> --wait 告诉 App Platform 从链接到该应用的分支启动新部署,--wait 标志会阻止工作流直到部署完成或失败,而不是立即返回,同时部署继续进行。如果您的工作流在部署后有验证步骤,这很重要。 对于新应用或主要配置更改(添加环境变量、更改实例大小),使用存储在您的存储库中的 YAML spec 文件,如 app.yaml,然后运行 doctl apps create --spec app.yaml 创建新应用或 doctl apps update <app-id> --spec app.yaml 更新现有应用。这种方法将您的整个应用结构保存为 git 中的代码,让您通过拉取请求审查基础设施更改,就像审查其他代码一样。 App Platform 按大小提供容器实例计划:从共享 1 vCPU / 512 MiB RAM / 50 GiB 传输在 $5/月,到共享 2 vCPU / 4 GiB RAM / 250 GiB 传输在 $50/月。免费计划仅支持静态网站(每个应用最多 3 个应用和 1 GiB 传输),非常适合在生产环境前测试工作流。

安全存储密钥(SSH 密钥/API 令牌)

SSH 私钥和 DigitalOcean 个人访问令牌等敏感数据必须永远不要直接写入工作流文件。GitHub 有一个内置的加密密钥系统,可在存储库设置 > Secrets 和变量 > Actions 中访问。存储的值已加密,不会在工作流日志中显示,即使对存储库所有者也是如此——您只能创建新值,无法检索旧值。 对于 SSH 密钥,创建一个专门用于 CI/CD 部署的独立密钥对,而不是您个人用于 SSH 进入服务器的密钥。使用 ssh-keygen -t ed25519 -C "github-actions-deploy" 生成它,然后将公钥添加到 Droplet 用户账户上的 ~/.ssh/authorized_keys(最好创建一个限制性的"部署"用户,只能访问应用文件夹,而不是 root)。将整个私钥文件(包括 -----BEGIN OPENSSH PRIVATE KEY----- 标头/页脚)复制到名为 SSH_PRIVATE_KEY 的密钥中。 对于 DigitalOcean 个人访问令牌,在 cloud.digitalocean.com/account/api/tokens 创建一个专门用于 CI/CD 的令牌,而不是您的个人令牌,这样如果需要,您可以立即撤销它而不会影响其他任务。将其存储在名为 DIGITALOCEAN_ACCESS_TOKEN 的密钥中,并仅授予必要的权限——例如,如果工作流只是触发现有应用部署,就没有必要授予创建/删除所有资源的权限。 对于具有多个环境(暂存和生产)的团队,使用 GitHub Environments 按环境分离密钥——相同的密钥名称在暂存和生产中可以具有不同的值。您还可以设置所需的审阅者,以便在生产部署作业运行前必须有人批准,在安全密钥存储之外增加额外的安全层。

要点总结: 始终使用 GitHub 加密密钥(设置 > Secrets 和变量 > Actions)——永远不要在 YAML 中直接写入密钥/令牌
  1. 始终使用 GitHub 加密密钥(设置 > Secrets 和变量 > Actions)——永远不要在 YAML 中直接写入密钥/令牌
  2. 使用 ssh-keygen -t ed25519 创建一个专门用于 CI/CD 的新 SSH 密钥对,与个人密钥分开,并使用限制性用户(不是 root)
  3. 在 cloud.digitalocean.com/account/api/tokens 创建一个专门用于 CI/CD 的独立 DigitalOcean 个人访问令牌,以便可以独立撤销
  4. 使用 GitHub Environments 在暂存和生产之间分离密钥,需要审阅者批准生产部署
  5. 遵循最小权限原则:仅向令牌/密钥授予它们实际需要的权限,并定期轮换它们

部署失败时的回滚

在工作流设计期间规划回滚策略,而不是在问题出现后。对于通过 SSH 部署到 Droplets,一个流行的模式将多个发布版本存储在单独的文件夹中,如 /var/www/myapp/releases/<timestamp>,符号链接名为 current 指向活跃发布。如果新部署失败或之后出现问题,只需一个命令即可立即将符号链接指向上一个发布——无需重新构建或重新拉取代码。 对于基于容器的 Droplets,回滚更简单:使用提交 SHA 而不仅仅是 latest 标记映像,如 myapp:${{ github.sha }}。回滚时,将 compose 文件中的映像标签更改为先前工作的提交的 SHA 并重启服务。 App Platform 通过 doctl apps list-deployments <app-id> 提供内置的部署历史记录,显示每个部署的 ID、状态和时间戳。如果最新部署失败或有问题,您可以立即从先前的提交重新部署。在某些情况下,如果新部署在构建或健康检查期间失败,App Platform 会自动回滚,保持旧版本运行,直到新版本成功。 从一开始就降低风险,在每个工作流中部署成功后添加一个烟雾测试步骤——curl 应用的健康检查端点并验证 HTTP 状态或响应内容。如果此步骤失败,立即将工作流标记为失败,以便团队在用户遇到问题之前收到警报。

  1. Droplet:使用 releases/<timestamp> 模式加上名为 current 的符号链接指向活跃发布,回滚时立即交换符号链接
  2. 容器:用提交 SHA 而不仅仅是 latest 标记映像,这样回滚是精确的——只需更改标签并重启
  3. App Platform:doctl apps list-deployments <app-id> 显示部署历史记录;从任何先前提交重新部署

何时值得使用此功能(真实用例)

投入时间编写 GitHub Actions 工作流对于频繁推送代码的团队或项目最有回报——每天或每周多次。每次节省手动 SSH 部署,价值就会累积。具有多个团队成员的项目立即受益,因为每个人都使用相同的部署流程,而不是各自采用各自的方式,消除了"在我的机器上运行"问题。 对于具有暂存环境(用于在生产前测试)之类的独立环境的项目,自动化工作流让您在每次合并到开发分支时立即部署到暂存环境,无需等待有人手动执行,然后在生产部署之前要求审阅者批准。这让团队可以快速测试暂存环境中的新功能,而不会影响真实用户。 相反,小型个人项目很少部署,或单个开发者项目中您已经习惯于手动 SSH 部署,可能不值得花费设置时间。对于这些项目,简单的 SSH 加部署脚本或 App Platform 的原生自动部署(无需工作流)可能会让您更快上线。 当您的管道需要超出基本构建的复杂步骤时,使用 GitHub Actions:部署前运行数据库迁移、部署前针对真实数据库的集成测试、Slack 通知告知部署成功或失败,或从 monorepo 中只构建有实际更改的服务的选择性部署。这些对于简单的原生自动部署是困难的或不可能的。

常见错误及其修复方法

用户经常问到的一点是:最常见的 SSH 部署错误是"Permission denied (publickey)",通常由以下三个原因之一引起:公钥未正确地添加到远程用户的 ~/.ssh/authorized_keys 中,密钥中的私钥有格式问题或缺少行(复制整个文件包括 -----BEGIN OPENSSH PRIVATE KEY----- 和 -----END OPENSSH PRIVATE KEY-----),或 .ssh 文件夹权限错误——出于安全考虑,SSH 可能会因权限过松而拒绝密钥。 工作流中 doctl 的另一个常见错误是 401 Unauthorized,尽管设置了密钥。通常是工作流文件中的密钥名称与您在存储库设置中创建的不匹配(检查大小写敏感性),或令牌已过期/被撤销。在工作流早期添加一个简单的验证步骤,如 doctl account get,以在实际部署步骤之前确认身份验证有效。 如果工作流报告成功但您的应用没有更改,您可能忘记了拉取新代码后的重启步骤——旧进程仍在内存中运行旧代码。添加重启步骤,如拉取代码后的 systemctl restart myappdocker compose up -d --build。另一个常见原因是工作流触发条件不正确,例如设置 branches: [master] 而存储库已切换为 main——工作流从不运行。 对于 App Platform,"unable to update app: spec is invalid"通常意味着 app.yaml 文件中有拼写错误或 App Platform 不支持的语法。运行 doctl apps spec validate app.yaml 以在工作流中创建或更新应用之前捕获语法错误。

  1. "Permission denied (publickey)":验证 authorized_keys 中的公钥、检查私钥格式在密钥中有完整标头/页脚、验证 .ssh 文件夹权限
  2. doctl 401 Unauthorized:检查密钥名称是否完全匹配(大小写敏感)并添加 doctl account get 测试步骤以在实际部署前验证身份验证
  3. 工作流成功但应用未更改:忘记拉取代码后的重启,如 systemctl restart myappdocker compose up -d --build
  4. 工作流根本不触发:检查 on.push 中的 branches: 是否与实际分支名称匹配(main vs master)

最佳实践

编写长期安全且可维护的工作流需要从一开始就培养几个良好习惯。明确固定您使用的 action 版本,如 actions/checkout@v4 而不是 @main 或浮动标签,以防止新 action 版本意外破坏曾经有效的工作流。安全意识的团队可能会固定到提交 SHA 而不是标签,因为标签稍后可以移动以指向不同的提交。 始终使用 needs: 将构建/测试作业与部署作业分开——部署应仅在测试完全通过后运行。在工作流级别添加 concurrency: 组以防止多个部署在有人快速连续推送两次时同时运行,如果两个部署竞争写入相同文件,可能会在生产中创建不可预测的状态。 使用 GitHub Environments 清晰地分离暂存和生产,即使在小团队中也始终要求生产部署审阅者——一个批准步骤是防止意外推送的好保障。保留部署日志,以便您可以审计历史记录:对 App Platform 使用 doctl apps list-deployments 或在 Droplet 上的文件中记录每个成功的 SSH 部署。 最后,在生产环境中使用工作流之前,先在暂存环境中测试整个工作流,包括测试回滚路径,而不仅仅是成功部署路径。从未在失败模式下测试过的工作流经常隐藏在您最需要可靠性时恰好浮现的错误。

领取 $200 免费额度 →

常见问题(FAQ)

我必须使用 GitHub Actions,还是可以与 DigitalOcean 一起使用其他 CI/CD 系统?
您不必使用 GitHub Actions。DigitalOcean 不受任何特定 CI/CD 系统的限制——您可以同样好地使用 GitLab CI、CircleCI 或其他。您只需要在该系统中将 SSH 密钥或个人访问令牌存储为密钥,并以相同的方式调用 doctl 或 SSH。
哪个更好:部署到 Droplets 还是 App Platform?
这取决于情况。Droplets 适合想要完全服务器控制、无限定制和高负载下较低成本的团队。App Platform 适合想要减少基础设施维护负担、无修补程序管理和内置自动扩展的团队——您用灵活性换取更少的操作开销。
如果工作流部署在运行中途卡住了怎么办?
您可以直接从存储库的 Actions 页面取消工作流运行。对于使用 --wait 的 App Platform,如果部署异常长时间挂起,请通过 doctl apps list-deployments 检查状态以查看是构建还是健康检查卡住。对于 Droplets,挂起的 SSH 通常意味着远程脚本在等待输入或进程不会退出——验证您的脚本始终完成而不需要交互式提示。
使用 GitHub Actions 部署到 DigitalOcean 需要向 GitHub 付费吗?
GitHub Actions 根据您的计划包含免费的每月运行时间(标准 runner 上的公共 repos 无限制)。成本来自您部署到的 DigitalOcean 资源——Droplets 和 App Platform 按其正常费率。连接这两个系统没有额外费用。
在使用 CI/CD 部署到生产环境之前,我必须有暂存环境吗?
不是必需的,但对于任何有真实用户的项目强烈建议。暂存在问题影响用户之前捕获问题。如果您没有单独的暂存资源,至少在每个生产部署后添加自动烟雾测试以快速捕获问题——比从用户那里发现要好。
在 GitHub Actions 中存储 SSH 私钥的安全性如何?
GitHub 的加密密钥提供 GitHub 为它们设计的安全性——值已加密且不会出现在日志中。总体安全性也取决于团队实践:使用仅用于 CI/CD 的密钥而不是个人密钥,将远程用户的权限限制为最小必要,并定期轮换密钥。