こんにちは sakasai です。
若干今更な内容ですが、今回改めて試してみたのでご紹介したいと思います。
はじめに
バッチ処理等をAmazon EC2で動かすために、EC2上でcronのスケジュールに設定して動かすケースはよくある(あった)と思いますが、ECSやAWS LambdaなどAWSの他のサービスを使ってくると、
サーバーに接続して設定するのめんどくさいとか、サーバー上に設定持ちたくないとか、サーバー上でスケージュール管理しているのイケてないとかとか、いろいろ考えるようになってきます。
というわけで、EC2上のcronの代わりにAmazon EventBridgeルールを利用してEC2のコマンド(cronに設定しているシェル等)を実行する方法を試してみました。
今回の流れ
- EC2インスタンスの起動
- Systems Managerを設定
- テスト用シェルスクリプトの作成と実行
- EC2上にcron設定
- Systems Manager Run Commandでシェルスクリプト実行
- EventBridgeルールの設定
では早速やっていきます。
EC2インスタンスの起動
最初にEC2インスタンスを作成して起動します。
今回は、OSはAmazonLinux2でインスタンスタイプは最近お気に入りのt4gを使います。


デフォルトVPCで、パブリックサブネット※1 を選択します。その他はデフォルトのままにしました。

ボリュームタイプはなんとなくgp3

その他はデフォルトのまま最後まで進んでEC2を起動します。
一応セキュリティグループは自分のIPからSSHだけ出来るようにしておきました。(今回使うことはありません)
しばらく待って無事に起動できていることを確認し、名前をつけておきます。


Systems Managerを設定
次にEC2インスタンスをSystems Managerから管理するための設定を行っていきます。
メニューの「高速セットアップ」をクリックし

起動したEC2と同じリージョンを選んでGet Startedを実行します。

「Create new configuration」で設定を作成

「Host Management」を選択

Configuration optionsでCloudWatchのチェックも一応チェックし(デフォルトではチェックなしになってます)、
Targetsは「Current Region」の「Manual」を選択し、マネージメント対象のEC2を選択してCreateします。

作成した設定が実行され

Configuration detailsがSuccessになれば成功です。

そうするとSystems Managerで管理対象にしたEC2インスタンスにセッションマネージャーで接続できるようになります。(Systems Managerの設定が完了してから接続できるようになるまでちょっと時間がかかりました)
EC2インスタンスを選択して「接続」をクリックし、セッションマネージャーで接続します。


テスト用シェルスクリプトの作成と実行
セッションマネージャーで接続した状態で作業を進めていきます。
ホームディレクトリで test.sh
を作成します。
$ cd
$ pwd
/home/ssm-user
$ vi test.sh
テストなので適当ですが、今回は以下の内容で作成しました。
ログを /tmp
ディレクトリに出力しつつ、実行ユーザーと実行ディレクトリを確認用で出力しています。

作成したら実行権限をつけて実行してみます。
$ chmod u+x test.sh
$ ./test.sh
[ssm-user][/home/ssm-user] Hello EC2!
ログも確認します。
$ cat /tmp/test.log
[1637231609] START
[1637231609] [ssm-user][/home/ssm-user] Hello EC2!
[1637231609] END
正常に実行されていることが確認できました。
EC2上にcron設定
作成したシェルスクリプトをcronで実行していきます。
5分ごとに実行するように設定します。
$ crontab -e
*/5 * * * * /home/ssm-user/test.sh
ログを tail
して正常に実行されていることを確認します。
$ tail -f /tmp/test.log
[1637232001] START
[1637232001] [ssm-user][/home/ssm-user] Hello EC2!
[1637232001] END
[1637232301] START
[1637232301] [ssm-user][/home/ssm-user] Hello EC2!
[1637232301] END
問題なく実行されていることが確認できました。
Systems Manager Run Commandでシェルスクリプト実行
Systems Managerで「Run Command」をクリックします。

コマンドドキュメントで「AWS-RunShellScript」を選択

コマンドパラメータに実行するシェルスクリプトを入力します。

「インスタンスを手動で選択する」でEC2インスタンスを選択します。

今回は出力オプションでS3バケットへの書き込みのチェックをオフにしました。(デフォルトはオン)

設定が終わったら実行します。
設定が問題なければシェルスクリプトが即時実行されて、すぐに正常終了すると思います。

インスタンスを選択し「出力の表示」を行うとOutputに標準出力で出力しているものが表示されていると思います。

ログも正常に出力されていました。
$ tail -f /tmp/test.log
[1637232369] START
[1637232369] [root][/usr/bin] Hello EC2!
[1637232369] END
Run Commandで実行すると、rootユーザーで /usr/bin
ディレクトリで実行されていることがわかります。
最初うっかりログ出力を相対パス( カレントディレクトリに出力するように test.log
)で実行していてログがどこに出力されているかわからず探してしまいました。(/usr/bin
下に出力されていました)
EventBridgeルールの設定
最後にEC2のcronを代替えするためのEventBridgeルールを設定します。
EventBridgeのルールを開き「ルールの作成」をクリックします

名前は適当につけて、パターンを「スケジュール」の「Cron式」で設定します。

ターゲットを「SSM Run Command」、ドキュメントを「AWS-RunShellScript」、ターゲットキーに「InstansIds」、ターゲット値に「作成したEC2インスタンスID」を入力します。(右の「追加」を押し忘れないように注意してください)
自動化パラメータの設定は、「定数」を選択し「Command」に作成したシェルスクリプトを入力(ここも右の「追加」を押し忘れないように注意してください)

あとはデフォルトのままルールの作成を完了します。
作成したルールが追加されました。

作成後ルールが有効になっているのでそのまま実行されるのを待ってみます。
ログを tail
するとEC2のcronとEventBridgeルールの両方で実行されていることが確認できました。
$ tail -f /tmp/test.log
[1637232902] START
[1637232902] [ssm-user][/home/ssm-user] Hello EC2!
[1637232902] END
[1637232906] START
[1637232906] [root][/usr/bin] Hello EC2!
[1637232906] END
[1637233201] START
[1637233201] [ssm-user][/home/ssm-user] Hello EC2!
[1637233201] END
[1637233207] START
[1637233207] [root][/usr/bin] Hello EC2!
[1637233207] END
あとはEC2のcronを削除し
$ crontab -r
$ crontab -l
no crontab for ssm-user
EventBridgeルールからのみ実行されていることを確認しEC2のcronからの移行が完了です。
$ tail -f /tmp/test.log
[1637233506] START
[1637233506] [root][/usr/bin] Hello EC2!
[1637233506] END
おまけ
ためしにPythonスクリプトも実行してみました。
シェルと同じディレクトリにtest.pyで以下のコードで作成しました。

EventBridgeルールでPythonスクリプトファイルを指定してルールを作成

正常に実行されログ出力されました。
$ tail /tmp/test2.log
2021-11-18 13:27:44,300 - __main__ - INFO - START
2021-11-18 13:27:44,301 - __main__ - WARNING - Hello EC2!!
2021-11-18 13:27:46,303 - __main__ - INFO - END
2021-11-18 13:30:44,031 - __main__ - INFO - START
2021-11-18 13:30:44,031 - __main__ - WARNING - Hello EC2!!
2021-11-18 13:30:46,033 - __main__ - INFO - END
さいごに
今までやってみようと思いつつ後回しにしていたのですが、やってみたら思ったよりも簡単にできました。
と書きつつちょっと調べていたら、EventBridgeを使わずにSystems Managerのメンテナンスウィンドウを使っても同じことが実現できるようです。(実際やってみたらできました)
https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/systems-manager-maintenance.html
もしかしたらこちらのほうがベストプラクティスかもしれませんが、ユースケースに応じてよりよい方法を使っていきたいと思います。
結論としてはEC2で動いてるバッチはEventBridge起動のECSに移行しよう!って感じでしょうか(え?)。
ではでは
※1:通常バッチサーバー等はプライベートサブネットを使うことが多いかと思いますが、今回はセッションマネージャーでEC2インスタンスに接続する方法を簡略化するためパブリックサブネットにしています。
参考:セッションマネージャーを使用してプライベートサブネットのLinux用EC2にアクセス(VPCエンドポイント編)