
最近折腾了 AWS CloudFormation(亚马逊云科技的基础设施即代码服务),踩了几个坑,这篇把核心概念、Stack 类型、生命周期状态和实际排错经验说清楚。相比手动在控制台点点点,用 JSON 或 YAML 写个模板,CloudFormation 帮你搞定剩下的事。
所有资源放在一个模板里,适合小项目。
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} 把大模板拆成模块化的子 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 分钟。嵌套要控制好深度。
用 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 理解这些状态能帮你判断当前发生了什么、为什么卡住了。
| 状态 | 含义 |
|---|---|
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 |
创建失败后的清理完成 |
部署前先验证:
aws cloudformation validate-template \ --template-body file://template.yaml \ --region us-west-2 查 AWS CloudFormation 文档确认属性名是否正确。拼错一个字母就直接 CREATE_FAILED 了。
取消更新或删除变更集:
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 如果 CI/CD 流水线(GitHub Actions、Azure DevOps)中途失败,在重试前加一步清理失败的变更集。不然下次运行会因为残留的变更集直接失败。
当一个资源需要另一个资源先存在时,用 DependsOn 显式声明:
Resources: MyLambda: Type: AWS::Lambda::Function DependsOn: MyDynamoDBStream Properties: ... CloudFormation 是 AWS 基础设施即代码的基础。单 Stack 求简单,嵌套 Stack 追求模块化,Export 实现跨 Stack 共享。搞清楚生命周期状态,部署前验证模板,从一开始就往流水线里加回滚机制。