なんでFargateでBastionとか作るの?CodeBuildにブレイクポイントを設定してSessionManagerで入ったらいいやん
- CodeBuildってデバッグしづらい毎回Buildを実行してるけど効率書きできないの?
- FargateやEC2でbastionを構築して作業をしている人(ssh で接続する必要がある場合はすべてsession managerで実現できます)
という方に向けた記事です。
CodeBuildにsession managerで入れることはみなせんご存知だと思います。
ただ、CodeBuildを立ち上げるのが面倒だったり、ブレイクポイントを仕込むのが面倒なのであまり活用してないのだと思います。
CodePipelineで連携されているCodeBuildのデバッグにはあまり意味がありません。というのも実行するときはCodeBuild単体での実行になるのでCodePipelienなどでソース連携されていてもソースは取得できません。
TerraformでCodeBuild単体のデバッグできるように作成する
CodeBuild内にSessionManagerで入るために必要なのは3つです。そのうちの2つは事前に準備できます。
- IAMでSessionManagerへのアクセスを許可する
- BuildSpecにブレイクポイントを仕込む
ブレイクポイントはこのような感じです。
version: 0.2
phases:
pre_build:
commands:
- aws --version
- codebuild-breakpoint
build:
commands:
- echo Build started on $(date)
finally:
- echo Build completed on $(date)
post_build:
commands:
- echo "[{\"name\":\"${local.service_env_ver}\", \"imageUri\":\"$${IMAGE_URL}\"}]" > taskdef.json
artifacts:
files:
- taskdef.json
codebuild-breakpointという一行を仕込むだけです。
IAMは通常のCodeBuildに加えて下記のものを追加します。
{
"Effect": "Allow",
"Action": [
"ssmmessages:CreateControlChannel",
"ssmmessages:CreateDataChannel",
"ssmmessages:OpenControlChannel",
"ssmmessages:OpenDataChannel"
],
"Resource": "*"
}
TerraformでCodeBuildを構築する
ディレクトリ構成
$ tree !10075
.
├── codebuild.tf
├── iam.tf
├── local.tf
├── provider.tf
├── var.tfvars
└── variables.tf
0 directories, 6 files
上から一気にファイルを記載していきます。
resource "aws_codebuild_project" "main" {
name = "${local.service_env_ver}-project"
service_role = aws_iam_role.codebuild.arn
artifacts {
type ="NO_ARTIFACTS"
}
source {
type ="NO_SOURCE"
buildspec = <<BUILDSPEC
version: 0.2
phases:
pre_build:
commands:
- aws --version
- codebuild-breakpoint
build:
commands:
- echo Build started on $(date)
finally:
- echo Build completed on $(date)
post_build:
commands:
- echo "[{\"name\":\"${local.service_env_ver}\", \"imageUri\":\"$${IMAGE_URL}\"}]" > taskdef.json
artifacts:
files:
- taskdef.json
BUILDSPEC
}
environment {
compute_type = "BUILD_GENERAL1_SMALL"
image = "aws/codebuild/amazonlinux2-x86_64-standard:3.0"
type = "LINUX_CONTAINER"
image_pull_credentials_type = "CODEBUILD"
privileged_mode = true
}
cache {
type = "LOCAL"
modes = ["LOCAL_DOCKER_LAYER_CACHE"]
}
}
data "aws_iam_policy_document" "codebuild_policy" {
statement {
effect = "Allow"
actions =[
"logs:CreateLogStream",
"logs:CreateLogStream",
"logs:PutLogEvents"
]
resources = ["*"]
}
statement {
effect = "Allow"
actions =[
"s3:PutObject",
"s3:GetObject",
"s3:GetOBjectVersion",
"s3:GetBucketAcl",
"s3:GetBucketLocation",
]
resources = ["*"]
}
statement {
effect = "Allow"
actions =[
"codebuild:CreateReportGroup",
"codebuild:CreateReport",
"codebuild:UpdateReport",
"codebuild:UpdateReport",
"codebuild:BatchPutTestCases",
]
resources = ["*"]
}
statement {
effect = "Allow"
actions =[
"ecr:BatchCheckLayerAvailability",
"ecr:CompleteLayerUpload",
"ecr:GetAuthorizationToken",
"ecr:InitiateLayerUpload",
"ecr:PutImage",
"ecr:UploadLayerPart",
]
resources = ["*"]
}
statement {
effect = "Allow"
actions =[
"ec2:CreateNetworkInterface",
"ec2:DescribeDhcpOptions",
"ec2:DescribeNetworkInterfaces",
"ec2:DeleteNetworkInterface",
"ec2:DescribeSubnets",
"ec2:DescribeSecurityGroups",
"ec2:DescribeVpcs",
"ec2:CreateNetworkInterfacePermission",
]
resources = ["*"]
}
statement {
effect = "Allow"
actions =[
"ecs:DescribeTaskDefinition",
]
resources = ["*"]
}
statement {
effect = "Allow"
actions =[
"ssmmessages:CreateControlChannel",
"ssmmessages:CreateDataChannel",
"ssmmessages:OpenControlChannel",
"ssmmessages:OpenDataChannel",
]
resources = ["*"]
}
}
resource "aws_iam_policy" "codebulid_policy" {
name = "${local.service_env_ver}-codebuild-policy"
path = "/service-role/"
policy = data.aws_iam_policy_document.codebuild_policy.json
}
resource "aws_iam_role" "codebuild" {
name ="${local.service_env_ver}-codebuild-role"
managed_policy_arns = [aws_iam_policy.codebulid_policy.arn, "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM"]
force_detach_policies = true
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Sid = ""
Principal = {
Service = "codebuild.amazonaws.com"
}
}
]
})
}
data "aws_region" "current" {}
data "aws_caller_identity" "current" {}
locals {
service_env_ver = "${var.service}-${var.env}-${var.ver}"
account_id = data.aws_caller_identity.current.account_id
region = data.aws_region.current.name
}
provider "aws" {
region = "ap-northeast-1"
}
terraform {
required_providers {
aws = {
version = "3.56.0"
}
}
backend "s3" {
bucket = "あなたの使用するバケット名"
region = "ap-northeast-1"
key = "保存したいキー名.tfstate"
}
}
env = "develop"
service = "codebuild"
ver = "1"
variable "env" { }
variable "service" { }
variable "ver" { }
こちらを作成して、terraformの実行を行ってみてください。
terraform init
terraform plan -var-file=var.tfvars
terraform apply -var-file=var.tfvars
CodeBuildの上書き実行で内部に入る
残念ながらAWSコンソール上でBuildの上書き実行をする以外でCodeBuild内部に入る方法はありません。
aws cli v2でsession manager pluginがあれば普通にcli出codebuildを立ち上げてterminalからsession managerを使用してinteractiveに実行で行きます。
awsのコンソールで入る場合はこちら
- 上書きでBuildを開始する。
- 高度なビルドの上書き
- セッションの有効化
- ビルドの開始
の手順で実行してください。
しばらくすると次のようにビルドの画面にAWSセッションマネージャーのリンクが出てくるのでそちらを押します。
これでCodeBuildの内部に入ることができました。これで、CodeBuildでどのようなことが実行可能なのか用意に確認できる環境が作成されました。
最後にcodebuildの起動とsession managerに入るスクリプトおいておきます。
jqとfzfとaws cliv2とsession manager pluginが必須です。
project=$(aws codebuild list-projects | jq -r ".projects[]" | fzf);echo "$project"; buildId=$(awsv2 codebuild start-build --project-name $project --debug-session-enabled | jq -r ".build.id" | fzf); echo "buildId:$buildId"
listBuildIds=$(awsv2 codebuild list-builds | jq -r ".ids[]"| fzf); session=$(awsv2 codebuild batch-get-builds --ids $listBuildIds --output json | jq -r ".builds[].debugSession.sessionTarget" | fzf); echo "session${session}"; awsv2 ssm start-session --target $session
Discussion