0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

AutoScalingで、EC2に別VPCのENIをアタッチ

Posted at

はじめに

先日、EC2のアップデートで、別のVPCのENIをアタッチできるようになり、それを記事にしました。

これを、AutoScalingでもやってみました。

概要

  • AutoScalingでENIを追加でアタッチする機能はない
  • AutoScalingグループにライフサイクルフックを追加して、EventBridge -> StepFunctionsでアタッチします
  • 事前に別VPCにENIを作っておきます
    • 同じAZに作っておく必要があります

参考

構成図

image.png

CloudFormationテンプレート

長いですが、CloudFormationは以下になります。

クリックで表示

EniTarget01とEniTarget02で、別VPC上に事前にENIを作成しています。
AMIのImageIdのデフォルトは東京リージョンのものです。

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
  InstanceProfile:
    DependsOn: SessionManagerRole
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: '/'
      Roles:
        - !Ref SessionManagerRole

  # LaunchTemplate
  myLaunchTemplate:
    Type: AWS::EC2::LaunchTemplate
    Properties:
      LaunchTemplateData:
        ImageId: !Ref ImageId
        InstanceType: !Ref InstanceTypeName
        IamInstanceProfile:
          Name: !Ref InstanceProfile
        SecurityGroupIds:
        - !GetAtt SecurityGroupForSource.GroupId

  myASG:
    Type: AWS::AutoScaling::AutoScalingGroup
    Properties:
      VPCZoneIdentifier:
      - !Ref PublicSubnetSource
      LaunchTemplate:
        LaunchTemplateId: !Ref myLaunchTemplate
        Version: !GetAtt myLaunchTemplate.LatestVersionNumber
      MaxSize: '1'
      MinSize: '0'
      DesiredCapacity: '1'
      LifecycleHookSpecificationList:
        - DefaultResult: CONTINUE
          HeartbeatTimeout: 60
          LifecycleHookName: testLifeCycleHook
          LifecycleTransition: autoscaling:EC2_INSTANCE_LAUNCHING
          NotificationMetadata: message

  ##################################################
  # 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

  ##################################################
  # Role 
  ##################################################
  # for StepFunctions
  StepFunctionsRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: 'Allow'
            Principal:
              Service:
                - 'states.amazonaws.com'
            Action:
              - 'sts:AssumeRole'
      Path: '/'
      Policies:
        - PolicyName: writeEni
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - ec2:DescribeNetworkInterfaces
                  - ec2:AttachNetworkInterface
                Resource: '*'

  # for EventBridge
  EventBridgeRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: 'Allow'
            Principal:
              Service:
                - 'events.amazonaws.com'
            Action:
              - 'sts:AssumeRole'
      Path: '/'
      Policies:
        - PolicyName: invokeEb
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - states:StartExecution
                Resource: '*'

  ##################################################
  # StateMachine
  ##################################################
  MyStateMachine:
    Type: AWS::StepFunctions::StateMachine
    Properties:
      StateMachineName: attachEniOnOtherVpc-StateMachine
      DefinitionString: !Sub |-
        {
          "Comment": "A description of my state machine",
          "StartAt": "DescribeNetworkInterfaces",
          "States": {
            "DescribeNetworkInterfaces": {
              "Type": "Task",
              "Parameters": {
                "Filters": [
                  {
                    "Name": "vpc-id",
                    "Values": [
                      "${VPCTarget}"
                    ]
                  },
                  {
                    "Name": "availability-zone",
                    "Values.$": "States.Array($['detail']['Details']['Availability Zone'])"
                  },
                  {
                    "Name": "status",
                    "Values": [
                      "available"
                    ]
                  }
                ]
              },
              "Resource": "arn:aws:states:::aws-sdk:ec2:describeNetworkInterfaces",
              "Next": "Count",
              "ResultPath": "$.filtered"
            },
            "Count": {
              "Type": "Pass",
              "Parameters": {
                "length.$": "States.ArrayLength($.filtered.NetworkInterfaces)"
              },
              "ResultPath": "$.count",
              "Next": "Choice"
            },
            "Choice": {
              "Type": "Choice",
              "Choices": [
                {
                  "Variable": "$.count.length",
                  "NumericGreaterThan": 0,
                  "Next": "AttachNetworkInterface"
                }
              ],
              "Default": "Fail"
            },
            "AttachNetworkInterface": {
              "Type": "Task",
              "Next": "Success",
              "Parameters": {
                "DeviceIndex": 1,
                "InstanceId.$": "$.detail.EC2InstanceId",
                "NetworkInterfaceId.$": "$.filtered.NetworkInterfaces[0].NetworkInterfaceId"
              },
              "Resource": "arn:aws:states:::aws-sdk:ec2:attachNetworkInterface"
            },
            "Success": {
              "Type": "Succeed"
            },
            "Fail": {
              "Type": "Fail"
            }
          }
        }
      RoleArn: !GetAtt StepFunctionsRole.Arn

  ##################################################
  # Events::Rule
  ##################################################
  MyNewEventsRule:
    Type: 'AWS::Events::Rule'
    Properties:
      Description: Test Events Rule
      Name: catchScaleout
      EventPattern:
        source:
          - aws.autoscaling
        detail-type: 
          - "EC2 Instance Launch Successful"
      State: ENABLED
      Targets:
        - Arn: !GetAtt MyStateMachine.Arn
          Id: Id1234
          RoleArn: !GetAtt EventBridgeRole.Arn

StepFunctionsの説明

フローは以下になります。
image.png

以下のような動きをします。

  1. DescribeNetworkInterfaces

    • 以下の条件に一致するENIを抽出します
      • 紐づけるENIを持つVPC上にある
      • 使用可能
      • スケールアウトして作成されたEC2と同じAZ上にある
        • インスタンスの実行AZがわかるAutoScalingのイベントが、スケールアウト成功なのでそれを使っています
  2. Count

    • 抽出したENIの個数をカウント
  3. Choice

    • Countの結果が0より多いかどうか判定
  4. AttachNetworkInterface

    • 抽出したENIの中で1つ目のENIを、スケールアウトして作成されたEC2にアタッチ

実行結果

CFnテンプレートを実行すると、EC2インスタンスが1つ作成され、別VPCのENIがアタッチされています。

image.png

StepFunctionsも実行されています。
image.png

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

StepFunctionsも実行されています。
image.png

AutoScalingの設定を変更して、2台動かすようにしてみます。
image.png

2台目にもアタッチされました。
image.png

StepFunctionsも実行されています。
image.png

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

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

おわりに

今回はAutoScalingでスケールアウト時に、別VPCの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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?