gokigenmaruのブログ

40から始めるクラウドエンジニア

Lambdaを使ってターゲットグループのIPアドレス情報を更新する

前回お話したIPv6の話の続きです。
(C) 2台の NLB でターゲットグループのタイプを「IPアドレス」にし ASG でノード入れ替えを検出する仕組みを実装
この仕組みを実装するためには、オートスケーリンググループでEC2が起動・停止した際に、起動・停止したEC2のIPv6アドレス情報を取得しターゲットグループに設定をしなければいけません。
仕組みとしては、以下2つに分かれます。
・EC2の起動・停止を捕捉する仕組みを作る
・捕捉したEC2のIPv6アドレスをターゲットグループで設定する

今回は「捕捉したEC2のIPv6アドレスをターゲットグループで設定する」のお話となります。

構成イメージ

構成図1

構成のイメージはこの通りです。
LoadBalancer(DualStack)のターゲットグループをIPタイプ(IPv6)で設定する必要があるのですが、IPタイプだとターゲットグループをAutoScalingGroupに設定が出来ず、EC2が起動・停止した際に自動で追従してくれないため、EventBridgeとLambdaで実装しようとするものです。

EventBridgeでとれる情報

EventBridgeでEC2の起動・停止のEventを検知した際、EventBridgeはその情報をJSON形式で送付してくれます。
実際にどのような内容かを確認するため、EventBridgeで検知した情報をメールで通知してもらったところ、検証時点は以下の情報を送ってくれました。

起動時

{
    "version": "0",
    "id": "xxxxxxxx-xxxxxxxx-xxxxxxxx",
    "detail-type": "EC2 Instance Launch Successful",
    "source": "aws.autoscaling",
    "account": "123456789012",
    "time": "2023-02-24T02:08:52Z",
    "region": "us-west-2",
    "resources": [
        "arn:aws:autoscaling:us-west-2:123456789012:autoScalingGroup:xxxxxxxx-xxxxxxxx-xxxxxxxx:autoScalingGroupName/test-EC2-asg",
        "arn:aws:ec2:us-west-2:123456789012:instance/i-xxxxxxxxxxxxxxxxxxxx"
    ],
    "detail": {
        "Origin": "EC2",
        "Destination": "AutoScalingGroup",
        "Description": "Launching a new EC2 instance: i-xxxxxxxxxxxxxxxxxxxx",
        "EndTime": "2023-02-24T02:08:52.968Z",
        "RequestId": "xxxxxxxx-xxxx-xxxx-xxxxx-xxxxxxxx",
        "ActivityId": "xxxxxxxx-xxxx-xxxx-xxxxx-xxxxxxxx",
        "StartTime": "2023-02-24T02:08:46.589Z",
        "EC2InstanceId": "i-xxxxxxxxxxxxxxxxxxxx",
        "StatusCode": "InProgress",
        "StatusMessage": "",
        "Details": {
            "Subnet ID": "subnet-xxxxxxxxxxxxxxx",
            "Availability Zone": "us-west-2b"
        },
        "Cause": "At 2023-02-24T02:08:40Z a user request update of AutoScalingGroup constraints to min: 1, max: 2, desired: 2 changing the desired capacity from 1 to 2.  At 2023-02-24T02:08:44Z an instance was started in response to a difference between desired and actual capacity, increasing the capacity from 1 to 2.",
        "AutoScalingGroupName": "test-EC2-asg"
    }
}

終了時

{
    "version": "0",
    "id": "xxxxxxxxx-xxxx-xxxx-xxxxxxxx",
    "detail-type": "EC2 Instance-terminate Lifecycle Action",
    "source": "aws.autoscaling",
    "account": "123456789012",
    "time": "2023-03-01T08:12:42Z",
    "region": "us-west-2",
    "resources": [
        "arn:aws:autoscaling:us-west-2:123456789012:autoScalingGroup:xxxxxxxxx-xxxx-xxxx-xxxxxxxx:autoScalingGroupName/test-EC2-asg",
        "arn:aws:ec2:us-west-2:123456789012:instance/i-xxxxxxxxxxxxxx"
    ],
    "detail": {
        "Origin": "AutoScalingGroup",
        "Destination": "EC2",
        "Description": "Terminating EC2 instance: i-xxxxxxxxxxxxxx",
        "EndTime": "2023-03-01T08:12:42.111Z",
        "RequestId": "xxxxxxxx-xxxx-xxxxx-xxxxxxxx",
        "ActivityId": "xxxxxxxx-xxxx-xxxxx-xxxxxxxx",
        "StartTime": "2023-03-01T08:06:30.045Z",
        "EC2InstanceId": "i-xxxxxxxxxxxxxx",
        "StatusCode": "InProgress",
        "StatusMessage": "",
        "Details": {
            "Subnet ID": "subnet-xxxxxxxxxxxxxxx",
            "Availability Zone": "us-west-2a"
        },
        "Cause": "At 2023-03-01T08:06:19Z a user request update of AutoScalingGroup constraints to min: 1, max: 8, desired: 1 changing the desired capacity from 6 to 1.  At 2023-03-01T08:06:29Z an instance was taken out of service in response to a difference between desired and actual capacity, shrinking the capacity from 6 to 1.  At 2023-03-01T08:06:29Z instance i-01d59fb8541769dcb was selected for termination.  At 2023-03-01T08:06:29Z instance i-0e85184471d0cf7d2 was selected for termination.  At 2023-03-01T08:06:29Z instance i-0549fb56aebf40758 was selected for termination.  At 2023-03-01T08:06:30Z instance i-xxxxxxxxxxxxxx was selected for termination.  At 2023-03-01T08:06:30Z instance i-xxxxxxxxxxxx was selected for termination.",
        "AutoScalingGroupName": "test-EC2-asg"
    }
}

IPアドレス情報が取れるとよかったのですが、残念ながら検証時点ではEC2のIPアドレス情報は取れず。
ただ、インスタンスIDが入っていたので、これをもとにLambdaでEC2の詳細情報を出してIPアドレス情報を抜き出すことにしました。

Lambdaのコード

コードはPythonで書きました。
Python初心者のインフラ屋が適当に作ったコードなので、ほかの方々から見れば「なんだこれ」って思うかもしれません…。

簡単に説明をすると、

  • import boto3 -> PythonAWSの各種サービスをいじれるようになる
  • event -> EventBridgeで送付されるJSON情報
  • detailtype -> EC2の状態を取得し変数に格納(起動 or 停止)
  • ec2info -> EventBridgeからとれるEC2のIDからEC2詳細情報を取得し変数に格納
  • avzone -> AZを変数に格納
  • ipv6addr -> インスタンスIPv6アドレスを変数に格納
  • detailtypeに応じて起動ならIPv6アドレスをターゲットグループに追加、停止ならIPv6アドレスをターゲットグループから削除
import json
import boto3


def lambda_handler(event, context):
    detailtype = event["detail-type"]
    ec2id = event["detail"]["EC2InstanceId"]
    
    print(ec2id)
    
    ec2 = boto3.client('ec2')
    ec2info = ec2.describe_instances(
                InstanceIds=[ec2id],
    )

    print(ec2info)


    avzone = ec2info["Reservations"][0]["Instances"][0]["Placement"]["AvailabilityZone"]
    ipv6addr = ec2info["Reservations"][0]["Instances"][0]["NetworkInterfaces"][0]["Ipv6Addresses"][0]["Ipv6Address"]
    
    print(avzone)
    print(ipv6addr)


    if detailtype == "EC2 Instance Launch Successful":
        client = boto3.client('elbv2')
        response = client.register_targets(
            TargetGroupArn='arn:aws:elasticloadbalancing:リージョン名:xxxxxxxxxxxxxx:targetgroup/ターゲットグループ名/xxxxxxxx',
            Targets=[
                {
                    'Id': ipv6addr,
                    'Port': 443,
                    'AvailabilityZone': avzone
                },
            ]
        )
    

    elif detailtype == "EC2 Instance-terminate Lifecycle Action":
        client = boto3.client('elbv2')
        response = client.deregister_targets(
            TargetGroupArn='arn:aws:elasticloadbalancing:リージョン名:xxxxxxxxxxxxxx:targetgroup/ターゲットグループ名/xxxxxxxx',
            Targets=[
                {
                    'Id': ipv6addr,
                    'Port': 443,
                    'AvailabilityZone': avzone
                },
            ]
        )
    

    else:
        return

Lambdaに設定するIAMロール

Lambdaに設定するIAMロールはEC2の情報が取れることと、TargetGroupのwirte権が必要です。
面倒だったのでEC2のFullAccessを与えていますが、本来はもっと絞ったほうがいいかなと…。

EventBridge側設定

前回作成したEventBridgeでターゲットをメールとかの適当なSNSにしていたと思いますが、このLambda作成後はターゲットをこのLambdaにすればOKです。

まとめ

これでオートスケーリンググループ内のEC2が起動・停止した際にターゲットグループのIPアドレスを変更することが出来ました。
基本はターゲットグループをインスタンスタイプで作成すると思うので、AWSの機能でうまくやってもらえるのですが、それが出来ない場合に少しでも参考になれば幸いです。
Pythonのプログラムはいけてないと思うので、出来る方々は直して使ってください。