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のみ選択すれば問題ありません。
作成されたトークンは控えておきましょう(再表示できません)。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135384/17e85248-3aca-cff1-c679-805a81c63c96.png)


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

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

送信元セクションでは、ソースプロバイダにGitHub Enterpriseを選択し、先ほど取得したPersonal access tokenを入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135384/43998689-3cf3-9310-61c8-b6e01004242e.png)
トークンの保存後、リポジトリのURLを入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135384/0d544748-17d2-e055-0266-c76908d818e2.png)

ウェブフックイベントセクションでは、再構築にチェックを入れ、イベントタイプにはプッシュを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135384/b55094ce-9566-b5c9-5f11-5db4242f6136.png)

環境セクションは下図の通り入力します。
イメージ内でDockerを起動するため、特権付与にチェックを入れます。
ロール名は任意の名称を入力してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135384/14f22d17-bb33-2d54-7270-600f3316f947.png)

環境セクションの追加設定で、下図の環境変数を設定します。
この環境変数は、後続に記載するbuildspec.yamlで利用します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135384/c2568ee1-711e-e611-4be0-242e17a96551.png)


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

```json: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のユーザー/グループを紐づける必要があります([先達の投稿](https://qiita.com/taishin/items/dfb9a5620f37ffb74fe9)がよくまとまっていると思います)。

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

```yaml: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リポジトリの直下に配置します。

```yaml: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プロジェクト名を設定します。

```bash: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を入力します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135384/c3344001-cb65-0943-8ea4-476009a71068.png)


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