RunCommandで1時間以上の処理を実行する方法
アノテーションのインフラチーム原田です。
業務で複数のAWS環境にあるリソース名を取得し棚卸を実施しています。
具体的には、IAMUserやIAMポリシー、セキュリティーグループなど削除忘れや意図していないリソースが作成されていないかをチェックするためです。
今回この業務を自動化するにあたって、オートメーションドキュメント内にRunCommandを記載することにしました。
しかし、長時間にわたる処理では処理が途中で強制終了してしまい、期待した結果が得られないという課題に直面したので、制限時間の変更方法を調べました。
結論として、長時間処理を実行するには、executionTimeout(EC2上の実行時間)を適切に設定する必要があります。また、処理完了を確実に待つためにwaitForCommandCompletionのtimeoutSecondsも十分な時間を設定することが重要です。
これまでの実施方法
これまではCloud9を利用して各環境にアクセスしていました。
Cloud9でシェルスクリプトを実行し、60以上のアカウントに対し、1つずつAthenaクエリを叩いていており、全ての処理が完了するまでに平均1時間から2時間程度の実行時間を要していました。
もちろんCloud9上では制限時間の問題はありませんでした。
しかしCloud9が新規アカウントに対しては提供されないことになり、同処理を行うためには代替する環境が必要でした。
そこで検討したのがRunCommandとオートメーションドキュメントを組み合わせることでした。
シェルスクリプトはそのまま利用することから、RunCommandでも実行時間は同程度、つまり1時間以上は掛かります。
制限時間設定の落とし穴と正しい対処法
ここで問題となったのが、実行時間の制限でした。
RunCommandの本来の主な利用目的は、パッチ適用などが想定されておりデフォルトでは1時間となっています。明示的に記載しないとデフォルトが適用されます。
つまり、1時間を過ぎると処理を終了してしまうためタイムアウトの設定をしなければ、結果がFailedとなります。
実際デフォルトのまま実行すると処理が途中で終わってしまいました。
しかし、マネジメントコンソールからはRunCommandの時間制限を設定する箇所がなくドキュメント内で記載する必要があります。
RunShellScriptは処理全体と考えていましたが、実際はRunCommandで記載したスクリプトを実行開始するだけであり、EC2上の処理完了は待ってくれないことに気づきました。
そのため、作成当初はRunShellScriptのみにTimeoutSecondsを記載していたのですが、RunCommandの実行に対して時間を制限するものでした。
失敗した時間指定は下記です。
inputs:
DocumentName: AWS-RunShellScript
InstanceIds:
- '{{ InstanceId }}'
TimeoutSeconds: 10800
Parameters:
commands:
- |
#!/bin/bash
(実際の処理)
- name: waitForCommandCompletion
action: aws:waitForAwsResourceProperty
timeoutSeconds: 10800
上記だとRunCommandの実行は3時間待機できますが、EC2の実行時間はデフォルトの1時間のままです。
1時間を超えても問題なく処理を行うには、executionTimeoutを追加し、EC2上の処理を3時間に設定する必要があります。
- name: runShellScript
action: aws:runCommand
nextStep: waitForCommandCompletion
isEnd: false
onFailure: step:stopInstance
inputs:
DocumentName: AWS-RunShellScript
InstanceIds:
- '{{ InstanceId }}'
Parameters:
executionTimeout: # ← ここを追加
- '10800'
commands:
- |
#!/bin/bash
(実際の処理のRunCommandを記載)
- name: waitForCommandCompletion
action: aws:waitForAwsResourceProperty
timeoutSeconds: 10800
少しややこしいのですが、TimeoutSeconds(Run Command全体のタイムアウト)はコマンドの送信から結果取得までの全体の制限時間であり、executionTimeout(実行タイムアウト)はEC2インスタンス上でスクリプトが実行できる時間です。
重要なのは、どちらか短い方の時間で処理が終了するという点です。
例えば、TimeoutSecondsが3時間設定で、executionTimeoutが1時間であれば処理は1時間で終了します。
逆にTimeoutSecondsが1時間設定で、executionTimeoutが3時間でも1時間で終了します。
なお、waitForCommandCompletionのtimeoutSecondsは、executionTimeoutより少し長めに設定することを推奨します。例えば、executionTimeoutが10800秒(3時間)なら、waitForCommandCompletionは11000秒程度に設定すると、
タイムアウト時のステータス確認が確実に行えます。
executionTimeoutとwaitForCommandCompletionのtimeoutSecondsを指定(ここでは3時間に設定)することで、途中で終了することなく処理を完了することができました。
オートメーションドキュメントから確認するとRunShellScriptの次ステップで完了を待つフローに変更されています。
終わりに
今回RunCommandの制限時間設定で躓いた経験を通じて、AWSサービスを利用する際は「デフォルト設定がどのような前提で設計されているか」を理解することの重要性を改めて実感しました。
RunCommandは本来、パッチ適用のような短時間処理を想定して設計されているため、1時間という制限は妥当な設定です。しかし、私たちのように異なる用途で使用する場合は、その前提を理解した上で適切な設定変更が必要になります。
特にTimeoutSecondsとwaitForCommandCompletionの使い分けは、ドキュメントを読んだだけでは分かりにくく、実際に動作させて初めて気づくポイントでした。
この記事が、どなたかの参考になれば幸いです。