site logo

Marico's space

学习 AWS CloudFormation:实用入门

服务器技术 2026-07-31 11:30:53 9

最近折腾了 AWS CloudFormation(亚马逊云科技的基础设施即代码服务),踩了几个坑,这篇把核心概念、Stack 类型、生命周期状态和实际排错经验说清楚。相比手动在控制台点点点,用 JSON 或 YAML 写个模板,CloudFormation 帮你搞定剩下的事。

为什么要用 CloudFormation?

  • 自动化 — 定义一次,部署到任何地方,不用手动操作
  • 一致性 — 同样的模板 = 同样的环境,每次都一样
  • 版本控制 — 模板是代码,可以存到 Git 里
  • 成本控制 — 一条命令删除整个 Stack,不留孤儿资源
  • 可扩展性 — 更新模板就能扩展基础设施

Stack 的几种类型

1. 单体 Stack

所有资源放在一个模板里,适合小项目。

AWSTemplateFormatVersion: '2010-09-09'
Resources: BlogSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Security group for blog app SecurityGroupIngress: - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 0.0.0.0/0 BlogInstanceRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: ec2.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess BlogEC2Instance: Type: AWS::EC2::Instance Properties: InstanceType: t2.micro ImageId: ami-0c55b159cbfafe1f0 SecurityGroupIds: - !Ref BlogSecurityGroup IamInstanceProfile: !Ref BlogInstanceProfile BlogStaticBucket: Type: AWS::S3::Bucket Properties: BucketName: !Sub blog-static-files-${AWS::StackName}

2. 嵌套 Stack

把大模板拆成模块化的子 Stack,父 Stack 引用存放在 S3 的子模板。适合把网络、计算、存储分离管理。

父模板:

AWSTemplateFormatVersion: '2010-09-09'
Parameters: VpcCidr: Type: String Default: 10.1.0.0/16 Resources: NetworkStack: Type: AWS::CloudFormation::Stack Properties: TemplateURL: https://s3.amazonaws.com/my-bucket/ecommerce-network.yaml Parameters: VpcCidr: !Ref VpcCidr AppServerStack: Type: AWS::CloudFormation::Stack Properties: TemplateURL: https://s3.amazonaws.com/my-bucket/ecommerce-app.yaml Parameters: SubnetId: !GetAtt NetworkStack.Outputs.PublicSubnetId SecurityGroupId: !GetAtt NetworkStack.Outputs.AppSecurityGroupId DatabaseStack: Type: AWS::CloudFormation::Stack Properties: TemplateURL: https://s3.amazonaws.com/my-bucket/ecommerce-database.yaml Parameters: SubnetId: !GetAtt NetworkStack.Outputs.PrivateSubnetId SecurityGroupId: !GetAtt NetworkStack.Outputs.DBSecurityGroupId

子模板 - 网络层(ecommerce-network.yaml):

AWSTemplateFormatVersion: '2010-09-09'
Parameters: VpcCidr: Type: String Resources: EcommerceVPC: Type: AWS::EC2::VPC Properties: CidrBlock: !Ref VpcCidr PublicSubnet: Type: AWS::EC2::Subnet Properties: VpcId: !Ref EcommerceVPC CidrBlock: 10.1.1.0/24 AppSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Security group for app servers VpcId: !Ref EcommerceVPC SecurityGroupIngress: - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 0.0.0.0/0 DBSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Security group for database VpcId: !Ref EcommerceVPC SecurityGroupIngress: - IpProtocol: tcp FromPort: 3306 ToPort: 3306 SourceSecurityGroupId: !Ref AppSecurityGroup Outputs: PublicSubnetId: Value: !Ref PublicSubnet PrivateSubnetId: Value: !Ref PrivateSubnet AppSecurityGroupId: Value: !Ref AppSecurityGroup DBSecurityGroupId: Value: !Ref DBSecurityGroup

教训: 一个父 Stack 带 8 个子 Stack,部署花了 25 分钟,而单体 Stack 只要 12 分钟。嵌套要控制好深度。

3. 通过 Export 跨 Stack 共享

Outputs + Export 在独立 Stack 之间共享值。

网络 Stack 导出子网 ID:

Outputs: SubnetIdExport: Value: !Ref AppSubnet Export: Name: AppSubnetExport

存储 Stack 导入它:

Resources: DBSubnetGroup: Type: AWS::RDS::DBSubnetGroup Properties: DBSubnetGroupDescription: Subnet group for RDS SubnetIds: - !ImportValue AppSubnetExport

Stack 生命周期状态

理解这些状态能帮你判断当前发生了什么、为什么卡住了。

状态 含义
CREATE_IN_PROGRESS 资源正在创建中
CREATE_COMPLETE Stack 创建成功
CREATE_FAILED 创建过程中出错了
UPDATE_IN_PROGRESS 正在应用变更
UPDATE_COMPLETE 更新成功
UPDATE_ROLLBACK_IN_PROGRESS 更新失败,正在回滚
UPDATE_ROLLBACK_COMPLETE 回滚完成,回到之前的状态
DELETE_IN_PROGRESS 正在删除 Stack
DELETE_COMPLETE 所有资源已删除
REVIEW_IN_PROGRESS 变更集正在验证
ROLLBACK_IN_PROGRESS 初始创建失败,正在清理
ROLLBACK_COMPLETE 创建失败后的清理完成

常见问题排错

1. 语法错误

部署前先验证:

aws cloudformation validate-template \ --template-body file://template.yaml \ --region us-west-2

2. 资源配置错误

查 AWS CloudFormation 文档确认属性名是否正确。拼错一个字母就直接 CREATE_FAILED 了。

3. 卡在 REVIEW_IN_PROGRESS

取消更新或删除变更集:

aws cloudformation cancel-update-stack \ --stack-name AppBase-US-West-2 \ --region us-west-2 aws cloudformation delete-change-set \ --stack-name AppBase-US-West-2 \ --change-set-name FailedChange \ --region us-west-2

万不得已直接删 Stack:

aws cloudformation delete-stack \ --stack-name AppBase-US-West-2 \ --region us-west-2

4. 流水线部署失败

如果 CI/CD 流水线(GitHub Actions、Azure DevOps)中途失败,在重试前加一步清理失败的变更集。不然下次运行会因为残留的变更集直接失败。

5. 依赖问题

当一个资源需要另一个资源先存在时,用 DependsOn 显式声明:

Resources: MyLambda: Type: AWS::Lambda::Function DependsOn: MyDynamoDBStream Properties: ...

上手建议

  1. 从小开始 — 先部署一个 EC2 实例,跑通了再加复杂的东西
  2. 用参数 — 从第一天就让模板支持多环境复用
  3. 在临时环境测试 — 永远不要直接在生产环境测试变更
  4. 盯紧 Events 标签页 — 这是控制台里最实用的排错界面
  5. 考虑回滚 — 流水线里一定要有清理失败的步骤

总结

CloudFormation 是 AWS 基础设施即代码的基础。单 Stack 求简单,嵌套 Stack 追求模块化,Export 实现跨 Stack 共享。搞清楚生命周期状态,部署前验证模板,从一开始就往流水线里加回滚机制。