CodeDeployフックのベストプラクティス

渡辺です。

CodeDeployでは、デプロイ時に任意のスクリプトを実行できます。 また、スクリプトの実行ポイントは複数あり、細かく制御可能です。 しかしながら、スクリプトが実行されないでハマることがあります。 フックスクリプト実行の仕組みを正しく理解し、ベストプラクティスを知ることで不要なトラブルを回避してください。

フックスクリプトはアプリケーションにバンドルする

フックスクリプトはアプリケーションのアーカイブ(zip)にバンドルし、S3やCodeCommitにアップロードします。 通常、アプリケーションにhooksディレクトリやScripsディレクトリを作成し、実行権を付与したスクリプトファイルを配置します。 スクリプトファイルはシェルスクリプトだけでなく、実行可能であればPythonやRubyのスクリプトでも構いません。

.
├── appspec.yml
├── app
└── hooks
    ├── after.sh
    ├── before.sh
    ├── start.sh
    ├── stop.sh
    └── validate.sh

実行されるスクリプトは解凍されたアーカイブ

CodeDeployでは、appspec.ymlにアプリケーションのデプロイ先を指定します。

files:
  - source: /
    destination: /opt/app

この設定では、アプリケーションは展開され、/opt/appにコピーされます。 当然、フックスクリプトhooks/start.shは、/opt/app/hooks/start.shに配置されます。 ここに大きな罠が潜んでいます。

CodeDeployでは、アプリケーションのアーカイブをダウンロードした後、///deployment-archive/ に展開されます(Amazon Linuxのdeployment-rootは、/opt/codedeploy-agent/deployment-root/)。 展開されたアプリケーションは/opt/appなどにコピーされますが、実行されるスクリプトは、///deployment-archive/hooks/start.sh です。 /opt/app/hooks/start.shではありません!

permissionsで実行権限を与えても無駄

フックスクリプトは、デプロイ先ではなく、deployment-rootにあります。 このため、permissionsセクションで、デプロイ先のスクリプトに実行権限を与えても意味がありません。 何故ならば、実行されるスクリプトはdeployment-rootにあるからです。

permissions:
  - object: /opt/app/hooks
    pattern: "*.sh"
    owner: root
    group: root
    mode: 755
    type:
      - file

この時、CodeDeoloyエージェントのログに警告メッセージが出力されます。

2017-xx-xx hh:mm:dd WARN  [codedeploy-agent(9918)]: InstanceAgent::Plugins::CodeDeployPlugin::HookExecutor: Script at specified location: hooks/start.sh is not executable.  Trying to make it executable.

したがって、アーカイブが展開された時点で実行権限があるように、アーカイブ化前に実行権限を与えましょう

バンドルしていないフックスクリプトを直接実行できない

さらに、フックスクリプトを絶対パスで指定して実行できないことに注意してください。 例えば、次のように絶対パスを指定しても、/opt/hooks/stop.shは実行されません。

hooks:
  ApplicationStop:
    - location: /opt/hooks/stop.sh
      timeout: 15
      runas: root

///opt/hooks/stop.shを実行しようとして、スクリプトがないと警告がでるでしょう。

フックスクリプトは必ずアプリケーションにバンドルしてください。 アプリケーションにバンドルされないスクリプトを実行するには、フックスクリプトから間接的に呼び出します。

CodeDeployのイベントと実行タイミングを理解する

CodeDeployでは複数のタイミングでフックスクリプトを実行出来ます。 インプレイスデプロイの場合、以下の5個を抑えておきましょう。

  • ApplicationStop
  • BeforeInstall
  • AfterInstall
  • ApplicationStart
  • ValidateService

ややこしいのが、ApplicationStopとBeforeInstallです。 フローとしてはApplicationStopにアプリケーションのダウンロードが行われ(DownloadBundle)、フックスクリプトも含めたアーカイブが展開されます。 したがって、初回デプロイ時にApplicationStopは実行されません。 初回デプロイ時にも事前処理を行いたい場合、BeforeInstallで実行しなければなりません。

ミドルウェアの起動停止などはBeforeInstall/AfterInstallで行う

ついApacheの再起動などをApplicationStopとApplicationStartで行いがちです。 しかし、Apacheなどアプリケーションの外側にあるミドルウェアの再起動を行う場合、BeforeInstall/AfterInstallで行います。 ApplicationStopとApplicationStartの場合、初回デプロイ時にApacheの停止が行われず、起動のみが行われてしまいます。

ApplicationStop/ApplicationStartは、デプロイ対象のアプリケーションの起動や停止を行うイベントです。 例えば、SpringBootで起動するJavaアプリケーションの起動であれば、ApplicationStop/ApplicationStartで制御します。

CodeDeployのログを監視する

CodeDeploy Agentでは、フックスクリプトが実行されなかった場合、警告メッセージが出力されます。 しかし、デプロイ自体は成功します。 このため、実はフックスクリプトが実行されていなかったという事件が発生します。 CodeDeploy AgentのログはCloudWatch Logsに転送し、フィルタでの簡易監視を行ってください。

まとめ

CodeDeployのフックスクリプトを利用する場合、次の4点に注意しましょう。

  • 実行権限をつけたフックスクリプトをアプリケーションにバンドルする
  • ミドルウェアの再起動はBeforeInstall/AfterInstallで行う
  • ApplicationStopは初回デプロイ時に実行されない
  • CodeDeploy Agentのログを監視する

以上です。