AWS CloudFormation:基础设施即代码模板
AWS CloudFormation:基础设施即代码模板
什么是基础设施即代码(IaC)
基础设施即代码是一种通过可读的配置文件来管理和配置整个数据中心的方式,而不是手动编辑服务器配置或使用交互式工具。AWS CloudFormation 正是 Amazon Web Services 提供的原生 IaC 服务,它让你能够通过编写模板来描述和预置 AWS 资源。
传统的手动部署不仅耗时,还极易出错,且难以复现。CloudFormation 把所有资源定义为代码,带来了以下几点关键优势:
- 可重复性:一键创建完全一致的开发、测试、生产环境。
- 版本控制:模板文件可以像应用代码一样存入 Git,实现变更跟踪与回滚。
- 依赖管理:自动分析资源间的依赖关系,按照正确的顺序创建或删除资源。
- 声明式语法:你只需声明“想要什么”,CloudFormation 负责推导“如何实现”。
CloudFormation 核心概念
在动手写模板之前,必须理解几个核心概念。
栈(Stack)
栈是 CloudFormation 管理资源的基本单元。一个栈对应一组由同一个模板创建出来的 AWS 资源的集合。创建、更新和删除都是以栈为单位进行。如果删除栈,其内部的所有资源都会被自动清理,避免遗留僵尸资源。
模板(Template)
模板是一个 JSON 或 YAML 格式的文本文件,它描述了栈中包含哪些资源以及这些资源的配置属性。模板是你与 CloudFormation 交互的核心组件。
变更集(Change Set)
在更新现有栈之前,你可以生成一个变更集,预览将要发生哪些修改,评估影响后再决定是否执行。这极大降低了直接更新带来的风险。
深入模板结构
一个 CloudFormation 模板由多个顶级字段组成,以下以 YAML 格式为例(推荐使用 YAML,因为它支持注释,且比 JSON 更简洁)。
一个最小的模板只需要 Resources 字段,但为了工程化实践,通常会包含其他关键部分。
AWSTemplateFormatVersion: "2010-09-09"
Description: "描述此模板"
Metadata:
# 元信息,如界面布局提示
Parameters:
# 输入参数,创建栈时可动态传入
Mappings:
# 条件映射表,常用于区域/环境相关值
Conditions:
# 条件判断,控制是否创建某些资源
Resources:
# 核心部分:你要定义的AWS资源
Outputs:
# 输出值,供其他栈或外部引用
1. Resources:模板的心脏
Resources 是唯一必须的段落。每个资源由逻辑 ID 和资源类型组成。
Resources:
MyEC2Instance: # 逻辑ID,模板内唯一标识
Type: AWS::EC2::Instance # 资源类型
Properties: # 具体配置
ImageId: ami-0abcdef1234567890
InstanceType: t2.micro
SecurityGroupIds:
- !Ref MySecurityGroup # 引用同一模板中的其他资源
MySecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Enable SSH access via port 22
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0
逻辑 ID 可以自由命名,但它们只在模板内部有意义。CloudFormation 会自动为物理资源分配实际 ID(如实例 ID 或安全组 ID),你可以通过 !Ref 内部函数来引用。
2. Parameters:动态输入
参数允许在创建或更新栈时传入自定义值,让同一个模板可以适配不同环境。
Parameters:
InstanceTypeParameter:
Type: String
Default: t2.micro
AllowedValues:
- t2.micro
- t3.small
- t3.medium
Description: Enter t2.micro, t3.small, or t3.medium. Default is t2.micro.
KeyName:
Type: AWS::EC2::KeyPair::KeyName
Description: Name of an existing EC2 KeyPair to enable SSH access
在 Resources 中通过 !Ref InstanceTypeParameter 引用参数。这样,同一个模板可以轻松创造出不同规格的测试环境。
3. Outputs:暴露关键信息
输出用于返回资源创建后的有用信息,特别适合多栈协作。例如,某个栈创建了 VPC,之后通过输出导出子网 ID,供其他栈导入使用。
Outputs:
InstancePublicIP:
Description: Public IP of the EC2 instance
Value: !GetAtt MyEC2Instance.PublicIp
Export:
Name: !Sub "${AWS::StackName}-PublicIP" # 全局唯一导出名
!GetAtt 函数可以获取资源的属性值。前面带 Export 的输出可以被其他栈用 Fn::ImportValue 引用,实现跨栈协作。
4. Mappings 与 Conditions:灵活逻辑
Mappings 用于固定的一组键值映射,常见例子是根据区域选择不同的 AMI ID:
Mappings:
RegionMap:
us-east-1:
HVM64: ami-0abcdef123
ap-southeast-1:
HVM64: ami-0abcdef456
使用时:!FindInMap [RegionMap, !Ref "AWS::Region", HVM64]
Conditions 通过条件表达式决定是否创建某个资源。常用于根据环境参数(如是否为生产环境)来控制资源配置:
Conditions:
IsProduction: !Equals [!Ref EnvironmentType, production]
Resources:
MorePowerSGT:
Condition: IsProduction
Type: AWS::EC2::SecurityGroup
...
实战:编写一个可复用的 Web 应用模板
下面是一个完整的示例,创建一个单实例 Web 服务器,包含安全组、用户数据脚本,并输出访问地址。
AWSTemplateFormatVersion: "2010-09-09"
Description: "部署简单的 Web 应用"
Parameters:
InstanceType:
Type: String
Default: t2.micro
AllowedValues: [t2.micro, t3.micro, t3.small]
LatestAmiId:
Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2
Resources:
WebServerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow HTTP and SSH
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0 # 生产环境应限制IP段
WebServerInstance:
Type: AWS::EC2::Instance
Properties:
InstanceType: !Ref InstanceType
ImageId: !Ref LatestAmiId
SecurityGroupIds:
- !Ref WebServerSecurityGroup
UserData:
Fn::Base64: !Sub |
#!/bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
echo "<h1>Hello World from $(hostname -f)</h1>" > /var/www/html/index.html
Outputs:
WebsiteURL:
Description: URL of the website
Value: !Sub http://${WebServerInstance.PublicDnsName}
关键点解析:
- 使用
AWS::SSM::Parameter::Value类型参数自动获取最新的 Amazon Linux 2 AMI ID,无需硬编码。 UserData通过 Base64 编码传递脚本,在实例启动时自动安装并启动 Apache。- 输出域名的格式为
http://<公共 DNS>,方便你立即测试。
最佳实践
利用伪参数
CloudFormation 提供了一些预定义的伪参数,例如 AWS::Region、AWS::StackName、AWS::AccountId。在参数或字符串中可以直接引用,无需手动传入。
使用 IAM 角色授予权限
创建栈时,CloudFormation 默认使用当前用户/角色的权限。更好的做法是指定一个 IAM服务角色(Service Role),授予 CloudFormation 所需资源的创建权限,这样即使使用者权限较低,也能成功部署。
嵌套栈与模块化
对于大型项目,避免编写数千行的单一模板。可以将通用资源定义为“嵌套栈”(Nested Stack),主模板通过 AWS::CloudFormation::Stack 资源类型引用它们。这有利于复用和独立维护。
启用回滚与删除保护
- 在创建栈时启用“终止保护”(Termination Protection),防止意外删除正在使用的重要栈。
- 创建变更集而不是直接执行更新,确保对变化有完全认知。
开发与调试技巧
- 使用 AWS CloudFormation Linter (cfn-lint) 在本地检查模板语法与最佳实践。
- 利用 AWS Toolkit for VS Code 等 IDE 插件,可以实时获得代码提示和模板验证。
- 快速迭代:在
Resources节中暂时注释掉某些资源,重复使用同一个模板进行小范围测试。
自定义资源
当内置资源类型无法满足需求时(如想要模板中包含第三方服务或执行复杂操作),你可以编写 Lambda 函数,并通过 AWS::CloudFormation::CustomResource 在模板生命周期事件中调用该函数,实现高度定制化。
常用内部函数速查
!Ref:返回资源的物理 ID 或参数的输入值。!GetAtt resource.attribute:获取资源的特定属性。!Sub "string":用${变量}语法替换字符串中的变量值。!Join ["分隔符", [值列表]]:拼接字符串。!If [条件, 真值, 假值]:条件选择输出。!Select [索引, [列表值]]:根据索引从列表取值。
总结
AWS CloudFormation 将基础设施视为代码,让你可以像管理软件工程一样管理云资源。从简单的单实例 Web 应用到复杂的多层架构,模板都能提供一致、可预测的部署。掌握模板结构、内部函数和最佳实践是高效使用 CloudFormation 的关键。现在,你可以尝试将上面模板保存为 webapp.yaml,并在 AWS 管理控制台或 CLI 中创建栈,亲身体验基础设施即代码的威力。