EC2 インスタンスでちょっとしたシェルスクリプトを cron で定時実行していたのを、SSM Run Command + EventBridge スケジューラに移行してみたという話です。以下、ざっくりと手順を。極力 AWS CLI を使ってコマンドベースで進められるように書いてます(古いバージョンでは動かないことがあるので最新版を推奨)。アカウント番号 999999999999 と インスタンス ID i-0123456789abcdef はお手持ちのものに読み替えて下さい。
EC2 インスタンス設定
何はともあれ定時実行したいシェルスクリプトを。本例では下記のようなローカルディスクの使用率を出力するスクリプトを用意しておきます。
$ cat <<'EOS' > /tmp/local-disk-usage.sh #!/bin/bash echo "$(hostname) のディスク使用率は $(df | awk '/nvme0n1p1/ {print $5}') です。" EOS
上記スクリプトをリモートで実行する為に Systems Manager の Run Command を使うので、当該インスタンスには SSM Agent が必要です。が、本件インスタンスの OS である Amazon Linux 2 にはデフォルトでインストールされているので、下記を実行して、Active: active (running) と表示されるなら OK。そうでないなら systemctl start しておきます。
$ sudo systemctl status amazon-ssm-agent
また、当該 EC2 インスタンスを Systems Manager から制御する旨の許可として、適用している IAM ロールに IAM ポリシー AmazonEC2RoleforSSM をアタッチしておきます。
EventBridge 用 IAM ロールの作成
EventBridge スケジューラが Run Command する時に必要な IAM ロール my-eventbridge-role を作成しておきます。ロールには、スケジューラからの利用を許可した信頼関係を持たせます。
$ __TRUSTPOLICY__=$(cat <<EOS { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "scheduler.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } EOS ) $ aws iam create-role \ --role-name my-eventbridge-role \ --assume-role-policy-document "$__TRUSTPOLICY__"
さらに、当該 EC2 インスタンスへの Run Command 実施を許可したポリシーを作成して、上記ロールにアタッチ。
$ __POLICY__=$(cat <<EOS { "Version": "2012-10-17", "Statement": [ { "Action": "ssm:SendCommand", "Effect": "Allow", "Resource": [ "arn:aws:ec2:ap-northeast-1:999999999999:instance/i-0123456789abcdef", "arn:aws:ssm:ap-northeast-1:*:document/AWS-RunShellScript" ] } ] } EOS ) $ aws iam create-policy \ --policy-name my-eventbridge-policy \ --policy-document "$__POLICY__" $ aws iam attach-role-policy \ --role-name my-eventbridge-role \ --policy-arn arn:aws:iam::999999999999:policy/my-eventbridge-policy
CloudWatch ログの設定
EventBridge スケジューラが Run Command 出力を保存する CloudWatch ログのロググループを作っておきます。
$ aws logs create-log-group \ --log-group-name my-eventbridge-shcedule-loggroup
EventBridge スケジューラを作成
これで準備が出来たので、EventBridge スケジューラを作成します。スケジュールのターゲットに Run Command を定義するには、コマンドをエスケープされた JSON 文字列で指定する必要があるので、予めコマンド定義をシェル変数にセットしておきます。
$ __COMMAND_INPUT__=$(cat <<EOS { "DocumentName": "AWS-RunShellScript", "InstanceIds": ["i-0123456789abcdef"], "Parameters": { "commands": ["bash /tmp/local-disk-usage.sh"] }, "CloudWatchOutputConfig": { "CloudWatchLogGroupName": "my-eventbridge-shcedule-loggroup", "CloudWatchOutputEnabled": true } } EOS )
EventBridge スケジュール定義を作成。下記は上記で定義したターゲットを毎日 22:30 に実行するという内容です。StartDate は最初の実行日時から5分前以内でなければならないとのこと。JSON 文字列のエスケープ処理には jq を使います。尚、set -f はヒアドキュメント中のワイルドカード展開を抑止するおまじない。
$ set -f $ __EB_SCHEDULE__=$(cat <<EOS { "EndDate": "2100-01-01T00:00:00+09:00", "FlexibleTimeWindow": { "Mode": "OFF" }, "GroupName": "default", "Name": "my-eventbridge-schedule-sample", "ScheduleExpression": "cron(30 22 * * ? *)", "ScheduleExpressionTimezone": "Asia/Tokyo", "StartDate": "2023-03-04T22:25:00+09:00", "State": "ENABLED", "Target": { "Arn": "arn:aws:scheduler:::aws-sdk:ssm:sendCommand", "Input": $(echo $__COMMAND_INPUT__ | jq '@json'), "RetryPolicy": { "MaximumEventAgeInSeconds": 86400, "MaximumRetryAttempts": 185 }, "RoleArn": "arn:aws:iam::999999999999:role/my-eventbridge-role" } } EOS ) $ set +f
上記で作成したスケジュールを EventBridge スケジューラに登録して設定は完了です。
$ aws scheduler create-schedule --cli-input-json "$__EB_SCHEDULE__"
確認
最初の実行時刻が過ぎたところで、CloudWatch ロググープ my-eventbridge-shcedule-loggroup にログストリームが生成されていて、/tmp/local-disk-usage.sh の出力が記録されているか確認します。