2021年12月9日 星期四

CodeBuild to Elastic Container Repository (ECR), and Let Elastic Kubernetes Service (EKS) Access it.

在 Elastic Kubernetes Service 中要使用 Docker Image 還是可以考慮用 Code Pipeline + Code Build 把 Docker image 編譯到 Elastic Container Repository (ECR) 中充當 Docker Hub 使用。


整體架構


先備條件:

  1. 假設你的專案已經有 Code Pipeline (CI/CD, Code Build) 可以自動讀取專案目錄下的 buildspec.yaml

在這個專案中要實現幾件事情:

  1. 使用 Terraform 開 ECR
  2. 套用一些權限到 Code Build, EKS 上
  3. 把專案的 Docker Image 自動 Build 到 ECR 上
  4. 讓 EKS 可以存取這個 Image

專案的 Build Spec


由於我的整個專案都在 Code Commit 上,基本上狀況都會簡單一些,程式專案下應該在根目錄要有一個 buildspec.yaml ,然後在 CodeBuild 執行的時候可以讀到這個檔案,自動跑 CI/CD。

buildspec.yaml

version: 0.2

phases:
  pre_build:
    commands:
      # push docker to ECR (這裡都是 AWS CodeBuild 上預設的變數,不用改)
      - 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

  install:
    commands:
      # for monolith app
      - go mod download

  build:
    commands:
      # for monolith app, first build then image can be compile
      # - go build (部署單體應用程式才需要用)

      # push docker to ECR ($YOUR_REPO_NAME, $IMAGE_TAG 要設定 CodeBuild 的 Tags)
      - echo Build docker image...
      - docker build . -t $YOUR_REPO_NAME:$IMAGE_TAG -f dockerfiles/你的dockefile.dockerfile
      - echo Build docker image completed...
      - docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG

  post_build:
    commands:
      - echo Build completed on `date`

      # push docker to ECR (有些是預設的,有些要設定到 tag)
      - echo Push image...
      - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$YOUR_REPO_NAME:$IMAGE_TAG
      - echo Push image completed...

# for monolith app (這是部署單體應用程式才有的)
#artifacts:
#  files:
##    - xxxxbinary
#    - appspec.yml
#    - scripts/deploy-clean.sh
#    - scripts/deploy-install.sh
#    - .env
#  name: "go-server-$(date +%Y-%m-%d)"
#  discard-paths: yes

# somethime buildspec cache will store the legacy changes
cache:
  paths:
    - /go/pkg/**/*

這個設定檔中有很多變數是 Code Build 中的 Tag,這可能會需要在 Terraform 一開始就給定,現在這個檔案應該會自動 Build 了,但目前還沒有 ECR,要使用 Terraform 建立一個,然後套用相關權限。

*關於 Code Pipeline, CodeBuild terraform 應該會在另一篇文章提及,本篇即當作已知。

目錄結構

  • terraform
    • modules
      • ecr <- 重點
        • main.tf
        • vars.tf
      • pipeline
      • eks
    • policies
      • build_role_policy.tpl
      • node_instance_role_policy.tpl
    • main.tf
首先,這是一個 ECR 建立的 Terraform:

ecr/main.tf:

resource "aws_ecr_repository" "_" {
  name                 = "${var.repo_name}"
  image_tag_mutability = "MUTABLE"

  image_scanning_configuration {
    scan_on_push = true
  }
}

ecr/vars.tf:

variable "repo_name" {
  type        = string
  description = "ecr repo name"
}


建立之後,現在要調整已知 pipeline 中,建立一個 iam.tf ,裡面給他套用 build_role_policy.tpl 這個設定:
resource "aws_iam_role_policy" "codebuild_role" {
  name = "${local.codebuild_role_name}-policy"
  role = aws_iam_role.codebuild_role.id # 套用現有的 codebuild_role
  policy = templatefile("./policies/build_role_policy.tpl")
}


其中 build_role_policy 檔案的內容是:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Resource": [
                "${public_bucket}",
                "${public_bucket}/*"
            ],
            "Action": [
                "s3:*"
            ]
        },
        {
            "Effect": "Allow",
            "Resource": ["*"],
            "Action": "cloudfront:CreateInvalidation"
        },
        {
            "Effect": "Allow",
            "Resource": ["*"],
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ]
        },
        {
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::codepipeline-${aws_region}-*",
                "${artifact_bucket}/*",
                "${artifact_bucket}"
            ],
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:GetObjectVersion",
                "s3:GetBucketAcl",
                "s3:GetBucketLocation"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "codebuild:CreateReportGroup",
                "codebuild:CreateReport",
                "codebuild:UpdateReport",
                "codebuild:BatchPutTestCases",
                "codebuild:BatchPutCodeCoverages",
                "ecr:BatchCheckLayerAvailability", # 這裡以下才是給予 ecr 權限的列表
                "ecr:CompleteLayerUpload",
                "ecr:GetAuthorizationToken",
                "ecr:InitiateLayerUpload",
                "ecr:PutImage",
                "ecr:UploadLayerPart"
            ],
            "Resource": [
              "*"
            ]
        }
    ]
}


用同一個方法可以對 EKS 做一樣的權限套用,但是要注意,這個 policy 要套用到 EKS 的 worker 或是 node 級的權限 role:
{
            "Effect": "Allow",
            "Action": [
                "ecr:BatchCheckLayerAvailability",
                "ecr:BatchGetImage",
                "ecr:GetDownloadUrlForLayer",
                "ecr:GetAuthorizationToken",
                "ecr:*",
                "cloudtrail:LookupEvents"
            ],
            "Resource": "*"
}



然後,在 k8s 的 deployment 中就可以使用網址直接下載 ECR 的東西:

...
template:
  metadata:
   name: test
   namespace: test
   labels:
    app: test
  spec:
   containers:
   - image: (你的帳號ID).dkr.ecr.(你的區域ID).amazonaws.com/(你的ECR專案名稱):latest
     #imagePullPolicy: Always
...



References:

https://docs.aws.amazon.com/codebuild/latest/userguide/sample-ecr.html
https://docs.aws.amazon.com/AmazonECR/latest/userguide/ECR_on_EKS.html

沒有留言:

張貼留言

© Mac Taylor, 歡迎自由轉貼。
Background Email Pattern by Toby Elliott
Since 2014