こんにちは。
AWS CLI が好きなテクニカルサポート課の市野です。
普段、AWS CLI 一辺倒なので、CloudFormation に関連するお問合せをいただいた際に、あれ?どうだったっけ?となる場面があったので、少し深掘りしてみました。
クリックで目次が表示されます。
そもそもの目的
EC2 インスタンスを作成する時に、ENI 周りの指定の仕方が、Type: AWS::EC2::Instance
だけでも成立するし、Type: AWS::EC2::Instance
とType: AWS::EC2::NetworkInterface
を利用して構築も可能ですし、Type: AWS::EC2::NetworkInterfaceAttachment
の指定をすることもできるため、何がどうだっけ?というのを混同していたところを整理したかった目的もあります。
あらためて整理すると、以下のような構成パターンとなるかと思います。
# | テンプレートでのリソースの指定 |
---|---|
1 | Type: AWS::EC2::Instance のみ |
2 | Type: AWS::EC2::NetworkInterface を利用し、Type: AWS::EC2::Instance で、NetworkInterfaceId として参照させる |
3 | Type: AWS::EC2::NetworkInterface を利用して ENI を構築し、Type: AWS::EC2::NetworkInterfaceAttachment を利用し NetworkInterfaceId として参照させ、InstanceId で、EC2 インスタンス ID を指定する |
それぞれの挙動の確認
前提(テンプレートの共通部分)
クリックで展開します。
AWSTemplateFormatVersion: "2010-09-09" Parameters: AmiId: Type: String Description: "AMI ID / Amazon Linux 2023" Default: 'ami-0cfc97bf81f2eadc4' Ec2UserKeyPairName: Type: String Description: KeyPair for EC2 Instance Default: 'sample' Ec2InstanceType: Type: String Description: Instance types used in EC2 Instance Default: 't2.micro' Ec2InstanceName01: Type: String Description: Name of the resource for EC2 Instance Default: 'sample01' PrivateIpAddress01: Type: String Description: Specify a private IP address for EC2 instance Default: '172.31.0.100' Ec2InstanceName02: Type: String Description: Name of the resource for EC2 Instance Default: 'sample02' PrivateIpAddress02: Type: String Description: Specify a private IP address for EC2 instance Default: '172.31.0.101' Ec2InstanceName03: Type: String Description: Name of the resource for EC2 Instance Default: 'sample03' PrivateIpAddress03: Type: String Description: Specify a private IP address for EC2 instance Default: '172.31.0.102' AvailabilityZone: Type: String Description: Specify the availability zone for EC2 instance Default: 'ap-northeast-1c' SubnetId: Type: String Description: SubnetID for EC2 instance. Default: 'subnet-xxxxxxxxxxxxxxxxx' SecurityGroupId: Type: String Description: SecurityGroupId for ENI Default: 'sg-xxxxxxxxxxxxxxxxx'
1.Type: AWS::EC2::Instance のみ での作成
テンプレートのリソース部分は、以下の通りとなります。
クリックで展開します。
Resources: # ------------------------------------------------------------# # 01. EC2 Instance # ------------------------------------------------------------# EC2: Type: AWS::EC2::Instance Properties: ImageId: !Ref AmiId KeyName: !Ref Ec2UserKeyPairName InstanceType: !Ref Ec2InstanceType AvailabilityZone: !Ref AvailabilityZone NetworkInterfaces: - Description: !Ref Ec2InstanceName01 DeviceIndex: 0 GroupSet: - !Ref SecurityGroupId SubnetId: !Ref SubnetId PrivateIpAddress: !Ref PrivateIpAddress01 Tags: - Key: Name Value: !Ref Ec2InstanceName01
1-1. CloudFormation イベントの確認
1-2. CloudFormation リソースの確認
リソースとしては、EC2 インスタンスのみが作られたことになっています。
1-3. CloudTrail での AWS API 発生状況の確認
CloudTrail でも、タグ付けの CreateTags 以外は、RunInstances のみが実行されたことになっています。
2.Type: AWS::EC2::NetworkInterface と Type: AWS::EC2::Instance の併用
テンプレートのリソース部分は、以下の通りとなります。
クリックで展開します。
Resources: # ------------------------------------------------------------# # 01. EC2 Instance # ------------------------------------------------------------# EC2: Type: AWS::EC2::Instance Properties: ImageId: !Ref AmiId KeyName: !Ref Ec2UserKeyPairName InstanceType: !Ref Ec2InstanceType AvailabilityZone: !Ref AvailabilityZone NetworkInterfaces: - NetworkInterfaceId: !Ref ENI DeviceIndex: 0 Tags: - Key: Name Value: !Ref Ec2InstanceName02 # ------------------------------------------------------------# # 02. Create ENI # ------------------------------------------------------------# ENI: Type: AWS::EC2::NetworkInterface Properties: GroupSet: - !Ref SecurityGroupId SubnetId: !Ref SubnetId PrivateIpAddress: !Ref PrivateIpAddress02 Tags: - Key: Name Value: !Ref Ec2InstanceName02
2-1. CloudFormation イベントの確認
処理の流れを見ると、ENI の作成が行われて、EC2 の作成が行われているように見受けられます。
2-2. CloudFormation リソースの確認
作成されたリソースも ENI と EC2 の2つということになっています。
2-3. CloudTrail での AWS API 発生状況の確認
CloudTrail では、タグ付けの CreateTags 以外は、CreateNetworkInterface が行われてから、RunInstances が実行されています。
3. Type: AWS::EC2::NetworkInterface と Type: AWS::EC2::NetworkInterfaceAttachment および Type: AWS::EC2::Instance の併用
テンプレートのリソース部分は、以下の通りとなります。
クリックで展開します。
Resources: # ------------------------------------------------------------# # 01. EC2 Instance # ------------------------------------------------------------# EC2: Type: AWS::EC2::Instance Properties: ImageId: !Ref AmiId KeyName: !Ref Ec2UserKeyPairName InstanceType: !Ref Ec2InstanceType AvailabilityZone: !Ref AvailabilityZone SubnetId: !Ref SubnetId Tags: - Key: Name Value: !Ref Ec2InstanceName03 # ------------------------------------------------------------# # 02. Create ENI # ------------------------------------------------------------# ENI: Type: AWS::EC2::NetworkInterface Properties: GroupSet: - !Ref SecurityGroupId SubnetId: !Ref SubnetId PrivateIpAddress: !Ref PrivateIpAddress03 Tags: - Key: Name Value: !Ref Ec2InstanceName03 # ------------------------------------------------------------# # 03. ENI Attachment # ------------------------------------------------------------# ENIAttachement: Type: AWS::EC2::NetworkInterfaceAttachment Properties: InstanceId: Ref: EC2 NetworkInterfaceId: Ref: ENI DeviceIndex: 1
3-1. CloudFormation イベントの確認
イベントを見ると ENI の作成と EC2 の作成が完了した後で、AWS::EC2::NetworkInterfaceAttachment
の実行が行われています。
3-2. CloudFormation リソースの確認
リソースを確認しても、ENI と EC2 の作成のほか、アタッチメントもリソースとして発生していることが確認できます。
3-3. CloudTrail での AWS API 発生状況の確認
CloudTrail での AWS API の発生状況を確認すると、CreateNetworkInterface が行われて、RunInstances が行われ、その後、AttachNetworkInterface, ModifyNetworkInterface が発生していることが確認できました。
作成された EC2 から見た比較
手法<2>と<3>の比較
両者を比較すると、<3>の手法で作成したインスタンスは、プライベート IPv4 アドレスが2つアタッチされており、ENI が複数割り当てられていることが確認できました。
<2>の手法で作成したインスタンス
<3>の手法で作成したインスタンス
このことから、Type: AWS::EC2::NetworkInterface
と Type: AWS::EC2::NetworkInterfaceAttachment
および Type: AWS::EC2::Instance
の併用をした場合、ENI を作成して、EC2 インスタンスを作成しながら、作成した ENI をアタッチする、の動作になるわけではないことが確認できます。
挙動としては、RunInstance の時点で、CloudFormation テンプレートで特段の指示がない場合でもプライマリの ENI が作成、アタッチされた状態で EC2 インスタンスが構築され、その後に、Type: AWS::EC2::NetworkInterface
の設定の ENI がアタッチされる挙動となります。
また、このことと関連して、 Type: AWS::EC2::NetworkInterfaceAttachment
で DeviceIndex を 0 と指定すると、上記の自動で作成、アタッチされる方の ENI と競合してしまうため、スタックの実行中にエラー発生し、ロールバックすることとなります。
手法<1>と<2>の比較
共通点
手法<1>および<2>で作成した場合、どちらも<3>のように複数の ENI が割り当てられた状態とはならず、EC2 インスタンスに一つの ENI が割り当てられた状態で作成されてきます。
<1>の手法で作成したインスタンス
<2>の手法で作成したインスタンス(再掲)
両者の差異
両者をネットワークインターフェイスの観点から確認すると、「終了時に削除」に差が生じることを確認しました。
<1>の手法で作成したネットワークインターフェイス
<2>の手法で作成したネットワークインターフェイス
ここで、ネットワークインターフェイスの終了時に削除を制御する DeleteOnTermination
を<1>および<2>に設定し、終了時の削除の変更を行おうとしたところ、下記の状況となりました。
手法 | 編集箇所 | スタック作成時の挙動 |
---|---|---|
1 | Type: AWS::EC2::Instance の NetworkInterfaces: に DeleteOnTermination: false を追記 |
以下のエラーが発生し、スタックの作成に失敗する。A network interface without a network interface ID must have delete on termination as true |
2 | Type: AWS::EC2::NetworkInterface に DeleteOnTermination: true 追記 |
以下のエラーが発生し、スタックの作成に失敗する。Properties validation failed for resource ENI with message: #: extraneous key [DeleteOnTermination] is not permitted |
上記のことから、Type: AWS::EC2::Instance
のみで ENI を作成しつつ EC2 に割り当てている場合は、ネットワークインターフェイスの「終了時に削除」を false にできず、インスタンス終了時に ENI が削除される状態で起動する形で構成されます。
反対に、Type: AWS::EC2::Instance
と Type: AWS::EC2::NetworkInterface
の併用を行い作成を行う場合は、ネットワークインターフェイスの「終了時に削除」を true にできず(というよりも DeleteOnTermination パラメータを設定することができない)、インスタンス終了時に ENI が削除されない状態で起動する形で構成されます。
まとめ
Type: AWS::EC2::NetworkInterface
とType: AWS::EC2::NetworkInterfaceAttachment
およびType: AWS::EC2::Instance
の併用をしても、ENI を作成して、EC2 インスタンスを作成しながら、作成した ENI をアタッチする、の動作になるわけではない。Type: AWS::EC2::Instance
のみで構成した場合と、Type: AWS::EC2::Instance
とType: AWS::EC2::NetworkInterface
の併用をした場合は、作成されたネットワークインターフェイスの観点で、インスタンス終了時の削除の扱いが異なる。
以上、AWS CloudFormation リファレンス では少々読み解きにくかった挙動の差異となります。
この記事がどなたかのお役に立てば幸いです。
ではまた。
市野 和明 (記事一覧)
マネージドサービス部・テクニカルサポート課
お客様から寄せられたご質問や技術検証を通じて得られた気づきを投稿していきます。
情シスだった前職までの経験で、UI がコロコロ変わる AWS においては GUI で手順を残していると画面構成が変わってしまって後々まごつくことが多かった経験から、極力変わりにくい AWS CLI での記事が多めです。
X(Twitter):@kazzpapa3(AWS Community Builder)