EKSを理解する(第1回)eksctl・IAM・RBAC

2020.04.09 Cloud

こんにちはクラウドCoEの宮國です。
プロジェクトでコンテナオーケストレーションを用いた構築を行う機会が増えたため、コンテナにまつわるコラムを書きたいと思います。

DXとDevOpsそしてコンテナ

近年では新しいデジタル技術を活用する事によって、新たな価値を創造しビジネスモデルの優位性を獲得するためのデジタルトランスフォーメーション(DX)への機運が高まっています。このDXを実現する上では、ITサービスの機能向上のスピード感が重要であり、従来型のソフトウェア開発ではなくDevOpsと呼ばれる手法で実装されるのが通例です。

DevOps手法では、開発とリリースを短期のサイクルで繰り返し実施し、継続的に性能や品質を改善します。このDevOpsの実現に大きく寄与するのがコンテナ技術です。
コンテナはアプリケーションの実行に必要なアプリケーションやライブラリ、ミドルウェアや設定ファイルをひとまとめにパッケージングするもので、リソース効率が良く可搬性に優れるという特徴があります。

一方で、コンテナで動作している多数のアプリケーションを複数のサーバへ展開し、管理するのは手間がかかります。死活監視であったり冗長化、スケーリング等を行おうとすると尚更です。
こうした多数のコンテナを運用する上で非常に役立つのがコンテナオーケストレーションです。コンテナオーケストレーションを利用する事で、複数のホストマシンにまたがるコンテナの配置やロードバランサとの紐づけを管理してくれます。

コンテナオーケストレーション×クラウド

と、駆け足ながらコンテナについておさらいしました。
少し余談ですが、以前新卒説明会で学生からこんな質問を受けた事があるのをよく覚えています。
― 次はコンテナの時代が来ると聞いたのですが、その場合クラウドは古い技術要素になり得るのでしょうか ―
最近の就活生はよく情報収集しているなと感心する一方で、耳慣れないカタカナばかりで飲み込んで理解するのには一苦労だろうなと感じました。

前述のDXの機運も後押しして、ここ数年はインフラ基盤を単にクラウドで運用するのではなく、その上で動作するアプリケーションレイヤにおいてもクラウドで最適化するフェーズに来ています。(クラウドネイティブとも呼ばれる)
従ってコンテナとクラウドはお互いの利点をより活かすことができる相思相愛な関係であり、クラウドネイティブの重要な技術要素としてこれからも伸びていくでしょう。

ITコンサルタントやエンジニアにとってクラウドの理解はもはや当たり前な昨今ですが、コンテナ・Kubernetesが当たり前になるような時期も近づいていると考えています。

AWSにおけるコンテナオーケストレーション

この記事では弊社でもよく扱うAWSに焦点を置いて、コンテナオーケストレーションについて説明します。
AWSにおけるコンテナオーケストレーションのマネージドサービスは、ECSとEKSの2種類があります。
前者はAWSが独自で開発したコンテナオーケストレーションツールで、AWS上の各種サービスとの連携がしやすい特徴を持ちます。後者はKubernetesのマネージドサービスです。

KubernetesはもともとはGoogleが設計したソフトウェアですが、現在はCloud Native Computer Foundation(CNCF)と呼ばれるクラウドネイティブの推進団体によって管理されているオープンソフトウェアのコンテナオーケストレーションツールです。
AzureやGCPでもKubernetesをベースとしたサービスを展開しており、Kubernetesがデファクトスタンダードとして確固たる地位を確立しつつあります。

そこで、今回はAWSにおけるKubernetesのマネージドサービスであるEKSについて深掘りしていきます。

EKSをキャッチアップするという事

まず、EKSの位置づけについて触れます。

EKSを学ぶ上で最初に勘違いされがちな事は「マネージドサービス」なので何でもやってくれるのでは?という所だと思います。
EKSを利用する事でKubernetesのマスターノード(コントロールプレーン)がマネージドになります。また、2019年11月のアップデートで発表されましたが、マネージド型のノードグループも提供されるようになり、ノードの運用においてマネージドの範囲が広がっているのは事実です。

ここ1年でもこれだけのスピード感で進化しており便利になっています。
(参考)
https://toris.io/2019/12/amazon-eks-this-year-in-review/

が、Kubernetes内のリソース管理まですべてマネージドになるわけではありません。
Kubernetesのクラスタ内で実行されているサービスは、マスターノードのAPI Serverを介して相互に通信しており、開発者がこのAPI Serverにアクセスするためにはkubectlを利用します。
したがってEKSクラスタ内で動作する個々のpod等のリソースはkubernetesのAPIの概念に従って面倒を見ていく必要があります。

また今回の記事の主題でもありますが、KubernetesとAWSで似たような機能を持つ(認証・認可、ネットワーク等)場合は、2つの世界を上手く連携する必要があります。

つまりEKSをキャッチアップするには

  • EKSの進化スピード
  • Kubernetes自体の学習難易度の高さ・進化スピード
  • AWSの他サービスの深い理解の必要性

という3要素がある事を心得たうえで勉強していく必要があります。

これが、一般的にEKSが難しいといわれる所以かなと感じています。コンテナやKubernetesはわかるけれどAWSはあまりわからない。AWSのサービスは詳しいが、Kubernetesはわからない。よくわからないがなんとなく始めてみたい。どのようなバックグラウンドの人にとっても躓きポイントが多いと思います。

クラウドネイティブの理想を追求するには、クラウドサービスとコンテナオーケストレーションの両方に対する深い理解と実装力が問われているなと日々実感しています。

この記事ではKubernetesの1つ1つのリソースを取り上げて説明するような事はあまりしないですが、
「EKSを使う事で、AWSのサービス群とKubernetesのリソース群をどのようにつないでいけばよいのか」
という観点に着目して記事にしていこうと思います。

EKSクラスタ構築

まずはEKSクラスタの構築方法についてです。
EKSのクラスタ構築は従来とても煩雑でしたが、現在はオープンソースであるeksctlと呼ばれるコマンドラインツールで行うのが一般的になりつつあると考えています。
https://eksctl.io/

EKSクラスタを構築する上で必要なVPC、サブネット、NATゲートウェイ、マスターノード、ワーカーノード等をまとめて作成してくれます。
この処理の実態はCloudFormationであり、マネジメントコンソールでCloudFormationを開けば、CloudFormationによって各種AWSリソースが作成されている事が確認できます。
eksctl create clusterという簡素なコマンドでクラスタを構成出来る一方で、引数やyamlで設定値を渡すことが出来ます。
cluster.yamlの例

apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
  name: eks-cluster-miya
  region: ap-northeast-1
nodeGroups:
  - name: nodegroup-miya
    instanceType: m5.large
    minSize: 3
    maxSize: 6
    desiredCapacity: 3
    privateNetworking: true
    ssh:
      allow: true
      publicKeyName: XXXX
    iam:
      withAddonPolicies:
        autoScaler: true
        externalDNS: true
        certManager: true
        albIngress: true

 

以下のようなコマンドでデプロイします。

eksctl create cluster -f cluster.yaml

 

一方で、VPCやサブネットといったいつものAWSリソースが、EKSのクラスタやノードグループと共にeksctlで管理される事にちょっとした違和感を覚える方もいると思います。
そういった場合はVPCやサブネットは別途CloudFormation等で作成し、既存のVPCに対してeksctlを通じてクラスタを作成する事も出来ます。
以下がyamlの例です。
https://github.com/weaveworks/eksctl/blob/master/examples/04-existing-vpc.yaml

また、ノードグループに対するIAMロールの付与もiamのwithaddonpoliciesで手軽に付与できるのも魅力です。

手軽さとカスタマイズ性を兼ね備えており、個人利用としてはもちろん、本番利用としてもeksctlでのEKSクラスタ構築は通用すると考えています。

IAM×RBAC

次はEKSの認証・認可についてです。
EKSの公式ユーザガイドにも記載されていますが、EKSではクラスタの認証にIAMを使用し、認可(日本語だと承認と翻訳されていますね)にはKubernetesのRBACを利用します。
(公式ガイド)
https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/managing-auth.html

クラスタの認証でIAMを利用するには、従来aws-iam-authenticatorを別途インストールする必要がありましたが、AWS CLIの1.18.17以降であればaws eks get-tokenコマンドによりサポートされるので、特に意識する必要はなくなっています。
https://docs.aws.amazon.com/eks/latest/userguide/managing-auth.html

RBACはKubernetesにおける標準的な認可方式です。
Role/RoleBinding/ClusterRole/ClusterRoleBindingの4つのリソースで構成されています。

  • Role・・・文字通り役割(複数の権限を纏めて定義)を指しており、アクセスできるリソースの種類と実施可能な操作(Kubernetesの用語ではverb)を定義します。対象範囲はNamespaceです。
  • RoleBinding ・・・UserAccount、Group、Service Accountに対して、どのRoleを紐づけるか定義するリソースです。
  • ClusterRole・・・Roleの対象範囲がNamespaceなのに対し、ClusterRoleはクラスタ全体の役割を指します。
  • ClusterRoleBinding・・・ UserAccount、Group、Service Accountに対して、どのClusterRoleを紐づけるかを定義するリソースです。

Namespace(名前空間)はクラスタを仮想的に分割するためのリソースです。

EKSクラスタを作成したIAMユーザ、IAMロールはクラスタの操作権限を有していますが、これはEKSクラスタを作成した際に、自動的にsystem:mastersと呼ばれるKubernetesのGroupに紐づけられているためです。

このsystem:mastersが何者か調べてみると、ClusterRoleBindingによってcluster-adminと呼ばれるClusterRoleに紐づけられている事が分かります。

$ kubectl get clusterrolebinding cluster-admin -o yaml          
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
(略)
  name: cluster-admin
(略)
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: system:masters 

そしてこのcluster-adminが何者かというと、クラスタに対する全ての権限を有するClusterRoleだという事がわかります。

$ kubectl get clusterrole cluster-admin -o yaml                
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
(略)
  name: cluster-admin
(略)
rules:
- apiGroups:
  - '*'
  resources:
  - '*'
  verbs:
  - '*'
- nonResourceURLs:
  - '*'
  verbs:
  - '*'

KubernetesのGroupに関するドキュメントは少ないですが、Kubernetes本家のドキュメントにもsystem:mastersにcluster-admin権限が付与されている事が書いてあるのが分かります。

参照
https://kubernetes.io/docs/reference/access-authn-authz/rbac/#default-roles-and-role-bindings

さてここまで、クラスタ作成者の認証・認可について整理しました。

では今度、クラスタ作成者以外がクラスタを操作する場合はどうすればよいか。というと、Kubernetes内のaws-auth(Configmapリソース)の設定を編集し、IAMユーザ、IAMロールをKubernetesのUserAccountやGroupに紐付ける必要があります。

具体的な手順は
https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/add-user-role.html

で紹介されています。

kubectl edit -n kube-system configmap/aws-auth

というように直接編集するのも手ですが、kubernetesの考え方からするとyaml化して管理するのが望ましいと考えられます。
※4/16追記 ただし、eksctl create nodegroupコマンドを打った際にnodegroupにアタッチしたIAMロールを自動的にaws-authに反映しているので、yaml化する場合デグレには要注意

apiVersion: v1
kind: ConfigMap
metadata:
  name: aws-auth
  namespace: kube-system
data:
  mapRoles: |
    - rolearn: arn:aws:iam:::role/<nodeinstancerole>
      username: system:node:{{EC2PrivateDNSName}}
      groups:
        - system:bootstrappers
        - system:nodes
    - rolearn: arn:aws:iam:::role/codebuild-prod-role
      username: codebuild-role
      groups:
        - system:masters
  mapUsers: |
    - userarn: arn:aws:iam:::user/someapp-operator
      username: app-operator
      groups:
        - someapp-operator-group

 

上記の例では、codebuild-prod-roleというIAMロールに対し、EKSクラスタ作成者と同様、system:mastersグループとマッピングする事によってクラスタの管理者権限を付与しています。(先ほど確認したように、system:mastersグループはcluster-adminというclusterroleがアタッチされているためです。)

また、someapp-operatorというIAMユーザに対し、someapp-operator-groupというグループを割り当てています。
このsomeapp-operator-groupというグループには何も権限を付与していないため、
RoleBindingによってRoleに紐づけを行い、特定のNamespaceに対する権限を付与してみます。(ClusterRoleはクラスタ全体に対する権限を定義するので、Namespaceごとに権限を付与する際には利用できません)

role.yaml

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: someapp-prod-full-role
  namespace: someapp-prod
rules:
  - apiGroups:
      - ""
      - "apps"
      - "batch"
      - "extensions"
    resources:
      - "*"
    verbs:
      - "create"
      - "delete"
      - "describe"
      - "get"
      - "list"
      - "patch"
      - "update"

このようにsomeapp-prodというnamespace全体に対するフル権限を有するRoleをyamlで定義し、applyします。

kubectl apply -f role.yaml

そして、RoleBindingによってsomeapp-prod-full-roleをsomeapp-operator-groupに対して紐づけます。

rolebinding.yaml

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: someapp-prod-full-rolebinding
  namespace: someapp-prod
subjects:
- kind: Group
  name: someapp-operator-group
roleRef:
  kind: Role
  name: someapp-prod-full-role
  apiGroup: rbac.authorization.k8s.io

同様にapplyします。

kubectl apply -f rolebinding.yaml

このような手順で、someapp-operatorというIAMユーザに対してsomeapp-prodというNamespaceに対する権限を付与する事が出来ます。

(参考)
eksworkshop RBACセクション
※クラスタに対する権限を付与したいIAMユーザをkubernetesのUseraccountにマッピングする例
https://eksworkshop.com/beginner/090_rbac/map_iam_user_to_k8s_user/

今回はクラスタに対する権限を付与したいIAMユーザをaws-authで記載してkubernetesのgroupにマッピングしましたが、クラスタを触りたいIAMユーザが増えるたびにaws-authをいじるのも微妙ではあります。

https://aws.amazon.com/jp/premiumsupport/knowledge-center/eks-iam-permissions-namespaces/

上記のように各IAMユーザがIAMロールを引き受けるような運用ができていれば、aws-auth側はよりシンプルになるといえます。
※4/16追記
上の例ではeksctl create iamidentitymapping コマンドを使用してaws-authを管理していますね。


今回はEKSの概要、クラスタ構築(eksctl)、IAM、RBACについて説明しました。

少し長くなったので次回以降、IRSAやスケーリング(CA、HPA)、CSIについて書いていきたいと思います。