2022/12/4更新
CodeBuildの「buildspec.yml」でCodeCommitから取得したソースを予めZIP圧縮してS3に格納しておく必要があったため、記事を更新しました。
はじめに
前回はChatbot
を使用してSlack
へ通知する仕組みを構築しました。
今回は、その1でマネジメントコンソール上から直接Systems Manager
のRunCommand
を実行して試したことをCodeBuild
から実行できるかを試してみようと思います。
- 【前】AnsibleをAWS Systems Managerから実行するCI/CDを構築する。(その3:SNS、Chatbotの準備)
- 【次】AnsibleをAWS Systems Managerから実行するCI/CDを構築する。(その5:CodePipelineを使用したCI/CD)
CodeBuildとは
「M2 MacでCodeBuildをローカルで実行する。」でも書きましたが、CodeBuild
の名の通り、コードをビルドすることを目的としたサービスです。
CodeBuild
でソースコードを実際に実行できるような形式にビルドする方法は、buildspec.yml
というビルドの設計図となるファイルにビルドコマンドや実行コマンドを記載して、実行アプリを生成するやり方となります。
通常はCodeBuild
でビルド後、ビルドしたアプリを実行するノードに対してCodeDeploy
等でアプリを配置してCodePipeline
で一連のサービスを繋げてCodeCommit
へのコミット契機でCodeBuild
、CodeDeploy
と行うのが一般的なCI/CD
の流れとなります。
今回の最終構成の場合はCodeDeploy
に当たる操作はSystems Manager
のRunCommand
で行うため、CodeDeploy
は使用しませんが、構成としては同等の構成を構築していきます。
CodeBuildの仕組み
CodeBuild
の仕組みを少し深堀りすると、ビルド実行時、まずビルド実行用のDocker
コンテナを起動して、事前に準備してあるbuildspec.yml
ファイルのビルド設計に従って処理を実行、生成物を出力する設定にしていれば生成物(アーティファクト)をS3バケットに格納する仕組みとなります。
したがって、buildspec.yml
を作成するということは、ビルド用コンテナで実行する内容を設定することとなります。
今回の構成
今回はCodeBuild
のビルドプロジェクトを作成して、前回作成したCodeCommit
に格納しているソースファイルを使い、CodeBuild
経由でSystems Manager
のRunCommand
を実行する以下のような構成を作ってみます。
前回RunCommand
でAnsible
を実行した際にはマネジメントコンソール画面上から実行しましたが、今回はコマンド操作でCodeBuild
から実行するようにします。
buildspec.ymlの作成
最初にビルドの設計図となるbuildspec.yml
ファイルを作成していきます。
大きく分けると以下9つのシーケンスと呼ばれる構文があり、少なくとも以下必須と書かれているversion
とphases
はbuildspec.yml
で指定する必要があります。
シーケンス | 説明 |
---|---|
version | buildspecのバージョンを指定(必須) |
run-as | (Linuxのみ)コマンド実行するLinuxユーザを指定 |
env | 環境変数を指定 |
proxy | プロキシサーバでビルドを実行する場合に指定 |
batch | ビルド失敗時の動作などを指定 |
phases | 実行コマンドを指定(必須) |
reports | テストレポートの形式やレポート出力などを指定 |
artifacts | 生成物の出力先を指定 CodeBuild画面で設定も可能 |
cache | キャッシュの場所の指定 |
詳しい記載方法などは以下リファレンスを参照。
また、CodeBuild
をローカルでテストしたい場合は以下参照してください。
作成するbuildspec.ymlについて
今回CodeBuild
からRunCommand
を実行したいので、ビルド用のコンテナからSystems Manager
のRunCommand
を実行するようにコマンドを記載します。
コマンドはAWSのリファレンスから調べて記載しても良いですが、Systems Manager
のRunCommand
画面に以下のようにコマンド実行用のCLIコマンドが表示されるため、そちらで表示されるコマンドをベースに作ります。
RunCommand
作成時でしか以下画面のようなCLIコマンドは確認できないため、新規に作成するか、RunCommand
の「コマンド履歴」から「Copy to new」ボタンを選択して確認しましょう。
CLIコマンドを参考に作成したbuildspec.yml
のファイル構成は以下となります。
本来はbuildspec.yml
ファイルだけで良いですが、汎用性を持たせるため、以下のような構成としました。
ファイル名 | 説明 |
---|---|
buildspec.yml | ベースとなるbuildspec.ymlファイル 以下ファイルを参照&生成して実行する処理を行う |
exec_host.json | ターゲットの指定を行うファイル |
sourceinfo.json | ソースファイルの場所を指定するファイル |
ansible.sh | RunCommand実行コマンドを記載した生成ファイル 実行後は自動削除 |
以降でファイルの内容について記載&解説します。
buildspec.yml
作成したbuildspec.yml
は以下となります。
version: 0.2
env:
shell: bash
variables:
EXEC_HOST_FILE: exec_host.json
SOURCEINFO_FILE: sourceinfo.json
EXEC_ANSIBLE_SHELL: ansible.sh
DOCUMENT_NAME: AWS-ApplyAnsiblePlaybooks
DOCUMENT_VERSION: 1
TIMEOUT_SECONDS: 600
MAX_CONCURRENCY: 50
MAX_ERRORS: 0
REGION: ap-northeast-1
SOURCETYPE: S3
INSTALLDEPENDENCIES: True
PLAYBOOKFILE: site.yml
EXTRAVARIABLES: SSM=True
CHECK: True
VERBOSE: "-v"
TIMEOUTSECONDS: 3600
SOURCEFILE: ssm_ansible.zip
phases:
install:
on-failure: ABORT
commands:
- zip -r ${SOURCEFILE} *
- aws s3 mv ${SOURCEFILE} $(jq -c . ${SOURCEINFO_FILE} | awk -F'[.:]' '{print "s3:"$3}')
pre_build:
on-failure: ABORT
commands:
- EXEC_HOST=$(cat ${EXEC_HOST_FILE} | jq -c .)
- SOURCEINFO=$(cat ${SOURCEINFO_FILE} | jq -c . | sed -e "s/\"/\\\\\\\"/g")
- |
CMD="aws ssm send-command
--document-name "${DOCUMENT_NAME}"
--document-version "${DOCUMENT_VERSION}"
--timeout-seconds "${TIMEOUTSECONDS}"
--max-concurrency "${MAX_CONCURRENCY}"
--max-errors "${MAX_ERRORS}"
--region "${REGION}"
--targets '${EXEC_HOST}'
--parameters '{
\"SourceType\":[\"${SOURCETYPE}\"],
\"SourceInfo\":[\"${SOURCEINFO}\"],
\"InstallDependencies\":[\"${INSTALLDEPENDENCIES}\"],
\"PlaybookFile\":[\"${PLAYBOOKFILE}\"],
\"ExtraVariables\":[\"${EXTRAVARIABLES}\"],
\"Check\":[\"${CHECK}\"],
\"Verbose\":[\"${VERBOSE}\"],
\"TimeoutSeconds\":[\"${TIMEOUTSECONDS}\"]
}'"
- echo $CMD > ${EXEC_ANSIBLE_SHELL}
build:
on-failure: ABORT
commands:
- bash ${EXEC_ANSIBLE_SHELL}
post_build:
commands:
- rm -f ${EXEC_ANSIBLE_SHELL}
env
の部分がRunCommand
の各種設定を変数化した内容となるため、実行環境に合わせて値を修正します。
phases
はinstall
、pre_build
、build
、post_build
と分かれており、以下のような処理を実行するようにしています。
ビルド種別 | 内容 |
---|---|
install | RunCommandで使用する各種ファイルの圧縮&S3アップロード |
pre_build | RunCommand実行用コマンドシェルの生成 |
build | RunCommand実行用コマンドシェルの実行 |
post_build | RunCommand実行用コマンドシェルの削除 |
install
ではpre_build
で使用する各種ファイルをZIP圧縮してS3バケットにアップロードを行っています。
aws s3 mv
で記載しているjq
コマンドはSOURCEINFO_FILE
からS3URI
を取得しているだけなので、jq
コマンドで整形しなくても、変数化して読み込ませてもOKです。
その1の「コマンドの実行」の設定項目で言うと、圧縮されたAnsible
のソースファイル格納先の設定となる「Source Info」と「ターゲット」の指定は、コマンド実行する際にJSON
形式で記載する必要があるため、それぞれ別ファイルとして読み込み、コマンド生成する際に整形してワンライナーのコマンドになるようにしました。
また、今回の場合、わざわざpre_build
シーケンスでRunCommand
実行用のシェルファイルを生成してからbuild
シーケンスで生成したシェルファイルを実行、post_build
で生成したシェルファイルを削除するような回りくどい方法を行っておりますが、YML
形式となるbuildspec.yml
でJSON
形式となるターゲット指定とソース指定のパラメータを記載すると、どうやってもフォーマットエラーとなってしまったため、一度シェルを生成してから実行するようにしました。
exec_host.json
RunCommand
をコマンド実行する場合、当然ながら画面上でターゲットを選択して実行するようなことができず、RunCommand
のCLI仕様上、JSON
形式で指定する必要があるため、別ファイルとしてターゲットを指定するようにしました。
[
{
"Key":"InstanceIds",
"Values":[
"i-0a93d08d7d8ed1597"
]
}
]
JSON
形式なので、以下のようにカンマで区切れば複数ターゲットを指定することも可能です。
[
{
"Key":"InstanceIds",
"Values":[
"i-0a93d08d7d8ed1597",
"i-0e0fc2d1b6cc86efd"
]
}
]
ターゲットをタグで指定したい場合は以下のように設定します。
以下はタグキーを「Name」、タグの値を「webserver」とした場合の例。
[
{
"Key":"tag:Name",
"Values":[
"webserver"
]
}
]
タグ指定で複数ターゲットを指定する場合は、タグキーも変わってくるため、以下のように指定。
[
{
"Key":"tag:Name",
"Values":[
"webserver"
]
},
{
"Key":"tag:Environment",
"Values":[
"dev"
]
}
]
ターゲットをリソースグループで指定した場合は今回試していませんが、RunCommand
のCLIコマンド欄に表示された内容を元に記載しているだけなので、リソースグループで指定したい場合もCLIコマンド欄に表示された内容を元にexec_host.json
に記載すれば使用できるかと思います。
sourceinfo.json
RunCommand
の「Source Info」欄に指定するJSON
形式の内容をそのままファイル化した内容となります。
「Source Info」で指定する内容をそのまま記載してください。
{
"path":"https://[バケット名].s3.[リージョン名].amazonaws.com"
}
ansible.sh
buildspec.yml
のpre_build
シーケンスで組み立てたコマンドが書かれる一時ファイル。
自動で生成され、実行後に自動削除されるため、あらかじめファイルを作成しておくなどの処理は不要です。
CodeBuildの設定
buildspec.yml
の準備ができたので、いよいよCodeBuild
の設定を以下の順番で行っていきます。
ビルドプロジェクトの作成
先程準備したbuildspec.yml
はビルド用コンテナ内で実行される処理の内容を記載したファイルとなりますが、ビルド用コンテナ自体をどのコンテナイメージで作成するか、ログ出力はどうするか等、コンテナ外での処理をビルドプロジェクトで設定していきます。
以下、CodeBuild
のダッシュボードからビルドプロジェクトを作成した場合の例です。
尚、設定を行った項目について以下表に載せておりますが、特に設定変更を行っていない項目については除外しております。
項目 | 設定 | 備考 |
---|---|---|
プロジェクト名 | ssm_ansible | 任意のプロジェクト名を入力 |
ソースプロバイダ | AWS CodeCommit | |
リポジトリ | ssm_ansible | 自分のリポジトリ名に合わせる |
環境イメージ | マネージド型イメージ | |
オペレーティングシステム | Amazon Linux 2 | |
ランタイム | Standard | |
イメージ | aws/codebuild/amazonlinux2-x86_64-standard:4.0 | |
イメージのバージョン | このランタイムバージョンには常に最新のイメージを使用してください | |
環境タイプ | Linux | |
特権付与 | チェック | |
サービスロール | 新しいサービスロール | |
ロール名 | 新規作成 | |
ビルド仕様 | buildspecファイルを使用する | |
アーティファクトタイプ | Amazon S3 | |
バケット名 | 任意 | 作成済みのS3バケット名を指定 |
名前 | ssm_ansible.zip | 任意の名前を入力 |
アーティファクトのパッケージ化 | Zip |
ビルドの実行
ビルドプロジェクトの作成ができたため、作成したビルドプロジェクトを使用して実際にビルドプロジェクトを動かしてみます。
いくつか実行する方法はありますが、CodeBuild
のダッシュボードより「ビルド」→「ビルドプロジェクト」から作成したビルドプロジェクト(今回はssm_ansible
)を選択して「ビルドを開始」を実行すれば動きます。
ビルド結果の確認
実行したビルドプロジェクトの「フェーズ詳細」に各ビルドフェーズのステータスが表示されるため、全て「成功」と表示されていれば成功です。
また、今回の場合、指定したバケットにアーティファクト(ビルド実行時のファイル等)がアップロードされているため、S3バケットにアップロードされているかも確認します。
おわりに
「その1」でSystems Manager
単体で行った方法と比べると、いくつかのファイルを扱っているため複雑に見えますが、汎用性を持たせるために入力ファイル等作り込んでいるだけで、やっていることはSystems Manager
の画面に表示されたCLIコマンドをCodeBuild
で実行していることと変わりません。
今回のbuildspec.yml
の中身を見ても分かる通り、ビルド以外でも何でも実行できるため、組み方次第で色々な用途で利用できるサービスになります。
次回は今まで作成してきたサービスをCodePipeline
で繋いで、コミット契機で実行できるようにしていきたいと思います。