aws-auth ConfigMapをEKSクラスタにCodeシリーズで自動デプロイする仕組みを構築してみた

こんにちは、かたいなかです。

EKSをチームで運用する際に、EKSクラスタにアクセスする必要があるIAMユーザ/ロールが増えるたび、すでにアクセスできる状態になっている人に依頼してaws-authConfigMapをeditしてもらうというのは大変ですよね。

そこで今回は以下の図のように、Codeシリーズを使用してaws-authConfigMapのYAMLの変更が、Kubernetesのクラスタに自動で反映されるようにする方法をご紹介します。

全体図

おさらい:aws-authConfigMapとは

aws-authConfigMapはIAMのユーザ/ロールをKubernetes内のユーザやロールに紐付けを定義します。EKSクラスタ作成者以外のIAMのユーザ/ロールは、このConfigMapに紐づけが定義されていないと、Kubernetesクラスタ内のリソースにアクセスすることができません。

具体的には以下のようなConfigMapとなります。

apiVersion: v1
kind: ConfigMap
data:
  mapRoles: |
    - rolearn: arn:aws:iam::XXXXXXXXXXXX:role/devel-worker-nodes-NodeInstanceRole-74RF4UBDUKL6
      username: system:node:{{EC2PrivateDNSName}}
      groups:
        - system:bootstrappers
        - system:nodes
  mapUsers: |
    - userarn: arn:aws:iam::XXXXXXXXXXXX:user/admin
      username: admin
      groups:
        - system:masters
    - userarn: arn:aws:iam::XXXXXXXXXXXX:user/ops-user
      username: ops-user
      groups:
        - system:masters

実際に構築してみる

流れ

以下の流れで進めます。

  1. CodeBuild用サービスロールの準備
    1. ロールの作成
    2. Kubernetesクラスタ内のaws-authに手動で設定
  2. CodeBuildのビルド環境用 Dockerイメージの準備
    1. ECRのリポジトリを作成
    2. イメージのビルド・プッシュ
  3. CodeCommitのリポジトリの準備
    1. aws-authConfigMapのYAML作成
    2. buildspec.yml作成
  4. CodePipeline/CodeBuild作成

前提条件

以下の前提条件を満たした状態であることを前提に進めていきます。

  • EKSのクラスタが作成されていること
  • docker,kubectl,aws-iam-authenticator等のツールが作業を行うPCにインストールされていること
  • kubectlでEKSのクラスタにアクセスできるようになっていること

CodeBuild用サービスロールの準備

まずは、CodeBuildのサービスロールを定義します。

CodeBuildのサービスロールには、以下のようなポリシードキュメントをもつIAM Policyを作成し、アタッチします。

{
  "Version": "2012-10-17",
  "Statement": [
    {
        "Sid": "EKSPolicy",
        "Effect": "Allow",
        "Action": "eks:DescribeCluster",
        "Resource": "*"
    },
    {
      "Sid": "CloudWatchLogsPolicy",
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": [
        "*"
      ]
    },
    {
      "Sid": "CodeCommitPolicy",
      "Effect": "Allow",
      "Action": [
        "codecommit:GitPull"
      ],
      "Resource": [
        "*"
      ]
    },
    {
      "Sid": "S3GetObjectPolicy",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:GetObjectVersion"
      ],
      "Resource": [
        "*"
      ]
    },
    {
      "Sid": "S3PutObjectPolicy",
      "Effect": "Allow",
      "Action": [
        "s3:PutObject"
      ],
      "Resource": [
        "*"
      ]
    }
  ]
}

EKSのクラスタをdescribeする権限とCodeBuildのサービスロールとして一般的に必要な権限を与えています。

信頼ポリシー

また、CodeBuildがAssume Roleできるようにするため、CodeBuildのサービスロールは以下のような信頼ポリシーを持つようにします。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Principal": {
                "Service": [
                    "codebuild.amazonaws.com"
                ]
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

EKSクラスタ内のaws-authConfigMapにCodeBuildのサービスロールを追加

CodeBuildのサービスロールをaws-authConfigMapに予め手動で追加しておきます。先に追加を済ませておかないとCodeBuildがConfigMapの編集を行えないためです。

以下のようなYAMLを作成します。EKSのクラスタにワーカーノードが追加された時点でワーカーノードのインスタンスのロールに関する設定はされているはずなので、新たにハイライトされた部分のCodeBuildのサービスロールの設定を追加します。

apiVersion: v1
kind: ConfigMap
metadata:
  name: aws-auth
  namespace: kube-system
data:
  mapRoles: |
    - username: system:node:{{EC2PrivateDNSName}}
      rolearn: <EKSのワーカーノードのインスタンスのロールのARN>
      groups:
      - system:bootstrappers
      - system:nodes
    - username: codebuild
      rolearn: <CodeBuildのサービスロールのARN>
      groups:
        - system:masters

そして、クラスタに対してこのYAMLをkubectl applyします。

$ kubectl apply -f aws-auth.yml

CodeBuildのビルド環境用 Dockerイメージの準備

ECRのリポジトリを作成

ECRのリポジトリを作成します。

CodeBuildがイメージをpullできるようにするため、以下のようなリポジトリポリシーを付与しておきます。

リポジトリポリシー

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "CodeBuildAccess",
      "Effect": "Allow",
      "Principal": {
        "Service": "codebuild.amazonaws.com"
      },
      "Action": [
        "ecr:BatchCheckLayerAvailability",
        "ecr:BatchGetImage",
        "ecr:GetDownloadUrlForLayer"
      ]
    }
  ]
}

Dockerイメージをビルド・ECRにプッシュ

まず、以下のようなDockerfileを用意します。

FROM alpine:3.8

# install AWS CLI
ENV AWS_CLI_VERSION 1.16.26
RUN apk --update --no-cache add \
        python \
        py-pip \
        && \ 
    pip install --upgrade awscli==${AWS_CLI_VERSION} && \
    apk -v --purge del py-pip

# install kubectl
ENV KUBECTL_VERSION 1.11.3
RUN wget -O /usr/local/bin/kubectl https://storage.googleapis.com/kubernetes-release/release/v${KUBECTL_VERSION}/bin/linux/amd64/kubectl && \
    chmod +x /usr/local/bin/kubectl

# install AWS IAM Authenticator
ENV AWS_IAM_AUTHENTICATOR_RELEASE_DATE 2018-07-26
ENV AWS_IAM_AUTHENTICATOR_VERSION 1.10.3
RUN wget -O /usr/local/bin/aws-iam-authenticator https://amazon-eks.s3-us-west-2.amazonaws.com/${AWS_IAM_AUTHENTICATOR_VERSION}/${AWS_IAM_AUTHENTICATOR_RELEASE_DATE}/bin/linux/amd64/aws-iam-authenticator && \
    chmod +x /usr/local/bin/aws-iam-authenticator

そして、以下のようなコマンドでDockerイメージをビルドし、ECRにプッシュします。

$ $(aws ecr get-login --no-include-email --region ap-northeast-1)
$ docker build -t <ECRのリポジトリのURI>:latest .
$ docker push <ECRのリポジトリのURI>:latest

YAMLを管理するCodeCommitリポジトリの準備

CodeCommitのリポジトリを作成し、以下に示すような2つのファイルがプッシュされた状態にします。

まずは先程、EKSのクラスタにapplyしたaws-authConfigMapのYAMLです。

apiVersion: v1
kind: ConfigMap
metadata:
  name: aws-auth
  namespace: kube-system
data:
  mapRoles: |
    - username: system:node:{{EC2PrivateDNSName}}
      rolearn: <EKSのインスタンスのロールのARN>
      groups:
      - system:bootstrappers
      - system:nodes
    - username: codebuild
      rolearn: <CodeBuildのサービスロールのARN>
      groups:
        - system:masters

そして、CodeBuildでYAMLの適用をおこなうためのbuildspec.ymlです

version: 0.2

phases:
  pre_build:
    commands:
      - aws eks update-kubeconfig --name ${EKS_CLUSTER_NAME}
  build:
    commands:
      - kubectl apply -f aws-auth.yml

CodePipeline/CodeBuildの設定

最後に、CodePipelineのパイプラインをは今まで作ったものをつなげて作成していきます。

  • CodePipelineのソースに作成したCodeCommitリポジトリのmasterブランチを選択します。
  • CodeBuildのプロジェクトを以下のような設定で作成し、CodePipelineに関連付けます。
    • サービスロールとして予め準備しておいたCodeBuildのロールを使用するようにします。
    • CodeBuildの環境として、ECRに予めプッシュしておいたイメージを使用するようにします。
    • ビルド定義ファイルはCodeCommitのリポジトリのbuildspec.ymlを指定します。

パイプライン作成が完了したら、処理が自動で開始されます。

正常に処理が完了すれば、構築が正しく行えています。

ここまででCodeCommit上でのaws-auth.yamlの変更が自動でEKSのクラスタに反映されるようになりました。

自動で適用されることを確認してみる

では、実際に反映が正しく行われるか確認してみましょう。

aws-auth.ymlに以下のハイライトの部分を追加してコミットし、CodeCommitにプッシュします。

apiVersion: v1
kind: ConfigMap
metadata:
  name: aws-auth
  namespace: kube-system
data:
  mapRoles: |
    - username: system:node:{{EC2PrivateDNSName}}
      rolearn: <EKSのインスタンスのロールのARN>
      groups:
      - system:bootstrappers
      - system:nodes
    - username: codebuild
      rolearn: <CodeBuildのサービスロールのARN>
      groups:
        - system:masters
  mapUsers: |
    - userarn: <追加するIAMユーザのARN>
      username: admin
      groups:
        - system:masters

そして、CodePipelineの処理が成功したのを確認したら、aws-auth.ymlに追加したIAMユーザを使用してkubectlでアクセスできることを確認してましょう。

$ kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.100.0.1   <none>        443/TCP   16h

正しくアクセスが行えました。

まとめ

今回は、EKSのクラスタに対してaws-auth ConfigMapが自動で適用されるようにする方法の例をご紹介しました。今回はCodeシリーズを使用していますが、GitHub等の他のGitホスティングサービスやCircle CI等他のCIツールを使用しても同様の処理を行うことももちろん可能です。

ソースコードリポジトリで管理されたYAMLの変更から aws-auth ConfigMapの適用を自動で行うしくみが作れると、すでにアクセス権を持っている人に依頼してConfigMapをeditしてもらうようなトイルになりがちな作業を削除できます。

実は今回使用した方法は、Kuberenetesの特定のリソースに限った方法ではなく、CodeBuildで実行されるコマンドさえ修正すれば他のKubernetesのリソースの自動デプロイにも応用できるものです。そのため、EKSクラスタを立てたらまずはこのような仕組みを作っておくことで、自動デプロイの仕組みを整えていくのに良いスタートになるのではないかなと思います。

EKSのクラスタを立てた際には試していただけますと幸いです。

参考資料

  • クラスターのユーザーまたは IAM ロールの管理:EKSのドキュメントのIAMユーザ/ロールとEKSのユーザの関連付けのページです。
  • actions/example-aws:GitHub ActionsでEKSへのCDの実装例のリポジトリです。
  • CRD implementation:現在はaws-auth ConfigMapで内のmapRoles, mapUsersというプロパティの属性としてYAMLの文字列を埋め込む形で設定していますが、将来的には、IAMロールとKubernetesのRBACのロールのヒモ付をCustomResourceDefinitionとして設定できるようにすることが検討されているようです。