LambdaでWorkSpacesを自動再起動する
ベルリンの半瀬です。
はじめに
「Workspaceに対してステータスチェックを行い、Unhealthyなステータスのものがあれば再起動を実行する」というフローを自動で行ないたいという相談を受けました。
EC2インスタンスのステータス変更をトリガーとするときは CloudWatch Eventsが便利ですが、WorksSpacesについては今のところ用意されていません。
そのため、今回は CloudWatch Alarm → SNS → Lambdaで作成する必要があります。
※ 参考 AWS Blackbelt 2015シリーズ Amazon CloudWatch & Amazon CloudWatch Logs 「CloudWatchを介したシステム運用の自動化」
手順
それではやってみましょう。
Amazon WorkSpacesを起動
テスト用のWorkSpaceを同ディレクトリ配下に2つ起動します。(testhanse, testhanse2)
CloudWatchのメトリックスを確認
Workspacesのメトリックスが作成されていることを確認します。
今回は組織名別(ディレクトリID別)のメトリックス「異常(UnHealthy)」を利用することになります。
CloudWatch Alarmを作成
- <CloudWatch>→<アラーム>で「アラーム作成」を選択します
- <メトリックスの選択>→<WorkSpacesメトリックス>→<ディレクトリID 別>を選択
- 対象となるディレクトリIDのメトリックス「異常(UnHealthy)」を指定し<次へ>をクリック
- 閾値は、「ディレクトリ配下のワークスペースに1台でも異常があった場合」を検知するため、「>0」とします。
要調整ですが、今回は「連続5回の失敗」でアラーム検知とします。 - 名前にはディレクトリIDを入れておきます。「{DirectoryId}_unhealthy-sns-lambda」としておきます。
- アクションの設定は後ほど行います。
SNS Topicを作成
Lambdaに渡すためのTopicです。今回は「topic-lambda-ws-reboot」と名付けます。
IAM Roleを作成
Lambda設定の前に、LambdaにWorkSpacesの操作権限を持たせるため、IAM Roleを準備します。
1. ロールタイプの選択で「AWS Lambda」を選択
2. ポリシー「AmazonWorkSpacesAdmin」を選択し作成します。
3. 作成完了。今回は「lambda-workspaces」と名付けています。
Lambdaを作成
メインとなるLambdaを設定していきます。
今回はpythonで記述をします。
1. <Create a Lambda function>
2. Step1: Select blueprintで「sns-message-python」を指定して<Next>
3. Step2: Configure event sourcesで先ほど作成したLambda用のSNS-Topic「topic-lambda-ws-reboot」を指定し、<Next>
4. Step3: Configure functionのLambda function codeで以下のコードを記載します。
下記コードですが、AWS APIの仕様上の上限により、25台以上のAD構成では機能しない可能性があります。25台以上の構成に対応したコードはただいま確認中です。 - 20160421
from __future__ import print_function import json import urllib import boto3 print('Loading function') client = boto3.client('workspaces') def lambda_handler(event, context): message = event['Records'][0]['Sns']['Message'] message = json.loads(message) dimensions = message['Trigger']['Dimensions'][0]['value'] directoryId = dimensions describe = client.describe_workspaces( DirectoryId = directoryId ) workspacesList = describe['Workspaces'] for workspace in workspacesList: workspaceId = workspace['WorkspaceId'] checkState = workspace['State'] if checkState == "UNHEALTHY": reboot = client.reboot_workspaces( RebootWorkspaceRequests=[ { 'WorkspaceId': workspaceId }, ] ) return reboot
5. 同Step3: Configure functionのLambda function handler and roleでLambda用のIAM Role「lambda-workspaces」を指定し、<Next>
6. Step4: Review のEvent sourcesで「Enable event source」を「Enable now」とし、<Create Function>。
7. Lambdaの設定は完了です
CloudWatch Alarmでアクション設定
最後にアラームのアクションを設定しておきます。
先ほど作成したCloudWatchアラーム「{DirectoryId}-unhealthy-lambda」内の通知アクションで、「警告」時の送信先をSNS-Topic「topic-lambda-ws-reboot」とします。
テスト
設定はすべて完了です。次に動作テストを行います。
設置したlambdaは以下のような動作をする様に作成しています。
1. SNS MessageからWorkspaceのDirectoryIdを受け取る
2. DirectoryIdから、当該ディレクトリ配下のWorkspaceId一覧を取得する
3. WorkspaceIdごとにStateをチェックし、"UNHEALTHY"なものはreboot実行
4. reboot実行結果を返り値として返す
※ 1台でもreboot実行されれば「200」、実行されなければ「null」になります。
通常時のテスト
ステータスが"AVAILABLE"(異常なし)のときに作動しないか念のため確認します。
1. テスト用jsonを読み込ませて実行します。
2. <Actions>→<Configure test event>→<Input test event>:<template>で以下を貼り付けます
{ "Records": [ { "EventVersion": "1.0", "EventSubscriptionArn": "arn:aws:sns:EXAMPLE", "EventSource": "aws:sns", "Sns": { "SignatureVersion": "1", "Timestamp": "1970-01-01T00:00:00.000Z", "Signature": "EXAMPLE", "SigningCertUrl": "EXAMPLE", "MessageId": "95df01b4-ee98-5cb9-9903-4c221d41eb5e", "Message": "{\"AlarmName\":\"AAAAAA\",\"AlarmDescription\":\"AAAAAA\",\"AWSAccountId\":\"NNNNNNNNNNNN\",\"NewStateValue\":\"ALARM\",\"NewStateReason\":\"Threshold Crossed: 1 datapoint (0.0) was greater than or equal to the threshold (0.0).\",\"StateChangeTime\":\"2016-02-13T18:15:41.677+0000\",\"Region\":\"APAC - Tokyo\",\"OldStateValue\":\"OK\",\"Trigger\":{\"MetricName\":\"Unhealthy\",\"Namespace\":\"AWS/WorkSpaces\",\"Statistic\":\"AVERAGE\",\"Unit\":null,\"Dimensions\":[{\"name\":\"DirectoryId\",\"value\":\"d-XXXXXXXXXX\"}],\"Period\":60,\"EvaluationPeriods\":1,\"ComparisonOperator\":\"GreaterThanOrEqualToThreshold\",\"Threshold\":0.0}}", "MessageAttributes": { "Test": { "Type": "String", "Value": "TestString" }, "TestBinary": { "Type": "Binary", "Value": "TestBinary" } }, "Type": "Notification", "UnsubscribeUrl": "EXAMPLE", "TopicArn": "arn:aws:sns:EXAMPLE", "Subject": "TestInvoke" } } ] }
※ "Message"の箇所をSNSから受け取るフォーマットに合わせています。
※ "Message"内 "d-XXXXXXXXXX"はテスト対象のディレクトリIDとしておく必要があります。
3. 上記を準備した上で、<Test>実行をします。
4. 対象ディレクトリ配下のWorkspaceが全て"AVAILABLE"であれば、Rebootは実行されませんので以下のような「null」が返されます。
5. 指定ディレクトリID配下のworkspaceが全て"AVAILABLE"であれば、Rebootされないことが確認できました。
Unhealthy時の動作テスト
- 意図的にWorkspaceを"UNHEALTHY"状態にしなければならないのですが、今回は対象のWorkspaceに対して無理やりネットワークアダプタを全て無効にする方法をとります。
※ これにより、管理ポートが閉じられてしまいますので、実際のReboot実行指示は対象のWorkspace側に届きません。また、テスト実行後にAVAILABLEに戻す場合は、Rebuildをする必要があります。 - "testhanse"と名付けたWorkspaceのみに実施し、約10分後に管理画面上で"UNHEALTHY"で検知されました。
- Cloudwatch上でもUnhealthyがputされ始め、閾値を超過(5回連続でUnhealthyが0異常)するまで待ちました。
- 閾値を超過し、アラーム状態となります。
- アラーム状態をトリガーに、SNS-topic「topic-lambda-ws-reboot」への通知が成功していることを履歴から確認します。
- WorkSpaces管理コンソール上で、"UNHEALTHY"となっていたWorkspace(testhanse)のみ管理画面上でRebootが実行されていることが確認できます。
- Lambda実行に問題がないことが確認できました。
- (テスト対象のネットワークアダプタを全て落としたので、"REBOOTING"から"UNHEALTHY"に戻りますが、1.で上げた通りです。。
まとめ
Reboot実行をするべきステータスのWorkspaceに対して、Lambdaによる自動再起動処理が走るところまで確認できました。
ポイントはアラーム検知の対象として、ディレクトリIDのメトリックスを指定するところです。
ワークスペースIDのメトリックスを指定すると、ワークスペース単位でしかアラームが発報されないため、ディレクトリID配下に数十台のワークスペースを運用する場合は、1台ずつアラーム設定をしていくことになってしまいます。
ディレクトリIDに対するチェックを行い、Lambdaで回すようにすれば、アラーム設定が1つですみます。
なお、CloudWatchアラームの閾値は「2〜3回連続で失敗」あたりで調整すると良いかと思います。
(テスト時に5回連続を指定すると再起動実行まで30分かかりました..
補足
AWSによると、現状Workspace側のStateは
PENDING | AVAILABLE | IMPAIRED | UNHEALTHY | REBOOTING | REBUILDING | TERMINATING | TERMINATED | SUSPENDED | ERROR
の10種類があり、そのうち「IMPAIRED」、「UNHEALTHY」がCloudWatch側で「異常(Unhealthy)」とカウントされるとのこと。
CloudWatchメトリクスが PUT されるタイミングと、WorkSpaces の State の変化のタイミングによっては、CloudWatch「Unhealthy」時に Workspaces のStateが 「Available」となる可能性はありますが、今回のLambdaファンクションでは、明示的にstateが"UNHEALTHY"なものを対象としている為、問題となりません。
それでは〜