概要
Auto Scaling Group 内のインスタンスで CodeDeploy を使用する場合、Agent のバージョンアップが手間なので AMI にインストールしない方がよいです。
Systems Manager を使ってインストールとバージョンアップを自動化しましょう。
背景
今関わっている案件のサービスでは基本的には EKS 上にアプリが展開されているのですが、一部 EC2 インスタンスを使用しています。
インスタンスへのアプリのデプロイは CodeDeploy により行っています。
しかし、ある日突然 CodeDeploy のデプロイが失敗すると連絡を受けました。
調査していると CodeDeploy Agent のバージョンが古かったことが原因の可能性が高く、今後同様のことが発生しにくい仕組みとして、インスタンス起動時に最新版のエージェントのインストールと定期的なバージョンアップを行う仕組みとして Systems Manager を使う方法を導入しました。
どういう事象だったか
非本番環境ではコスト削減のため、毎晩 Auto Scaling Group の台数を 0 にして、毎朝戻すという対応を行なっています。
ある朝、前触れなく CodeDeploy が失敗して LB のヘルスチェックに失敗することに気づきました。
調査したところ下記のことがわかりました。
- CodeDeploy の処理のうち tomcat を起動するステップで失敗している。
- 失敗したインスタンスにログインして手動で tomcat を起動したら正常に起動できる。
- ログの終了時刻から、tomcat が依存しているサービスの起動前に失敗が記録している。
- AMI にインストールする CodeDeploy Agent を最新化したら CodeDeploy が成功した。
バージョンが古かったことが明確な原因であるとは言えない状況ですが、バージョンアップにより解消したことと、最新バージョンのリリースノートの下記の記述を元にバージョンアップにより対応しました。
Fixed: An issue that would cause the agent to fail.
AWS のドキュメントにも AMI にインストールするエージェントのバージョンを最新化するよう推奨されています。
解決策の選択肢
恒久対応の選択肢として下記の 3 つがありました。
- AMI にインストールする CodeDeploy Agent を最新化する。
- user_data により起動時にインストールする。(AWS Docs)
- Systems Manager によりインストールする。(AWS Docs)
上記のうち、自動でバージョンアップをしてくれる 3 を選択しました。
どういう仕組みか
Systems Manager により CodeDeploy Agent をインストールするには、下記の複数の Systems Manager の機能を活用します。
- コンプライアンス
- Run Command
- ステートマネージャー
- ディストリビューター
- ドキュメント
全体のイメージ
ステートマネージャー
インスタンスを定義された状態に保つための機能です。
(Run Command を介して) ドキュメントを使用してインスタンスの操作を行います。
具体的なユースケース (AWS ドキュメントより抜粋)
- スタートアップ時に特定のソフトウェアを使用してノードをブートストラップする。
- 定義済みのスケジュールに従ってエージェント (SSM Agent など) をダウンロードして更新する。
- ネットワーク設定を設定する。
- Microsoft Active Directory ドメインにノードを結合する。
- ライフサイクルを通じてソフトウェアの更新でノードにパッチをあてる。
- ライフサイクルを通じて Linux、macOS、および Windows マネージドノードでスクリプトを実行します。
対象となるインスタンスを見つけてきて Run Command を呼び出す役割となります。
設定画面では Run Command の存在は見えず、直接ドキュメントを呼び出しているように見えます。
cron 式を使用して定期実行を定義できます。
Run Command
インスタンスの OS 内で一度限りの処理を実行します。
一度限りの処理を実行するという役割において、自動化 (Automation) と似ていますが、対象インスタンスの OS 内で処理する点が異なります。
ステートマネージャーの設定画面では Run Command は見えないですが、実際の処理の履歴や結果を確認する際は Run Command から行います。
ドキュメント
Run Command や Automation などの Systems Manager の各機能で処理する具体的な内容を定義する機能。
JSON / YAML で処理内容やユーザーが指定するパラメーターなどを定義します。
下記のような AWS 定義のドキュメントの他、自分で作成することもできます。
- Ansible の Playbook を適用する (
AWS-ApplyAnsiblePlaybooks
) - Package をインストールする (
AWS-ConfigureAWSPackage
) - shell スクリプトを実行する (
AWS-RunShellScript
)
今回は AWS 定義の AWS-ConfigureAWSPackage
を使用します。
ディストリビューター
ソフトウェアをインスタンスにインストールするためにパッケージ化する機能です。
ドキュメント AWS-ConfigureAWSPackage
によりインストールします。
AWS が公開するパッケージとサードパーティーが公開するパッケージ、自分で公開するパッケージが利用できます。今回利用する CodeDeploy Agent は AWS が公開済みです。
Terraform によるサンプルコード
Terraform で Systems Manager の設定をするコードのサンプルは下記の通りです。
resource "aws_ssm_association" "codedeploy_agent" {
name = "AWS-ConfigureAWSPackage"
association_name = "sampleapp-install-codedeployagent" # Systes Manager > State Manager の一覧で「関連付けの名前」として表示
parameters = {
"action" = "Install"
"name" = "AWSCodeDeployAgent"
"version" = var.codedeploy_agent_version == "" ? null : var.codedeploy_agent_version # 1. variable からバージョンを指定
}
# 2. Systems Manager によりインストール対象のインスタンスを指定
targets {
key = "tag:Name"
values = [
"sampleapp-dev",
]
}
}
- 今回、本番環境と UAT 環境はバージョンを固定し、その他の環境は最新バージョンをインストールする設計だったため、バージョンを指定できるようにしています。
version
自体を指定しなければ最新バージョンとなります。 - インストール対象とするインスタンスは
Name
タグかインスタンス ID のどちらかを指定することで制御します。
その他応用
ドキュメントで Ansible の実行や Shell スクリプトの実行ができるため、割となんでもできそうです。
素の AMI を利用して OS 内の設定はステートマネージャー経由で行い、管理を行うことで AMI の管理を止めることもできそうです。
ただ、ステートマネージャーは複数の処理の実行ができない点や、AMI にインストール・設定を行う場合に比べてインスタンスの起動時間がかかるデメリットがあります。
まとめ
インスタンスの管理はコストがそれなりにかかりますが、Systems Manager がそのコストを軽減してくれます。
色々できそうですが、デメリットもあるので計画的にご利用したいところです。