タケユー・ウェブ日報

Ruby on Rails や Flutter といったWeb・モバイルアプリ技術を武器にお客様のビジネス立ち上げを支援する、タケユー・ウェブ株式会社の技術ブログです。

EC2 ImageBuilder コンポーネントのデバッグ

まとめ

  • Terminate instance on failurefalse にしてもテスト用のインスタンスは殺される
  • S3へのログ保存は絶対に有効にしろ
  • ステップはなるべく小分けに

事の起こり

  • EC2 ImageBuilder でゴールデンAMIを作っている
  • ある日昨日まで成功してたテストをパスしなくなった
  • エラー内容を見ても failed to run commands: exit status 1 のようなのでよくわからない

f:id:uzuki05:20200329090414p:plain
EC2 ImageBuilder で謎のエラー

デバッグ

AWS Systems Manager オートメーション

EC2 ImageBuilder のタスクは、AWS Systems Manager オートメーションを利用して動いています。 [AWS Systems Manager] => [アクションと変更] => [自動化] から自動化の実行の一覧を確認します。

  • ImageBuilderBuildImageDocument
  • ImageBuilderTestImageDocument

がそれぞれ、ビルド、テストに使ったドキュメントです。

ですが、ここだけ見てもエラーの詳細はさっぱりわからないので後に書く S3 へのログのアップロードを有効化しておく必要があります。

f:id:uzuki05:20200329092454p:plain
エラー画面

Image Pipeline の Troubleshooting settings

Image Pipeline を作るとき、変更するときに指定できるデバッグ用のオプションが用意されています。 デフォルトでは無効になっているので注意しましょう。

Terminate instance on failure

デフォルトで true チェックを外すとビルド失敗時にビルドに使ったEC2インスタンスを起動したままにしておいてくれるので、実際に環境にログインして調査することができます。

ただ、テストフェーズで失敗した場合は機能しないようです。

なお、EC2インスタンスはプライベートサブネットに作成されるため、SSHには踏み台が必要です。

Key pair

EC2インスタンスに設定するSSHのキーペア。

Logs

設定すると、自動化処理中の以下のようなログを指定したS3バケットにアップロードしてくれます。 Image pipelineの実行IAM roleに指定したS3バケットへのPut権限をつける必要があると思います。 S3バケット中のフォルダ名は、AWS Systems Manager オートメーションの自動化の実行IDを元にした名前になるので、まず実行IDを特定して、S3を探すことになります。

実行IDは EC2 Image Builder images の実行の詳細画面 Reason for failure に書いてあります。 または、AWS Systems Manager オートメーションの自動化の一覧から探してもよいでしょう。

f:id:uzuki05:20200329095417p:plain

f:id:uzuki05:20200329093017p:plain

ログフォルダの中身は以下のような内容になっています。

detailedoutput.json

どのステップで失敗したのか?どんなコマンドを実行したのか?がわかります。

{
    "executionId": "02bb195c-7143-11ea-a1f0-0603d891b362",
    "status": "failed",
    "startTime": "2020-03-29T07:25:22+09:00",
    "endTime": "2020-03-29T07:27:32+09:00",
    "failureMessage": "Document TOE_2020-03-28_22-25-21_UTC-0_02bb195c-7143-11ea-a1f0-0603d891b362/0__myapp-install__0.0.7_1.yml failed!",
    "documents": [
        {
            "name": "MyApp Install",
            "filePath": "TOE_2020-03-28_22-25-21_UTC-0_02bb195c-7143-11ea-a1f0-0603d891b362/0__myapp-install__0.0.7_1.yml",
            "status": "failed",
            "description": "This is MyApp Install testing document.",
            "startTime": "2020-03-29T07:25:22+09:00",
            "endTime": "2020-03-29T07:27:32+09:00",
            "failureMessage": "Phase test failed!",
            "phases": [
                {
                    "name": "test",
                    "status": "failed",
                    "startTime": "2020-03-29T07:25:22+09:00",
                    "endTime": "2020-03-29T07:27:32+09:00",
                    "failureMessage": "Step CheckResponse failed!",
                    "steps": [
                        {
                            "name": "HelloWorldStep",
                            "status": "success",
                            "failureMessage": "",
                            "timeoutSeconds": 7200,
                            "onFailure": "Abort",
                            "maxAttempts": 1,
                            "action": "ExecuteBash",
                            "startTime": "2020-03-29T07:25:22+09:00",
                            "endTime": "2020-03-29T07:25:22+09:00",
                            "inputs": "[{\"commands\":[\"echo \\\"Hello World! Test.\\\"\"]}]",
                            "outputs": "[{\"stdout\":\"Hello World! Test.\"}]"
                        }
                        {
                            "name": "CheckResponse",
                            "status": "failed",
                            "failureMessage": "exit status 244",
                            "timeoutSeconds": 7200,
                            "onFailure": "Abort",
                            "maxAttempts": 3,
                            "action": "ExecuteBash",
                            "startTime": "2020-03-29T07:27:26+09:00",
                            "endTime": "2020-03-29T07:27:32+09:00",
                            "inputs": "[{\"commands\":[\"echo \\\"curl -LI  http://localhost/\\\"\\nSTATUS_CODE=$(curl -LI  http://localhost/ -o /dev/null -w '%{http_code}\\\\n' -s)\\nif [[ $STATUS_CODE == \\\"200\\\" ]]; then\\n    echo \\\"MyApp was successfully invoked.\\\"\\nelse\\n    echo \\\"MyApp was not successfully invoked. Failing.\\\"\\n    exit $STATUS_CODE\\nfi\\n\"]}]",
                            "outputs": "null"
                        },
console.log

どのステップで失敗したのか?どんなコマンドを実行したのか?がわかります。

2020-03-29 07:25:22 Info Document TOE_2020-03-28_22-25-21_UTC-0_02bb195c-7143-11ea-a1f0-0603d891b362/0__myapp-install__0.0.7_1.yml
2020-03-29 07:25:22 Info Phase test
2020-03-29 07:25:22 Info Step HelloWorldStep
2020-03-29 07:25:22 Info Command execution completed successfully
2020-03-29 07:25:22 Info Stdout: Hello World! Test.
2020-03-29 07:25:22 Info Stderr: 
2020-03-29 07:25:22 Info ExitCode 0
2020-03-29 07:27:26 Info Step CheckMonitorStatus
2020-03-29 07:27:27 Info Command execution resulted in an error
2020-03-29 07:27:27 Info Stdout: curl -LI  http://localhost/
MyApp was not successfully invoked. Failing.
2020-03-29 07:27:27 Info Stderr: 
2020-03-29 07:27:27 Info ExitCode 244
2020-03-29 07:27:29 Info Command execution resulted in an error
2020-03-29 07:27:29 Info Stdout: curl -LI  http://localhost/
MyApp was not successfully invoked. Failing.
2020-03-29 07:27:29 Info Stderr: 
2020-03-29 07:27:29 Info ExitCode 244
2020-03-29 07:27:31 Info Command execution resulted in an error
2020-03-29 07:27:31 Info Stdout: curl -LI  http://localhost/
MyApp was not successfully invoked. Failing.
2020-03-29 07:27:31 Info Stderr: 
2020-03-29 07:27:31 Info ExitCode 244

なお、今回の原因

ログを確認したところ、テストフェーズでdockerのインストールなどを行ったこともあり、disk full になったのが原因でした。 ImageBuilder で使うEC2インスタンスのストレージサイズを変更したレシピを作成したところ成功しました。