AWS CodeBuild入門:セキュアなCIをTerraformで構築したよ
このブログを要約すると
CodeBuildでCIだけAWSに寄せてみたら、認証情報の管理がめっちゃラクになった話。
Terraformでサクッと構築して、GitHub連携やECRへのPushも試してみたよ。
CodeBuildの初体験メモとしてどうぞ。
はじめに
AWS CodeBuild(以降CodeBuild)に入門したので、以下に初感をつらつらと綴ってみます。
CodeBuildとは
GitHub ActionsではまるッとCI/CDを実現できますが、CodeBuildはCI(Continuous Integration)を切り出して、コードのビルド、テスト、コンテナ化をマネージドとして提供するサービスです。
例えばのユースケースとして、GitHub ActionsなどのAWS外のサービスでCI/CDを実装しようとなった場合に、CIの中でE2Eテストを組む際はDB認証情報などをAWS外に出す必要があります。
それをCodeBuildでは、CIをAWS内で完結できるので、IAMポリシーさえしっかり組んでいれば、認証情報を外出しせずにE2Eテストを実施できます。こうした認証情報をAWS内から出さないといったメリットがあるので、CIをCodeBuildで実装してテスト、ビルド、プッシュ(アーティファクトの格納)などを行い、CD(Continuous Derivery)をGitHub Actionsなどで行うのは良い選択肢あると感じました。
そんなCodeBuildを、今回はTerraformで組んでみました。
実装
今回のCodeBuildでは、Pythonコードをdocker buildでImage化を行い、ImageをECRにPushするのみの単純なbuildspecを組んでいます。
ポリシー
CodeBuildが使用するロールに紐づくポリシーは以下の通りです。
(今回は解説を省きますが、AssumeRoleではCodeBuildを指定ください)
ECRへのログインとPush、BuildのストリームログのCloudWatchへの格納と、GitHubへの接続のためのCodeConnections取得のみをできるポリシーとしています。
data "aws_iam_policy_document" "codebuild" {
statement {
effect = "Allow"
sid = "ECRAccess"
actions = [
"ecr:CompleteLayerUpload",
"ecr:UploadLayerPart",
"ecr:InitiateLayerUpload",
"ecr:BatchCheckLayerAvailability",
"ecr:PutImage",
"ecr:BatchGetImage"
]
resources = [data.aws_ecr_repository.ecr_repository.arn]
}
statement {
effect = "Allow"
sid = "ECRAuth"
actions = [
"ecr:GetAuthorizationToken"
]
resources = ["*"]
}
statement {
effect = "Allow"
sid = "CloudWatchLogsAccess"
actions = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
]
resources = ["*"]
}
statement {
effect = "Allow"
sid = "CodeConnectionAccess"
actions = [
"codeconnections:GetConnectionToken",
"codeconnections:GetConnection"
]
resources = [aws_codeconnections_connection.github.arn]
}
}
CodeBuild
キャッシュをS3に格納したり、 CodeBuildの実態の環境を定義したりしています。今回はEC2のLinux Containerを選択しています。実行時間が長いのであればEC2、すぐに終わるのであればLambdaといったように選択するといいと思います。
さらに今回は、GitHubにあるコードを使ってCIしたいので、sourceでGitHubを選択し、かつ認証にはGitHub Appを使いたいのでCodeConnectionsを利用しています。
resource "aws_codebuild_project" "codebuild" {
name = local.codebuild_project_name
description = "practice for CI from GitHub"
service_role = aws_iam_role.codebuild.arn
build_timeout = 5
artifacts {
type = "NO_ARTIFACTS"
}
cache {
type = "S3"
location = aws_s3_bucket.codebuild.bucket
}
environment {
# https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-compute-types.html#environment.types
compute_type = "BUILD_GENERAL1_SMALL"
type = "LINUX_CONTAINER"
# https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-available.html
# EC2インスタンスは"aws/codebuild/ami/amazonlinux-x86_64-base:latest"や
# "aws/codebuild/ami/amazonlinux-arm-base:latest"のみ。基本的には自前でビルドが必要。
image = "aws/codebuild/standard:6.0" # Ubuntu 22.04
image_pull_credentials_type = "CODEBUILD"
}
source {
type = "GITHUB"
location = "https://github.com/jnytnai0613/blue-green-upgarade-blueprints"
report_build_status = true
buildspec = "system/assets/sample-app/container/buildspec.yml"
auth {
type = "CODECONNECTIONS"
resource = aws_codeconnections_connection.github.arn
}
}
logs_config {
cloudwatch_logs {
group_name = "buildlog"
stream_name = "log-stream"
}
}
}
Webhook
今回はGitHubで発生するイベントをトリガーに、buildspec.ymlに定義したCIを実行したいです。そのため、WebhookではPull Requestのマージをトリガーとしています。さらに、対象リポジトリにはTerraformコードもあるので、アプリケーションコードのみが変更された場合にトリガーしたいのでパスでfilterをかけています。
resource "aws_codebuild_webhook" "github" {
project_name = aws_codebuild_project.codebuild.name
build_type = "BUILD"
filter_group {
filter {
type = "EVENT"
pattern = "PULL_REQUEST_MERGED"
}
filter {
type = "FILE_PATH"
pattern = "system/assets/sample-app/container/.*"
}
}
}
また、このresourceをapplyすることでGitHubのSettings/WebhooksにWebhook定義が生えてくるのが確認できるかと思います。
ちなみにこのWebhook、少々applyにコツが要ります。
CodeBuildと一緒にデプロイしようとすると、CodeBuildの定義内の以下個所でARNが無効のエラーが発生します。これはCodeConnectionsはGitHub AppをGitHubにインストールするのですが、それまでは、接続が保留されるためです。そのため、保留中のCodeConnectionsを指定したCodeBuildにWebhookを紐づけようとすると、Webhook側で無効なARNが検出されるというわけです。
WebhookのapplyまでにはCodeConnectionsを承認しましょう
source {
type = "GITHUB"
;
auth {
type = "CODECONNECTIONS"
resource = aws_codeconnections_connection.github.arn
}
}
buildspec.yml
なんてことはないDocker Imageを作成してECRへPushするだけのスクリプトです。
version: 0.2
env:
variables:
IMAGE_REPO_NAME: blue-green-ecr
phases:
pre_build:
commands:
- export IMAGE_TAG=$(TZ=Asia/Tokyo date '+%Y%m%d%H%M%S')
- export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output=text)
- 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
build:
commands:
- docker build system/assets/sample-app/container -t $IMAGE_REPO_NAME:$IMAGE_TAG
- docker tag docker.io/library/$IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
post_build:
commands:
- docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
さいごに
今まで個人や業務ではGitHub ActionsでCI/CDを実装してきましたが、AWSのマネージドサービスであるCodeBuildでセキュアなCIが実装できるのは新たな扉を開いた感があります。
これを応用すると、AWSで完結するE2EテストのCI実装や、SQL変更とDBの問い合わせ結果の突合などをPRにコメントするCIなどできそうです。
Terraformでは上記のコードを利用すればある程度実装できるかと思いますので、ぜひやってみてください!
Discussion