appspec.ymlとは?CodeDeployのAppSpecファイルの書き方・記述例・エラー対処
appspec.yml(AppSpecファイル)は、AWS CodeDeployがデプロイの手順を読み取るための設計図です。「どのファイルをどこへ置くか」「各段階でどのスクリプトを実行するか」をこの1ファイルで宣言し、CodeDeployはこれに従ってデプロイを進めます。ところが同じCodeDeployでも、EC2/オンプレミス・AWS Lambda・Amazon ECSでは書くべきセクションもファイル名の制約も異なり、ここを取り違えるとデプロイが起動時に失敗します。本記事ではappspec.ymlの役割から基本構造、ライフサイクルフック、3プラットフォーム別の記述例、配置とインデントの落とし穴、よくあるエラーの対処までをAWS公式仕様に沿って整理します。
まとめ:appspec.ymlの要点
- 役割:CodeDeployがデプロイ手順を読むファイル。
versionは現状0.0のみ指定可。 - ファイル名と場所:EC2/オンプレミスは YAML形式・ファイル名 appspec.yml・リビジョンのルート直下が必須。外すとデプロイが失敗する。Lambda・ECSはYAML/JSONどちらでも可。
- 構造はプラットフォームで違う:EC2は
os/files/permissions/hooks、Lambda・ECSはresources/hooks。 - hooks:EC2はライフサイクルイベントごとにスクリプトを実行、Lambda・ECSはLambda検証関数を実行する。
- つまずきやすい点:インデント(スペース数)とファイル配置。YAMLのスペーシング仕様を外すと原因の分かりにくいエラーになる。
以降で、各プラットフォームの構造・フック・記述例・エラー対処を順に見ていきます。
appspec.ymlとは|CodeDeployにおける役割と読み込みタイミング
appspec.ymlは、CodeDeployがアプリケーションリビジョン(デプロイ対象一式をまとめたzip/tarなどのアーカイブ)内で最初に読み取る管理ファイルです。デプロイ対象のコードと一緒にリビジョンへ同梱し、S3バケットまたはGitリポジトリへアップロードして使います。CodeDeployはこのファイルの記述だけを頼りに「何を」「どの順で」実行するかを決めるため、appspec.ymlの内容がそのままデプロイの挙動になります。
appspec.ymlが読み込まれるタイミング
EC2/オンプレミスのデプロイでは、CodeDeployエージェントが DownloadBundle イベントでリビジョンをインスタンスへ展開し、その後の各ライフサイクルイベント(BeforeInstall や AfterInstall など)でappspec.ymlの hooks を参照してスクリプトを起動します。files セクションは Install イベントでの配置内容を決めます。なお ApplicationStop だけは例外で、今回のリビジョンではなく前回成功したリビジョンのappspec.ymlとスクリプトが使われます。インスタンスへの初回デプロイ時はまだ前回分が存在しないため、ApplicationStop は実行されません。
appspec.yml・appspec.yaml・JSONの違い(混同しやすい命名)
拡張子と形式の許容範囲はプラットフォームで異なります。EC2/オンプレミスはYAML形式で、ファイル名は appspec.yml でなければなりません(appspec.yaml やJSONは不可)。一方でLambda・ECSのAppSpecファイルはYAML/JSONのどちらでもよく、CodeDeployコンソールに内容を直接入力することもできます。「appspec.yaml でも動くはず」という思い込みはEC2デプロイで失敗の原因になるため、EC2では拡張子まで含めて appspec.yml に固定してください。
プラットフォーム別のappspec.yml基本構造
appspec.ymlのトップレベルセクションは、デプロイ先のコンピューティングプラットフォームで変わります。version は3プラットフォーム共通で 0.0 を指定します(CodeDeployが予約している値で、これ以外は使えません)。
| セクション | EC2/オンプレミス | AWS Lambda | Amazon ECS |
|---|---|---|---|
| version | 必須(0.0) | 必須(0.0) | 必須(0.0) |
| os | 必須(linux/windows) | ― | ― |
| files | あり | ― | ― |
| permissions | あり(Linux系のみ) | ― | ― |
| resources | ― | あり(Lambda関数) | あり(タスク定義等) |
| hooks | スクリプト | Lambda検証関数 | Lambda検証関数 |
EC2/オンプレミス:os・files・permissions・hooks
os にはデプロイ先の linux または windows を指定します。files は Install イベントでインスタンスへコピーするファイルの source(リビジョン内のパス)と destination(配置先)の対応を書きます。permissions はコピーしたファイルの所有者やモードを指定する任意セクションで、Amazon Linux・Ubuntu Server・RHELのみが対象です。hooks には各ライフサイクルイベントで動かすスクリプトを紐づけます。
AWS Lambda:resources・hooks
Lambdaデプロイでは os や files は使いません。Resources にデプロイするLambda関数のバージョン(名前・エイリアス・現行/目標バージョン)を記述し、Hooks にトラフィック切り替え前後で走らせる検証用Lambda関数を指定します。利用できるフックは BeforeAllowTraffic と AfterAllowTraffic の2つだけです。Lambda・ECSのキーは Resources・Hooks と大文字で始まる点に注意してください(EC2の files・hooks は小文字。version のみ全プラットフォーム共通で小文字です)。
Amazon ECS:resources・hooks
ECSデプロイでは Resources にタスク定義(TaskDefinition のARN)や、トラフィックを切り替えるコンテナ名・ポート(LoadBalancerInfo)、必要に応じてプラットフォームバージョン・サブネット・セキュリティグループを記述します。Hooks にはデプロイ検証用のLambda関数を指定します。ECSのAppSpecファイル自体はYAMLでもJSONでも記述でき、書き方の詳細はAWS ECSとは?コンテナの仕組み・起動タイプ・料金・EKSとの違いの運用と合わせて設計すると分かりやすくなります。
ライフサイクルイベントフックの一覧と実行順序
hooks はappspec.ymlの中心です。CodeDeployはデプロイを一連のライフサイクルイベントに分割し、各イベントに紐づけたスクリプト(EC2)または検証関数(Lambda/ECS)を実行します。フックを書かなかったイベントでは何も実行されません。
EC2/オンプレミスのフックと実行順序
インプレースデプロイでは、フックは次の順で実行されます。
ApplicationStop
DownloadBundle # 予約(スクリプト不可)
BeforeInstall
Install # 予約(スクリプト不可)
AfterInstall
ApplicationStart
ValidateService
# ↓ デプロイグループにロードバランサーがある場合のみ
BeforeBlockTraffic
BlockTraffic # 予約(スクリプト不可)
AfterBlockTraffic
BeforeAllowTraffic
AllowTraffic # 予約(スクリプト不可)
AfterAllowTraffic
DownloadBundle・Install・BlockTraffic・AllowTraffic はCodeDeployエージェント専用の予約イベントで、スクリプトを割り当てられません。トラフィックの遮断・許可に関する6つのフックは、デプロイグループにClassic/Application/Network Load Balancerを指定している場合にだけ実行されます。ApplicationStop・ValidateService などの主要イベントは、それぞれ「前バージョンの安全な停止」「デプロイ後の疎通確認」といった役割で使います。
フックエントリの書き方(location・timeout・runas)
各フックにはスクリプトを配列で複数指定できます。エントリの要素は次の3つです。
- location(必須):実行するスクリプトのパス。リビジョンのルートからの相対パスで書く。
- timeout(任意):スクリプトの制限時間(秒)。既定は3600秒で、これが1イベントあたりの上限。1イベント内の全スクリプトの合計がこれを超えるとデプロイは失敗する。
- runas(任意):スクリプトを実行するユーザー。Amazon Linux・Ubuntu Serverのみ有効。CodeDeployはパスワードを保持しないため、パスワードが必要なユーザーには成りすませない。
Lambda・ECSのフック
Lambda・ECSの hooks はスクリプトではなくLambda検証関数を指定します。関数はデプロイの妥当性を検査し、putLifecycleEventHookExecutionStatus でCodeDeployへ Succeeded または Failed を通知します。この通知が1時間以内に返らないと、そのデプロイは失敗と見なされます。ECSで使えるフックは BeforeInstall・AfterInstall・AfterAllowTestTraffic・BeforeAllowTraffic・AfterAllowTraffic の5つです。いずれかの検証関数が失敗(Failed を通知)すると、デプロイは停止し直前の安定したタスクセットへロールバックされます。
appspec.ymlの記述例(EC2・Lambda・ECS)
EC2/オンプレミスの記述例
Linuxインスタンスへ静的コンテンツを配置し、インストール前後とアプリ起動・停止でスクリプトを実行する例です。files でソースと配置先を対応させ、hooks で各段階のスクリプトを指定します。
version: 0.0
os: linux
files:
- source: /
destination: /var/www/html/WordPress
hooks:
BeforeInstall:
- location: scripts/install_dependencies.sh
timeout: 300
runas: root
AfterInstall:
- location: scripts/change_permissions.sh
timeout: 300
runas: root
ApplicationStart:
- location: scripts/start_server.sh
ApplicationStop:
- location: scripts/stop_server.sh
timeout: 300
runas: root
AWS Lambdaの記述例
Lambda関数のバージョンを切り替え、トラフィック移行の前後で検証関数を走らせる例です。os や files は登場しません。
version: 0.0
Resources:
- myLambdaFunction:
Type: AWS::Lambda::Function
Properties:
Name: "myLambdaFunction"
Alias: "myLambdaFunctionAlias"
CurrentVersion: "1"
TargetVersion: "2"
Hooks:
- BeforeAllowTraffic: "BeforeTrafficHook"
- AfterAllowTraffic: "AfterTrafficHook"
Amazon ECSの記述例
タスク定義とロードバランサー先のコンテナ・ポートを指定し、検証用フックを紐づける例です。
version: 0.0
Resources:
- TargetService:
Type: AWS::ECS::Service
Properties:
TaskDefinition: "arn:aws:ecs:ap-northeast-1:123456789012:task-definition/my-task:1"
LoadBalancerInfo:
ContainerName: "web"
ContainerPort: 80
Hooks:
- BeforeInstall: "BeforeInstallHook"
- AfterAllowTestTraffic: "TestTrafficHook"
- AfterAllowTraffic: "AfterTrafficHook"
YAMLインデントと配置場所の落とし穴
appspec.ymlのトラブルは、記述内容そのものより「置き場所」と「空白」に起因することが多いです。ここは競合記事でも軽く流されがちですが、失敗を最初に踏むポイントなので押さえておきます。
appspec.ymlはリビジョンのルート直下に置く
EC2/オンプレミスでは、appspec.ymlをリビジョン(デプロイするソース一式)のルートディレクトリの直下に置かなければなりません。サブフォルダに入れたり、ファイル名を appspec.yaml にしたりすると、CodeDeployがファイルを認識できずデプロイに失敗します。hooks の location に書くスクリプトのパスも、このルートからの相対パスとして解決される点に注意してください。
スペーシング(インデント)の仕様
YAMLはインデントで階層を表すため、スペースの数がずれると意味が変わります。CodeDeployはスペーシングが正しくないと原因を特定しにくいエラーを出します。公式が示す基本ルールは次のとおりです。
version:やos:のコロンと値の間はスペース1つ。filesやhooks配下の各項目はスペース2つ+ハイフンで始める。timeoutやrunasなどのプロパティはさらに深くインデントする。- タブは使わずスペースで統一し、YAMLの文字列は原則クォートで囲まない。
手元でインデントを崩さないよう、YAML対応エディタのバリデーションを有効にしておくと、デプロイ実行前に構文エラーを潰せます。
appspec.ymlでよくあるエラーと対処法
「AppSpecファイルが見つからない・内容が不正」
最も多いのが配置とファイル名の問題です。EC2/オンプレミスでは、リビジョンのルートに appspec.yml(この名前・この拡張子)が存在するかをまず確認します。ビルド成果物をzip化する際に階層が1つ深くなり、appspec.ymlがサブフォルダに潜っているケースが典型です。
ApplicationStopで失敗する
ApplicationStop は前回成功したリビジョンのスクリプトを実行するため、過去に配置した停止スクリプトが壊れていると、今回のデプロイがこの段階で止まります。前回のスクリプトを修正できない場合は、該当インスタンスの直近成功リビジョン情報をリセットするなどの対処が必要です。初回デプロイでは ApplicationStop は実行されない点も踏まえて切り分けます。
フックスクリプトの失敗・タイムアウト
スクリプトが0以外の終了コードを返す、または timeout を超えるとそのフックは失敗し、デプロイが止まります。スクリプト内で参照するファイルは相対パスの解決基準(エージェントの作業ディレクトリ)に依存するため、DEPLOYMENT_ID や DEPLOYMENT_GROUP_ID などの環境変数を使った絶対パス参照にすると安定します。原因はインスタンス上のCodeDeployエージェントのログで確認できます。
「too many individual instances failed deployment…」
このメッセージはデプロイグループ全体のヘルス条件を満たせなかったときに出ます。個々のインスタンスでフックスクリプトが失敗しているのが起点であることが多いため、まずは各インスタンスでどのライフサイクルイベントが失敗したかを特定し、そのフックのスクリプトとログを追うのが近道です。
CI/CDパイプラインでのappspec.ymlの位置づけ
appspec.ymlは単体で使うより、CI/CDパイプラインの一部として運用するのが一般的です。ビルド成果物とappspec.ymlをまとめてリビジョン(zip/tar)にし、S3またはGitHubへ配置してCodeDeployが取得します。AWS CodePipelineとは何か?その基本的な概要と機能についてで組んだパイプラインからCodeDeployのデプロイステージを呼び出す構成が定番で、ビルドをGitHub Actionsでビルド・自動テストを設定する方法や各種CIツールに任せ、appspec.ymlはリポジトリで管理します。CIツールの選定はGitHubとGitLabの違い(CI/CD・セキュリティで選ぶ判断基準)も参考にしてください。
よくある質問
appspec.ymlとappspec.yamlはどちらが正しいですか?
EC2/オンプレミスのデプロイでは appspec.yml でなければならず、appspec.yaml やJSONは認識されません。Lambda・ECSではYAML/JSONのどちらでも記述でき、コンソールへの直接入力も可能です。
appspec.ymlはどこに置けばよいですか?
EC2/オンプレミスでは、デプロイするソース一式(リビジョン)のルートディレクトリの直下に置きます。サブフォルダに入れるとデプロイが失敗します。
hooksセクションは必須ですか?
必須ではありません。スクリプトやLambda検証関数を実行する場合にのみ記述します。フックを書かなければ、そのイベントでは何も実行されません。
Lambda用のappspec.ymlにosやfilesは必要ですか?
不要です。Lambda(およびECS)では resources と hooks を使い、os・files・permissions はEC2/オンプレミス専用のセクションです。
versionに0.0以外の値は指定できますか?
できません。version はCodeDeployが予約しており、現状は 0.0 のみが許可された値です。将来の拡張用に確保されている値なので変更しないでください。