LoginSignup
0
0

More than 1 year has passed since last update.

AnsibleをAWS Systems Managerから実行するCI/CDを構築する。(その4:CodeBuildからのAnsible実行)

Last updated at Posted at 2022-12-03

2022/12/4更新
CodeBuildの「buildspec.yml」でCodeCommitから取得したソースを予めZIP圧縮してS3に格納しておく必要があったため、記事を更新しました。

はじめに

前回はChatbotを使用してSlackへ通知する仕組みを構築しました。

今回は、その1でマネジメントコンソール上から直接Systems ManagerRunCommandを実行して試したことをCodeBuildから実行できるかを試してみようと思います。

CodeBuildとは

M2 MacでCodeBuildをローカルで実行する。」でも書きましたが、CodeBuildの名の通り、コードをビルドすることを目的としたサービスです。

CodeBuildでソースコードを実際に実行できるような形式にビルドする方法は、buildspec.ymlというビルドの設計図となるファイルにビルドコマンドや実行コマンドを記載して、実行アプリを生成するやり方となります。

通常はCodeBuildでビルド後、ビルドしたアプリを実行するノードに対してCodeDeploy等でアプリを配置してCodePipelineで一連のサービスを繋げてCodeCommitへのコミット契機でCodeBuildCodeDeployと行うのが一般的なCI/CDの流れとなります。

今回の最終構成の場合はCodeDeployに当たる操作はSystems ManagerRunCommandで行うため、CodeDeployは使用しませんが、構成としては同等の構成を構築していきます。

CodeBuildの仕組み

CodeBuildの仕組みを少し深堀りすると、ビルド実行時、まずビルド実行用のDockerコンテナを起動して、事前に準備してあるbuildspec.ymlファイルのビルド設計に従って処理を実行、生成物を出力する設定にしていれば生成物(アーティファクト)をS3バケットに格納する仕組みとなります。

したがって、buildspec.ymlを作成するということは、ビルド用コンテナで実行する内容を設定することとなります。

今回の構成

今回はCodeBuildビルドプロジェクトを作成して、前回作成したCodeCommitに格納しているソースファイルを使い、CodeBuild経由でSystems ManagerRunCommandを実行する以下のような構成を作ってみます。

前回RunCommandAnsibleを実行した際にはマネジメントコンソール画面上から実行しましたが、今回はコマンド操作でCodeBuildから実行するようにします。

buildspec.ymlの作成

最初にビルドの設計図となるbuildspec.ymlファイルを作成していきます。

大きく分けると以下9つのシーケンスと呼ばれる構文があり、少なくとも以下必須と書かれているversionphasesbuildspec.ymlで指定する必要があります。

シーケンス 説明
version buildspecのバージョンを指定(必須)
run-as (Linuxのみ)コマンド実行するLinuxユーザを指定
env 環境変数を指定
proxy プロキシサーバでビルドを実行する場合に指定
batch ビルド失敗時の動作などを指定
phases 実行コマンドを指定(必須)
reports テストレポートの形式やレポート出力などを指定
artifacts 生成物の出力先を指定
CodeBuild画面で設定も可能
cache キャッシュの場所の指定

詳しい記載方法などは以下リファレンスを参照。

また、CodeBuildをローカルでテストしたい場合は以下参照してください。

作成するbuildspec.ymlについて

今回CodeBuildからRunCommandを実行したいので、ビルド用のコンテナからSystems ManagerRunCommandを実行するようにコマンドを記載します。

コマンドはAWSのリファレンスから調べて記載しても良いですが、Systems ManagerRunCommand画面に以下のようにコマンド実行用のCLIコマンドが表示されるため、そちらで表示されるコマンドをベースに作ります。

RunCommand作成時でしか以下画面のようなCLIコマンドは確認できないため、新規に作成するか、RunCommandの「コマンド履歴」から「Copy to new」ボタンを選択して確認しましょう。

Monosnap_20220923_230736.png

CLIコマンドを参考に作成したbuildspec.ymlのファイル構成は以下となります。

本来はbuildspec.ymlファイルだけで良いですが、汎用性を持たせるため、以下のような構成としました。

ファイル名 説明
buildspec.yml ベースとなるbuildspec.ymlファイル
以下ファイルを参照&生成して実行する処理を行う
exec_host.json ターゲットの指定を行うファイル
sourceinfo.json ソースファイルの場所を指定するファイル
ansible.sh RunCommand実行コマンドを記載した生成ファイル
実行後は自動削除

以降でファイルの内容について記載&解説します。

buildspec.yml

作成した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の各種設定を変数化した内容となるため、実行環境に合わせて値を修正します。

phasesinstallpre_buildbuildpost_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.ymlJSON形式となるターゲット指定とソース指定のパラメータを記載すると、どうやってもフォーマットエラーとなってしまったため、一度シェルを生成してから実行するようにしました。

exec_host.json

RunCommandをコマンド実行する場合、当然ながら画面上でターゲットを選択して実行するようなことができず、RunCommandのCLI仕様上、JSON形式で指定する必要があるため、別ファイルとしてターゲットを指定するようにしました。

exec_host.json
[
  {
    "Key":"InstanceIds",
    "Values":[
      "i-0a93d08d7d8ed1597"
    ]
  }
]

JSON形式なので、以下のようにカンマで区切れば複数ターゲットを指定することも可能です。

複数ターゲットを指定する場合のexec_host.json
[
  {
    "Key":"InstanceIds",
    "Values":[
      "i-0a93d08d7d8ed1597",
      "i-0e0fc2d1b6cc86efd"
    ]
  }
]

ターゲットをタグで指定したい場合は以下のように設定します。

以下はタグキーを「Name」、タグの値を「webserver」とした場合の例。

タグ指定する場合のexec_host.json
[
  {
    "Key":"tag:Name",
    "Values":[
      "webserver"
    ]
  }
]

タグ指定で複数ターゲットを指定する場合は、タグキーも変わってくるため、以下のように指定。

タグ指定で複数ターゲット指定する場合のexec_host.json
[
  {
    "Key":"tag:Name",
    "Values":[
      "webserver"
    ]
  },
  {
    "Key":"tag:Environment",
    "Values":[
      "dev"
    ]
  }
]

ターゲットをリソースグループで指定した場合は今回試していませんが、RunCommandのCLIコマンド欄に表示された内容を元に記載しているだけなので、リソースグループで指定したい場合もCLIコマンド欄に表示された内容を元にexec_host.jsonに記載すれば使用できるかと思います。

sourceinfo.json

RunCommandの「Source Info」欄に指定するJSON形式の内容をそのままファイル化した内容となります。

Source Info」で指定する内容をそのまま記載してください。

sourceinfo.json
{
  "path":"https://[バケット名].s3.[リージョン名].amazonaws.com"
}

ansible.sh

buildspec.ymlpre_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)を選択して「ビルドを開始」を実行すれば動きます。

ビルド結果の確認

実行したビルドプロジェクトの「フェーズ詳細」に各ビルドフェーズのステータスが表示されるため、全て「成功」と表示されていれば成功です。

Monosnap_20221130_214432.png

また、今回の場合、指定したバケットにアーティファクト(ビルド実行時のファイル等)がアップロードされているため、S3バケットにアップロードされているかも確認します。

おわりに

その1」でSystems Manager単体で行った方法と比べると、いくつかのファイルを扱っているため複雑に見えますが、汎用性を持たせるために入力ファイル等作り込んでいるだけで、やっていることはSystems Managerの画面に表示されたCLIコマンドをCodeBuildで実行していることと変わりません。

今回のbuildspec.ymlの中身を見ても分かる通り、ビルド以外でも何でも実行できるため、組み方次第で色々な用途で利用できるサービスになります。

次回は今まで作成してきたサービスをCodePipelineで繋いで、コミット契機で実行できるようにしていきたいと思います。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0