更新 CloudFormation 堆栈 - Amazon CloudFormation
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

更新 CloudFormation 堆栈

注意

本教程基于 在 Amazon EC2 上部署应用程序 教程中的概念生成。如果您尚未完成该教程,我们建议您先完成该教程,以了解使用 CloudFormation 进行的 EC2 引导的过程。

本主题演示了正在运行的堆栈的简单更新进展。我们将带您逐步了解以下步骤:

  1. 创建起始堆栈:使用基础 Amazon Linux 2 AMI 创建堆栈,从而使用 CloudFormation 帮助程序脚本安装 Apache Web Server 和简单的 PHP 应用程序。

  2. 更新应用程序:使用 CloudFormation 更新应用程序中的一个文件并部署软件。

  3. 添加密钥对:向实例中添加 Amazon EC2 密钥对,然后更新安全组以允许 SSH 访问实例。

  4. 更新实例类型:更改底层 Amazon EC2 实例的实例类型。

  5. 更新 AMI:在您的堆栈中更改 Amazon EC2 实例的亚马逊机器映像(AMI)。

注意

CloudFormation 使用免费,但您需要为自己创建的 Amazon EC2 资源付费。如果您不熟悉 Amazon,可以利用免费套餐来最大限度地降低或避免在学习过程产生费用。

第 1 步:创建起始堆栈

我们将从创建可在本主题剩下的所有内容中使用的堆栈开始。我们已提供了一个简单的模板来启动在 Apache Web Server 中托管并在 Amazon Linux 2 AMI 上运行的简单实例 PHP Web 应用程序。

Apache Web Server、PHP 和简单的 PHP 应用程序全部都由默认安装在 Amazon Linux 2 AMI 上的 CloudFormation 帮助程序脚本进行安装。以下模板代码段显示描述待安装软件包和文件的元数据,此情况下为 Amazon Linux 2 AMI 的 Yum 存储库中的 Apache Web Server 和 PHP 基础设施。代码段还显示 Services 部分,以确保 Apache Web Server 处于运行状态。

WebServerInstance: Type: AWS::EC2::Instance Metadata: AWS::CloudFormation::Init: config: packages: yum: httpd: [] php: [] files: /var/www/html/index.php: content: | <?php echo '<h1>Hello World!</h1>'; ?> mode: '000644' owner: apache group: apache services: systemd: httpd: enabled: true ensureRunning: true

应用程序本身是“Hello World”示例,在模板中进行了完整定义。对于现实工作中的应用程序,文件可存储在 Amazon S3、GitHub 或另一个存储库中,并通过模板进行参考。CloudFormation 可下载软件包(如 RPM 或 RubyGem),并能引用单个的文件以及展开 .zip.tar 文件,以在 Amazon EC2 实例上创建应用程序项目。

模板用于启用和配置 cfn-hup 进程守护程序以侦听 Amazon EC2 实例元数据中所定义配置的更改。您可以使用 cfn-hup 进程守护程序更新应用程序软件,如 Apache 或 PHP 的版本,也可通过 CloudFormation 更新 PHP 应用程序文件。来自该模板中同一 Amazon EC2 资源的以下代码片段展示了配置 cfn-hup 所需的必要部分,即每两分钟调用 cfn-init 一次以通知并应用元数据的更新。否则,cfn-init 只能在启动时运行一次。

files: /etc/cfn/cfn-hup.conf: content: !Sub | [main] stack=${AWS::StackId} region=${AWS::Region} # The interval used to check for changes to the resource metadata in minutes. Default is 15 interval=2 mode: '000400' owner: root group: root /etc/cfn/hooks.d/cfn-auto-reloader.conf: content: !Sub | [cfn-auto-reloader-hook] triggers=post.update path=Resources.WebServerInstance.Metadata.AWS::CloudFormation::Init action=/opt/aws/bin/cfn-init -s ${AWS::StackId} -r WebServerInstance --region ${AWS::Region} runas=root services: systemd: cfn-hup: enabled: true ensureRunning: true files: - /etc/cfn/cfn-hup.conf - /etc/cfn/hooks.d/cfn-auto-reloader.conf

为了完成堆栈,在 Amazon EC2 实例定义中的 Properties 部分,UserData 属性包含调用 cfn-init 以安装软件包和文件的 cloud-init 脚本。有关更多信息,请参阅《Amazon CloudFormation 模板参考指南》中的 CloudFormation 帮助程序脚本参考。该模板还创建了一个 Amazon EC2 安全组。

AWSTemplateFormatVersion: 2010-09-09 Parameters: LatestAmiId: Description: The latest Amazon Linux 2 AMI from the Parameter Store Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id> Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2' InstanceType: Description: WebServer EC2 instance type Type: String Default: t3.micro AllowedValues: - t3.nano - t3.micro - t3.small - t3.medium - t3a.nano - t3a.micro - t3a.small - t3a.medium - m5.large - m5.xlarge - m5.2xlarge - m5a.large - m5a.xlarge - m5a.2xlarge - c5.large - c5.xlarge - c5.2xlarge - r5.large - r5.xlarge - r5.2xlarge - r5a.large - r5a.xlarge - r5a.2xlarge ConstraintDescription: must be a valid EC2 instance type. Resources: WebServerInstance: Type: AWS::EC2::Instance Properties: ImageId: !Ref LatestAmiId InstanceType: !Ref InstanceType SecurityGroupIds: - !Ref WebServerSecurityGroup UserData: Fn::Base64: !Sub | #!/bin/bash -xe # Get the latest CloudFormation package yum update -y aws-cfn-bootstrap # Run cfn-init /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource WebServerInstance --region ${AWS::Region} || error_exit 'Failed to run cfn-init' # Start up the cfn-hup daemon to listen for changes to the EC2 instance metadata /opt/aws/bin/cfn-hup || error_exit 'Failed to start cfn-hup' # Signal success or failure /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource WebServerInstance --region ${AWS::Region} Metadata: AWS::CloudFormation::Init: config: packages: yum: httpd: [] php: [] files: /var/www/html/index.php: content: | <?php echo "<h1>Hello World!</h1>"; ?> mode: '000644' owner: apache group: apache /etc/cfn/cfn-hup.conf: content: !Sub | [main] stack=${AWS::StackId} region=${AWS::Region} # The interval used to check for changes to the resource metadata in minutes. Default is 15 interval=2 mode: '000400' owner: root group: root /etc/cfn/hooks.d/cfn-auto-reloader.conf: content: !Sub | [cfn-auto-reloader-hook] triggers=post.update path=Resources.WebServerInstance.Metadata.AWS::CloudFormation::Init action=/opt/aws/bin/cfn-init -s ${AWS::StackId} -r WebServerInstance --region ${AWS::Region} runas=root services: systemd: httpd: enabled: true ensureRunning: true cfn-hup: enabled: true ensureRunning: true files: - /etc/cfn/cfn-hup.conf - /etc/cfn/hooks.d/cfn-auto-reloader.conf CreationPolicy: ResourceSignal: Timeout: PT5M WebServerSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Enable HTTP access via port 80 SecurityGroupIngress: - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 0.0.0.0/0 Outputs: WebsiteURL: Value: !Sub 'http://${WebServerInstance.PublicDnsName}' Description: URL of the web application
要使用此模板启动堆栈
  1. 复制该模板并将其作为文本文件本地保存到您的系统中。注意保存位置,因为您需要在后续步骤中使用此文件。

  2. 登录到 Amazon Web Services 管理控制台 并打开 Amazon CloudFormation 控制台 https://console.aws.amazon.com/cloudformation

  3. 依次选择创建堆栈、使用新资源(标准)

  4. 选择选择现有模板

  5. 指定模板下,选择上传模板文件,浏览到您在第一步中创建的文件,然后选择下一步

  6. 指定堆栈详细信息页面上,输入 UpdateTutorial 作为堆栈名称。

  7. 参数下,保持所有参数不变,然后选择两次下一步

  8. 审核并创建屏幕上,选择提交

当您的堆栈状态变成 CREATE_COMPLETE 后,输出选项卡会显示网站的 URL。如果您选择 WebsiteURL 的输出值,您将看到新 PHP 应用程序在工作。

第 2 步:更新应用程序

现在您已部署好堆栈,接下来更新应用程序吧。我们将对应用程序所打印出的文本进行简单更改。要执行此操作,我们将添加回显命令至 index.php 文件,如此模板代码段所示:

files: /var/www/html/index.php: content: | <?php echo "<h1>Hello World!</h1>"; echo "<p>This is an updated version of our application.</p>"; ?> mode: '000644' owner: apache group: apache

使用文本编辑器手动编辑您保存在本地的模板文件。

现在,更新堆栈。

要使用更新后的模板更新堆栈
  1. 在 CloudFormation 控制台中,选择您的 UpdateTutorial 堆栈。

  2. 选择更新、直接更新

  3. 选择替换现有模板

  4. 指定模板下,选择上传模板文件,上传修改的模板文件模板,然后选择下一步

  5. 指定堆栈详细信息页面上,保持所有参数相同,然后选择两次下一步

  6. 审核页面上,审核您的更改。在更改下,您应该会看到 CloudFormation 将更新 WebServerInstance 资源。

  7. 选择提交

当您的堆栈处于 UPDATE_COMPLETE 状态时,您可以再次选择 WebsiteURL 输出值以验证应用程序的更改已生效。默认状态下,cfn-hup 进程守护程序每 2 分钟运行一次,因此最多能花 2 分钟在堆栈更新后更改应用程序。

要查看已更新资源集,请转至 CloudFormation 控制台。在事件选项卡上,查看堆栈事件。在此特别示例中,Amazon EC2 实例 WebServerInstance 的元数据已更新,这导致 CloudFormation 也会重新评估其他资源(WebServerSecurityGroup)以确保没有其他更改。其他堆栈资源都未修改。CloudFormation 将只更新堆栈中受堆栈的任何更改影响的资源。此类更改可直接进行,如属性或元数据更改,也可由依赖项或 RefGetAtt 中的数据流或其他内部模板函数导致。有关更多信息,请参阅内置函数参考

这一简单更新对此过程进行了阐述。但是,您可以对您的 Amazon EC2 实例中所部署文件和软件包进行更复杂的更改。例如,您可能会确定需要将 MySQL 与 MySQL 的 PHP 支持一起添加到实例中。要执行此操作,只需要将附加软件包和文件与任何附加服务一起添加到配置中,然后更新堆栈以部署更改。

packages: yum: httpd: [] php: [] mysql: [] php-mysql: [] mysql-server: [] mysql-libs: [] ... services: systemd: httpd: enabled: true ensureRunning: true cfn-hup: enabled: true ensureRunning: true files: - /etc/cfn/cfn-hup.conf - /etc/cfn/hooks.d/cfn-auto-reloader.conf mysqld: enabled: true ensureRunning: true

您可以更新 CloudFormation 元数据,将应用程序所使用的软件包更新到新版本。前述示例中,每个软件包的版本属性都为空,这表示 cfn-init 应安装最新版的软件包。

packages: yum: httpd: [] php: []

您可以视需要指定软件包的版本字符串。如果您在后续更新堆栈调用中更改版本字符串,则会部署新版软件包。此处显示 RubyGems 软件包版本号的使用示例。支持版本化的任何软件包都可以有特定版本。

packages: rubygems: mysql: [] rubygems-update: - "1.6.2" rake: - "0.8.7" rails: - "2.3.11"

第 3 步:添加使用密钥对的 SSH 访问权限

您还可以更新模板中的资源,以添加原先未在模板中指定的属性。为了阐明上述操作,我们将会添加 Amazon EC2 密钥对到现有 EC2 实例中,然后在 Amazon EC2 安全组中打开端口 22,从而使您可以使用 Secure Shell(SSH)访问实例。

向现有 Amazon EC2 实例添加 SSH 访问权限
  1. 向模板额外添加两个参数,从而以现有 Amazon EC2 密钥对和 SSH 位置的名称进行传递。

    Parameters: KeyName: Description: Name of an existing EC2 KeyPair to enable SSH access to the instance Type: AWS::EC2::KeyPair::KeyName ConstraintDescription: must be the name of an existing EC2 KeyPair. SSHLocation: Description: The IP address that can be used to SSH to the EC2 instances in CIDR format (e.g. 203.0.113.1/32) Type: String MinLength: 9 MaxLength: 18 Default: 0.0.0.0/0 AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$' ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x.
  2. KeyName 属性添加到 Amazon EC2 实例。

    WebServerInstance: Type: AWS::EC2::Instance Properties: ImageId: !Ref LatestAmiId InstanceType: !Ref InstanceType KeyName: !Ref KeyName SecurityGroupIds: - !Ref WebServerSecurityGroup
  3. 将端口 22 和 SSH 位置添加到 Amazon EC2 安全组的入口规则。

    WebServerSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Enable HTTP access via port 80 and SSH access via port 22 SecurityGroupIngress: - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 0.0.0.0/0 - IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: !Ref SSHLocation
  4. 使用与 第 2 步:更新应用程序 中所述的相同步骤更新堆栈。

第 4 步:更新实例类型

现在,让我们演示一下如何通过更改实例类型来更新底层基础设施。

我们到目前为止所建立的堆栈使用 t3.micro Amazon EC2 实例。假设您新建的网站获取的流量比 t3.micro 实例能处理的流量多,且您现在想移动到 m5.large Amazon EC2 实例类型中。如果实例类型的架构发生变化,则必须使用不同的 AMI 创建实例。但是,t3.micro 和 m5.large 都使用相同的 CPU 架构并运行 Amazon Linux 2(x86_64)AMI。有关更多信息,请参阅《Amazon EC2 用户指南》中的更改实例类型的兼容性

让我们使用在上一步中进行修改的模板更改实例类型。由于 InstanceType 是模板的输入参数,我们不需要修改模板;我们能在指定堆栈详细信息页面上更改参数值。

要使用新的参数值更新堆栈
  1. 在 CloudFormation 控制台中,选择您的 UpdateTutorial 堆栈。

  2. 选择更新、直接更新

  3. 选择使用当前模板,然后选择下一步

  4. 指定堆栈详细信息页面上,将文本框中 InstanceType 的值从 t3.micro 更改为 m5.large。然后,选择两次下一步

  5. 审核页面上,审核您的更改。在更改下,您应该会看到 CloudFormation 将更新 WebServerInstance 资源。

  6. 选择提交

对于 EBS 支持的 Amazon EC2 实例,可以通过启动和停止实例来动态更改实例类型。CloudFormation 会尝试通过更新实例类型并重启实例来优化更改,因此实例 ID 不会更改。但是,实例重启时,实例的公用 IP 地址会更改。为了确保在更改后正确绑定弹性 IP 地址,CloudFormation 还会更新弹性 IP 地址。您可以在事件选项卡上的 CloudFormation 控制台中看到更改。

要通过 Amazon Web Services 管理控制台 检查实例类型,请打开 Amazon EC2 控制台并在其中查找您的实例。

第 5 步:更新 AMI

现在,让我们更新堆栈以使用下一代 Amazon Linux,即 Amazon Linux 2023。

更新 AMI 是一项重大更改,需要更换实例。我们不能只通过启动和停止实例来修改 AMI;CloudFormation 会将此视为对资源不可变属性的更改。要更改不可变属性进,CloudFormation 必须启动替代资源,在此例中是运行新 AMI 的新 Amazon EC2 实例。

让我们来看看如何更新堆栈模板以使用 Amazon Linux 2023。关键更改包括更新 AMI 参数和从 yum 更改为 dnf 程序包管理器。

AWSTemplateFormatVersion: 2010-09-09 Parameters: LatestAmiId: Description: The latest Amazon Linux 2023 AMI from the Parameter Store Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id> Default: '/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-x86_64' InstanceType: Description: WebServer EC2 instance type Type: String Default: t3.micro AllowedValues: - t3.nano - t3.micro - t3.small - t3.medium - t3a.nano - t3a.micro - t3a.small - t3a.medium - m5.large - m5.xlarge - m5.2xlarge - m5a.large - m5a.xlarge - m5a.2xlarge - c5.large - c5.xlarge - c5.2xlarge - r5.large - r5.xlarge - r5.2xlarge - r5a.large - r5a.xlarge - r5a.2xlarge ConstraintDescription: must be a valid EC2 instance type. KeyName: Description: Name of an existing EC2 KeyPair to enable SSH access to the instance Type: AWS::EC2::KeyPair::KeyName ConstraintDescription: must be the name of an existing EC2 KeyPair. SSHLocation: Description: The IP address that can be used to SSH to the EC2 instances in CIDR format (e.g. 203.0.113.1/32) Type: String MinLength: 9 MaxLength: 18 Default: 0.0.0.0/0 AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$' ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x. Resources: WebServerInstance: Type: AWS::EC2::Instance Properties: ImageId: !Ref LatestAmiId InstanceType: !Ref InstanceType KeyName: !Ref KeyName SecurityGroupIds: - !Ref WebServerSecurityGroup UserData: Fn::Base64: !Sub | #!/bin/bash -xe # Get the latest CloudFormation package dnf update -y aws-cfn-bootstrap # Run cfn-init /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource WebServerInstance --region ${AWS::Region} || error_exit 'Failed to run cfn-init' # Start up the cfn-hup daemon to listen for changes to the EC2 instance metadata /opt/aws/bin/cfn-hup || error_exit 'Failed to start cfn-hup' # Signal success or failure /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource WebServerInstance --region ${AWS::Region} Metadata: AWS::CloudFormation::Init: config: packages: dnf: httpd: [] php: [] files: /var/www/html/index.php: content: | <?php echo "<h1>Hello World!</h1>"; echo "<p>This is an updated version of our application.</p>"; echo "<p>Running on Amazon Linux 2023!</p>"; ?> mode: '000644' owner: apache group: apache /etc/cfn/cfn-hup.conf: content: !Sub | [main] stack=${AWS::StackId} region=${AWS::Region} # The interval used to check for changes to the resource metadata in minutes. Default is 15 interval=2 mode: '000400' owner: root group: root /etc/cfn/hooks.d/cfn-auto-reloader.conf: content: !Sub | [cfn-auto-reloader-hook] triggers=post.update path=Resources.WebServerInstance.Metadata.AWS::CloudFormation::Init action=/opt/aws/bin/cfn-init -s ${AWS::StackId} -r WebServerInstance --region ${AWS::Region} runas=root services: systemd: httpd: enabled: true ensureRunning: true cfn-hup: enabled: true ensureRunning: true files: - /etc/cfn/cfn-hup.conf - /etc/cfn/hooks.d/cfn-auto-reloader.conf CreationPolicy: ResourceSignal: Timeout: PT5M WebServerSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Enable HTTP access via port 80 and SSH access via port 22 SecurityGroupIngress: - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 0.0.0.0/0 - IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: !Ref SSHLocation Outputs: WebsiteURL: Value: !Sub 'http://${WebServerInstance.PublicDnsName}' Description: URL of the web application

使用与 第 2 步:更新应用程序 中所述的相同步骤更新堆栈。

在新实例运行之后,CloudFormation 将更新堆栈中的其他资源以指向新资源。创建所有新资源并删除旧资源的过程称为 UPDATE_CLEANUP。此时,您将注意到堆栈中实例的实例 ID 和应用程序 URL 已随着更新而更改。事件表中的事件包含描述“Requested update has a change to an immutable property and hence creating a new physical resource”,以指示资源已被替代。

或者:如果您已将应用程序代码写入您想更新的 AMI 中,您可以使用同一堆栈更新机制更新 AMI 以加载您的新应用程序。

要使用自定义应用程序代码更新 AMI
  1. 创建含有应用程序或操作系统更改的新 AMI。有关更多信息,请参阅《Amazon EC2 用户指南》中的创建 Amazon EBS-backed AMI

  2. 更新您的模板以合并新 AMI ID。

  3. 使用与 第 2 步:更新应用程序 中所述的相同步骤更新堆栈。

在您更新堆栈时,CloudFormation 检测到 AMI ID 已更改,然后用我们启动前一个更新所使用的方法触发堆栈更新。

可用性和影响注意事项

不同的属性会对堆栈中的资源造成不同的影响。您可以使用 CloudFormation 更新任何属性,但是您应该在进行任何更改之前考虑以下问题:

  1. 更新会如何影响资源本身? 例如,更新警报阈值会使警报在更新期间处于非活动状态。正如我们所见,更改实例类型时需要停止并重启实例。CloudFormation 使用底层资源的更新或修改操作来对资源进行更改。要了解更改的影响,您应该查看特定资源的文档。

  2. 更改可变还是不可变? 对资源属性的某些更改,如更改 Amazon EC2 实例上的 AMI,不受基础服务的支持。如果更改可变,CloudFormation 将使用适用于基础资源的“Update”或“Modify”类型 API。对于不可变的属性更改,CloudFormation 将用更新后的属性创建新资源,然后再删除旧资源之前将此资源链接至堆栈。虽然 CloudFormation 尝试减少堆栈资源的停机时间,但替代资源是一个多步骤过程,需要时间。重新配置堆栈期间,您的应用程序不能全面运行。例如,它可能不能为请求提供服务或访问数据库。

相关资源

有关使用 CloudFormation 启动应用程序的更多信息以及集成其他配置与 Puppet 和 Opscode Chef 等部署服务的更多信息,请参阅以下白皮书: