CodeBuildでEKSへBuild&Deploy


はじめに

AWS CodeBuildを用いてDocker build および EKSヘのデプロイを行うサンプルです。
GitHub Enterprise(GHI)へのPushをトリガーにCodeBuildをkickしてみます。

※当初は、CodePiplelineでGHEへのPushトリガーを受けようと考えていたのですが、CodePipelineはGHEに対応していないらしく、CodeBuildのみでまかなうことにしました。

前提

  • Date : 2020/2 時点
  • EKS Kubernetes version: 1.14
  • ImageリポジトリはECRを利用し、かつセットアップ済み (ECRの設定は本記事には記載しない)
  • EKSはセットアップ済み(EKSの設定は本記事に記載しない)
  • GHEへアプリケーションソースコードおよびDockerfileを格納済み(すでに存在する前提)
  • リポジトリ直下に、Dockerfileが格納されている(docker buildで利用)
  • リリース対象のEKS(K8s)リソースは、Deploymentを想定

GHEのPersonal access tokenの作成

GHEの右上個人メニューからSeeting -> Developer settingsと遷移し、Personal access tokensタブから作成します。
scopeはrepoのみ選択すれば問題ありません。
作成されたトークンは控えておきましょう(再表示できません)。

CodeBuildプロジェクトの作成

マネジメントコンソールから、ビルドプロジェクトを作成します。
※なお、設定値に言及がない場合は、デフォルト値を採用しています。

プロジェクトの設定セクションは任意に入力してください。

送信元セクションでは、ソースプロバイダにGitHub Enterpriseを選択し、先ほど取得したPersonal access tokenを入力します。

トークンの保存後、リポジトリのURLを入力します。

ウェブフックイベントセクションでは、再構築にチェックを入れ、イベントタイプにはプッシュを選択します。

環境セクションは下図の通り入力します。
イメージ内でDockerを起動するため、特権付与にチェックを入れます。
ロール名は任意の名称を入力してください。

環境セクションの追加設定で、下図の環境変数を設定します。
この環境変数は、後続に記載するbuildspec.yamlで利用します。

IAMポリシー&ロールの編集

CodeBuildプロジェクト作成時に作成したサービスロールに、ECRおよびEKSへのアクセスを許可するポリシーを作成します。

policy
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ecr:GetDownloadUrlForLayer",
                "ecr:BatchGetImage",
                "ecr:CompleteLayerUpload",
                "ecr:GetAuthorizationToken",
                "ecr:UploadLayerPart",
                "ecr:InitiateLayerUpload",
                "ecr:BatchCheckLayerAvailability",
                "ecr:PutImage"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": "eks:DescribeCluster",
            "Resource": "arn:aws:eks:*:*:cluster/*"
        }
    ]
}

ポリシー作成後、サービスロールにアタッチします(スクショは割愛)。

IAM RoleとEKS RBACとの紐付け

外部からEKSのK8sリソースにアクセスする場合、IAMロールとKubernetesのユーザー/グループを紐づける必要があります(先達の投稿がよくまとまっていると思います)。

「aws-auth」というEKS固有のConfigMapを編集し、CodeBuild用サービスロールをK8sのsystem:mastersグループに紐付けます。
※system:mastersというグループは、いわゆるadmin的な権限を持つデフォで存在するグループです。

aws-auth
$ kubectl edit cm aws-auth -n kube-system

apiVersion: v1
data:
  mapRoles: |
    - groups:
      - system:bootstrappers
      - system:nodes
      rolearn: arn:aws:iam::xxxx:role/xxxx
      username: system:node:{{EC2PrivateDNSName}}
##add from
    - rolearn: arn:aws:iam::xxxxx:role/codebuild-hoge-service-role
      username: codebuild 
      groups:
      - system:masters 
##add to
kind: ConfigMap
metadata:
  creationTimestamp: null
  name: aws-auth
  selfLink: /api/v1/namespaces/kube-system/configmaps/aws-auth  

ここで、注意点です。
rolearnには、パスを含めてはいけません。

○: arn:aws:iam::xxxxx:role/codebuild-hoge-service-role
×: arn:aws:iam::xxxxx:role/service-role/codebuild-hoge-service-role

マネジメントコンソールに表示されるarnにはパスが含まれるのですが、これをコピペするとロールが正しく認識されず、you must login みたいなエラーになりました。

buildspec.yaml

buildspec.yamlの内容は以下の通りです。
簡単に処理内容を書くと、dockerを起動して、buildして、pushして、k8sのdeploymentをrolloutすることでPodを再作成し、イメージの変更を反映させています。
このファイルをGHEリポジトリの直下に配置します。

buildspec.yaml
version: 0.2 

phases: 
  install:
    runtime-versions:
        docker: 18
    commands:
      # install kubectl
      - wget -O /usr/local/bin/kubectl https://storage.googleapis.com/kubernetes-release/release/v1.17.0/bin/linux/amd64/kubectl
      - chmod +x /usr/local/bin/kubectl
  pre_build:
    commands:
    - echo "Logging in to Amazon ECR...."
    - aws --version
    - $(aws ecr get-login --no-include-email --region $CI_REGION)
    - echo "the repo/version now is ${REPO_URL}:${LATEST_VERSION}"

    - echo "Creating folders for pid files"
    - mkdir shared
    - mkdir shared/pids
    - mkdir shared/sockets
  build: 
    commands: 
    - echo "Build started on `date`"
    - echo "Building the Docker image.. ${REPO_URL}:${LATEST_VERSION}"

    # dokcer build & push
    - docker build -t tempimage:latest .
    - docker tag tempimage:latest ${REPO_URL}:${LATEST_VERSION}
    - echo "dokcer build completed on `date`" 
    - echo "pushing to repo ${REPO_URL}:${LATEST_VERSION}"
    - docker push ${REPO_URL}:${LATEST_VERSION}
  post_build: 
    commands: 
    - echo "Build completed on `date`"
    # rollout kubernetes(eks) deployment
    - echo "rollout eks deployment = ${K8S_DEPLOY_NAME} @ ${EKS_CLUSTER_NAME}"
    - aws eks update-kubeconfig --name ${EKS_CLUSTER_NAME} --verbose
    - kubectl rollout restart deployments/${K8S_DEPLOY_NAME} -n ${K8S_NAMESPACE}

CodeBuildにWebHookを追加

以下のコマンドでCodeBuildにWebHookを追加します。
--project-nameオプションは、CodeBuildプロジェクト名を設定します。

create-webhook

$ aws codebuild create-webhook --project-name hoge
{
    "webhook": {
        "payloadUrl": "https://codebuild.ap-northeast-1.amazonaws.com/webhooks?t=eyJlbmNyeXB...<省略>",
        "secret": "5ZUJKd8wA...<省略>",
        "lastModifiedSecret": 1595395732.558
    }
}    

GHEにWebhook URLを登録

GHEリポジトリのSettings画面からWebHookを追加します。
ここで、Payload URLとSecretには、先の手順コマンドで取得したURLとSecretを入力します。

以上で手順は終了です。
GHEリポジトリに変更をpushすることで自動的にBuild&Deployが開始されるはずです。