2022年1月10日 星期一

Golang Private Library in AWS CodeCommit: Access from CodeBuild and using Dockerfile build Golang Application Image

 使用 AWS 全家桶的服務 Backend,使用 Goland 引用 Private Library 的問題處理,以及讓 Private Repository 進入 CI/CD 流程。


這篇文章是專為了使用 AWS CodeCommit 當作 Git Repository 的情境所記錄的解決方案,Golang 若要引用自家在 AWS CodeCommit 上的 Repositroy ,有幾個必要條件,會在第一段進行說明。


從本地端如何建立、使用 CodeCommit Private Repo 開始講起,我的情境是發現有很多 Repo 都有共同 Library 需要拆出來,於是就需要把它們放在一個 utiltity 的 project 中,上到 private repository。


你必須在 AWS CodeCommit 先建立 Repo,然後用 ssh clone 那個空 Repo 回本地端,在這個專案中我的 Repo Region 是放在新加坡: ap-southeast-1。


首先,對 Utiltity Project 做 go mod 初始化:

go mod init git-codecommit.ap-southeast-1.amazonaws.com/v1/repos/my-utiltity.git

請將 AWS Region 和後面的 [my-utiltity.git] 改成自己的,最後結尾一定要加上 .git,否則會讓 Golang 引用不成功。


把 Code 都移動到 Project 之後,做 Commit 然後 Push 上去,接下來是其他專案引用的部分,引用之前通常會做 go get -u,但在這之前,要按照 AWS 2021 新的方式使用 credential_helper 協助取得私人 Repo 訪問權限,需要執行下面的指令:

git config --global credential.helper '!aws codecommit credential-helper $@'
git config --global credential.UseHttpPath true
go env -w GOPRIVATE=git-codecommit.ap-southeast-1.amazonaws.com

上面的 AWS Region 仍需要換成自己的,此時,進行 go get -u 應該就會成功:

go get -u git-codecommit.ap-southeast-1.amazonaws.com/v1/repos/my-utiltity.git

接下來就要讓 Code 直接引用,引用方式也要特別注意,都有後贅 .git 要加上去


import (
	"fmt"

    ...
	"git-codecommit.ap-southeast-1.amazonaws.com/v1/repos/my-utiltity.git/logservice"
	"git-codecommit.ap-southeast-1.amazonaws.com/v1/repos/my-utiltity.git/database"
	...
)


當每次這樣的 Repository 更新的時候,每一個引用到的專案都要使用 go get -u xxx 重新更新,我時常在更新會遇到類似這樣的錯誤,解決方案是去修復舊版的 import 引用,包含已經不存在的引用,透過 go mod tidy 就可以修正了。

go get -u git-codecommit.ap-southeast-1.amazonaws.com/v1/repos/my-utiltity.git
go: downloading git-codecommit.ap-southeast-1.amazonaws.com/v1/repos/my-utiltity.git v0.0.0-20220110033125-2d6e7ce212cf
panic: internal error: can't find reason for requirement on golang.org/x/[email protected]


CodeBuild 處理步驟


這樣的 Library 進到 CI/CD 流程,通常還是會遇到相同的問題,無法做 go get,在 Code Build 要做的事情是先去 Code Build 的 IAM Role,賦予它操作 CodeCommit 的所有權限。


賦予權限之後,剩下就是 buildspec.yml 如何寫的問題,請參考下方:


version: 0.2

# env:
#   git-credential-helper: yes # 這一段可以取代下方 commands 使用 git config 那兩段程式碼
phases:
  install:
    commands:
      - git config --global credential.helper '!aws codecommit credential-helper $@'
      - git config --global credential.UseHttpPath true
      - go env -w GOPRIVATE=git-codecommit.ap-southeast-1.amazonaws.com


      - go mod download
  build:
    commands:
      - go build
     
cache:
  paths:
    - /go/pkg/**/*


這一段對一般 Golang Application build 不成問題,但是如果是使用 Build Docker Image 的架構怎麼辦? 所以將進入下一章節,如果沒有使用 Docker Image 的話,就不需要往下參考了。


CodeBuild Docker Build Image 使用 Golang Private Repository 的方法


我嘗試過 Pass AWS Credentials 的方法到 Docker Image,它的具體作法是帶出兩個參數給 dockerfile,讓他的 aws-cli 可以自動幫你處理:

ARG AWS_DEFAULT_REGION
ARG AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
RUN curl "http://169.254.170.2${AWS_CONTAINER_CREDENTIALS_RELATIVE_URI}"

但是這個方法並不是那麼好用,最好的解決方案是直接使用 vendor mode,反正是在 CodeBuild 上做,也就是使用 go mod vendor 指令,再把資料 pass 到 docker image 一起 build, buildspec.yml 如下:

version: 0.2

phases:
  install:
    commands:
      # private repo env setup
      - git config --global credential.helper '!aws codecommit credential-helper $@'
      - git config --global credential.UseHttpPath true
      - go env -w GOPRIVATE=git-codecommit.ap-southeast-1.amazonaws.com

  
  pre_build:
    commands:
      #################
      # push docker to ECR
      - 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

      # auto versioning
      - IMAGE_TAG=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
      #################

  build:
    commands:
      # for monolith app, first build then image can be compile
      - go build

      ################
      # push docker to ECR
      - echo Build docker image
      - go mod download
      - go mod vendor # 這一個指令可以把 vendor 加進來
      - docker build --build-arg AWS_DEFAULT_REGION=$AWS_DEFAULT_REGION --build-arg AWS_CONTAINER_CREDENTIALS_RELATIVE_URI=$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI . -t $IMAGE_REPO_NAME:$IMAGE_TAG -f dockerfiles/my.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 (push version as id)
      - echo Push image ...
      - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
      - echo Push image completed...
      #################


      #- echo "$(aws sts get-caller-identity)" # 除錯檢查 IAM Identity

cache:
  paths:
    - /go/pkg/**/*


而 Dockerfile 的設定為 (dockerfiles/my.dockerfile):

# syntax=docker/dockerfile:1

FROM golang:1.17 as builder

# first (build) stage

WORKDIR /app
COPY . /app
RUN CGO_ENABLED=0 go build

# final (target) stage

FROM alpine:3.14
COPY --from=builder /app/project_name /

EXPOSE 8080

CMD [ "./project_name", "serve" ]



References:

https://pkg.go.dev/cmd/go@master#hdr-Configuration_for_downloading_non_public_code
https://stackoverflow.com/questions/69667155/get-a-private-repository-from-aws-codecommit-using-https-grc#

https://blog.jwr.io/aws/codebuild/container/iam/role/2019/05/30/iam-role-inside-container-inside-aws-codebuild.html

https://docs.aws.amazon.com/codebuild/latest/userguide/troubleshooting.html#troubleshooting-versions

https://stackoverflow.com/questions/67923109/codebuild-failing-to-pull-file-from-s3-in-docker-build



沒有留言:

張貼留言

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