CloudFormation で EC2インスタンスを構築する際、どのような処理が動いているのかみてみた

記事タイトルとURLをコピーする

こんにちは。
AWS CLI が好きなテクニカルサポート課の市野です。

普段、AWS CLI 一辺倒なので、CloudFormation に関連するお問合せをいただいた際に、あれ?どうだったっけ?となる場面があったので、少し深掘りしてみました。

クリックで目次が表示されます。

そもそもの目的

EC2 インスタンスを作成する時に、ENI 周りの指定の仕方が、Type: AWS::EC2::Instance だけでも成立するし、Type: AWS::EC2::InstanceType: 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::NetworkInterfaceType: 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::InstanceNetworkInterfaces:DeleteOnTermination: false を追記 以下のエラーが発生し、スタックの作成に失敗する。
A network interface without a network interface ID must have delete on termination as true
2 Type: AWS::EC2::NetworkInterfaceDeleteOnTermination: 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::InstanceType: AWS::EC2::NetworkInterface の併用を行い作成を行う場合は、ネットワークインターフェイスの「終了時に削除」を true にできず(というよりも DeleteOnTermination パラメータを設定することができない)、インスタンス終了時に ENI が削除されない状態で起動する形で構成されます。

まとめ

  • Type: AWS::EC2::NetworkInterfaceType: AWS::EC2::NetworkInterfaceAttachment および Type: AWS::EC2::Instance の併用をしても、ENI を作成して、EC2 インスタンスを作成しながら、作成した ENI をアタッチする、の動作になるわけではない。
  • Type: AWS::EC2::Instance のみで構成した場合と、 Type: AWS::EC2::InstanceType: AWS::EC2::NetworkInterface の併用をした場合は、作成されたネットワークインターフェイスの観点で、インスタンス終了時の削除の扱いが異なる。

以上、AWS CloudFormation リファレンス では少々読み解きにくかった挙動の差異となります。
この記事がどなたかのお役に立てば幸いです。

ではまた。

市野 和明 (記事一覧)

マネージドサービス部・テクニカルサポート課

お客様から寄せられたご質問や技術検証を通じて得られた気づきを投稿していきます。

情シスだった前職までの経験で、UI がコロコロ変わる AWS においては GUI で手順を残していると画面構成が変わってしまって後々まごつくことが多かった経験から、極力変わりにくい AWS CLI での記事が多めです。

X(Twitter):@kazzpapa3(AWS Community Builder)