RareJob Tech Blog

レアジョブテクノロジーズのエンジニア・デザイナーによる技術ブログです

AWS SAM CLI のトラブルシューティング

はじめに

こんにちは、DevOps グループの中島です。

サーバレスアプリケーションを AWS 上に構築する際に便利なフレームワークとして AWS SAM を利用する方法があります。 弊社でもいくつかのプロジェクトでサーバレスアプリケーションの構築に利用しています。

今回は AWS SAM CLI を利用したデプロイにおいて、見慣れないエラーが発生した際に行った トラブルシューティング内容について紹介します。

エラーの内容

sam deploy コマンドにおいて、以下のエラーが発生しました。

File with same data already exists at xxx, skipping upload
...
    Deploying with following values
    ===============================
    Stack name                   : ...
    Region                       : ap-northeast-1
    Confirm changeset            : False
    Disable rollback             : False
    Deployment s3 bucket         : xxx
    Capabilities                 : ["CAPABILITY_IAM"]
    Parameter overrides          : {"xxx": "xxx"}
    Signing Profiles             : {}
Initiating deployment
=====================
File with same data already exists at xxx.template, skipping upload
Waiting for changeset to be created..
Error: Waiter ChangeSetCreateComplete failed: Max attempts exceeded
ERROR: Job failed: exit code 1

Error: Waiter ChangeSetCreateComplete failed: Max attempts exceeded

エラーの内容から、チェンジセットの作成完了を待つ Waiter で試行回数を超過していることが読み取れます。

Waiter とは

Waiter は AWS SDK に実装されている、非同期処理の完了までブロックする機能です。

例えば EC2 インスタンスの作成など時間がかかる API では、作成を開始したというレスポンスがすぐに返却されますが、その時点では作成完了していません。
Waiter を利用することで、作成完了してステータスが running になった時点で処理を継続することができます。

今回の ChangeSetCreateComplete は Cloudformation の Waiter で、
チェンジセットの作成完了を待機するものです。

チェンジセットは作成されたのか

SAM CLI からはチェンジセット作成完了まで確認ができなかったということなので
実際に作成されているかをコンソールから確認してみると、問題なく作成はされています。
結果的に作成は完了しているが、AWS SAM 上ではそれが確認できずにエラーとなっていることが分かります。

Waiter のリトライ頻度

aws cli waiter changeset などで検索して AWS CLI のドキュメント を見つけると、
以下のように記載があります。

It will poll every 30 seconds until a successful state has been reached. This will exit with a return code of 255 after 120 failed checks.

30秒 * 120 回なので 1 時間 でタイムアウトとなるようになっています。
しかし、今回のデプロイはそれほど待っていないので、この値が使われていないと考えられます。

AWS のせいにしたい気持ち

ここまで来たら Cloudformation の特定のエンドポイント (チェンジセットの作成状態を確認する API) が落ちてるのではないか、そう考えるのが自然ではないでしょうか。
大体いつもしれっと落ちてしれっと復旧して報告ないですからね。
しかし、以下のように AWS CLI から実行してみると、すぐに完了してしまいました。

aws cloudformation wait change-set-create-complete --change-set-name xxx

再度デプロイしても同様のエラーだったのでエンドポイントが落ちていたわけではなさそうです。

AWS SAM CLI のソースを見に行く

ここでタイムアウトの設定値が短すぎる説を検証します。
AWS SAM CLI のコードを適当に grep すると、 ユーザ設定の値で上書きしている ことが分かります。

# Wait for changeset to be created
waiter = self._client.get_waiter("change_set_create_complete")
# Use default client_sleep to set the delay between polling
# To override use SAM_CLI_POLL_DELAY environment variable
waiter_config = {"Delay": self.client_sleep}
try:
    waiter.wait(ChangeSetName=changeset_id, StackName=stack_name, WaiterConfig=waiter_config)

client_sleep の値は デフォルトでは 0.5 秒 となっていて
リトライ回数は未設定なのでデフォルトの 120 回とすると 0.5 秒 * 120 回となり 1 分でタイムアウトしそうです。
デプロイから 1 分ほどでエラーとなっていたので、このタイムアウト値なら今回の事象と合致します。

解決

コードのコメント欄に書いてあるとおり、環境変数を設定して実行したところ、正常にデプロイされました。もともと template.yml が大きかったので、変更によって限界を超えてしまったものと思われます。

SAM_CLI_POLL_DELAY=5 sam deploy

SAM CLI のドキュメント の一番下にも記載されていましたが、今回のエラー内容ではこの環境変数を変えれば良いとすぐには思いつかないですね。。

終わりに

前回 と異なり、今回はコードを見に行くことで早期に解決ができました。
ChatGPT も正しい回答はしてくれませんでしたが、この記事をもとに回答してくれる日が来るかもしれません。

We're hiring!

rarejob-tech.co.jp