0
0

AutoScalingで、EC2に別VPCのENIをアタッチ(ユーザーデータ編)

Last updated at Posted at 2024-01-21

はじめに

以前、スケールアウトするEC2に別のVPCのENIをアタッチする仕組みをStep Functionsで作りました。

これを、ユーザーデータでやってみました。

概要

  • AutoScalingでENIを追加でアタッチする機能はない
  • ユーザーデータでインスタンス起動時に、ENIをアタッチするシェルを実行します
    • 事前に別VPCにENIを作っておきます
  • ENIは同じAZに作っておく必要があります
  • EC2にENI関連の権限を付与しておく必要があります

参考

構成図

image.png

以前の方法と比較してStep Functionsが無い分、シンプルになっています。

CloudFormationテンプレート

CloudFormationは以下になります。
以前と同様、EniTarget01とEniTarget02という名前で、別VPC上に事前にENIを作成しています。

クリックで表示
AWSTemplateFormatVersion: '2010-09-09'

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: Parameters
        Parameters:
          - VpcNameSource
          - VpcCIDRSource
          - VpcNameTarget
          - VpcCIDRTarget
          - ImageId
          - InstanceTypeName
          - AvailabilityZone

Parameters:

  VpcNameSource:
    Type: String
    Default: sourceVpc
    Description: Name of the VPC
  VpcCIDRSource:
    Type: String
    Default: 10.79.0.0/16
    Description: CIDR block for the VPC

  VpcNameTarget:
    Type: String
    Default: targetVpc
    Description: Name of the VPC
  VpcCIDRTarget:
    Type: String
    Default: 10.80.0.0/16
    Description: CIDR block for the VPC

  ImageId:
    Type: String
    Default: ami-012261b9035f8f938
  InstanceTypeName:
    Type: String
    Default: t3.nano
  AvailabilityZone:
    Type: AWS::EC2::AvailabilityZone::Name

Resources:
  ##################################################
  # Source VPC
  ##################################################
  VPCSource:
    Type: 'AWS::EC2::VPC'
    Properties:
      CidrBlock: !Ref VpcCIDRSource
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: !Ref VpcNameSource

  InternetGatewaySource:
    Type: 'AWS::EC2::InternetGateway'
    Properties:
      Tags:
        - Key: Name
          Value: !Sub ${VpcNameSource}-igw

  VPCGatewayAttachmentSource:
    Type: 'AWS::EC2::VPCGatewayAttachment'
    Properties:
      VpcId: !Ref VPCSource
      InternetGatewayId: !Ref InternetGatewaySource

  PublicSubnetSource:
    Type: 'AWS::EC2::Subnet'
    Properties:
      VpcId: !Ref VPCSource
      AvailabilityZone: !Ref AvailabilityZone
      CidrBlock: !Select [ 0, !Cidr [ !Ref VpcCIDRSource, 24, 8 ] ]
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub ${VpcNameSource}-subnet

  PublicRouteTableSource:
    Type: 'AWS::EC2::RouteTable'
    Properties:
      VpcId: !Ref VPCSource
      Tags:
        - Key: Name
          Value: !Sub ${VpcNameSource}-rtb-public
  PublicRouteSource:
    Type: 'AWS::EC2::Route'
    DependsOn: VPCGatewayAttachmentSource
    Properties:
      RouteTableId: !Ref PublicRouteTableSource
      DestinationCidrBlock: '0.0.0.0/0'
      GatewayId: !Ref InternetGatewaySource

  SubnetRouteTableAssociationSource:
    Type: 'AWS::EC2::SubnetRouteTableAssociation'
    Properties:
      SubnetId: !Ref PublicSubnetSource
      RouteTableId: !Ref PublicRouteTableSource

  SecurityGroupForSource:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: !Ref VPCSource
      GroupDescription: "Source VPC SG"

  ##################################################
  # EC2
  ##################################################
  SessionManagerRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: 'Allow'
            Principal:
              Service:
                - 'ec2.amazonaws.com'
            Action:
              - 'sts:AssumeRole'
      Path: '/'
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
      Policies:
        - PolicyName: writeEni
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - ec2:DescribeNetworkInterfaces
                  - ec2:AttachNetworkInterface
                Resource: '*'
  InstanceProfile:
    DependsOn: SessionManagerRole
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: '/'
      Roles:
        - !Ref SessionManagerRole

  ##################################################
  # Target VPC
  ##################################################
  VPCTarget:
    Type: 'AWS::EC2::VPC'
    Properties:
      CidrBlock: !Ref VpcCIDRTarget
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: !Ref VpcNameTarget

  InternetGatewayTarget:
    Type: 'AWS::EC2::InternetGateway'
    Properties:
      Tags:
        - Key: Name
          Value: !Sub ${VpcNameTarget}-igw

  VPCGatewayAttachmentTarget:
    Type: 'AWS::EC2::VPCGatewayAttachment'
    Properties:
      VpcId: !Ref VPCTarget
      InternetGatewayId: !Ref InternetGatewayTarget

  PublicSubnetTarget:
    Type: 'AWS::EC2::Subnet'
    Properties:
      VpcId: !Ref VPCTarget
      AvailabilityZone: !Ref AvailabilityZone
      CidrBlock: !Select [ 0, !Cidr [ !Ref VpcCIDRTarget, 24, 8 ] ]
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub ${VpcNameTarget}-subnet

  PublicRouteTableTarget:
    Type: 'AWS::EC2::RouteTable'
    Properties:
      VpcId: !Ref VPCTarget
      Tags:
        - Key: Name
          Value: !Sub ${VpcNameTarget}-rtb-public
  PublicRouteTarget:
    Type: 'AWS::EC2::Route'
    DependsOn: VPCGatewayAttachmentTarget
    Properties:
      RouteTableId: !Ref PublicRouteTableTarget
      DestinationCidrBlock: '0.0.0.0/0'
      GatewayId: !Ref InternetGatewayTarget

  SubnetRouteTableAssociationTarget:
    Type: 'AWS::EC2::SubnetRouteTableAssociation'
    Properties:
      SubnetId: !Ref PublicSubnetTarget
      RouteTableId: !Ref PublicRouteTableTarget

  SecurityGroupForTarget:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: !Ref VPCTarget
      GroupDescription: "Target VPC SG"

  EniTarget01:
    Type: AWS::EC2::NetworkInterface
    Properties:
      Tags:
        - Key: Name
          Value: TargetEni01
      GroupSet:
        - !GetAtt SecurityGroupForTarget.GroupId
      SubnetId: !Ref PublicSubnetTarget

  EniTarget02:
    Type: AWS::EC2::NetworkInterface
    Properties:
      Tags:
        - Key: Name
          Value: TargetEni02
      GroupSet:
        - !GetAtt SecurityGroupForTarget.GroupId
      SubnetId: !Ref PublicSubnetTarget

  ##################################################
  # AutoScaling
  ##################################################
  # LaunchTemplate
  myLaunchTemplate:
    Type: AWS::EC2::LaunchTemplate
    Properties:
      LaunchTemplateData:
        ImageId: !Ref ImageId
        InstanceType: !Ref InstanceTypeName
        IamInstanceProfile:
          Name: !Ref InstanceProfile
        SecurityGroupIds:
          - !GetAtt SecurityGroupForSource.GroupId
        UserData:
          Fn::Base64: !Sub
            - |
              #!/bin/bash

              function eni_attach() {
                INSTANCE_ID=`ec2-metadata -i | awk '{print $2}'`
                TARGET_AZ=`ec2-metadata -z | awk '{print $2}'`

                ALLOC_ID=$(/usr/bin/aws ec2 describe-network-interfaces \
                  --filters "Name=vpc-id, Values=${VPCTarget}" "Name=availability-zone, Values=$TARGET_AZ" "Name=status, Values=available" \
                  --query "sort_by(NetworkInterfaces, &PrivateIpAddress)[].NetworkInterfaceId | [0]" \
                  --output text)
                /usr/bin/aws ec2 attach-network-interface --instance-id=$INSTANCE_ID \
                  --device-index=1 --network-interface-id=$ALLOC_ID
              }
              eni_attach || sudo shutdown -h now
            - {
                VPCTarget: !Ref VPCTarget
              }

  myASG:
    Type: AWS::AutoScaling::AutoScalingGroup
    Properties:
      VPCZoneIdentifier:
      - !Ref PublicSubnetSource
      LaunchTemplate:
        LaunchTemplateId: !Ref myLaunchTemplate
        Version: !GetAtt myLaunchTemplate.LatestVersionNumber
      MaxSize: '1'
      MinSize: '0'
      DesiredCapacity: '1'

ユーザーデータは、以下のサイトを参考にしました。ほぼ同じなので詳細は以下をご覧ください。

インスタンスのVPCやAZの情報は、以下のサイトを参考にec2-metadataで取得しています。

実行結果

CFnテンプレートを実行すると、10.80.0.0/16にENIが作られます。
image.png

加えて、EC2インスタンスが1つ作成され、別VPCのENIがアタッチされています。
image.png

この1台を終了して、AutoScalingで立ち上げさせます。
別のマシンが立ち上がり、そちらでもENIがアタッチされました。
image.png

AutoScalingの設定を変更して、2台動かすようにしてみます。
2台目にもアタッチされました。
image.png

ENIも2つ使われています。
image.png

また1台に戻してみます。
片方のENIが開放されました。
image.png

おわりに

今回はユーザーデータを使って、スケールアウト時にEC2に別VPCのENIをアタッチしました。
以前のStep Functionsで行う方法では、スケールアウト成功のイベント時をトリガーにしていましたが、今回はEC2起動時なのでENIがアタッチするタイミングは早くなったと思われます。
一方、EC2に余分な権限(ENIを検索・アタッチ)を付けていますので、その点許容する必要があります。

この記事がどなたかのお役に立てれば幸いです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0