aws cloudformation 使用CommandRunner在堆栈中运行脚本

参考资料

由于cloudformation语法和资源的限制,有些场景下我们可能会希望执行一些自定义逻辑。实际上cloudformation已经提供了自定义资源和宏实现这个目的。而CommandRunner则从另一个角度解决此问题。

commandrunner本质上就是启动额外的ec2来执行指定的逻辑,和自定义资源的lambda类似的思路,只是资源类型不同

测试过程

安装commandrunner

git clone https://github.com/aws-cloudformation/aws-cloudformation-resource-providers-awsutilities-commandrunner.git
cd aws-cloudformation-resource-providers-awsutilities-commandrunner
curl -LO https://github.com/aws-cloudformation/aws-cloudformation-resource-providers-awsutilities-commandrunner/releases/latest/download/awsutility-cloudformation-commandrunner.zip
./scripts/register.sh --set-default

具体的执行过程如下,核心具体就是将awsutility-cloudformation-commandrunner.zip注册到cfn中,需要临时s3桶存储该zip文件

Creating Execution Role...
Waiting for execution role stack to complete...
Waiting for execution role stack to complete...
Creating/Updating Execution Role complete.
Creating temporary S3 Bucket b34239efa0b947bebd574be7fa6d7e2d...
Creating temporary S3 Bucket b34239efa0b947bebd574be7fa6d7e2d complete.
Configuring S3 Bucket Policy for temporary S3 Bucket b34239efa0b947bebd574be7fa6d7e2d...
An error occurred (MalformedPolicy) when calling the PutBucketPolicy operation: Policy has invalid resource
Configuring S3 Bucket Policy for temporary S3 Bucket b34239efa0b947bebd574be7fa6d7e2d complete.
Copying Schema Handler Package to temporary S3 Bucket b34239efa0b947bebd574be7fa6d7e2d...
Copying Schema Handler Package to temporary S3 Bucket b34239efa0b947bebd574be7fa6d7e2d complete.
Creating CommandRunner Log Group called awsutility-cloudformation-commandrunner-logs2...
Creating CommandRunner Log Group complete.
Registering AWSUtility::CloudFormation::CommandRunner to AWS CloudFormation...
RegistrationToken: 5a22a5d4-723e-4d2a-b20d-eff95352830c
Waiting for registration to complete...
Waiting for registration to complete...
Waiting for registration to complete...
Waiting for registration to complete...
Registering AWSUtility::CloudFormation::CommandRunner to AWS CloudFormation complete.
Setting current version as default...
Setting current version as default complete. (Current Version is 00000001)
Cleaning up temporary S3 Bucket...
Deleting SchemaHandlerPackage from temporary S3 Bucket b34239efa0b947bebd574be7fa6d7e2d...
Deleting SchemaHandlerPackage from temporary S3 Bucket b34239efa0b947bebd574be7fa6d7e2d complete.
Cleaning up temporary S3 Bucket complete.

以上命令会使用默认的awscli凭证在账户中创建cfn资源

在这里插入图片描述

之后可以在cfn模板中直接使用,这里创建简单的s3资源

Resources:
  S3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      VersioningConfiguration:
        Status: Suspended
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
  CommandRunner1:
      DependsOn: S3Bucket
      Type: AWSUtility::CloudFormation::CommandRunner
      Properties:
         Command: echo justfortest > /command-output.txt
         Role: MyEc2AdministratorAccess
         SubnetId: subnet-0270xxxxxxxd
  CommandRunner2:
      Type: AWSUtility::CloudFormation::CommandRunner
      Properties:
         Role: MyEc2AdministratorAccess
         Command: echo helloworld > /command-output.txt
         SubnetId: subnet-02xxxxxxxd

Outputs:
    Output1:
        Description: The output of the CommandRunner.
        Value: !GetAtt CommandRunner1.Output
    Output2:
        Description: The output of the CommandRunner.
        Value: !GetAtt CommandRunner2.Output

由于创建了两个commandrunner资源,因此会创建额外的两个堆栈

在这里插入图片描述

启动两台ec2示例,默认为t2.medium

在这里插入图片描述

以下是AWSUtility-CloudFormation-CommandRunner资源的模板截取,逻辑较为简单,只需要关注userdata部分,相关的概念的命令在之前的文章中都提到过,不赘述

  • 执行init初始化
  • 执行指定的command命令
  • 执行signal,向cfn响应,响应内容是command-output.txt文件中的内容
Parameters:
...Command:
    Type: String
    Default: >-
      yum install jq -y && aws ssm get-parameter --name RepositoryName --region
      us-east-1 | jq -r .Parameter.Value > /commandrunner-output.txt
  LogGroup:
    Type: String
    Default: cloudformation-commandrunner-log-group
Resources:
  SecurityGroup:
    Condition: CreateSecurityGroup
    Type: AWS::EC2::SecurityGroup
  EC2Instance:
    Type: AWS::EC2::Instance
    Metadata:
      AWS::CloudFormation::Init:
        config:
          packages:
            yum:
              awslogs: []
          files:
            /etc/awslogs/awslogs.conf:
              content:
                Fn::Sub: |
                  [general]
                  state_file= /var/awslogs/state/agent-state
                  [/var/log/cloud-init.log]
                  file = /var/log/cloud-init.log\n
                  log_group_name = ${LogGroup}
                  log_stream_name = {instance_id}/cloud-init.log
                  ... 略
            /etc/awslogs/awscli.conf:
              content:
                Fn::Sub: |
                  [plugins]
                  cwlogs = cwlogs
                  [default]
                  region = ${AWS::Region}
          commands:
            01_create_state_directory:
              command: mkdir -p /var/awslogs/state
          services:
            sysvinit:
              awslogsd:
                enabled: true
                ensureRunning: true
                files:
                  - /etc/awslogs/awslogs.conf
    Properties:
      UserData:
        Fn::Base64:
          Fn::Sub: >-
            #!/bin/bash
            yum install -y aws-cfn-bootstrap
            /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource
            EC2Instance  --region ${AWS::Region}
            ${Command}
            /opt/aws/bin/cfn-signal -r 'Command ran successfully.' -e 0 --id
            'Command Output' --data "$(cat /command-output.txt)"
            '${WaitConditionHandle}'
            echo Contents of /command-output.txt = $(cat /command-output.txt)
  WaitConditionHandle:
    Type: AWS::CloudFormation::WaitConditionHandle
  WaitCondition:
    Type: AWS::CloudFormation::WaitCondition
    Properties:
      Count: 1
      Handle:
        Ref: WaitConditionHandle
      Timeout:
        Ref: Timeout
Outputs:
  Result:
    Description: The output of the commandrunner.
    Value:
      Fn::Select:
        - 3
        - Fn::Split:
            - '"'
            - Fn::GetAtt:
                - WaitCondition
                - Data

最终能够获取到output

在这里插入图片描述

总结commandrunner的特点如下

  • 便宜,据说短时间的ec2启动费用要小于lambda
  • 灵活,用户自行决定资源的阻塞和依赖
  • 方便,不需要写代码,lambda不能直接执行bash脚本,降低了使用门槛
  • 慢,启动ec2的相比调用lambda慢很多,而且貌似没法进行资源复用
  • 不好排查,因为都是在实例上,需要查日志,而且这个资源类型具体做什么我们看不到

相关错误

(1)waitcondition超时

可能是由于ec2没有权限,ec2网络配置有误,在syslog中能看到相关信息

(2)输出结果无效,cli命令要确保写入到文件中

Resource handler returned message: "Either the command failed to execute, the value written to /command-output.txt was invalid or the Subnet specified did not have internet access. The value written to /command-output.txt must be a non-empty single word value without quotation marks. Check cloud-init.log in the LogGroup specified for more information." (RequestToken: c7ebb7d1-ec0a-43e0-ea18-f13a331fc00d, HandlerErrorCode: NotStabilized)
posted @ 2023-02-09 17:18  zhaojie10  阅读(7)  评论(0)    收藏  举报  来源