用 API / Terraform 批量开 VPS 与自动化运维

手动在面板一台台点机器太低效。本文讲清为什么用 Vultr/CloudCone 等官方 API 或 Terraform Provider 做基础设施即代码(IaC),批量开机、销毁,并给出密钥保管与防失控账单的成本护栏。

延伸阅读

更多相关攻略推荐:从 Git Push 到秒级上线:CI/CD 流水线与"无中断"发布【VPS 进阶玩法精选 030】用 VPS 搭建云端 IDE(cod连云厂商也看不到你的数据?机密计算(Confidential Com被割裂的"云上互联网":数据主权(GDPR)、本地化存储与主权云(S什么时候非得要独立 IPv4?便宜国外 VPS 独立 IP 适用场景

一、手动点面板,点不动十台以上的机器

刚玩 VPS 时,在商家面板点几下开一台机器很爽。可一旦你要开 10 台做分布式爬虫、20 台做压测节点、或者按业务每天定时起一批临时机器,纯手工点面板就崩了:每台都得选机房、选系统、贴 SSH 公钥、等开通、再手动记 IP,错一台就得重来。更糟的是,机器多了之后「哪些还在跑、谁该关没关」全靠脑子记,月底账单直接吓一跳。这时候就得上基础设施即代码(IaC):把机器写成代码,一键批量开、一键批量销,状态可回放、可版本化。本文以 VultrCloudCone 为主,顺带提 DigitalOcean,讲清楚怎么用官方 API 和 Terraform 把开机器变成写配置。

二、为什么需要 IaC:可复现、可版本化、省人力

IaC 的核心思想就一句:把服务器当成代码来管理。好处是实打实的:

  • 可复现:同样的 .tf 文件,今天跑和三个月后跑,开出来的机器一模一样,不依赖某人「当时点了啥」的记忆。
  • 可版本化:配置进 Git,谁改了什么一清二楚,回滚就像 git revert。审计、协作都踏实。
  • 省人力terraform apply 一条命令顶你点一百次面板,半夜批量开测试机、跑完 terraform destroy 一键清空,零人工值守。
  • 防漂移:Terraform 记录状态,你手动在面板改了东西它下次会纠正,基础设施不会悄悄「长歪」。

对只开一台长期稳定机器的个人用户,面板足够;但凡涉及批量、临时、多环境,IaC 就是分水岭。

三、主流厂商 API 概览

三家都提供 REST API,鉴权基本是 Bearer Token:

  • Vultr:API v2,基址 https://api.vultr.com/v2,请求头带 Authorization: Bearer 你的KEY。功能最全,实例、裸机、对象存储、网络、K8s 都能管。官方还有 vultr-cli 命令行。
  • CloudCone:API 在 api.cloudcone.com,用 API Key + API Secret 组合鉴权(curl 里用 -u KEY:SECRET)。主打便宜大盘和按小时计费,适合批量临时机。
  • DigitalOcean:API v2,基址 https://api.digitalocean.com/v2,Bearer Token 鉴权,官方命令行 doctl。生态成熟,文档友好。

用 curl 列 Vultr 可用套餐的示例(把 $VULTR_API_KEY 换成你的密钥):

curl -H "Authorization: Bearer $VULTR_API_KEY" https://api.vultr.com/v2/plans

CloudCone 实例:

curl -u 你的APIKEY:你的APISECRET https://api.cloudcone.com/v1/instances

DigitalOcean 列 droplet:

curl -H "Authorization: Bearer $DO_TOKEN" https://api.digitalocean.com/v2/droplets

四、Terraform Provider 实战:几行配置开一台

Terraform 是 HashiCorp 的 IaC 工具,用 HCL 声明式语言描述资源。Vultr 有官方 provider。先写 provider 配置:

terraform {
  required_providers {
    vultr = {
      source  = "vultr/vultr"
      version = "~> 2.26"
    }
  }
}

provider "vultr" {
  api_key = var.VULTR_API_KEY
}

variable "VULTR_API_KEY" {}

密钥放 terraform.tfvars(别提交进 Git),里面写 VULTR_API_KEY = "你的真实密钥"。然后定义一个实例:

resource "vultr_instance" "web" {
  label       = "web-01"
  plan        = "vc2-1c-1gb"
  region      = "sgp"
  os_id       = 2284
  enable_ipv6 = true
}

output "ip" {
  value = vultr_instance.web.main_ip
}

初始化并应用:

terraform init
terraform plan
terraform apply

terraform plan 先预览会改什么,terraform apply 真正开机,结束会打印输出里的 IP。想销毁就 terraform destroy。os_id 2284 是 Ubuntu 24.04,region sgp 是新加坡,plan 代码可在 plans 接口里查。

五、批量开:用 count 一把开 N 台

要开 5 台做节点,不用复制 5 遍,用 count 元参数:

resource "vultr_instance" "nodes" {
  count  = 5
  label  = format("node-%d", count.index)
  plan   = "vc2-1c-1gb"
  region = "sgp"
  os_id  = 2284
}

这一个块会开出 node-0 到 node-4 共 5 台,terraform apply 一次搞定。需要不同机房混布,就把 region 抽成变量或用 for_each 配 map。CloudConeDigitalOcean 也有对应 provider(或直接在脚本里调 API),思路完全一致:声明想要什么,工具负责怎么实现

六、API 密钥的安全保管:别把钥匙交出去

API Key 等于你账户的「提款权」,泄露了别人能开机器刷爆你的卡。守则:

  • 永远别把密钥写进代码提交 Git:放 terraform.tfvars(加入 .gitignore)或用环境变量,CI 里用 Secrets 注入。
  • 最小权限Vultr 可建子 API Key 限定权限范围;CloudCone 的 Key/Secret 分开保管。只给「开机/销机」权限,不给「改账户/付款」权限。
  • 本地文件权限:tfvars 设 chmod 600,别放公开仓库或贴到论坛。
  • 定期轮换:人员变动或怀疑泄露,立刻在面板吊销旧 Key 换新。

七、批量开/销与成本护栏:别让账单失控

自动化的反面是「脚本一跑开了 200 台忘了关」。护栏必须提前埋:

  • 先用 plan 再 apply:apply 前看清楚会新建几台、花多少钱档位。
  • 设预算告警VultrDigitalOcean 后台都能设月预算阈值,超了发邮件,相当于闹钟。
  • 临时机加生命周期:测试机用 terraform destroy 或定时任务自动销,别靠记忆。
  • 小范围试点:新配置先 count=1 跑通,再放大,避免一把开错配置烧一天钱。
  • 用变量约束上限:把 count 抽成变量并写注释上限,CI 里加校验拒绝超大值。

八、和手工面板比,到底强在哪

对比一下就明白:

  • 一致性:面板点 10 台,第 3 台容易选错系统;代码 10 台一模一样。
  • 速度:面板逐台等开通;apply 并行拉起,喝口水就好。
  • 可追溯:面板操作不留痕;Git 提交即审计。
  • 销毁:面板得逐台找、逐台删;destroy 一键归零,账单立刻停。

代价是要学一点 HCL 和鉴权流程,但开过一次后,第二次到第 N 次都是复制粘贴改参数的事。

九、注意事项:state 文件与权限

  • state 文件是命根子terraform.tfstate 记录了「实际开了哪些资源」,删了它就认不出已有机器,可能重复创建或误删。强烈建议用远程后端(如 Vultr 对象存储 S3 后端、Terraform Cloud)存 state,别只留本地。
  • state 含敏感信息:state 里可能带 IP 甚至密钥痕迹,远程后端务必设私有访问。
  • 并发与锁:远程后端提供状态锁,防止两个人同时 apply 互相覆盖。
  • 权限最小化:运行 Terraform 的机器只装必要 provider,API Key 权限按上文收紧。
  • 变更要走 review:生产环境的 .tf 改动走 PR 审核,plan 输出贴到评审里,避免手滑。

RackNerd 这类主打便宜年付的厂商多数没有官方 Terraform provider,但一般提供 API 或允许用脚本调其下单接口;批量场景若依赖它,建议自己封装一层小脚本 + 配置文件,思路同样是「声明式 + 可复现」。

十、完整可抄的 Terraform 示例:开一批带 SSH 密钥的节点

前面都是片段,这里给一段能直接落地的完整配置:一批节点统一打同一把 SSH 公钥、共用一个资源标签,用 for_each 而非 count,这样每台机器的名字是显式键而非数字下标,删掉中间一台不会把其余机器的编号重排(这是 count 的经典坑)。

variable "VULTR_API_KEY" {}

variable "ssh_key" {
  default = "ssh-ed25519 AAAA...你的公钥..."
}

variable "nodes" {
  type    = map(string)
  default = {
    web-01 = "sgp"
    web-02 = "sgp"
    web-03 = "nrt"
  }
}

terraform {
  required_providers {
    vultr = {
      source  = "vultr/vultr"
      version = "~> 2.26"
    }
  }
}

provider "vultr" {
  api_key = var.VULTR_API_KEY
}

resource "vultr_ssh_key" "main" {
  name    = "ci-key"
  ssh_key = var.ssh_key
}

resource "vultr_instance" "node" {
  for_each    = var.nodes
  label       = each.key
  region      = each.value
  plan        = "vc2-1c-2gb"
  os_id       = 2284
  ssh_key_ids = [vultr_ssh_key.main.id]
}

output "ips" {
  value = { for k, v in vultr_instance.node : k => v.main_ip }
}

要点:for_each 的 map 里值是机房,删掉 web-02 只销毁那一台,其余两台的名字和 IP 都不变,不会像 count 那样把 node-2 重命名成 node-1;vultr_ssh_key 先声明公钥,再用 ssh_key_ids 让每台机器开机就能用你的公钥登录,省去手动贴;最后用 for 表达式把所有 IP 汇成一个 map 输出,方便灌进 Ansible 的 inventory。把变量抽到 terraform.tfvarsterraform initterraform apply 即可一次性跨机房起三台节点。

十一、常见 API 错误排查:401 / 403 / 429 怎么治

脚本调 API 十有八九会撞错,按状态码对症下药,能省下大量瞎猜的时间:

  • 401 Unauthorized:密钥没带或带错。检查请求头 Authorization: Bearer 你的KEY 是否拼对,CloudCone-u KEY:SECRET 而非 Bearer;确认密钥没被面板吊销、字符串前后没多打空格或换行。
  • 403 Forbidden:密钥有效但权限不够,比如用只给「只读」的子 Key 去开机。去面板把对应 Key 的权限加到「实例创建 / 销毁」,或换一个全权限 Key 试一次排除法。
  • 404 Not Found:接口路径或资源 ID 错。region、plan、os_id 不存在都会报这个;先用 plans 接口核对自己填的 region(如 sgp、nrt)和 plan 代码在目标机房真实有效,有时某套餐在特定机房下架。
  • 409 Conflict:资源状态冲突,常见于对同一台机器并发操作(开机瞬间又去删、或两台脚本抢同一 label)。加退避重试,或把脚本改成串行排队。
  • 429 Too Many Requests:触发厂商限流。Vultr 对创建类接口有速率上限,批量开几十台要自己限速,比如每开一台 sleep 2,或用队列错峰,别一个循环瞬间打爆。
  • 5xx(500/502/503):服务端临时抽风,不是你的问题。指数退避重试通常能过;若持续 5xx 再开工单,附上响应里的请求 ID 和 UTC 时间戳,厂商排查更快。

调试技巧:curl 加 -i 看完整响应头与正文,加 -s -w "%{http_code}" 单独打印状态码;Terraform 失败时在命令前加 TF_LOG=DEBUG terraform apply,它能把实际发出的 HTTP 请求和收到的响应体都打出来,往往一眼就能区分是鉴权问题还是参数问题。把这套排查流程写进你团队的运维手册,新人接手脚本时少踩一半坑。