![Backlog の Git リポジトリをソースとした Docker イメージのビルドパイプラインを作ってみた](https://devio2023-media.developers.io/wp-content/uploads/2019/06/eyecatch-backlog.png)
Backlog の Git リポジトリをソースとした Docker イメージのビルドパイプラインを作ってみた
こんにちは!AWS事業本部コンサルティング部のたかくに(@takakuni_)です。
今回は、Backlog の Git リポジトリ(以後、Backlog Git)をソースとして、 Docker イメージをビルドパイプラインを作ってみようと思います。
コミットIDをイメージタグにしたい
皆さんは Backlog Git のコミットID を Docker イメージのタグとして利用したいなと思ったことはありますか? 私はあります。
「CodePipeline のソースに、Backlog Git が指定できないな...でも、Backlog Git で CI/CD をやってみたいな」という方もいらっしゃるのではないでしょうか。
では、さっそくやってみようと思います。
構成図
構成図は次の通りです。
また、ざっくりした仕組みは以下になります。
- Backlog Git の WebフックURL に API Gateway の URL を指定し、 push 時に API リクエストを送信
- API Gateway は CodeBuild プロジェクトを開始させ、 CodeBuild は Backlog Git に対して
git clone
- クローンした成果物を元に Docker イメージのビルド、 ECR へのプッシュ、アーティファクトの配置
- アーティファクトの配置をトリガーに CodePipeline を開始する
- CodeDeploy が項番4のアーティファクトを元に ECS へデプロイ
コード
今回利用したコードは以下に格納されています。
適宜実装したい環境に合わせてコードの変更してご利用いただければ幸いです。
ここからは、どのような実装であるか解説していこうと思います。
Backlog から API Gateway の部分
Backlog では git push
時に指定した URL に POST リクエストを行う Webフック機能が提供されています。
API Gateway で POST リクエストを受け付け、後続の処理を行うように作ってみました。
API Gatewayの実装
CodeBuild へのリクエスト
API Gateway は、 CodeBuild に HTTP API を利用して StartBuild
アクションを実行します。
「どの CodeBuild のプロジェクトを開始するのか」を指定するには、リクエストで利用する HTTP ヘッダー周りに追加で2つの設定が必要です。
- HTTP ヘッダーに
X-Amz-Target: 'CodeBuild_20161006.StartBuild'
,Content-Type: 'application/x-amz-json-1.1'
を含める Content-Type: 'application/x-www-form-urlencoded'
に対して、{"projectName": "CodeBuildのプロジェクト名"}
を含め開始するプロジェクトを指定
IP制限
Backlog Git が利用する IP は公開されているため、気持ちばかりの IP 制限を API Gateway に付与してあげました。
Backlog ヘルプセンター Webhook 送信サーバーの IP アドレスを教えてください
ただ、以下のようにアドレスの変更もあるため、IP制限をかける / かけないは注意が必要です。
なお、 IP アドレス制限は AWS WAF ではなくリソースベースポリシーを利用しています。
ログ
今回はテスト用のためログ記録を有効化していませんが、実行ログを取るのもおすすめです。
「AWS Foundational Security Best Practices コントロール」でも、ログ記録を有効にするよう推奨されているため、ぜひ有効化してみてください。
CodeBuild の実装
buildspec
CodeBuild で利用する buildspec は以下の通りです。
git-remote-codecommit
をインストール- 非対話形式に SSH でログインするため、 Backlog のホスト情報を登録
- Backlog Git に SSH で
git clone
- Docker イメージタグに Git のコミットID(Backlog GUI と同じ桁数)を利用
- Docker イメージのビルド、 ECR へのプッシュ
- Blue/Green デプロイメントのためにアーティファクト作成およびオブジェクト配置
- (オプション) S3に
git-remote-codecommit
のキャッシュ配置
version: 0.2 env: variables: DOCKER_BUILDKIT: "1" AWS_PAGER: "" phases: install: commands: - pip3 install git-remote-codecommit pre_build: commands: - eval $(ssh-agent) - ssh-add - <<< "$BACKLOG_GIT_CREDENTIAL" - ssh-keyscan $BACKLOG_SPACE_ID.git.$BACKLOG_DOMAIN_NAME >> ~/.ssh/known_hosts build: commands: - echo Cloning repository ... - git clone $BACKLOG_SPACE_ID@$BACKLOG_SPACE_ID.git.$BACKLOG_DOMAIN_NAME:/$BACKLOG_PROJECT_KEY/$BACKLOG_REPOSITORY_NAME.git - cd $BACKLOG_REPOSITORY_NAME - COMMIT_HASH=$(git log -n 1 --pretty=format:"%H" | cut -c 1-10) - IMAGE_TAG=${COMMIT_HASH:=latest} - echo Build started on `date` - echo Building Docker image... - docker image build -f docker/Dockerfile -t $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$ECR_REPOSITORY_NAME:$IMAGE_TAG docker post_build: commands: - echo Build completed on `date` - echo Logging in to Amazon ECR... - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com - echo Pushing Docker image... - docker image push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$ECR_REPOSITORY_NAME:$IMAGE_TAG - printf '{"ImageURI":"%s"}' $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$ECR_REPOSITORY_NAME:$IMAGE_TAG > $CODEBUILD_SRC_DIR/imageDetail.json - cd codesries_asset - sed -i -e "s#<TASK_FAMILY>#${TASK_FAMILY}#" taskdef.json - sed -i -e "s#<TASK_EXECUTION_ROLE_ARN>#${TASK_EXECUTION_ROLE_ARN}#" taskdef.json - sed -i -e "s#<CONTAINER_NAME>#${CONTAINER_NAME}#" taskdef.json - sed -i -e "s#<AWS_DEFAULT_REGION>#${AWS_DEFAULT_REGION}#" taskdef.json - sed -i -e "s#<LOG_GROUP_NAME>#${LOG_GROUP_NAME}#" taskdef.json - sed -i -e "s#<LOG_STREAM_PREFIX>#${LOG_STREAM_PREFIX}#" taskdef.json - sed -i -e "s#<CONTAINER_NAME>#${CONTAINER_NAME}#" appspec.yaml - cp -p taskdef.json $CODEBUILD_SRC_DIR/ - cp -p appspec.yaml $CODEBUILD_SRC_DIR/ artifacts: files: - imageDetail.json - taskdef.json - appspec.yaml cache: paths: - /root/.cache/pip/**/*
Backlog の認証情報
CodeBuild では Backlog Git に対して SSH でgit clone
するように設定しました。
Backlog Git へのgit clone
で利用するパラメーターとして以下を利用しました。
- スペースID
- ドメイン
- プロジェクトキー
- レポジトリ名
- 秘密鍵
パラメーターの数が多いため、 Key/Value 形式で取得できる Secrets Manager を利用して1つのシークレットにまとめて保管することにしました。
閲覧権限を分けたいやコスト要件もあるため、実際は Parameter Store や別シークレットで分けて管理もいいと思います。
resource "aws_secretsmanager_secret" "backlog_info" { name = "${local.prefix}/backlog_info" recovery_window_in_days = 0 policy = templatefile("${path.module}/iam_policy_document/resource_secretsmanager.json", { role_arn = aws_iam_role.codebuild.arn }) } resource "aws_secretsmanager_secret_version" "backlog_info" { secret_id = aws_secretsmanager_secret.backlog_info.id secret_string = jsonencode({ space_id = var.backlog.space_id domain_name = var.backlog.domain_name project_key = var.backlog.project_key repository_name = var.backlog.repository_name ssh_key = tls_private_key.backlog.private_key_pem }) lifecycle { ignore_changes = [ secret_string ] } }
CodePipeline
後続の CodePipeline は S3 をトリガーにパイプラインの開始を行いました。
当初、 ECR へのプッシュをトリガーにしようと考えていましたが、設定パラメータを見るとイメージタグを固定する必要があり断念しました。
(buildspec でイメージタグを書き換えるパターンもありですが、なるべく仕組みをシンプルにしたい結果、 S3 オブジェクト配置をトリガーにしてみました。)
ImageTag 必須 いいえ
イメージに使用するタグ。
注記 ImageTag の値を指定しない場合、デフォルト値は latest になります。
AWS CodePipeline ユーザーガイド Amazon ECR
今回のコードを利用してみて、オブジェクトを配置したが CodePipeline がトリガーされない方は以下を併せてご覧ください。
動作検証
では実際に動作検証してみましょう。
Terraform
variables.tf
を確認するといくつか変数が定義されています。変数backlog
にあたる部分を埋めます。
variable "system" { type = string default = "backlog" } variable "env" { type = string default = "cicd" } variable "cidr" { default = { vpc = "192.168.0.0/16" public_a = "192.168.0.0/24" public_c = "192.168.1.0/24" private_a = "192.168.2.0/24" private_c = "192.168.3.0/24" } } variable "backlog" { default = { space_id = "" # htts://sample.backlog.jp の sample の部分 domain_name = "" # backlog.jp または backlog.com project_key = "" # https://backlog.com/ja/enterprise-help/userguide/userguide353/ repository_name = "" # Backlog Git のレポジトリ名 webhook_ip = [ "54.248.107.22/32", "54.248.105.89/32", "54.238.168.195/32", "52.192.66.90/32", "54.65.251.183/32", "54.250.148.49/32", "35.166.55.243/32", "50.112.242.159/32", "52.199.112.83/32", "35.73.201.244/32", "35.72.166.154/32", "35.73.143.41/32", "35.74.201.20/32", "52.198.115.185/32", "35.165.230.177/32", "18.236.6.123/32", ] # https://support-ja.backlog.com/hc/ja/articles/360035645534-Webhook-%E9%80%81%E4%BF%A1%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E3%81%AE-IP-%E3%82%A2%E3%83%89%E3%83%AC%E3%82%B9%E3%82%92%E6%95%99%E3%81%88%E3%81%A6%E3%81%8F%E3%81%A0%E3%81%95%E3%81%84 } }
準備ができたら Terraform でデプロイします。
terraform init terraform plan terraform apply
Backlog への公開鍵の登録
今回 Terraform の tls_private_key
とlocal_file
リソースを利用して、秘密鍵を Secrets Manager 、公開鍵をローカルに保管するように設定しています。
ローカルに保管された公開鍵を Backlog に登録していきます。
Webhook URL の登録
API Gateway コンソールから API リクエストを送るための URL を取得します。
取得した URL を利用して、 WebフックURL を登録します。
Backlog Git へのソースコードの配置
GitHub に上がっている次のフォルダ(codesries_asset
とdocker
)を Backlog Git レポジトリに配置します。
BACKLOG_GIT_REPO ├── codesries_asset │ ├── appspec.yaml │ └── taskdef.json └── docker └── Dockerfile
codesries_asset
フォルダでは、Blue Green デプロイのためのファイル。docker
フォルダは Docker イメージを作成するためのファイルが格納されいます。
このフォルダ周りの構成を変えたい場合は、 buildspec と調整しながらカスタマイズしてください。
cp -pR backlog-git-docker-build/BACKLOG_GIT_REPO/* {BACKLOG_GITのレポジトリ名}/ cd {BACKLOG_GITのレポジトリ名}/ git add . git commit -m 'ADD docker image build asset.' git push
Docker イメージを確認
コードのプッシュが終わると CodeBuild が起動していると思います。プッシュから時間が立っていなければ、ステータスが進行中かと思います。
ステータスが成功になると、 ECR を見てみましょう。初めの要件であった、Backlog Git のコミットID と ECR のイメージタグが一致してると思います。
Backlog Git 側
ECR 側
※ イメージタグ first
は、 Terraform の初回デプロイで利用したイメージになります。
Blue/Green デプロイ
少し忙しいですが、後続の CodePipeline が起動していると思います。
テストポートへの新バージョンの配置は5分間で設定しているため、必要に応じて時間を調節してください。
後片付け
後片付けが必要な方は、backlog-git-docker-build
フォルダに移動後、terraform destroy
で片付け可能です。
cd backlog-git-docker-build terraform destroy
Backlog に登録した公開鍵、WebフックURLも必要に応じて削除してください。
参考
今回、以下のブログを大変参考にさせていただきました!ありがとうございます!
まとめ
以上、「Backlog の Git リポジトリをソースとした Docker イメージのビルドパイプラインを作ってみた」でした。
実際にテストして動きをご確認いただいた方が、イメージつきやすいと思うのでぜひお試しいただけると嬉しいです。
この記事がどなたかの参考になれば幸いです。
AWS事業本部コンサルティング部のたかくに(@takakuni_)でした!