この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
みなさん、こんにちは! AWS事業本部の青柳@福岡オフィスです。
Amazon WorkSpaces には、常時起動する「AlwaysOn」と、一定時間利用されないと自動的に停止する「AutoStop」の2通りの実行モードがあります。
「AutoStop」を採用するとランニングコストを抑えることができるのですが、WorkSpaceが停止状態にある時にWorkSpacesクライアントやWebブラウザから接続して利用しようとすると、起動するのに数分待つ必要があります。
そこで、毎朝始業時刻の数分前になったら自動的にWorkSpaceを起動しておいて、クライアントから接続するとすぐに利用できるような仕組みを作ってみました。
自動起動を実現する仕組み
WorkSpaces環境に存在する仮想デスクトップ (WorkSpace) を起動するLambda関数を作成します。
そして、EventBridgeのスケジュールを使ってLambda関数を毎朝決められた時刻に実行します。
Lambda関数
lambda_function.py
import boto3
import os
client = boto3.client('workspaces')
def lambda_handler(event, context):
target_directory_id = os.environ.get('TARGET_DIRECTORY_ID', '')
target_tag_key = os.environ.get('TARGET_TAG_KEY', '')
target_tag_value = os.environ.get('TARGET_TAG_VALUE', '')
check_workspaces(target_directory_id, target_tag_key, target_tag_value)
def check_workspaces(target_directory_id, target_tag_key, target_tag_value):
# WorkSpaceのリストを取得
if target_directory_id == '':
response = client.describe_workspaces()
else:
response = client.describe_workspaces(DirectoryId=target_directory_id)
# 全てのWorkSpaceについて繰り返し
for workspace in response['Workspaces']:
# WorkSpaceのID
workspace_id = workspace['WorkspaceId']
# 対象判定: タグに特定キー/値が設定されている場合のみを対象とする
flg = False
response = client.describe_tags(ResourceId=workspace_id)
for tag in response.get('TagList'):
if (tag.get('Key') == target_tag_key) and (tag.get('Value') == target_tag_value):
flg = True
break
if flg == False:
print('WorkSpace "{0}" skipped: Specified tag is not attached.'.format(workspace_id))
continue
# 対象判定: ステータスが「STOPPED」の場合のみを対象とする
if workspace.get('State') != 'STOPPED':
print('WorkSpace "{0}" skipped: State is not STOPPED.'.format(workspace_id))
continue
# WorkSpaceが対象であった場合、起動を試みる
r = start_workspace(workspace_id)
if r == 0:
print('WorkSpace "{0}" started successfully.'.format(workspace_id))
else:
print('WorkSpace "{0}" tried to start, but an error occurred while starting.'.format(workspace_id))
def start_workspace(workspace_id):
# IDで指定されたWorkSpaceを起動する
response = client.start_workspaces(
StartWorkspaceRequests=[
{
'WorkspaceId': workspace_id
},
]
)
# エラー判定
failed_requests = response.get('FailedRequests')
if not failed_requests:
return 0
else:
print('[Error] {0}'.format(failed_requests))
return 1
Lambda関数の処理内容ですが、以前公開したブログ記事を応用したものになっています。
AWS SDK for Python (Boto3) の “Client API” と “Resource API” の違いについて調べてみた | Developers.IO
今回のLambda関数の大まかな処理の流れは、以下の通りです。
describe_workspaces()
メソッドを使って全てのWorkSpaceの情報を取得するfor ~ in ~
構文により、WorkSpaceの情報を一つずつ取り出して処理する- タグの付与状況およびWorkSpaceのステータスを確認して、対象か否かを判定する
- 対象であれば、
start_workspaces()
メソッドを使ってWorkSpaceを起動する
設定手順
1. Lambda関数を作成する
Lambda関数を「一から作成」します。
- 関数名: warmup-workspaces
- ランタイム: Python 3.8
- IAMロール: 基本的なLambdaアクセス権限で新しいロールを作成
Lambda関数が作成されたら、前述のコード内容を貼り付けます。
環境変数を設定します。(「編集」をクリック)
以下のように環境変数を追加します。
TARGET_DIRECTORY_ID
: 自動起動の対象とするWorkSpacesディレクトリを指定 (省略すると全てのディレクトリが対象となります)TARGET_TAG_KEY
、TARGET_TAG_VALUE
: ここで指定したタグが付与されたWorkSpaceのみが自動起動の対象となります
対象のWorkSpaceの数にもよりますが、数が多い場合にはLambda関数のタイムアウトを長めに変更しておいた方が良いでしょう。
2. IAMロールのアクセス権限ポリシーを設定する
Lambda関数の実行IAMロールに対して、WorkSpacesへの必要なアクセス権限ポリシーを追加します。
まず、IAMポリシーを以下の内容で作成します。
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"workspaces:DescribeWorkspaces",
"workspaces:StartWorkspaces",
"workspaces:DescribeTags"
],
"Effect": "Allow",
"Resource": "*"
}
]
}
Lambda関数の実行IAMロールを開いて、さきほど作成したIAMポリシーをアタッチします。
3. EventBridgeのスケジュールを設定する
Lambdaの画面に戻り、「トリガーを追加」をクリックします。
トリガーを以下のように設定します。
- トリガーの種類: CloudWatch Events/EventBridge
- ルール: 新規ルールの作成
- ルール名: warmup-workspaces-schedule
- ルールタイプ: スケジュール式
- スケジュール式:
cron(55 23 ? * SUN-THU *)
- トリガーの有効化: チェックを入れる
スケジュール式は、cronの記述方法に準拠します。(UTCでの記述になることに注意してください)
この例では、UTCで「毎週 日曜日から木曜日、23:55に実行」を指定していることになります。 (日本時間に変換すると「毎週 月曜日から金曜日、08:55に実行」となります)
※ スケジュール式の記述方法の詳細については、AWSドキュメントを参照してください。 ルールのスケジュール式 - Amazon EventBridge
以下のように、Lambda関数のトリガーとして「CloudWatch Events/EventBridge」が設定されたことを確認します。
4. 対象としたいWorkSpaceにタグを付与する
Lambda関数の設定手順で説明した通り、特定のタグが付与されたWorkSpaceのみが自動起動の対象となります。
対象としたいWorkSpaceを選択した状態で、「アクション」→「タグの管理」を選択します。
Lambda関数の環境変数で指定した通りのタグを付与します。
これで、全ての準備は完了です。
動作確認
スケジュールで指定した時刻まで待って、WorkSpaceが自動的に起動することを確認しましょう。
各WorkSpaceの状態は以下のようになっています。
- 4番目のWorkSpaceのみ起動状態、その他は停止状態
- 5番目のWorkSpace以外は指定したタグを付与済み
スケジュールで指定した時刻になると、以下のようになりました。
1~3番目のWorkSpaceが自動的に起動しました!
4番目のWorkSpaceは、ステータスが「停止 (STOPPED)」ではなかったため、処理が行われませんでした。 5番目のWorkSpaceは、指定したタグが付与されていないため、対象外となりました。
Lambdaの実行ログは以下のようになっています。
START RequestId: 6b545e30-f57a-4025-8c7b-0b155f6ce203 Version: $LATEST
WorkSpace "ws-gk22b70mp" started successfully.
WorkSpace "ws-f7b1gt1mt" started successfully.
WorkSpace "ws-jnl1ww1y6" started successfully.
WorkSpace "ws-h1gj8cb07" skipped: State is not STOPPED.
WorkSpace "ws-79v5p552g" skipped: Specified tag is not attached.
END RequestId: 6b545e30-f57a-4025-8c7b-0b155f6ce203
REPORT RequestId: 6b545e30-f57a-4025-8c7b-0b155f6ce203 Duration: 2672.77 ms Billed Duration: 2700 ms Memory Size: 128 MB Max Memory Used: 71 MB Init Duration: 304.35 ms
「自動起動」が意味の無い場合がある??
せっかく作ってみた「自動起動」の仕組みですが、シチュエーションによっては「使う意味が無い」場合があります。
このことを説明するためには、実行モード「AutoStop」と「AlwaysOn」の料金体系を確認する必要があります。
料金表の「月額料金」が「AlwaysOn」、「時間料金」が「AutoStop」の料金です。
WoakSpacesのバンドル (スペック) の選択にもよりますが、概ね、月の利用時間が「80時間」を超えたところで「AlwaysOn」と「AutoStop」の料金が逆転します。
例えば「月曜日から金曜日まで、1日8時間」利用したとすると、月の利用時間は約160~180時間ほどになります。
この場合、価格的に「AutoStop」を使うメリットは無いため、必然的に「AlwaysOn」を採用することになります。
つまり、常時起動しているため、今回の自動起動の仕組みを使う必要が無いということになってしまいます。
おわりに
実行モード「AutoStop」を採用している場合の使い勝手の改善を期待して、WorkSpacesを自動起動する仕組みを実装してみました。
上で述べたように、WorkSpacesの利用状況によってはあまり使えないかもしれませんが、例えば「週に1、2日だけ利用する」「1日に2~3時間だけ利用する」といった場合であれば有効活用できるかもしれません。
結果的に「技術的な実験デモ」のようになってしまった感もありますが、今回ご紹介した「仕組み」が何かの参考になれば幸いです。