DigitalOcean API v2 指南 — 开发者自动化
DigitalOcean API v2 实践开发者指南——身份验证、Droplets/DNS/防火墙的 curl 示例、速率限制和自动化脚本编写
目录
REST API v2 概览
DigitalOcean API v2 是一个标准的 REST API,通过 HTTP 使用 JSON 格式进行请求和响应通信。基础 URL 是 https://api.digitalocean.com/v2/,按功能分隔资源,如 /v2/droplets、/v2/domains、/v2/databases、/v2/kubernetes/clusters、/v2/load_balancers 和 /v2/firewalls。这种结构使得控制面板中几乎每个功能都可以通过代码调用,无需手动登录网页界面。身份验证使用包含在 HTTP 标头中的 Bearer 令牌 Authorization: Bearer $TOKEN,每个请求都需要包含(详见下一节中创建令牌的内容)。HTTP 方法遵循标准 REST 约定:GET 用于检索数据,POST 用于创建新资源,PUT/PATCH 用于更新,DELETE 用于删除。例如,检索账户中的所有 Droplets 可以使用 curl -X GET -H "Authorization: Bearer $TOKEN" "https://api.digitalocean.com/v2/droplets"。返回的响应是包含资源键(如 droplets)的 JSON,以及显示总数的对象链接和元数据。对于包含许多项的列表,响应包含 links.pages 对象,提供下一页和上一页的 URL,无需手动计算偏移。对于不想每次都手动编写 HTTP 请求的用户,DigitalOcean 提供了多种语言的官方客户端库:Go 的 godo 和 Python 的 pydo,它们将 API v2 封装成现成可用的函数。还有一个官方 CLI 工具叫 doctl,它是此 API 的命令行接口,无需编写 curl 命令——非常适合经常运行的任务或 shell 脚本。完整的 API 参考文档位于 developers.digitalocean.com,应始终与本文并行打开,因为每个资源都有自己的特定字段。例如,Droplets 需要指定 size、image 和 region,而负载均衡器需要 forwarding_rules,等等。首先理解这个基本结构将帮助你更快地阅读后续部分的代码示例,因为每个端点都遵循相同的模式:标头、JSON 正文和根据 REST 标准的 HTTP 方法。
- 基础 URL 是 https://api.digitalocean.com/v2/,涵盖账户中几乎所有功能
- 通过 Authorization 标头用 Bearer 令牌进行身份验证,每个请求都需要
- 响应始终是 JSON 格式,通过 links.pages 进行分页
- 官方客户端库(godo、pydo)和 CLI 工具 doctl 可作为直接编写 curl 的替代方案
- 完整文档位于 developers.digitalocean.com,应始终打开
创建 Personal Access Token
根据我们的实测——在调用 API 之前,你必须先创建一个 Personal Access Token。这是从位于 cloud.digitalocean.com/account/api/tokens 的控制面板生成的,点击生成新令牌按钮。为令牌指定一个有意义的名称,如指定使用它的脚本或服务器,然后选择范围:只读或完全访问(读写)。如果脚本只检索和显示数据,选择只读以最小化令牌被泄露时的风险。如果你需要创建或删除资源,也必须选择写。DigitalOcean 允许你在创建时为令牌设置过期日期——例如 30 天、90 天或无过期。对于长期自动化脚本中使用的令牌,建议设置过期日期以及日历提醒来在过期前创建新令牌,防止自动化系统因令牌过期而突然停止。单击生成时,系统仅显示一次令牌值。立即复制并保存它,关闭页面后无法再次查看——你必须创建新令牌。推荐的存储方式是将其存储在环境变量中,而不是直接硬编码到源文件中,例如 export DIGITALOCEAN_TOKEN="dop_v1_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",然后在脚本或 CI/CD 流水线中引用 $DIGITALOCEAN_TOKEN 代替。这种方法可以防止意外将令牌提交到 Git 仓库,这是令牌泄露的最常见原因。如果使用 Git,始终将包含令牌的文件添加到 .gitignore 中。如果发现令牌以任何方式被泄露,立即转到 API 令牌页面并单击撤销,因为未过期、未被撤销的令牌可以像账户密码一样用于调用具有完整权限的 API。通过向账户端点发出简单请求来测试令牌是否正常工作,例如 curl -X GET -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" "https://api.digitalocean.com/v2/account"。如果令牌有效,你会收到包含账户详情(如电子邮件和身份验证状态)的 JSON,但如果令牌不正确或已过期,你会收到 HTTP 状态 401 和错误消息。
- 在 cloud.digitalocean.com/account/api/tokens 创建令牌,根据任务选择只读或读写范围
- 为长期令牌设置过期日期,以防止脚本意外中断
- 令牌值仅显示一次——立即复制并保存
- 将令牌存储在环境变量中,不要硬编码或提交到 Git
curl 示例:创建和删除 Droplets
通过 API 创建 Droplet 需要向 /v2/droplets 发送 POST 请求,JSON 正文中至少指定 4 个值:name(droplet 名称)、region(如 sgp1 代表新加坡,距离泰国用户最近的区域)、size(大小 slug,如 s-1vcpu-1gb,对应具有 1 GiB RAM/1 vCPU 的基础 Droplet 计划,价格为 $6/月)和 image(如 ubuntu-24-04-x64)。完整的示例命令:curl -X POST -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" -H "Content-Type: application/json" -d '{"name":"web-01","region":"sgp1","size":"s-1vcpu-1gb","image":"ubuntu-24-04-x64"}' "https://api.digitalocean.com/v2/droplets"。成功时,你收到 HTTP 状态 202 Accepted 和包含新 Droplet 的 JSON,状态为 new,带有当前运行的操作 id。由于 Droplet 创建不是即时的,你必须轮询 /v2/actions/$ACTION_ID 端点直到状态字段变为 completed,然后调用 GET /v2/droplets/$DROPLET_ID 来检索实际分配的 IP 地址以供下一步使用。查看账户中的所有 Droplets 可以使用 GET /v2/droplets,如第一节所述。删除 Droplet 使用 DELETE,Droplet id 附加到 URL,例如 curl -X DELETE -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" "https://api.digitalocean.com/v2/droplets/12345678"。成功删除返回 HTTP 状态 204 无内容(无响应正文),意味着 Droplet 被永久销毁。如果你之前没有创建快照,磁盘上的所有数据立即丢失,所以在发出 DELETE 之前务必验证你有正确的 id,特别是在删除多个 Droplets 的自动化脚本中。你应该包含一个干运行步骤或记录要删除的 id 列表供检查,然后再执行实际删除。除了简单删除,还可以通过 POST /v2/droplets/$ID/actions 使用其他操作,如 power_off、reboot、resize 或 snapshot,所有这些都返回带有 id 的对象来追踪状态,方式与 Droplet 创建期间相同。
- 用 POST /v2/droplets 创建 Droplet,指定 name、region、size(如 s-1vcpu-1gb = $6/月)、image
- 创建是异步的——轮询 /v2/actions/$ID 直到状态变为 completed
- 用 GET /v2/droplets 查看所有 Droplets
通过 API 管理 DNS、快照和防火墙
除了 Droplets,API v2 还涵盖三个在自动化中常用的附加功能:DNS、快照和防火墙。在 DNS 方面,用 POST /v2/domains 向系统添加域名,指定域名和初始 A 记录 IP 地址,然后在 /v2/domains/$DOMAIN/records 管理其他记录。例如,添加 CNAME 记录:curl -X POST -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" -H "Content-Type: application/json" -d '{"type":"CNAME","name":"www","data":"@","ttl":3600}' "https://api.digitalocean.com/v2/domains/example.com/records"。这对于自动创建许多子域名特别有用,例如一个系统通过自动化为客户配置新 Droplets 然后立即分配子域名,无需手动逐个访问控制面板。对于快照,如前所述通过 POST /v2/droplets/$ID/actions 从 Droplet 操作创建它们,请求正文为 {"type":"snapshot","name":"backup-2026-07-17"},非常适合为自动每日备份设置 cron 作业。注意快照会产生存储成本,价格为 $0.06 每 GiB 每月。如果设置脚本每天创建快照而不删除旧快照,存储费用会持续累积。因此,你应该编写脚本以删除超过某个阈值的快照,如仅保留最后 7 天,使用 DELETE /v2/snapshots/$SNAPSHOT_ID 删除较旧的快照。对于防火墙(云防火墙,无额外成本),用 POST /v2/firewalls 创建,指定 inbound_rules、outbound_rules 和 droplet_ids 来将此防火墙附加到。仅允许端口 22 (SSH) 和 443 (HTTPS) 的规则示例:curl -X POST -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" -H "Content-Type: application/json" -d '{"name":"web-fw","inbound_rules":[{"protocol":"tcp","ports":"22","sources":{"addresses":["0.0.0.0/0"]}},{"protocol":"tcp","ports":"443","sources":{"addresses":["0.0.0.0/0"]}}],"droplet_ids":[12345678]}' "https://api.digitalocean.com/v2/firewalls"。通过 API 创建防火墙允许你将标准安全策略定义为代码,并将其附加到通过同一脚本配置的每个新 Droplet,降低手动创建新服务器时忘记防火墙配置的风险。
- 用 POST /v2/domains 添加域名,然后在 /v2/domains/$DOMAIN/records 管理记录
- 通过 Droplet 操作类型 snapshot 创建快照,成本为 $0.06/GiB/月;记得通过脚本删除旧的未使用快照
- 用 DELETE /v2/snapshots/$ID 删除旧快照以控制成本
- 用 POST /v2/firewalls 创建防火墙,指定入站/出站规则和 droplet_ids(无额外成本)
- 通过同一脚本将标准防火墙附加到每个新 Droplet,降低忘记配置的风险
速率限制和错误处理
API v2 实施速率限制以防止任何单个用户以足够频繁的请求频率影响共享基础设施。要检查你的剩余配额,不要猜测——每个 API 响应都包含报告当前状态的 HTTP 标头:RateLimit-Limit(此期间的最大配额)、RateLimit-Remaining(剩余请求)和 RateLimit-Reset(配额重置的 Unix 时间戳)。进行许多请求的脚本应该在每个响应上读取这些标头并在 RateLimit-Remaining 接近零时放慢速度,而不是进行快速请求直到被阻止。由于实际限制可能因令牌类型或端点而异,不要在代码中硬编码固定数字;相反每次都读取标头值。使用 curl 检查标头的示例:curl -I -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" "https://api.digitalocean.com/v2/droplets" 使用 -I 标志仅获取标头而不获取完整正文,适合在进行真实请求前轻松检查配额。当配额真正耗尽时,API 响应 HTTP 状态 429 请求过多,你的脚本应该捕获并在延迟后重试(不是立即),而不是继续尝试访问端点。一个流行的重试模式是指数退避:第一次等待 1 秒,然后 2 秒,然后 4 秒在每次失败重试后,直到配置的上限。其他常见错误包括 401 未授权(令牌错误或过期)、404 未找到(资源 id 不存在或拼写错误)、422 无法处理的实体(发送畸形的数据字段,如无效的大小 slug)和 500/503(可能重试的服务器端问题)。所有错误响应都包含一致格式的 JSON 正文:{"id": "not_found", "message": "The resource you were accessing could not be found."},你的脚本应该每次解析和记录 message 字段,而不仅仅是 HTTP 状态代码,因为消息通常会澄清到底是什么出错了——例如哪个字段发送不正确——使调试快得多。这对于在 cron 或 CI 上运行的无人值守脚本尤其重要,这些脚本没有人实时观察日志。
- 从 HTTP 标头 RateLimit-Limit/RateLimit-Remaining/RateLimit-Reset 检查每个响应上的配额,而不是硬编码固定数字
- 配额耗尽时,你收到 HTTP 429 请求过多;用指数退避重试
- 401 = 令牌错误/过期,404 = id 未找到,422 = 畸形数据字段
使用 API 进行自动化脚本
一旦你理解了主要端点和错误处理,下一步是将所有内容组合成一个可重用的脚本,在无人干预的情况下运行。最简单的例子是 bash 脚本,使用 curl 结合 jq(命令行 JSON 解析器)来创建 Droplet、等待它准备好并打印其 IP 地址:DROPLET_ID=$(curl -s -X POST -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" -H "Content-Type: application/json" -d '{"name":"web-01","region":"sgp1","size":"s-1vcpu-1gb","image":"ubuntu-24-04-x64"}' "https://api.digitalocean.com/v2/droplets" | jq -r '.droplet.id')。然后使用 while 循环通过 GET /v2/droplets/$DROPLET_ID 反复检查状态直到状态字段变为 active,然后从 .droplet.networks.v4[0].ip_address 用 jq 提取 IP 以在下一个自动化步骤中使用,例如自动添加指向该 IP 的 DNS 记录。对于像每日备份这样的计划任务,使用 cron 调用脚本来创建快照然后删除旧快照,如前所述,条目如 0 3 * * * /home/user/scripts/do-backup.sh 每天早上 3 点运行。如果你的团队已经使用 Python,用 requests 库代替 curl 编写相同的逻辑,或使用官方的 pydo 库,它将端点封装为现成函数,减少 URL 或字段名中的拼写错误机会。另一个替代方案是 doctl,DigitalOcean 的官方 CLI,它已经封装了 API v2;在 macOS 上通过 brew 或在 Linux 上通过 snap 安装它,使用相同的 Personal Access Token 用 doctl auth init 进行身份验证,然后命令如 doctl compute droplet create web-01 --region sgp1 --size s-1vcpu-1gb --image ubuntu-24-04-x64 做同样的事情作为 curl 示例但短得多,适合强调可读性的脚本。对于像 GitHub Actions 这样的 CI/CD 系统,将令牌存储为加密的仓库密钥,然后在你的工作流文件中通过 ${{ secrets.DIGITALOCEAN_TOKEN }} 引用它,以允许流水线调用 API 或 doctl 而不在代码或日志中暴露令牌,将配置和部署变成完全自动化的流水线步骤,无需任何人手动逐个运行命令。
- 将 curl + jq 组合成 bash 脚本来创建 Droplet、轮询状态直到 active、然后提取并使用 IP
- 设置 cron 作业在你需要的计划上调用快照/备份脚本
- 使用 Python requests 库或官方 pydo 库代替 curl 进行类似逻辑
- doctl(官方 CLI)已经封装了 API;命令比 curl 短,更适合可读性强的脚本
- 将令牌存储为 CI/CD 中的加密密钥(如 GitHub Actions)来完全自动化配置/部署
常见错误和修复
用户经常问到的一点是:当在生产脚本中使用 API v2 时,脚本会重复运行或与其他系统集成,某些错误会反复出现。最频繁的是处理 401 未授权。许多团队编写脚本,在 401 上立即崩溃,而不区分根本原因是令牌过期还是只是新机器上环境变量配置不当。更好的方法是记录完整的响应正文(包含解释原因的 message 字段),而不仅仅检查状态代码,这样你可以判断问题是令牌轮换还是仅仅是配置。另一个常见错误是处理 422 无法处理的实体,这源于发送畸形数据——例如大小 slug 中的拼写错误(如 s-1vcpu-1g 代替 s-1vcpu-1gb)或不存在的区域。许多脚本一遍又一遍地重试相同的失败请求在 422 上,这永远不会成功,因为问题在请求本身,而不是临时服务器问题。你应该清楚地分离可重试错误(429、500、503)与不可重试错误(400、401、404、422),让不可重试组立即失败并进行详细记录,而不是浪费时间重试。分页错误是另一个常见问题:获取大量 Droplets 或记录列表的脚本忘记在每页上检查 links.pages.next,所以它们在默认 per_page 限制处仅检索第一页,错误地认为它们拥有一切。通过循环 links.pages.next 直到没有 next 键来修复此问题,或从一开始在你知道会有许多结果的脚本中指定更高的 per_page,例如 curl -X GET -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" "https://api.digitalocean.com/v2/droplets?per_page=200"。最后,一个幂等性问题:像 POST /v2/droplets 这样的端点缺乏内置幂等性密钥。如果你的脚本由于网络超时或重试逻辑而再次进行相同的 POST,而不先检查,你可能会无意中创建重复的 Droplets。解决方法:在重试资源创建前,GET 并通过名称或其他标识符检查它是否已存在,或在应用程序级别添加幂等性逻辑。更危险的是 429 上的重试风暴:如果多个脚本同时都触到速率限制然后立即无退避或抖动地重试,它们会进一步加剧问题。始终在重试等待中包含随机延迟(抖动),而不是对所有重试使用相同的固定间隔,以将它们分散开而不是让它们聚在一起。
- 清楚地分离可重试错误(429/500/503)与不可重试错误(401/404/422);不要重试畸形请求
- 在 401 上记录完整响应正文(message 字段)而不是仅从状态代码猜测根本原因
- 获取大量列表时每页检查 links.pages.next,或从一开始增加 per_page;避免分页错误
- 重试创建资源前检查资源是否已存在;避免来自幂等性问题的重复 Droplets/记录
- 向重试间隔添加抖动(随机延迟),不要固定时序,以防止多个脚本同时触到 429 时的重试风暴
最佳实践
当在长期生产系统中运行 API v2 时,几个具体的实践可以减少问题和风险。从令牌范围开始:按工作职能创建单独的令牌,而不是在任何地方使用单个令牌。例如,备份脚本的令牌应该仅具有它需要的范围,如果该脚本从不接触 Droplets,就不应该有删除 Droplets 的权限。这样,如果任何单个令牌泄露,损害仅限于该令牌的范围,而不是整个账户。将此与定期令牌轮换结合——每 90 天创建一个新令牌并撤销旧令牌,而不是使用相同的令牌多年而不轮换。接下来是 webhook 验证:如果你的外部系统在事件发生时收到回调(如警报策略通知),接收 webhook 的端点必须在信任负载前验证请求来源——检查源 IP 或验证签名标头。不要在没有某种形式的身份验证的情况下信任 webhook 数据,因为无人守卫的 webhook 端点是通过请求欺骗的频繁攻击目标。在重试逻辑上,使用带抖动的指数退避作为所有脚本调用的共享函数,而不是在每个地方单独重写。例如,编写一次:retry_with_backoff() { local attempt=0; local max=5; until curl -sf -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" "$1"; do attempt=$((attempt+1)); [ $attempt -ge $max ] && return 1; sleep $((2**attempt)); done; } 所以所有脚本共享一致的重试行为而不重复逻辑,并且你可以在一个地方修复问题。最后,日志和可观测性:cron 或 CI 上的无人值守自动化脚本应该至少记录每个 API 调用的时间戳、调用的端点、收到的 HTTP 状态和涉及的资源 id(如 droplet id)。将日志保存在单独的文件中,而不仅仅是 stdout,因为当后来出现问题时——如 Droplet 意外消失或快照在计划上未创建——你可以向后追踪日志以查看脚本在该时间实际进行了什么 API 调用。对于具有许多自动化脚本的系统,考虑将日志发送到集中日志系统以便于跨脚本事件检查。
- 按工作职能分离令牌;将范围设置为每个脚本仅需要的内容;限制令牌泄露时的损害
- 定期轮换令牌(如每 90 天)而不是无限期使用相同令牌
- 在信任数据前验证 webhook/回调来源——检查 IP 或签名标头;不要接受未经身份验证的负载
- 使用指数退避 + 抖动作为所有脚本调用的单个共享函数,而不是每次单独重写