エラーの概要CodeDeployのデプロイが途中で失敗し、次のようなエラーが表示されます:
Script at specified location: scripts/before_install.sh failed to complete in 300 seconds
LifecycleEvent - BeforeInstall
Status - Failed
Deployment failed
CodeDeployコンソールでデプロイが失敗とマークされ、EC2インスタンスは中途半端なデプロイ状態のまま放置されます。これはBeforeInstall、AfterInstall、ApplicationStart、ValidateServiceなど、あらゆるライフサイクルフックで発生する可能性があります。
根本原因CodeDeployはライフサイクルフックスクリプトごとに**300秒(5分)**のハードタイムアウトを適用します。スクリプトが時間内に終了しない場合、CodeDeployはそれを強制終了してデプロイを失敗させます。
スクリプトがハングしたり長時間実行される典型的な原因は以下の通りです:
- パッケージマネージャー(
apt、yum)がyes/noプロンプトを待っているか、低速なミラーでスタックしている- EC2インスタンスから到達不能な内部URLやエンドポイントにスクリプトがアクセスしようとして、TCPコネクションがハングしたまま- 数百のパッケージを含むnpm install、重いML依存関係のpip install、または実際に数分かかる同様のインストール処理- スクリプトがsystemdサービスのactive状態への移行を待ってから処理を続行しようとしている- スクリプト末尾のexit 0が欠落しており、実際の処理完了後もバックグラウンドプロセスがシェルを開いたままにしている-gpg、debconf、または特定のインストーラーで発生しやすい、スクリプト内で意図せずインタラクティブプロンプトが起動されている## 修正方法1:appspec.ymlでタイムアウトを増やすスクリプトが正当に長い時間を必要とする場合の最速の修正方法です。appspec.ymlの各フックはtimeoutキーを秒単位で受け付けており、最大3600秒(1時間)まで設定できます:
hooks:
BeforeInstall:
- location: scripts/before_install.sh
timeout: 600
runas: root
ApplicationStart:
- location: scripts/start_server.sh
timeout: 900
runas: ec2-user
保存後に再デプロイしてください。CodeDeployはそれらのスクリプトの完了を10〜15分まで待つようになります。 **使用すべき場面:**依存関係のインストールやデータベースマイグレーションなど、5分以上かかる処理が実際に必要な場合です。なぜスクリプトが遅いのかを確認せずにタイムアウトを延ばすのは最初の手段としては避けてください。単にハングをマスキングしているだけかもしれません。
修正方法2:スクリプトが実際に何をしているか診断するEC2インスタンスにSSHでログインし、新しいデプロイをトリガーしながらCodeDeployエージェントのログをtailします:
sudo tail -f /var/log/aws/codedeploy-agent/codedeploy-agent.log
出力がどの行で止まるかを確認してください。それでも不明な場合は、スタックしている箇所を特定するためにスクリプトに直接タイムスタンプを追加します:
#!/bin/bash
set -e
echo "[$(date)] Starting before_install"
echo "[$(date)] Installing dependencies..."
apt-get install -y nginx
echo "[$(date)] Done"
ログで最後に表示されたタイムスタンプから、どのコマンドがハングしているかが正確にわかります。デプロイごとのスクリプトログも確認できます:
/opt/codedeploy-agent/deployment-root/{deployment-group-id}/{deployment-id}/logs/scripts.log
修正方法3:パッケージインストールを非インタラクティブにする最もよくある原因の一つです。apt-getがキーボード入力を待ったり、低速なミラーからパッケージを取得しようとしてスタックします。非インタラクティブモードを強制することで修正できます:
#!/bin/bash
set -e
export DEBIAN_FRONTEND=noninteractive
apt-get update -y
apt-get install -y --no-install-recommends nginx
Amazon LinuxやRHELベースのインスタンスでは、yumはより簡単です。-yフラグで対応できます:
yum install -y nginx
CodeDeployスクリプト内ではapt-get installに必ず-yを付けてください。付けないとパッケージマネージャーが永遠に確認待ちでブロックされます。
修正方法4:ネットワーク呼び出しにタイムアウトを追加する設定ファイルの取得、内部APIの呼び出し、S3からのダウンロードを行うスクリプトは、VPCルートが欠落していたりセキュリティグループが誤設定されていると無期限にハングすることがあります。常に明示的なタイムアウトを設定してください:
# 危険 — タイムアウトなし、不正なルートで永遠にハング
curl https://internal-config.example.com/config.json -o /etc/app/config.json
# 安全 — 30秒後に素早く失敗し、3回リトライ
curl --max-time 30 --retry 3 --retry-delay 5 \
https://internal-config.example.com/config.json \
-o /etc/app/config.json
wgetでも同様です:
wget --timeout=30 --tries=3 https://internal-config.example.com/config.json
1回の試行に30秒、3回のリトライで最悪のケースは90秒となり、300秒の制限内に十分収まります。
修正方法5:バックグラウンドプロセスがスクリプトを開いたままにするのを防ぐこれは微妙な問題です。スクリプトが実際の処理を完了し、バックグラウンドサービスを起動したにもかかわらず、シェルがそのサービスがactiveになるのを待ち続けます。実際にはすべてが正常に動作しているにもかかわらず、CodeDeployがタイムアウトします。
#!/bin/bash
set -e
# サービスがactiveになるまで時間がかかる場合、これはブロックする可能性がある
systemctl start myapp
# より良い方法 — 起動して処理を続ける
systemctl start myapp || true
# または待機するが、60秒で上限を設ける
timeout 60 bash -c 'until systemctl is-active myapp; do sleep 2; done'
バックグラウンドジョブも完全にデタッチする必要があります。アンパサンド単独では不十分です:
# まだシェルにアタッチされている
./start_worker.sh &
# 完全にデタッチ — CodeDeployはこれを待たない
nohup ./start_worker.sh > /var/log/worker.log 2>&1 &
disown
修正方法6:スクリプトを常にexit 0で終了する短い修正ですが、時に重要です。一部のシェル設定では、明示的なexitがないとプロセスが宙ぶらりんの状態になります。CodeDeployはゼロ以外の終了コードを失敗として扱います。すべてのスクリプトをきれいに終了させてください:
#!/bin/bash
set -e
# ... スクリプトのロジック ...
exit 0
修正の確認再デプロイして、同時に3か所を確認します:
# EC2インスタンス上 — リアルタイムのエージェント出力
sudo tail -f /var/log/aws/codedeploy-agent/codedeploy-agent.log
# AWSコンソール
# CodeDeploy → デプロイメント → [対象のデプロイメント] → イベントの表示
# デプロイごとのスクリプトログ
ls /opt/codedeploy-agent/deployment-root/
# デプロイフォルダに移動 → logs/scripts.log
正常な実行では、コンソールで各ライフサイクルイベントにStatus: Succeededが表示されます。スクリプトログには末尾が切れた行なしの完全な出力が記録されます。

