📚

なんでFargateでBastionとか作るの?CodeBuildにブレイクポイントを設定してSessionManagerで入ったらいいやん

2022/01/19に公開
  • 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

上から一気にファイルを記載していきます。

codebuild.tf
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"]
  }
}
iam.tf
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"
        }
      }
    ]
  })
}
local.tf
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.tf
provider "aws" {
  region = "ap-northeast-1"
}

terraform {
  required_providers {
    aws = {
      version = "3.56.0"
    }
  }
  backend "s3" {
    bucket = "あなたの使用するバケット名"
    region = "ap-northeast-1"
    key = "保存したいキー名.tfstate"
  }
}
var.tfvars
env = "develop"
service = "codebuild"
ver = "1"
variables.tf
variable "env" { }
variable "service" { }
variable "ver" { }

こちらを作成して、terraformの実行を行ってみてください。

shell
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のコンソールで入る場合はこちら

  1. 上書きでBuildを開始する。
  2. 高度なビルドの上書き
  3. セッションの有効化
  4. ビルドの開始

の手順で実行してください。

しばらくすると次のようにビルドの画面に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

ログインするとコメントできます