前回お話したIPv6の話の続きです。
(C) 2台の NLB でターゲットグループのタイプを「IPアドレス」にし ASG でノード入れ替えを検出する仕組みを実装
この仕組みを実装するためには、オートスケーリンググループでEC2が起動・停止した際に、起動・停止したEC2のIPv6アドレス情報を取得しターゲットグループに設定をしなければいけません。
仕組みとしては、以下2つに分かれます。
・EC2の起動・停止を捕捉する仕組みを作る
・捕捉したEC2のIPv6アドレスをターゲットグループで設定する
今回は「捕捉したEC2のIPv6アドレスをターゲットグループで設定する」のお話となります。
構成イメージ
構成のイメージはこの通りです。
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 -> PythonでAWSの各種サービスをいじれるようになる
- 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です。