ElasticBeansTalk のWorkerノードを一時的に停止する方法


## はじめに
最近とあるAWS上のシステムのRDSをSingle-AZからMulti-AZに変更する作業が必要になった。

このシステムではIoTデバイスから取得したデータをEC2のWebサーバで受信し、受信したデータをそのままSQSに転送している。
このSQSのキューはElasticBeansTalk(以下EB)で作成したもので、キューの後ろにはそのデータを処理するWorkerノードを構成している。

Multi-AZ構成に変更する際にWorkerノードが起動した状態のままだと、DBへの登録が失敗し、データがDeadLetterQueueに転送される可能性があった。
そのため、Multi-AZへの構成変更の間、SQSからのメッセージ取得を一時的に止める必要があった。

EBのCLI（ebコマンド）やAWSコンソールから一時的にWorkerの動作を停止することはできないことが分かったため、代替手順を実施した。

この記事はその備忘録である。

## 手順
手順は以下の流れで行う。

1. WorkerのAutoScale機能を停止
2. WorkerのEC2インスタンスを停止
3. RDSの構成変更
4. WorkerのEC2インスタンスを起動
5. WorkerのAutoScalingGroupの状態を手動でHealthyに変更
6. WorkerのAutoScale機能を開始

システムの構成情報を以下に示す。

* WorkerのAutoScalingGroup名: awseb-e-xxxxxxxxxx-stack-AWSEBAutoScalingGroup-XXXXXXXXXXXXX

```
% aws autoscaling describe-auto-scaling-groups --auto-scaling-group-name awseb-e-xxxxxxxxxx-stack-AWSEBAutoScalingGroup-XXXXXXXXXXXXX
{
    "AutoScalingGroups": [
        {
            "AutoScalingGroupName": "awseb-e-xxxxxxxxxx-stack-AWSEBAutoScalingGroup-XXXXXXXXXXXXX",
            "LaunchConfigurationName": "awseb-e-xxxxxxxxxx-stack-AWSEBAutoScalingLaunchConfiguration-YYYYYYYYYYYYY"",
            "MinSize": 1,
            "MaxSize": 4,
            "DesiredCapacity": 1,
            "DefaultCooldown": 360,
            "AvailabilityZones": [
                "ap-northeast-1b",
                "ap-northeast-1c"
            ],
            "LoadBalancerNames": [],
            "TargetGroupARNs": [],
            "HealthCheckType": "EC2",
            "HealthCheckGracePeriod": 0,
…
```

MinSizeが1なので、低負荷時はインスタンスが1つしか存在していない。

### 1. WorkerのAutoScale機能を停止
SuspendProcessesの設定に「Terminate」を追加する。

```
% aws autoscaling describe-auto-scaling-groups --auto-scaling-group-name awseb-e-xxxxxxxxxx-stack-AWSEBAutoScalingGroup-XXXXXXXXXXXXX | jq .AutoScalingGroups[0].SuspendedProcesses
[]
% aws autoscaling suspend-processes --auto-scaling-group-name awseb-e-xxxxxxxxxx-stack-AWSEBAutoScalingGroup-XXXXXXXXXXXXX --scaling-processes Terminate
% aws autoscaling describe-auto-scaling-groups --auto-scaling-group-name awseb-e-xxxxxxxxxx-stack-AWSEBAutoScalingGroup-XXXXXXXXXXXXX | jq .AutoScalingGroups[0].SuspendedProcesses
[
  {
    "ProcessName": "Terminate",
    "SuspensionReason": "User suspended at 2018-02-22T11:11:24Z"
  }
]
% aws autoscaling describe-auto-scaling-groups --auto-scaling-group-name awseb-e-xxxxxxxxxx-stack-AWSEBAutoScalingGroup-XXXXXXXXXXXXX | jq .AutoScalingGroups[0].Instances
[
  {
    "InstanceId": "i-1234567abcdefghij",
    "AvailabilityZone": "ap-northeast-1c",
    "LifecycleState": "InService",
    "HealthStatus": "Healthy",
    "LaunchConfigurationName": "awseb-e-xxxxxxxxxx-stack-AWSEBAutoScalingLaunchConfiguration-YYYYYYYYYYYYY",
    "ProtectedFromScaleIn": false
  }
]
```

### 2. WorkerのEC2インスタンスを停止
* EC2のコンソールから当該インスタンスを停止
  * ElasticBeansTalk上でも重大エラーになる

#### AutoScale状態確認
インスタンスを停止するとHealthStatusが「Unhealty」になることが確認できる。

```
% aws autoscaling describe-auto-scaling-groups --auto-scaling-group-name awseb-e-xxxxxxxxxx-stack-AWSEBAutoScalingGroup-XXXXXXXXXXXXX | jq .AutoScalingGroups[0].Instances
[
  {
    "InstanceId": "i-1234567abcdefghij",
    "AvailabilityZone": "ap-northeast-1c",
    "LifecycleState": "InService",
    "HealthStatus": "Unhealthy",
    "LaunchConfigurationName": "awseb-e-xxxxxxxxxx-stack-AWSEBAutoScalingLaunchConfiguration-YYYYYYYYYYYYY",
    "ProtectedFromScaleIn": false
  }
]
```

この時点でSQSのキューを確認すると、メッセージが処理されずキューに蓄積されて行くことが確認できる。
メッセージの可視性が保たれる期間内にWorkerを復旧する。

### 3. RDSの構成変更
詳細手順は割愛。

### 4. WorkerのEC2インスタンスを起動
* EC2のコンソールから当該インスタンスを起動
  * ElasticBeansTalk上ではエラー解消になる
  
インスタンスを起動してもHealthStatusは自動的に「Healty」にはならないため次の手順で手動で「Healthy」へ状態を遷移させる。

### 5. WorkerのEC2インスタンスの状態を手動でHealthyに変更
※この手順を省くと、次の手順を実施した際に当該インスタンスは削除され、新規のインスタンスが生成される。

```
% aws autoscaling set-instance-health --instance-id i-1234567abcdefghij --health-status Healthy
% aws autoscaling describe-auto-scaling-groups --auto-scaling-group-name awseb-e-xxxxxxxxxx-stack-AWSEBAutoScalingGroup-XXXXXXXXXXXXX | jq .AutoScalingGroups[0].Instances
[
  {
    "InstanceId": "i-1234567abcdefghij",
    "AvailabilityZone": "ap-northeast-1c",
    "LifecycleState": "InService",
    "HealthStatus": "Healthy",
    "LaunchConfigurationName": "awseb-e-xxxxxxxxxx-stack-AWSEBAutoScalingLaunchConfiguration-YYYYYYYYYYYYY",
    "ProtectedFromScaleIn": false
  }
]
```

### 6. WorkerのAutoScale機能を開始
SuspendProcessesの設定から「Terminate」を削除する。

```
% aws autoscaling resume-processes --auto-scaling-group-name awseb-e-xxxxxxxxxx-stack-AWSEBAutoScalingGroup-XXXXXXXXXXXXX --scaling-processes Terminate
% aws autoscaling describe-auto-scaling-groups --auto-scaling-group-name awseb-e-xxxxxxxxxx-stack-AWSEBAutoScalingGroup-XXXXXXXXXXXXX | jq .AutoScalingGroups[0].SuspendedProcesses
[]
```

## 参考
https://qiita.com/MasaoDX/items/07e46186c450799c6350