创建AMI映像作为cloudformation堆栈的一部分 [英] Create AMI image as part of a cloudformation stack

查看:88
本文介绍了创建AMI映像作为cloudformation堆栈的一部分的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想创建一个EC2云形成堆栈,基本上可以用以下步骤描述:



1.-启动实例



2。-设置实例



3.-停止实例并从中创建AMI映像



4.--将创建的AMI映像作为源来创建一个自动伸缩组,以启动新实例。



基本上,我可以在一个cloudformation中进行1和2模板和第二个模板中的4。我似乎无法执行的操作是从cloudformation模板内的实例创建AMI图像,这基本上会产生以下问题:如果我要删除堆栈,则必须手动删除AMI。



话虽这么说,我的问题是:



1.-是否可以通过实例在cloudformation模板内部创建AMI映像?



2。-如果对1的回答为否,是否可以添加AMI图像(或与此相关的任何其他资源)以使其成为一部分一个完整的堆栈?



编辑:



为了澄清,我已经解决了创建AMI并在cloudformation模板中使用它,我只是无法在cloudformation模板中创建AMI或以某种方式将其添加到已创建的堆栈中。



我对Rico的评论答案,我现在要做的是使用一个基本的剧本,该剧本基本上包括3个步骤:



1.-使用cloudformation模板创建基本实例



2.-使用ansible创建在步骤1中创建的实例的AMI



3。-创建堆栈的其余部分( ELB,自动伸缩组等),带有第二个cloudformation模板,该模板可更新在步骤1中创建的模板,并使用在步骤2中创建的AMI启动实例。



现在是我的管理方式,但是我想知道是否有任何方法可以在cloudformation模板中创建AMI,或者是否可以将创建的AMI添加到堆栈中(就像告诉堆栈,嘿,这属于您同样,也请处理。

解决方案

是的,您可以通过CloudFormation模板中的EC2实例创建AMI,方法是:实现

 描述:从EC2实例创建AMI。 
参数:
ImageId:
说明:基本EC2实例的映像ID。
类型:AWS :: EC2 :: Image :: Id
#amzn-ami-hvm-2016.09.1.20161221-x86_64-gp2
默认值:ami-9be6f38c
实例类型:
说明:启动EC2实例的实例类型。
类型:字符串
默认值:m3.medium
AllowedValues:[m3.medium,m3.large,m3.xlarge,m3.2xlarge]
资源:
#在完全调配实例并准备创建AMI时完成。
AMICreate:
类型:AWS :: CloudFormation :: WaitCondition
CreationPolicy:
ResourceSignal:
超时:PT10M
实例:
类型: AWS :: EC2 :: Instance
属性:
ImageId:!Ref ImageId
InstanceType:!Ref InstanceType
UserData:
Fn :: Base64:!Sub |
#!/ bin / bash -x
yum -y install mysql#配置示例
/ opt / aws / bin / cfn-signal \
-e $? \
--stack $ {AWS :: StackName} \
--region $ {AWS :: Region} \
-资源AMI创建
shutdown -h now
AMI:
类型:自定义:: AMI
DependsOn:AMICreate
属性:
ServiceToken:!GetAtt AMIFunction.Arn
InstanceId:!Ref实例
AMIFunction:
类型:AWS :: Lambda :: Function
属性:
处理程序:index.handler
作用:!GetAtt LambdaExecutionRole.Arn
代码:
ZipFile:!Sub |
var response = require(‘cfn-response’);
var AWS = require('aws-sdk');
exports.handler =函数(事件,上下文){
console.log(收到请求:\n,JSON.stringify(event));
var physicalId = event.PhysicalResourceId;
函数success(data){
return response.send(event,context,response.SUCCESS,data,physicalId);
}
函数失败(e){
返回response.send(event,context,response.FAILED,e,physicalId);
}
//调用ec2.waitFor,如果未在Lambda函数超时之前完成,则继续。
function wait(waiter){
console.log( Waiting:,JSON.stringify(waiter));
event.waiter =服务员;
event.PhysicalResourceId = physicalId;
var request = ec2.waitFor(waiter.state,waiter.params);
setTimeout(()=> {
request.abort();
console.log(已达到超时,继续执行功能。参数:\n,JSON.stringify(event) );
var lambda = new AWS.Lambda();
lambda.invoke({
FunctionName:context.invokedFunctionArn,
InvocationType:'Event',
有效负载:JSON.stringify(event)
})。promise()。then((data)=> context.done())。catch((err)=> context.fail(err));
},context.getRemainingTimeInMillis()-5000);
return request.promise()。catch((err)=>
(err.code =='RequestAbortedError')?
新Promise(()=> context.done( )):
Promise.reject(err)
);
}
var ec2 = new AWS.EC2(),
instanceId = event.ResourceProperties.InstanceId;
if(event.waiter){
wait(event.waiter).then((data)=> success({}))。catch((err)=> failed(err)) ;
}否则,如果(event.RequestType ==‘创建’|| event.RequestType ==‘更新’){
if(!instanceId){失败(需要实例ID); }
ec2.waitFor('instanceStopped',{InstanceIds:[instanceId]})。promise()
.then((data)=>
ec2.createImage({
InstanceId:instanceId,
名称:event.RequestId
})。promise()
).then((data)=>
wait({
state :'imageAvailable',
参数:{ImageIds:[physicalId = data.ImageId]}
})
).then((data)=> success({}))。catch ((err)=> failed(err));
} else if(event.RequestType =='Delete'){
if(physicalId.indexOf('ami-')!== 0){返回成功({});}
ec2.describeImages({ImageIds:[physicalId]})。promise()
.then((data)=>
(data.Images.length == 0)?success({}) :
ec2.deregisterImage({ImageId:physicalId})。promise()
).then((data)=>
ec2.describeSnapshots({Filters:[{
名称:'description',
值:[ * + physicalId + *]
}]}))。promise()
).then((data)=>
(data.Snapshots.length === 0)?success({}):
ec2.deleteSnapshot({SnapshotId:data.Snapshots [0] .SnapshotId})。promise()
).then((data)=>成功({}))。catch((err)=> failed(err));
}
};
运行时:nodejs4.3
超时:300
LambdaExecutionRole:
类型:AWS :: IAM :: Role
属性:
AssumeRolePolicyDocument:
版本: 2012-10-17
声明:
-效果:允许
委托人:{服务:[lambda.amazonaws.com]} ​​
操作:['sts :AssumeRole']
路径:/
ManagedPolicyArns:
-arn:aws:iam :: aws:policy / service-role / AWSLambdaBasicExecutionRole
-arn:aws:iam :: aws:policy / service-role / AWSLambdaRole
策略:
-PolicyName:EC2Policy
PolicyDocument:
版本: 2012-10-17
声明:
-效果:允许
操作:
-'ec2:DescribeInstances'
-'ec2:DescribeImages'
-'ec2:CreateImage'
-'ec2: DeregisterImage'
-'ec2:DescribeSnapshots'
-'ec2:D eleteSnapshot'
资源:['*']
输出:
AMI:
值:!Ref AMI


I want to create an EC2 cloudformation stack which basically can be described in the following steps:

1.- Launch instance

2.- Provision the instance

3.- Stop the instance and create an AMI image out of it

4.- Create an autoscaling group with the created AMI image as source to launch new instances.

Basically I can do 1 and 2 in one cloudformation template and 4 in a second template. What I don't seem able to do is to create an AMI image from an instance inside a cloudformation template, which basically generates the problem of having to manually remove the AMI if I want to remove the stack.

That being said, my questions are:

1.- Is there a way to create an AMI image from an instance INSIDE the cloudformation template?

2.- If the answer to 1 is no, is there a way to add an AMI image (or any other resource for that matter) to make it part of a completed stack?

EDIT:

Just to clarify, I've already solved the problem of creating the AMI and using it in a cloudformation template, I just can't create the AMI INSIDE the cloudformation template or add it somehow to the created stack.

As I commented on Rico's answer, what I do now is use an ansible playbook which basically has 3 steps:

1.- Create a base instance with a cloudformation template

2.- Create, using ansible, an AMI of the instance created on step 1

3.- Create the rest of the stack (ELB, autoscaling groups, etc) with a second cloudformation template that updates the one created on step 1, and that uses the AMI created on step 2 to launch instances.

This is how I manage it now, but I wanted to know if there's any way to create an AMI INSIDE a cloudformation template or if it's possible to add the created AMI to the stack (something like telling the stack, "Hey, this belongs to you as well, so handle it").

解决方案

Yes, you can create an AMI from an EC2 instance within a CloudFormation template by implementing a Custom Resource that calls the CreateImage API on create (and calls the DeregisterImage and DeleteSnapshot APIs on delete).

Since AMIs can sometimes take a long time to create, a Lambda-backed Custom Resource will need to re-invoke itself if the wait has not completed before the Lambda function times out.

Here's a complete example:

Description: Create an AMI from an EC2 instance.
Parameters:
  ImageId:
    Description: Image ID for base EC2 instance.
    Type: AWS::EC2::Image::Id
    # amzn-ami-hvm-2016.09.1.20161221-x86_64-gp2
    Default: ami-9be6f38c
  InstanceType:
    Description: Instance type to launch EC2 instances.
    Type: String
    Default: m3.medium
    AllowedValues: [ m3.medium, m3.large, m3.xlarge, m3.2xlarge ]
Resources:
  # Completes when the instance is fully provisioned and ready for AMI creation.
  AMICreate:
    Type: AWS::CloudFormation::WaitCondition
    CreationPolicy:
      ResourceSignal:
        Timeout: PT10M
  Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref ImageId
      InstanceType: !Ref InstanceType
      UserData:
        "Fn::Base64": !Sub |
          #!/bin/bash -x
          yum -y install mysql # provisioning example
          /opt/aws/bin/cfn-signal \
            -e $? \
            --stack ${AWS::StackName} \
            --region ${AWS::Region} \
            --resource AMICreate
          shutdown -h now
  AMI:
    Type: Custom::AMI
    DependsOn: AMICreate
    Properties:
      ServiceToken: !GetAtt AMIFunction.Arn
      InstanceId: !Ref Instance
  AMIFunction:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Role: !GetAtt LambdaExecutionRole.Arn
      Code:
        ZipFile: !Sub |
          var response = require('cfn-response');
          var AWS = require('aws-sdk');
          exports.handler = function(event, context) {
            console.log("Request received:\n", JSON.stringify(event));
            var physicalId = event.PhysicalResourceId;
            function success(data) {
              return response.send(event, context, response.SUCCESS, data, physicalId);
            }
            function failed(e) {
              return response.send(event, context, response.FAILED, e, physicalId);
            }
            // Call ec2.waitFor, continuing if not finished before Lambda function timeout.
            function wait(waiter) {
              console.log("Waiting: ", JSON.stringify(waiter));
              event.waiter = waiter;
              event.PhysicalResourceId = physicalId;
              var request = ec2.waitFor(waiter.state, waiter.params);
              setTimeout(()=>{
                request.abort();
                console.log("Timeout reached, continuing function. Params:\n", JSON.stringify(event));
                var lambda = new AWS.Lambda();
                lambda.invoke({
                  FunctionName: context.invokedFunctionArn,
                  InvocationType: 'Event',
                  Payload: JSON.stringify(event)
                }).promise().then((data)=>context.done()).catch((err)=>context.fail(err));
              }, context.getRemainingTimeInMillis() - 5000);
              return request.promise().catch((err)=>
                (err.code == 'RequestAbortedError') ?
                  new Promise(()=>context.done()) :
                  Promise.reject(err)
              );
            }
            var ec2 = new AWS.EC2(),
                instanceId = event.ResourceProperties.InstanceId;
            if (event.waiter) {
              wait(event.waiter).then((data)=>success({})).catch((err)=>failed(err));
            } else if (event.RequestType == 'Create' || event.RequestType == 'Update') {
              if (!instanceId) { failed('InstanceID required'); }
              ec2.waitFor('instanceStopped', {InstanceIds: [instanceId]}).promise()
              .then((data)=>
                ec2.createImage({
                  InstanceId: instanceId,
                  Name: event.RequestId
                }).promise()
              ).then((data)=>
                wait({
                  state: 'imageAvailable',
                  params: {ImageIds: [physicalId = data.ImageId]}
                })
              ).then((data)=>success({})).catch((err)=>failed(err));
            } else if (event.RequestType == 'Delete') {
              if (physicalId.indexOf('ami-') !== 0) { return success({});}
              ec2.describeImages({ImageIds: [physicalId]}).promise()
              .then((data)=>
                (data.Images.length == 0) ? success({}) :
                ec2.deregisterImage({ImageId: physicalId}).promise()
              ).then((data)=>
                ec2.describeSnapshots({Filters: [{
                  Name: 'description',
                  Values: ["*" + physicalId + "*"]
                }]}).promise()
              ).then((data)=>
                (data.Snapshots.length === 0) ? success({}) :
                ec2.deleteSnapshot({SnapshotId: data.Snapshots[0].SnapshotId}).promise()
              ).then((data)=>success({})).catch((err)=>failed(err));
            }
          };
      Runtime: nodejs4.3
      Timeout: 300
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal: {Service: [lambda.amazonaws.com]}
          Action: ['sts:AssumeRole']
      Path: /
      ManagedPolicyArns:
      - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      - arn:aws:iam::aws:policy/service-role/AWSLambdaRole
      Policies:
      - PolicyName: EC2Policy
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Action:
              - 'ec2:DescribeInstances'
              - 'ec2:DescribeImages'
              - 'ec2:CreateImage'
              - 'ec2:DeregisterImage'
              - 'ec2:DescribeSnapshots'
              - 'ec2:DeleteSnapshot'
              Resource: ['*']
Outputs:
  AMI:
    Value: !Ref AMI

这篇关于创建AMI映像作为cloudformation堆栈的一部分的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆