2台のEC2インスタンスの状態を自動検知し、ALBのレスポンスを切り替える手順

## はじめに
AWS環境において、2台のEC2インスタンスが停止した際に、ALB（Application Load Balancer）の固定レスポンス機能を使って「Sorryページ」を表示し、1台でも起動している場合は通常のページを表示する仕組みを構築します。

このプロセスでは、Amazon EventBridge、Lambda、ALBを組み合わせて自動化します。

## 1. Amazon EventBridge で EC2 インスタンスの状態を検知
まず、EC2インスタンスが停止したことを検知するために、EventBridgeルールを設定します。

ここでは、「i-0458be1d8c980e8ec」と「i-0b27db6a9b067f345」という2台のEC2インスタンスを監視対象とします。

以下のJSONルールを使って、これらのインスタンスが停止状態に入った時点でEventBridgeがトリガーされるように設定します。

```json
{
  "source": ["aws.ec2"],
  "detail-type": ["EC2 Instance State-change Notification"],
  "detail": {
    "state": ["stopped"],
    "instance-id": ["i-0458be1d8c980e8ec", "i-0b27db6a9b067f345"]
  }
}
```

このルールにより、どちらかのインスタンスが停止状態になった場合にLambda関数が実行されます。

## 2. Lambda関数の作成
次に、2台のEC2インスタンスが停止しているかどうかを確認し、その結果に基づいてALBのリスナールールを変更するLambda関数を作成します。

2台とも停止している場合にはSorryページを、1台でも起動している場合には通常のページを表示します。

```python
import boto3

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

    # 監視する2台のEC2インスタンスのID
    instance_ids = ["i-0458be1d8c980e8ec", "i-0b27db6a9b067f345"]
    
    # EC2インスタンスの状態を取得
    response = ec2_client.describe_instances(InstanceIds=instance_ids)
    
    # 停止しているインスタンスをカウント
    instances_stopped_count = sum(
        1 for reservation in response['Reservations']
        for instance in reservation['Instances']
        if instance['State']['Name'] == 'stopped'
    )

    if instances_stopped_count == 2:  # 2台とも停止している場合
        # Sorryページに切り替え
        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
                },
            ]
        )

    # 現在のルール設定を確認
    result = elbv2_client.describe_rules(
        ListenerArn='arn:aws:elasticloadbalancing:ap-northeast-1:YOUR_LISTENER_ARN'
    )
    return result
```

:::note
**Lambda関数のコード解説**
2台のインスタンスが停止している場合：ALBのリスナールールを変更して「Sorryページ」に切り替えます。

1台でも起動している場合：通常のページに戻します。
:::

### 簡単なフロー処理

```yaml
開始
  |
  v
EC2クライアントとELBクライアントを初期化
  |
  v
監視するEC2インスタンスのIDを設定
  |
  v
EC2インスタンスの状態を取得
  |
  v
停止しているインスタンスの数をカウント
  |
  v
インスタンスが2台とも停止しているか？
  |                    |
  |                    |
  |はい                 |いいえ
  |                    |
  v                    v
Sorryページに切り替え  通常ページに戻す
  |                    |
  v                    v
ルールの優先度を設定     ルールの優先度を設定
  |                    |
  v                    v
現在のルール設定を確認
  |
  v
終了
```


## IAMの権限設定
Lambda関数がEC2インスタンスの状態を確認し、ALBのリスナールールを変更するためには、Lambdaに適切な権限を持たせる必要があります。

以下の手順でIAMロールを作成し、必要なポリシーを追加します。

**IAMロールの作成手順**
IAMコンソールにアクセスし、ロールの作成を開始します。
Lambdaサービス用にロールを作成し、以下のIAMポリシーを追加します。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ec2:DescribeInstances",
                "elasticloadbalancing:DescribeRules",
                "elasticloadbalancing:SetRulePriorities"
            ],
            "Resource": "*"
        }
    ]
}
```

:::note
**IAMポリシーの解説**
ec2:DescribeInstances：
EC2インスタンスの状態を確認するための権限。

elasticloadbalancing:DescribeRules：
ALBのリスナールールを確認するための権限。

elasticloadbalancing:SetRulePriorities：
ALBのリスナールールの優先順位を変更するための権限。
:::

このポリシーを持つロールを作成し、そのロールをLambda関数にアタッチします。

## 4. ALBの設定
ALB（Application Load Balancer）側のリスナールールを設定し、固定レスポンス機能を使ってSorryページを表示できるようにします。

**リスナールールの設定**
```
通常ページのルール：
EC2インスタンスが1台でも稼働している場合、通常のウェブサイトを表示します。

Sorryページのルール：
2台のインスタンスがすべて停止している場合、Sorryページ（固定レスポンス）を返します。
```

Lambda関数で使用するために、これらのリスナールールのARNを控えておきます。

**固定レスポンスの設定例**
ALBのコンソールで固定レスポンスを設定するには、以下の手順を行います。

```
ALBコンソールにアクセスし、ターゲットリスナーを選択します（例：ポート80または443）。

ルールの編集をクリックして、新しいルールを作成します。

条件として、すべてのトラフィックに対して固定レスポンスを設定するようにします。

アクションとして、「固定レスポンスを返す」を選択し、以下の情報を入力：
・ステータスコード：503
・レスポンスボディ：カスタムHTMLメッセージを入力。
```

この設定が完了すると、指定された条件が満たされた場合（つまり、すべてのインスタンスが停止している場合）、ALBは自動的にSorryページを返すようになります。


## 5. EventBridge と Lambda の統合
最後に、Amazon EventBridgeとLambda関数を統合します。

```
手順:
EventBridgeのルールを作成し、指定したインスタンスが停止した際にLambdaをトリガーするよう設定します。

ターゲットとして作成したLambda関数を指定します。
```

これにより、2台のEC2インスタンスが停止した場合に、ALBのリスナールールの優先順位が自動的に切り替わり、レスポンスが適切に制御されます。

## まとめ
この手順を通して、EC2インスタンスの状態に基づいて、ALBが自動的にレスポンスを切り替えるシステムを構築しました。

インスタンスが2台とも停止している場合にはSorryページが表示され、1台でも起動している場合には通常のページに戻すことができます。

この仕組みを使えば、ダウンタイムの際にもユーザーに適切なフィードバックを提供することが可能です。

## 参考記事
https://blog.denet.co.jp/elb-sorrypage/