CloudFormation:无法使用相同的Elastic IP重新创建EC2实例

huangapple go评论61阅读模式
英文:

CloudFormation: unable to recreate EC2 instance with the same ElasticIp

问题

我正在尝试创建和更新一个包含1个EC2实例和1个EIP的CloudFormation堆栈。

参考我的代码,我正在使用Node.js SDK。

这是大致的模板(我删除了一些部分并省略了Outputs):

{
  AWSTemplateFormatVersion: '2010-09-09',
  Description: '',
  Parameters: {
    ClusterStackName: {
      Description: ...,
      Type: 'String',
      Default: ...,
    },
    AmiId: {
      Description: ...,
      Type: 'String',
      Default: ...,
    },
  },
  Resources: {
    ElasticNetworkInterface: {
      Type: 'AWS::EC2::NetworkInterface',
      Properties: {
        GroupSet: [...],
        SubnetId: ...,
        Tags: [...],
      },
    },
    Instance: {
      Type: 'AWS::EC2::Instance',
      DependsOn: 'ElasticNetworkInterface',
      Properties: {
        ImageId: {
          Ref: 'AmiId',
        },
        UserData: ...,
        InstanceType: ...,
        Tenancy: 'default',
        KeyName: ...,
        PropagateTagsToVolumeOnCreation: true,
        BlockDeviceMappings: [
          {
            DeviceName: ...,
            Ebs: { VolumeSize: 'x', Encrypted: true },
          },
          {
            DeviceName: ...,
            Ebs: { VolumeSize: 'y', Encrypted: true },
          },
        ],
        NetworkInterfaces: [
          {
            NetworkInterfaceId: { Ref: 'ElasticNetworkInterface' },
            DeviceIndex: '0',
          },
        ],
        Tags: [...],
      },
    },
    ElasticIpAddress: {
      Type: 'AWS::EC2::EIP',
      Properties: {
        Domain: 'vpc',
        InstanceId: { Ref: 'Instance' },
        Tags: [...],
      },
    },
  },
}

创建工作得很好,我获得了EC2实例、网络接口和EIP。我希望能够重新创建该机器而不会丢失EIP,例如,如果我更新AMI并更新相应的参数,更新会失败并显示错误:

Interface: [eni-aabbccdd] in use. (Service: AmazonEC2; Status Code: 400; Error Code: InvalidNetworkInterface.InUse; Request ID: xxx; Proxy: null)

在我看来,这是因为CloudFormation需要能够安全地回滚。是否有更好的方法来实现这一点?如果我创建两个网络接口(一个用于私有,一个用于公共),然后只分离公共的,是否会起作用?

提前感谢您的回答。

我尝试过使用网络附件和DependsOn属性的各种组合,但都没有成功。

英文:

I am trying to create and update a CloudFormation stack consisting of 1 EC2 instance with 1 EIP.

For reference, I am using the nodejs sdk.

This is the rough template (I redacted parts and omitted the Outputs):

{
AWSTemplateFormatVersion: '2010-09-09',
Description: '',
Parameters: {
ClusterStackName: {
Description: ...,
Type: 'String',
Default: ...,
},
AmiId: {
Description: ...,
Type: 'String',
Default: ...,
},
},
Resources: {
ElasticNetworkInterface: {
Type: 'AWS::EC2::NetworkInterface',
Properties: {
GroupSet: [...],
SubnetId: ...,
Tags: [...],
},
},
Instance: {
Type: 'AWS::EC2::Instance',
DependsOn: 'ElasticNetworkInterface',
Properties: {
ImageId: {
Ref: 'AmiId',
},
UserData: ...,
InstanceType: ...,
Tenancy: 'default',
KeyName: ...,
PropagateTagsToVolumeOnCreation: true,
BlockDeviceMappings: [
{
DeviceName: ...,
Ebs: { VolumeSize: 'x', Encrypted: true },
},
{
DeviceName: ...,
Ebs: { VolumeSize: 'y', Encrypted: true },
},
],
NetworkInterfaces: [
{
NetworkInterfaceId: { Ref: 'ElasticNetworkInterface' },
DeviceIndex: '0',
},
],
Tags: [...],
},
},
ElasticIpAddress: {
Type: 'AWS::EC2::EIP',
Properties: {
Domain: 'vpc',
InstanceId: { Ref: 'Instance' },
Tags: [...],
},
},
},
}

The creation works just fine, I get my EC2 instance with the network interface and the EIP.
I want to be able to recreate the machine without losing the EIP, so for example if I update the AMI and update the corresponding parameter, the update fails with the error:

Interface: [eni-aabbccdd] in use. (Service: AmazonEC2; Status Code: 400; Error Code: InvalidNetworkInterface.InUse; Request ID: xxx; Proxy: null)

For my understanding, this happens because CloudFormation needs to be able to rollback safely.
Is there a better way to achieve this? Would this work if I create 2 network interfaces (one for the private and one for the public) and detach the public only?

Thanks in advance

I tried using some combination of Network Attachments and DependsOn attribute but to no avail

答案1

得分: 0

我的解决方案是添加一个次要的网络接口,在重新创建实例时会分离和连接。

需要牢记的最重要一点是,在AWS EC2实例中,主要网络接口无论如何都无法被分离(https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-eni.html#:~:text=You%20cannot%20detach%20a%20primary,network%20interface%20per%20instance%20type.)。

因此,在CloudFormation模板中,只能使用Instance资源内的NetworkInterfaces块来完全指定主要网络接口。这是关键的,因为如果您以任何其他方式定义它(比如创建一个NetworkInterface资源,然后在实例的NetworkInterfaces属性中引用它),CloudFormation将尝试通过分离来保留它,而如果它完全包含在实例资源中,它将被删除和重新创建。

这也意味着,在重新创建实例时保留私有IP地址的唯一方法是创建一个附加的NetworkInterface,我们可以分离和连接,而这台机器的“官方”私有IP地址将是分配给次要网络接口(即eth1)的地址。

像这样对我有用:

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "",
  "Parameters": {
    "ClusterStackName": {
      "Description": "...",
      "Type": "String",
      "Default": "..."
    },
    "AmiId": {
      "Description": "...",
      "Type": "String",
      "Default": "..."
    }
  },
  "Resources": {
    "SecondaryElasticNetworkInterface": {
      "Type": "AWS::EC2::NetworkInterface",
      "Properties": {
        "GroupSet": [...],
        "SubnetId": "...",
        "Tags": [...]
      }
    },
    "SecondaryNetworkInterfaceAttachment": {
      "Type": "AWS::EC2::NetworkInterfaceAttachment",
      "Properties": {
        "DeviceIndex": "1",
        "InstanceId": { "Ref": "Instance" },
        "NetworkInterfaceId": { "Ref": "SecondaryElasticNetworkInterface" }
      }
    },
    "ElasticIpAddress": {
      "Type": "AWS::EC2::EIP",
      "Properties": {
        "Domain": "vpc",
        "Tags": [...]
      }
    },
    "ElasticIpAssociation": {
      "Type": "AWS::EC2::EIPAssociation",
      "Properties": {
        "AllocationId": { "Fn::GetAtt": ["ElasticIpAddress", "AllocationId"] },
        "NetworkInterfaceId": { "Ref": "SecondaryElasticNetworkInterface" }
      }
    },
    "Instance": {
      "Type": "AWS::EC2::Instance",
      "DependsOn": "ElasticNetworkInterface",
      "Properties": {
        "ImageId": {
          "Ref": "AmiId"
        },
        "UserData": "...",
        "InstanceType": "...",
        "Tenancy": "default",
        "KeyName": "...",
        "PropagateTagsToVolumeOnCreation": true,
        "BlockDeviceMappings": [
          {
            "DeviceName": "...",
            "Ebs": { "VolumeSize": "x", "Encrypted": true }
          },
          {
            "DeviceName": "...",
            "Ebs": { "VolumeSize": "y", "Encrypted": true }
          }
        ],
        "NetworkInterfaces": [
          {
            "SubnetId": "...",
            "GroupSet": [...],
            "DeviceIndex": "0"
          }
        ],
        "Tags": [...]
      }
    }
  }
}
英文:

My solution here is to add a secondary network interface which will be detached and attached when recreating the instance.

The most important thing to keep in mind is that in AWS EC2 instance, the primary network interface cannot be detached in any way (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-eni.html#:~:text=You%20cannot%20detach%20a%20primary,network%20interface%20per%20instance%20type.)

As a consequence, the primary network interface in the CloudFormation template can only be entirely specified using the NetworkInterfaces block inside the Instance resource. This is crucial because if you define it in any another way (say you create a NetworkInterface resource and you reference it inside the NetworkInterfaces property in the instance), CloudFormation will try to keep it by detaching while if it is fully enclosed in the instance resource, it will be deleted and recreated.

This also means that the only way to retain the private ip address when recreating an instance is to create an additional NetworkInterface that we can detach and attach and also the “official” private ip address for this machine will be the one assigned to the secondary network interface (which is eth1).

The private ip address of the primary network interface will change every time the instance is recreated, but the ip addresses of the secondary (private and public) will stay the same.

Something like this is working for me:

{
AWSTemplateFormatVersion: '2010-09-09',
Description: '',
Parameters: {
ClusterStackName: {
Description: ...,
Type: 'String',
Default: ...,
},
AmiId: {
Description: ...,
Type: 'String',
Default: ...,
},
},
Resources: {
SecondaryElasticNetworkInterface: {
Type: 'AWS::EC2::NetworkInterface',
Properties: {
GroupSet: [...],
SubnetId: ...,
Tags: [...],
},
},
SecondaryNetworkInterfaceAttachment: {
Type: 'AWS::EC2::NetworkInterfaceAttachment',
Properties: {
DeviceIndex: '1',
InstanceId: { Ref: 'Instance' },
NetworkInterfaceId: { Ref: 'SecondaryElasticNetworkInterface' },
},
},
ElasticIpAddress: {
Type: 'AWS::EC2::EIP',
Properties: {
Domain: 'vpc',
Tags: [...],
},
},
ElasticIpAssociation: {
Type: 'AWS::EC2::EIPAssociation',
Properties: {
AllocationId: { 'Fn::GetAtt': ['ElasticIpAddress', 'AllocationId'] },
NetworkInterfaceId: { Ref: 'SecondaryElasticNetworkInterface' },
},
},
Instance: {
Type: 'AWS::EC2::Instance',
DependsOn: 'ElasticNetworkInterface',
Properties: {
ImageId: {
Ref: 'AmiId',
},
UserData: ...,
InstanceType: ...,
Tenancy: 'default',
KeyName: ...,
PropagateTagsToVolumeOnCreation: true,
BlockDeviceMappings: [
{
DeviceName: ...,
Ebs: { VolumeSize: 'x', Encrypted: true },
},
{
DeviceName: ...,
Ebs: { VolumeSize: 'y', Encrypted: true },
},
],
NetworkInterfaces: [
{
SubnetId: ...,
GroupSet: [...],
DeviceIndex: '0',
},
],
Tags: [...],
},
},
},
}

huangapple
  • 本文由 发表于 2023年2月27日 02:23:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/75574124.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定