AWS LambdaとSSMを使用したEC2インスタンスの状態監視とALBの動的切り替え②

## はじめに
このブログ記事では、AWS Lambdaを使用してEC2インスタンスのhttpdサービスの状態を監視し、サービスの状態に応じてALB (Application Load Balancer) のリスナールールを動的に変更する方法を紹介します。

メンテナンスモードや通常モードへの切り替えを自動化することで、システムの可用性を高めることができます。

## 全体のコード
```py
import boto3

def lambda_handler(event, context):
    ec2_client = boto3.client('ec2')
    ssm_client = boto3.client('ssm')
    elbv2_client = boto3.client('elbv2')

    # 監視対象のEC2インスタンスIDリスト
    instance_ids = ["i-0458be1d8c980e8ec", "i-0b27db6a9b067f345"]

    # httpdサービスの状態を確認するためのSSM Run Command
    command = "systemctl is-active httpd"

    # httpdサービスが停止しているインスタンス数をカウント
    instances_stopped_count = 0
    for instance_id in instance_ids:
        try:
            response = ssm_client.send_command(
                InstanceIds=[instance_id],
                DocumentName="AWS-RunShellScript",
                Parameters={'commands': [command]}
            )
            
            command_id = response['Command']['CommandId']
            output = ssm_client.get_command_invocation(
                CommandId=command_id,
                InstanceId=instance_id
            )

            # httpdサービスが停止している場合にカウント
            if output['StandardOutputContent'].strip() != 'active':
                instances_stopped_count += 1

        except Exception as e:
            print(f"Error checking httpd service on instance {instance_id}: {e}")

    # インスタンスの状態に応じてALBリスナールールの優先度を変更
    if instances_stopped_count == len(instance_ids):
        # 全てのインスタンスでhttpdが停止している場合、メンテナンスモードへ切り替え
        response = elbv2_client.set_rule_priorities(
            RulePriorities=[
                {
                    'RuleArn': 'arn:aws:elasticloadbalancing:ap-northeast-1:YOUR_SORRY_PAGE_RULE_ARN',
                    'Priority': 1
                },
                {
                    'RuleArn': 'arn:aws:elasticloadbalancing:ap-northeast-1:YOUR_NORMAL_RULE_ARN',
                    'Priority': 2
                },
            ]
        )
    else:
        # 少なくとも1台が稼働中の場合、通常モードに戻す
        response = elbv2_client.set_rule_priorities(
            RulePriorities=[
                {
                    'RuleArn': 'arn:aws:elasticloadbalancing:ap-northeast-1:YOUR_NORMAL_RULE_ARN',
                    'Priority': 1
                },
                {
                    'RuleArn': 'arn:aws:elasticloadbalancing:ap-northeast-1:YOUR_SORRY_PAGE_RULE_ARN',
                    'Priority': 2
                },
            ]
        )
    
    return {
        'statusCode': 200,
        'body': 'Lambda execution completed successfully'
    }
```

## プログラムの処理内容
### 1. EC2クライアントとNLBクライアントの作成
boto3 ライブラリを使用して、AWSのEC2、SSM、ELBのクライアントを作成します。このライブラリを使うことで、PythonからAWSリソースにアクセスできます。

### 2. EC2インスタンスの状態確認
監視するEC2インスタンスのIDをリストとして定義し、各インスタンスでsystemctl is-active httpdコマンドを実行します。このコマンドにより、httpdサービスが稼働しているかどうかを確認します。

### 3. インスタンスの停止状態を確認
各インスタンスのhttpdサービスが停止している場合、その数をカウントします。send_commandとget_command_invocationメソッドを使用して、コマンドの実行結果を取得します。

### 4. ALBリスナールールの切り替え
2台ともhttpdサービスが停止している場合には、ALBリスナールールの優先度を変更して、メンテナンスモード（Sorryページ）に切り替えます。少なくとも1台が稼働中の場合は、通常ページを優先します。

### 5. 処理の結果を返す
処理が完了したことを示すステータスコードとメッセージを返します。

## 必要な設定とIAMポリシー
### EC2インスタンスの設定
**SSMエージェントのインストール:** 
EC2インスタンスにSSMエージェントがインストールされていることを確認します。多くのAmazon Linux 2 AMIでは、デフォルトでSSMエージェントがインストールされています。

```
sudo yum install -y amazon-ssm-agent
sudo systemctl enable amazon-ssm-agent
sudo systemctl start amazon-ssm-agent
```

**IAMロールの設定:** 
EC2インスタンスに以下のポリシーを持つIAMロールをアタッチします。

```
AmazonEC2RoleforSSM
AmazonSSMManagedInstanceCore
```

### Lambda関数の設定
IAMロールの設定: 
Lambda関数に以下のポリシーを持つIAMロールをアタッチします。

```
ssm:SendCommand
ssm:GetCommandInvocation
elasticloadbalancing:SetRulePriorities
elasticloadbalancing:DescribeRules
```

## 注意点

:::note alert
注意点は、以下になります。
**リトライとエラーハンドリング:** 
実際の運用環境では、コマンド実行や結果取得が失敗する場合もあるため、リトライ処理やエラーハンドリングを追加することをお勧めします。

**コマンド結果の取得タイミング:** 
コマンドの出力を取得するために、適切なポーリング間隔を設定してください。現在はシンプルにtime.sleep()で待機していますが、より適切な実装を行うと良いでしょう。

:::

## まとめ
この手法を使うことで、AWS LambdaとSSMを活用してEC2インスタンスの状態を詳細に監視し、動的にALBのリスナールールを変更することが可能になります。

メンテナンス作業やサーバーのダウンタイム時にも柔軟に対応できるため、より高い可用性とユーザー体験を提供することができます。

これで、EC2インスタンスの状態に応じたメンテナンスモードの切り替えを自動化する仕組みが完成です。