COLUMN

EventBridge SchedulerとSSM Run Commandを使ってEC2インスタンス上で定期的にシェルスクリプトを実行してみた

こんにちは、のんピです。

EC2インスタンス上で定期的にシェルスクリプトを実行したいと思ったことはありますか? 私はあります。

パッと思いつく方法としては、EC2インスタンス上でcronやsystemd timerを用いてシェルスクリプトを使用する方法でしょう。

ただし、複数のEC2インスタンスでまとめて操作を行いたい場合、各EC2インスタンスで設定をするのは少々面倒だったりします。

そのような場合に役立つのはEventBridge SchedulerとSSM Run Commandを組み合わせる方式です。

SSM Run Commandでは複数のターゲットを指定することが可能です。これによってEC2インスタンスが複数存在する場合でも手間を削減することが可能です。

また、実行結果をCloudWatch LogsやS3バケットに出力させることも可能です。

実際に試してみたので紹介します。

1.検証環境

検証環境は以下のとおりです。

各EC2インスタンスではそれぞれNginxをインストールしています。

こちらのEC2インスタンスに対してEventBridge Schedulerを介してSSM Run Commandを叩き、Nginxの再起動を行います。

2.やってみる

実際にやってみましょう。

2-1.スケジュールグループの作成

まず、スケジュールグループを作成します。

EventBridge Schedulerはスケジュールグループに所属させることが可能です。

現時点ではEventBridge Scheduler単位でのメトリクスを確認することはできません。役割ごとにスケジュールグループを分離させると良いでしょう。

今回は’non-97-test-schedule-group’というスケジュールグループを作成しました。

2-2.EventBridge Schedulerの作成

続いて、EvenBridge Schedulerを作成します。

まず、スケジュール名やスケジュールグループ、実行間隔を指定します。今回は毎分実行するように設定してみました。

次にターゲットを選択します。今回はSSM Run Commandを実行したいため、’Systems Manager’を選択します。

SSM Run Commandでコマンドを実行するAPIは’SendCommand’です。’SendCommand’を選択して、APIのパラメーターを入力します。

指定したパラメーターは以下のとおりです。

{
 // 任意のシェルスクリプトを実行したいため "AWS-RunShellScript" を指定
 "DocumentName": "AWS-RunShellScript",
 "Parameters": {
    // 実行するコマンドを指定
    "commands": [
       "set -xu",
       "systemctl status nginx",
       "systemctl restart nginx",
       "systemctl status nginx"
    ]
 },
 // ターゲットはインスタンスIDではなく、Name : AL 2023 のタグが付与されたもの
 "Targets": [
    {
       "Key": "tag:Name",
       "Values": [
          "AL2023"
       ]
    }
 ],
 // 最大同時実行数
 "MaxConcurrency": "1",
 // 許容するエラー数
 "MaxErrors": "0",
 // タイムアウトするまでの秒数
 "TimeoutSeconds": 60,
 // 状態遷移時の通知先および通知条件
 // コマンド実行が失敗もしくは成功した際にSNSトピックを介して通知
 "NotificationConfig": {
    "NotificationArn": "arn:aws:sns:us-east-1:<AWSアカウントID>:non-97-test",
    "NotificationEvents": [
       "Failed",
       "Success"
    ],
    "NotificationType": "Command"
 },
 // SNSトピックにPublishする権限を持ったIAMロール
 "ServiceRoleArn": "arn:aws:iam::<AWSアカウントID>:role/non-97-test-role-sns",
 // 実行ログ出力先のS3バケット
 "OutputS3BucketName": "<出力先のS3バケット名>",
 "OutputS3KeyPrefix": "ssm-runcommand/"
}

SNS通知の設定および、IAMロール、IAMポリシーはこちらのAWS公式ドキュメントを参考に設定しました。

‘non-97-test-role-sns’はにアタッチされているIAMポリシーは以下のとおりです。指定したSNSトピックにPublishできるようにしています。

{
 "Version": "2012-10-17",
 "Statement": [
    {
       "Effect": "Allow",
       "Action": [
          "sns:Publish"
       ],
       "Resource": "arn:aws:sns:us-east-1:<AWSアカウントID>:non-97-test"
    }
 ]
}

IAMロールの信頼されたエンティティは以下のとおりです。

{
 "Version": "2012-10-17",
 "Statement": [
     {
         "Sid": "",
         "Effect": "Allow",
         "Principal": {
             "Service": "ssm.amazonaws.com"
         },
         "Action": "sts:AssumeRole"
     }
 ]
}

各種設定を行います。SendCommandやSNSのPublishができるIAMロールへのPassRoleが行えるIAMロールをアタッチします。

こちらのIAMロールにアタッチされているIAMポリシーは以下のとおりです。

{
 "Version": "2012-10-17",
 "Statement": [
    // AWS-RunShellScript のドキュメントのみを許可
    {
       "Effect": "Allow",
       "Action": "ssm:SendCommand",
       "Resource": [
          "arn:aws:ssm:*:*:document/AWS-RunShellScript"
       ]
    },
    // Name : AL2023 のタグが付与されたEC2インスタンスのみをターゲットとして許可
    {
       "Effect": "Allow",
       "Action": "ssm:SendCommand",
       "Resource": "*",
       "Condition": {
          "StringEquals": {
             "aws:ResourceTag/Name": "AL2023"
          }
       }
    },
    // SNSのPublishを許可するIAMロールを付与できるように許可
    {
       "Effect": "Allow",
       "Action": "iam:PassRole",
       "Resource": "arn:aws:iam::<AWSアカウントID>:role/non-97-test-role-sns"
    }
 ]
}

IAMロールの信頼されたエンティティは以下のとおりです。

{
 "Version": "2012-10-17",
 "Statement": [
     {
         "Effect": "Allow",
         "Principal": {
             "Service": "scheduler.amazonaws.com"
         },
         "Action": "sts:AssumeRole"
     }
 ]
}

最後に作成する設定値を確認します。

2-3.動作確認 (正常終了)

動作確認をします。

しばらく放置をしていると、1分間隔でコマンド実行していることが分かりました。

実行結果を確認すると、指定したEC2インスタンスでいずれも実行が正常完了したことが分かります。

出力先を確認すると、確かに指定したコマンドが実行されていることが分かります。

出力先のS3バケットを確認すると、’<指定したプレフィックス>/<コマンドID>/<インスタンスID>/<ドキュメント名>/<番号>.<ドキュメント名>/’に’stdout’と’stderr’が出力されていました。

内容を確認すると、いずれもマネジメントコンソールで確認した出力先の内容と一致していました。

メールの受信ボックスを確認すると、’EC2 Run Command Notification us-east-1’というタイトルでメールを受信していました。ステータスが’Success’になっていますね。

「ちょっと通知内容が味気ないな」と感じた方はEventBridgeのInput Transformerを使って文面をカスタマイズして、通知してみると良いでしょう。

その際は以下ブログが参考になります。

2-4.動作確認 (異常終了)

異常終了する場合も確認しましょう。

試しに片方のEC2インスタンスからNginxをアンインストールします。

アンインストールすることでNginxの再起動に失敗するはずです。

すると、次回の実行からコマンド実行が失敗するようになりました。Nginxをアンインストールした方のEC2インスタンスで失敗しています。

出力先を確認すると、「Nginxのユニットファイルが存在しない」とエラーになっていますね。意図したとおりです。

メールの受信ボックスを確認すると、’EC2 Run Command Notification us-east-1’というタイトルでメールを受信していました。ステータスが’Failed’になっていますね。

失敗したことについても気付けそうです。

3.まとめ

EventBridge SchedulerとSSM Run Commandを使ってEC2インスタンス上で定期的にシェルスクリプトを実行する方法を紹介しました。

EventBridge SchedulerとSSM Run Commandを組み合わせることで複数のEC2インスタンスに対してまとめて処理を行わせることが可能です。ログ出力も可能ですし、失敗時の通知も行うことが可能です。

EC2インスタンスがSSMのマネージドノードである場合は、十分選択肢に入るのではないでしょうか。

裏を返すとSSMのマネージドノードとして動作できない状況においては使用できません。もちろん、EventBridgeとSSMのサービスとしての可用性の影響も受けることになります。選択する際は関連するAWSサービスの可用性も加味すると良いでしょう。

この記事が誰かの助けになれば幸いです。

以上、クラスメソッドの のんピ でした!

著者

クラスメソッド株式会社

AWS事業本部 コンサルティング部

山本 涼太(のんぴ)

https://dev.classmethod.jp/author/non____97/

クラスメソッド株式会社はアマゾン ウェブ サービスをはじめ、データ分析、モバイル、IoT、AI/機械学習等の分野で企業向け技術支援を行っています。2023年にはアジア最優秀SIパートナーとして「SI Partner of the Year – APJ」を受賞。現在までの技術支援実績は3,000社以上、AWS環境については25,000アカウント以上となりました。社員による技術情報発信にも注力し、オウンドメディア「DevelopersIO」では4万本以上の記事を公開中です。

クラスメソッド株式会社

https://classmethod.jp/

技術ブログ「DevelopersIO」

ページ上部へ戻る

相談無料!プロが中立的にアドバイスいたします

クラウド・AWS・Azureでお困りの方はお気軽にご相談ください。