Language:Chinese VersionEnglish Version

面向独立开发者的基础设施即代码:Terraform、Pulumi,以及何时 Shell 脚本足够

你有一个 VPS、一个域名,以及一个刚开始获得真实流量的副项目。凌晨 2 点出问题了,你通过 SSH 登录,手动修复,然后意识到你不知道上次改了什么。三个月后,你需要搭建一个暂存环境,却发现你的生产服务器是一个”雪花服务器”——一个独特的人工制品,由数十个未记录的调整组成,这些调整只存在于那台机器上,别处无处可寻。

这是独立开发者第一次遇到基础设施即代码的时刻:不是从平台工程团队借鉴的理论最佳实践,而是真正的个人需求。问题不在于是否采用 IaC,而在于你实际需要多少。

当你就是整个团队时,为什么 IaC 很重要

基础设施即代码的标准论点围绕协作展开——版本控制、代码审查、团队间的共同理解。对于独立开发者,去掉所有这些,论点仍然成立,但出于完全不同的原因:你的记忆并不可靠

你基础设施的声明性描述作为活文档存在。它记录的不仅是存在什么,还有你的意图。当你六个月后回到一个项目,一个 Terraform 文件会准确告诉你配置了哪些 DNS 记录、实施了哪些防火墙规则以及哪些云资源正在运行。你的 bash 历史记录不会。

还有灾难恢复的角度。独立开发者很少会考虑这个问题,直到他们需要时。如果你的 VPS 提供商出现故障,或者你不小心销毁了一个云服务器,重建一切需要多长时间?有了合适的 IaC,答案是几分钟。没有的话,答案就是”我花多长时间才能记得我做了什么”。

但好处伴随着真正的成本:学习曲线、额外的工具、状态管理开销,以及过度工程的诱惑。诀窍是在你的实际情况下,找到合适抽象级别的合适工具。

Terraform:行业标准

Terraform 仍然是最广泛采用的 IaC 工具,理由充分。它使用 HashiCorp 配置语言 (HCL) 的声明性模型与大多数人思考基础设施的方式清晰对应:描述你想要的,然后让工具弄清楚如何实现。

一个面向独立开发者的最小化 Terraform 配置可能如下所示:

resource "digitalocean_droplet" "web" {
  image  = "ubuntu-24-04-x64"
  name   = "web-production"
  region = "nyc3"
  size   = "s-1vcpu-1gb"

  ssh_keys = [digitalocean_ssh_key.main.fingerprint]
}

resource "cloudflare_record" "web" {
  zone_id = var.cloudflare_zone_id
  name    = "app"
  content = digitalocean_droplet.web.ipv4_address
  type    = "A"
  proxied = true
}

即使你从未接触过 Terraform,这也是可读的。一个 droplet 被创建,一个 DNS 记录指向它。运行 terraform apply,两个资源就存在了。运行 terraform destroy,它们就消失了。

独立使用的优势: HCL 简单到可以在一个周末学会。提供者生态系统非常庞大 — 几乎每个云服务都有 Terraform 提供者。plan/apply 工作流程让你在变更发生前可以预览,当你是唯一能发现错误的人时,这非常宝贵。

摩擦点: HCL 不是一种编程语言。一旦你需要条件逻辑、对动态数据的循环或超出基本插值的字符串操作,你就开始与语法作斗争。Terraform 的类型系统很简单,复杂的配置常常感觉像是在绕着语言工作,而不是与语言协作。

Pulumi:编写真实代码

Pulumi 采取了根本不同的方法:不是使用领域特定语言,而是用 TypeScript、Python、Go 或 C# 编写基础设施定义。对于已经习惯用代码思考的开发者来说,这感觉自然得多。

import * as digitalocean from "@pulumi/digitalocean";
import * as cloudflare from "@pulumi/cloudflare";

const droplet = new digitalocean.Droplet("web", {
    image: "ubuntu-24-04-x64",
    name: "web-production",
    region: "nyc3",
    size: "s-1vcpu-1gb",
});

new cloudflare.Record("web", {
    zoneId: config.require("cloudflareZoneId"),
    name: "app",
    content: droplet.ipv4Address,
    type: "A",
    proxied: true,
});

结果与 Terraform 示例功能相同。但因为这是 TypeScript,你获得完整的 IDE 支持、类型检查、编写函数和抽象的能力,以及对整个 npm 生态系统的访问。需要在部署期间从 API 获取某些内容?导入 axios。需要复杂的字符串格式化?只需编写 JavaScript。

对于独立开发者,Pulumi 的优势是认知整合。如果你已经为应用程序编写 TypeScript,用同一种语言编写基础设施意味着减少一次上下文切换。你可以在应用程序和基础设施代码之间共享类型。你可以使用已经熟悉的相同测试框架为基础设施编写单元测试。

缺点是复杂性。Pulumi 需要运行时,有自己的状态管理服务(尽管你可以自托管状态后端),其文档虽然在改进,但不如 Terraform 成熟。当出现问题时,调试涉及你的代码和 Pulumi 的内部引擎,这可能不够透明。

CDKTF:折中方案

Terraform 云开发工具包 (CDKTF) 试图将 Terraform 的提供者生态系统与通用编程语言相结合。你编写 TypeScript(或 Python、Java、C#、Go),然后 CDKTF 将其合成为 Terraform JSON,再通过标准 Terraform 引擎应用。

这在理论上很有吸引力:你可以获得 Terraform 经实战检验的提供者和状态管理,同时拥有真实编程语言的表现力。在实践中,CDKTF 添加了一个编译层,可能会在你编写的内容和 Terraform 执行的内容之间引入细微的不匹配。错误消息有时会引用生成的 JSON 而不是你的源代码,使调试变得令人沮丧。

对于独立开发者,如果你已经投入 Terraform 生态系统但觉得 HCL 限制太多,CDKTF 值得考虑。否则,对于小规模基础设施,额外的抽象层很少能证明其合理性。

工具比较

标准 Terraform (HCL) Pulumi CDKTF Shell 脚本 Ansible
语言 HCL (DSL) TS, Python, Go, C# TS, Python, Go, C#, Java Bash / Zsh YAML + Jinja2
学习曲线 低-中 中-高
状态管理 内置(本地/远程) Pulumi Cloud 或自托管 Terraform 后端 无(无代理)
提供者生态系统 优秀 良好(增长中) 优秀(使用 TF 提供者) 不适用(原始 API 调用) 良好(模块)
幂等性 需要手动处理
独立开发者最佳用途… 管理 3+ 个云资源 你希望使用一种语言处理所有事情 HCL 感觉过于限制 1-2 个简单资源 资源配置后的服务器配置
成本(独立使用) 免费 提供免费层级 免费 免费 免费
偏差检测 是(terraform plan 是(pulumi preview 是(通过 Terraform) 部分

何时 Shell 脚本真正足够

这里有一个观点可能会引起基础设施即代码(IaC)纯粹主义者的反对:有时候,shell脚本是正确的解决方案

如果你的整个基础设施只是一台你一次性配置并偶尔维护的VPS,那么使用Terraform或Pulumi的开销可能会超过其带来的好处。一个编写良好的shell脚本,通过调用云服务提供商的CLI,可以用50行代码配置服务器、设置DNS和配置防火墙。它不是幂等的,不跟踪状态,也不会处理漂移检测——但如果你每年重建服务器时只运行一次,这些缺点就无关紧要了。

当出现以下任何一种情况时,shell脚本就变得不够用了:

  • 你需要跨多个服务提供商管理资源(VPS + DNS + CDN + 对象存储)
  • 你需要定期重现你的基础设施(暂存环境、客户项目)
  • 你的基础设施有必须按顺序创建的依赖关系
  • 你希望在应用更改前预览变更
  • 你曾因手动更改导致服务中断而吃过苦头

对于大多数独立开发者来说,实际的门槛是:一旦你管理的相互关联的云资源超过三个左右,投资一个合适的IaC工具就开始回本了。

没有团队的状态管理

对于刚接触IaC的开发者来说,状态是最容易被误解的概念。Terraform的状态文件是一个JSON文档,将你的配置映射到实际资源。如果丢失它,Terraform就不再知道它管理的是什么。如果损坏它,你将面临痛苦的恢复过程。

对于独立开发者,状态管理的选择与团队有所不同:

本地状态配合版本控制:最简单的方法。将你的terraform.tfstate文件保存在私有Git仓库中。从技术上讲这不推荐,因为状态文件可能包含敏感信息,但对于使用私有仓库且状态中没有敏感凭证的独立开发者来说,这是可行的。如果你处理任何敏感信息,可以使用git-cryptsops对文件进行加密。

使用S3或等效服务的远程状态:将你的状态存储在启用了版本控制的对象存储桶中。这种方法更健壮——你可以通过桶版本控制获得自动备份,并且如果本地机器损坏,也能消除丢失状态的风险。对于个人使用,Backblaze B2或Cloudflare R2桶几乎不花钱,同时能提供适当的状态锁定功能。

Pulumi Cloud免费套餐:如果你使用Pulumi,他们的托管服务会为你管理状态。免费套餐可以轻松满足独立开发者的需求。这完全消除了你对状态管理的担忧,代价是依赖第三方服务。

无论你选择哪种方法,规则都很简单:状态必须备份,并且绝不能在公共仓库中分享

Ansible 用于配置 vs. Terraform 用于预配置

一个常见的混淆来源是预配置和配置之间的界限。Terraform 创建基础设施:服务器、网络、DNS 记录、负载均衡器。Ansible 配置在该基础设施上运行的内容:安装软件包、部署应用程序、管理 systemd 服务、编辑配置文件。

这些工具是互补的,而非竞争关系。一个实用的独立开发者工作流程如下所示:

  • Terraform 预配置一台 VPS,设置 DNS 记录,在提供商级别配置防火墙规则,并创建您需要的任何对象存储桶或托管数据库。
  • Ansible 通过 SSH 连接到预配置的服务器,安装 Docker,配置 Nginx 或 Caddy,设置 SSL 证书,部署您的应用程序,并管理持续的配置更改。

您可以使用 Terraform 的 remote-exec 预配置器在服务器创建后运行命令,但这是有意限制的。HashiCorp 明确建议,对于基本引导之外的所有操作,应使用专用的配置管理工具。

对于认为这两种工具都过度的独立开发者,Ansible 单独可以处理相当多的任务。它可以通过自己的模块生态系统预配置云资源(尽管不如 Terraform 优雅),并且其无代理、基于 SSH 的方法意味着您的服务器上无需安装任何东西。如果您只打算学习一个工具,Ansible 更广泛的应用性使其成为一个强有力的选择。

过度工程的代价

基础设施即代码生态系统有一种向复杂性的引力。阅读足够的博客文章,您会说服自己,您的双服务器项目需要 Terraform 工作区、带有 DynamoDB 锁定的远程状态、基础设施变更的 CI/CD 管道、自动漂移检测,以及为您自研 API 定制的提供商。

这是过度工程,它给独立开发者带来了真实的成本:

  • 花在工具而非产品上的时间。 每小时配置您的 IaC 管道的时间,都是没有花在用户真正关心的应用程序上的时间。
  • 维护负担。 Terraform 提供商会更新,API 会变更,您精心制作的配置会出现弃用警告,每次运行计划时都会提醒您。
  • 抽象债务。 过度抽象的基础设施代码,在您离开数月后回来时,会变得更难理解。一个扁平、重复的 Terraform 文件比深度嵌套的模块层次结构更容易理解。

解药是实用主义。从最小可行的 IaC 开始,只有在实际问题迫使您时才扩展。

实用的入门模式:VPS + DNS + SSL

对于准备采用基础设施即代码的独立开发者,这里有一个具体的起点,涵盖了最常见的设置:

项目结构:

infra/
  main.tf          # VPS + DNS 资源
  variables.tf     # 提供者令牌、域名、区域
  outputs.tf       # IP 地址、主机名
  terraform.tfvars # 实际值(已 git 忽略)
  ansible/
    playbook.yml   # 服务器配置
    inventory.ini  # 从 Terraform 输出生成

Terraform 管理的内容:

  • 一个带有您首选操作系统镜像的 VPS 实例
  • 将您的域名指向 VPS 的 DNS A 记录
  • 电子邮件的 DNS 记录(MX、SPF、DKIM,如适用)
  • 仅允许端口 22、80、443 的云防火墙
  • 向云提供商注册 SSH 密钥

Ansible 管理的内容:

  • 系统更新和无人值守升级
  • 通过 Let’s Encrypt 自动提供 SSL 的 Caddy 或 Nginx
  • Docker 和 Docker Compose 安装
  • 通过 Docker Compose 进行应用程序部署
  • 基本加固:fail2ban、仅 SSH 密钥认证、防火墙规则

这种模式可以处理 80% 的独立开发者基础设施需求。它在五分钟内完成配置,可重现,并且总配置足够小,可以记在脑子里。只有当您的项目需要时,才从这里扩展。

做出决定

独立开发者关于基础设施即代码的问题,最终归结为一种针对您个人情况的成本效益分析。这是一个决策框架:

使用 shell 脚本,如果您有一台很少重建的服务器,并且您能够接受未记录的手动更改所带来的风险。

使用 Terraform,如果您管理多个云资源,想要变更预览,并且倾向于使用文档完善的专注工具。

使用 Pulumi,如果您希望基础设施与应用程序使用同一种语言定义,并且能够接受较小的社区规模。

单独使用 Ansible,如果您的基础设施简单但服务器配置复杂,或者您想要一个同时用于配置和部署的工具。

一起使用 Terraform + Ansible,如果您希望在配置和部署之间有清晰的分离,并计划管理可能增长的基础设施。

最糟糕的选择是根本不做选择——继续 SSH 到服务器并手动更改,同时告诉自己稍后会记录。您不会稍后记录。选择覆盖您当前需求的最简单工具,将您的配置提交到私有仓库,然后继续构建真正重要的事情。


Michael Sun 为独立开发者构建基础设施并进行写作。他当前的技术栈使用 Terraform 进行资源预配置,Ansible 进行配置管理,偶尔使用 shell 脚本处理中间的所有任务。

By Michael Sun

Founder and Editor-in-Chief of NovVista. Software engineer with hands-on experience in cloud infrastructure, full-stack development, and DevOps. Writes about AI tools, developer workflows, server architecture, and the practical side of technology. Based in China.

Leave a Reply

Your email address will not be published. Required fields are marked *

You missed