column

コラム

EBS CSI driverをEKSアドオンとして導入してみた

  • TAG

    EKS
  • UPDATE

    2022/09/27

はじめに

こんにちは。DX事業部の宮國です。

今回はEKSアドオンとしてEBS CSI driverを導入して、PodからEBSボリュームにアクセスすることを試したので、まとめてみたいと思います。

EBS CSI driver関連のアップデート状況は

  • EKSでEBS CSI driverが一般利用可能になったのが2021年5月
  • EKSアドオンで EBS CSI driverが一般利用可能になったのが2022年3月
 

となっており、このトピックの旬はとうに過ぎているのですが、

EKSのユーザガイドで、Kubernetes 1.23にバージョンアップする際、クラスタで EBS ボリュームを使用する場合にEBS CSI driverをインストールしないと、ワークロードが中断される可能性がある。という旨が記載されていたのを見て、

そもそもPVとしてEBSを使ったことが無く、EKSアドオンも意識的に触ったことが無い事を思い出し、試したいと思って記事にしました。

Kubernetesにおける永続データの扱い

EBS CSI driverを試す前に、なぜEKSでAWSのストレージサービスを組み合わせたくなるのか、おさらいしてみたいと思います。

Pod(Kubernetesにおけるコンテナの最小実行単位・1つ以上のコンテナで構成)は、Pod内にディスク領域を持ちますが、一時的なものであるためPodの削除と同時にデータは消失してしまいます。

hostpathを使ってノードのボリュームにデータを保持するという考え方もありますが、ノード自体も破棄可能なものとしてとらえるのが自然ですし、

永続化が必要なデータに関しては、Kubernetesクラスタとは分離してデータを保存することが望ましいです。

したがって、外部ストレージと連携した永続ボリュームを利用したくなります。

 

Kubernetesにおいてストレージに関わる主要なリソースは以下の通りです。

  • StorageClass(SC)
    • PVの作成に使用するためのストレージの仕様を定義するリソース
    • ※Provisionerで、どのボリュームプラグイン(ボリュームを作成、管理するための拡張機能)を使うか定義する。
  •  PersistentVolume(PV)
    • 永続ボリュームを表すリソース
  •  PersistnetVolumeClaim(PVC)
    • 永続ボリュームの割り当てを要求するリソース

これらを適切に定義することによって、外部のストレージサービスから永続ボリュームを切り出します。

EKSからEBSを使うために必要なプラグイン

StorageClassで利用可能なProvisionerは多種多様ですが、EKSを利用していると、AWSのストレージサービスを使いたくなります。

ノードとして利用しているEC2にアタッチするストレージとしてはEBS、EFSが代表的です。

AZに限定されず、何かと使い勝手が良いEFSをプロジェクトでは利用しているのですが、今回はEBSの記事なので、ここからは便宜上、EBSの話に絞っていきます。

KubernetesからEBSを利用したい。というニーズは大分前からありました。

そのため、EBSを利用するためのボリュームプラグインはin-tree(コードロジックがKubernetesに組み込まれる形で開発)で開発されてきました。(kubernetes.io/aws-ebs)

しかし、現在(2022/9)は、このin-treeのボリュームプラグインを利用することは推奨されていません。※EKS Kubernetes 1.23を利用している場合、CSIMigrationAWS機能が有効になっているため、利用することはないはずです。

Podから永続ボリュームとしてEBSを利用する場合、EBS CSI driverが必要になってきます。

CSIとは

CSIという言葉を前置きなしに使っているので、CSIについても軽く触れます。

CSI(Container Storage Interface)は、Kubernetesなどのコンテナオーケストレーションと、任意のブロックおよびファイルストレージシステムを繋ぐための標準化されたインタフェースです。

先ほどEBS用のプラグインがin-treeで開発されていたことについて触れた通り、かつてKubernetesで永続ボリュームを提供するための、ストレージのProvisioner毎のコードは、Kubernetesプロジェクトのコードに含まれていました。

しかしこの状態では、ストレージプラグイン開発者がボリュームプラグインのバグを修正したいだけでも、Kubernetesのリリースプロセスに合わせる必要がある。リリースが複雑になる。といった問題を抱えていました。

CSIが登場し、Kubernetesで利用可能になったことで、ストレージプラグイン開発者がKubernetesのソースに組み込まずにストレージ関連の機能を実装し、機能を提供することが可能となりました。

CSIに準拠したdriverは、ベンダや機種ごとにストレージの仕様が異なっていても、統一的なマニフェストファイルの書き方で利用できるようになっています。

 

EBS CSI driverも、AWS上で稼働するKubernetesクラスタのアプリケーションからEBSを利用するための、CSIに準拠したドライバーとなっており、Kubernetesコミュニティによって開発されています。

EKSからEBSを利用する場合、このEBS CSI driverを使う必要があります。

EKSアドオンとは

前置きがとても長いことを自覚しています。最後にEKSアドオンについて触れさせてください。

用語の説明、歴史の振り返りがないと、なぜEBS CSI driverをEKSアドオンで入れたくなるのかわからないと思ったからです。

EKSアドオンはEKSクラスタ向けの重要な機能を提供するアドオンを一元的にインストール、管理する機能で、AWSから提供されています。

EKSでよく使うアドオンを、eksctlやマネジメントコンソール、AWS CLI で簡単に導入できるため、管理するマニフェストファイルが削減できるなど、運用の作業量を減らす効果がある。と言われています。

現時点(2022/9)では以下のアドオンが対応しています。

  • Amazon VPC CNI plugin for Kubernetes
  • CoreDNS
  • kube-proxy
  • ADOT
  • Amazon EBS CSI

 

実際の所、EKSをプロジェクトで利用しようと思うと、ロードバランサを使いたい、ノードをスケーリングしたい、ファイルシステムを使いたい、監視用のメトリクスサーバやagentを入れたい。などなどのニーズから色々なアドオンを入れたくなります。

これらのアドオンを入れてメンテナンスするためには、それぞれのアドオンのGitHubリポジトリを覗いてインストールし、リリースノートを確認し、マニフェストファイルを管理していく必要があります。

デフォルトの状態から何か値をいじるのであれば、差分を管理していく必要もあります。

AWSのリソースに対して何か指示をするのであれば、IAMポリシーを定義する必要がありますし、途中で必要なポリシーが変わる可能性もあるので、ソース管理の対象になるでしょう。

このように、たくさんのアドオンを入れるとその分マニフェストファイル管理、運用、アップデート作業が大変になるので、できるだけシンプルに、楽に管理をしたいところです。

パッケージマネージャーであるHelmを利用する事でも幾分楽になりますが、EKSアドオンはAWSにサポートされている分、それ以上の運用容易性向上の可能性を秘めているかもしれません。

 

そこで、今回はEBS CSI driver をEKSアドオンとして入れてみたいと思います。(前置きおしまい)

EKSアドオンでEBS CSI driverをいれてみた

それでは実際に導入してみたいと思います。

EKSクラスタへの接続環境としてCloud9を利用します。

セットアップはEKS workshopをベースにしているので、ここでの説明は省略します。(※今もなお、メンテナンスされていて素晴らしい学習コンテンツだと思います。)

 

クラスタ作成

現時点(2022/9)の最新バージョンであるEKS 1.23でクラスタを作成します。

クラスタ構築にはeksctlを利用します。

基本的にマニフェストファイルを利用する前提で記載していきますが、オプションとして渡すこともできますし、むしろ手軽です。(実運用を想定した時にマニフェストファイル化した方がいいと考えているので、マニフェストファイルでやっています。)

 

以下のようなeksctl用のマニフェストファイルを用意して、クラスタを作成します。

eks.yaml

apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
 
metadata:
  name: ebs-test-cluster
  region: us-west-2
  version: "1.23"
iam:
  withOIDC: true
managedNodeGroups:
  - name: nodegroup
    instanceType: m5.large
    desiredCapacity: 1
    privateNetworking: true

 

eksctl create cluster -f eks.yaml

クラスタが正しく作成されたことを確認します。

$ eksctl get cluster
NAME REGION EKSCTL CREATED
ebs-test-cluster us-west-2 True

$ eksctl get nodegroup --cluster ebs-test-cluster
CLUSTER NODEGROUP STATUS CREATED MIN SIZE MAX SIZE DESIRED CAPACITY INSTANCE TYPE IMAGE ID ASG NAME TYPE
ebs-test-cluster nodegroup ACTIVE 2022-09-26T09:24:22Z 1 1 1m5.large AL2_x86_64 eks-nodegroup-d0c1bc8e-4bcf-22b4-b43d-af143697e7ff managed

$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
ip-192-168-162-107.us-west-2.compute.internal Ready <none> 3m33s v1.23.9-eks-ba74326

 

OIDCプロバイダーが作成されていることを確認します。

oidc_id=$(aws eks describe-cluster --name ebs-test-cluster --query "cluster.identity.oidc.issuer" --output text | cut -d '/' -f 5)    
aws iam list-open-id-connect-providers | grep $oidc_id

 "Arn": "arn:aws:iam::<account-id>:oidc-provider/oidc.eks.us-west-2.amazonaws.com/id/HOGEHOGE"

 

IAMロール作成

EBS CSI driverでは、ユーザに代わってAWSのAPIの呼び出しを行うため、IAM アクセス許可が必要です。したがって、IAMポリシーを定義する必要があります。

今までアドオンに必要なIAMポリシーは自ら作って管理する必要がありましたが、EBS CSI driver用のIAMポリシーがサービスロールとして提供されているので、そちらを使う事ができます。おそらくEKSアドオンとしてサポートされたからでしょう。

以下のようなyamlを用意します。ServiceAccountは後で作成するので、role-onlyで作成します。

 

eks-policy.yaml

apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
 
metadata:
  name: ebs-test-cluster
  region: us-west-2
  version: "1.23"
iam:
  withOIDC: true
  serviceAccounts:
  - metadata:
      name: ebs-csi-controller-sa
      namespace: kube-system
    attachPolicyARNs:
    - "arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy"
    roleName: AmazonEKS_EBS_CSI_DriverRole
    roleOnly: true

 

eksctl create iamserviceaccount -f eks-policy.yaml --approve
$ eksctl get iamserviceaccount --cluster ebs-test-cluster
NAMESPACE       NAME                    ROLE ARN
kube-system     aws-node                arn:aws:iam::<account-id>:role/eksctl-ebs-test-cluster-addon-iamserviceacco-Role1-1NLMN0MSW3P25
kube-system     ebs-csi-controller-sa   arn:aws:iam::<account-id>:role/AmazonEKS_EBS_CSI_DriverRole

 

EBS CSI driverの追加

EKSアドオンとしてEBS CSI driverをクラスタに適用します。

eks-addon.yaml

apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
 
metadata:
  name: ebs-test-cluster
  region: us-west-2
  version: "1.23"
iam:
  withOIDC: true
addons:
- name: aws-ebs-csi-driver
  serviceAccountRoleARN: "arn:aws:iam::<account-id>:role/AmazonEKS_EBS_CSI_DriverRole"

 

eksctl create addon -f eks-addon.yaml 

 

EKSアドオンとしてEBS CSI driverが管理されていることが確認できます。

$ eksctl get addon --name aws-ebs-csi-driver --cluster ebs-test-cluster
NAME                    VERSION                 STATUS  ISSUES  IAMROLE                          UPDATE AVAILABLE
aws-ebs-csi-driver      v1.11.2-eksbuild.1      ACTIVE  0       arn:aws:iam::<account-id>:role/AmazonEKS_EBS_CSI_DriverRole

 

また、EBS CSI driver関連のリソースが作成されていることが確認できます。

$ kubectl get sa -n kube-system | grep ebs
ebs-csi-controller-sa                1         3m58s
ebs-csi-node-sa                      1         3m58s

$ kubectl get pod -n kube-system                                    
NAME                                 READY   STATUS    RESTARTS   AGE
aws-node-r9vmm                       1/1     Running   0          65m
coredns-57ff979f67-7ppgt             1/1     Running   0          74m
coredns-57ff979f67-zl4bg             1/1     Running   0          74m
ebs-csi-controller-d76cfdc66-672dn   6/6     Running   0          4m37s
ebs-csi-controller-d76cfdc66-mxdf9   6/6     Running   0          5m7s
ebs-csi-node-xnmns                   3/3     Running   0          5m7s
kube-proxy-thqfv                     1/1     Running   0          65m

 

以上でEBSを利用する準備が整いました。通常のやり方でEKSにアドオンを導入するより、かなり手順が少ない事がうかがえると思います。

動作確認

実際にサンプルアプリを適用してpodからEBSが利用可能になったかどうか試していきたいと思います。

Kubernetesにおけるストレージ利用では、静的プロビジョニングと動的プロビジョニングの2つの方式がありますが、今回は、動的プロビジョニング(ストレージ要求(PVC)に対応して、PVを動的に作成して紐付ける)のパターンでやっていきます。

ここからは、公式ブログに準拠しながらやっていきます。

 

まずStorageClassを確認すると、Provisionerがkubernetes.io/aws-ebsとなっているStorageClassが適用されていることが確認できます。(あくまでEKS 1.23時点です)これがin-treeのボリュームプラグインです。

$ kubectl get sc
NAME            PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
gp2 (default)   kubernetes.io/aws-ebs   Delete          WaitForFirstConsumer   false                  84m

 

provisionerとしてEBS CSI driverが指定されたStorageClassを新たに作成します。

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: gp3
allowVolumeExpansion: true
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
parameters:
  type: gp3

 

$ kubectl get sc
NAME            PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
gp2 (default)   kubernetes.io/aws-ebs   Delete          WaitForFirstConsumer   false                  94m
gp3             ebs.csi.aws.com         Delete          WaitForFirstConsumer   true                   4s

次にPVCを作成します。

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-csi
spec:
  accessModes:
  - ReadWriteOnce
  storageClassName: gp3
  resources:
    requests:
      storage: 1Gi

 

$ kubectl apply -f ebs-test/pvc-csi.yaml 
persistentvolumeclaim/pvc-csi created

$ kubectl get pvc
NAME      STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc-csi   Pending                                      gp3            7s

PVCがPendingになっているのは、VolumeBindingMode が WaitForFirstConsumer(Podが作成された時にPVを生成してバインドする仕様)であるためです。

 

次にPodを作成します。

apiVersion: v1
kind: Pod
metadata:
  name: app-gp3
spec:
  containers:
  - name: app
    image: centos
    command: ["/bin/sh"]
    args: ["-c", "while true; do echo $(date -u) >> /data/out.txt; sleep 5; done"]
    volumeMounts:
    - name: persistent-storage
      mountPath: /data
  volumes:
  - name: persistent-storage
    persistentVolumeClaim:
      claimName: pvc-csi

 

$ kubectl apply -f ebs-test/pod-csi.yaml 
pod/app-gp3 created

各種ステータスを確認すると、PVが生成されていること、PVCがバウンドされていること、Podがrunningになっていること、PodからEBSがマウントされていることが確認できます。

 

$ kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM             STORAGECLASS   REASON   AGE
pvc-c2001a90-a1d2-453c-a238-096af6fe87e2   1Gi        RWO            Delete           Bound    default/pvc-csi   gp3                     1s

$ kubectl get pvc
NAME      STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc-csi   Bound    pvc-c2001a90-a1d2-453c-a238-096af6fe87e2   1Gi        RWO            gp3            3m20s

$ kubectl get pod
NAME      READY   STATUS    RESTARTS   AGE
app-gp3   1/1     Running   0          47s

$ kubectl exec -it app-gp3 -- cat /data/out.txt
Mon Sep 26 10:58:29 UTC 2022
Mon Sep 26 10:58:34 UTC 2022
Mon Sep 26 10:58:39 UTC 2022
Mon Sep 26 10:58:44 UTC 2022
Mon Sep 26 10:58:49 UTC 2022
Mon Sep 26 10:58:54 UTC 2022

 

マネジメントコンソールからEBSを確認すると、gp3のEBSが作成されていることが確認できます。

動作確認は以上です。

 

まとめ

このように、EKSアドオンを利用してEBS CSI driverを適用することで、簡単に導入することができました。

一通りやってみての感想ですが、EBS CSI driver単体だけで考えたらぜひEKSアドオンを利用したいな。と思いました。

デフォルトのパラメータから特にいじる事が無ければ、とても楽だと思いました。

一方で、実運用で今すぐ使いたいかと考えると疑問でした。

1つ目の理由は、EKSアドオンとして管理できるアドオンが限られているからです。

コンテナロードマップを見ても、AWS Load Balancer ControllerEFS CSI driverCalicoCluster Autoscaler等よく使われるアドオンはまだEKSアドオン対応の見通しが立っていません。

このアドオンはEKSアドオンで管理して、このアドオンはHelmで管理して、このアドオンはkubectlで適用して、というようなばらつきがあると、運用が複雑になってしまいます。

2つめの理由は、デフォルトのパラメータから何か変えたい時にかえってややこしそうだからです。

EKSアドオンが管理しているパラメータを変更した場合、その後のEKSアドオンの更新によって再度アドオンのデフォルトに戻されてしまいます。

変更可能なパラメータだとしても、変更には気を使うと思います。

Helmでもそれなりにマニフェストファイル管理の見通しは良くなるので、

Chartを取得して、修正したい値を変えて適用してあげる方が、マニフェストファイル管理やデフォルトとの差分の管理が楽なのではないかなと感じました。

 

デフォルトの設定から特に変えないのであれば、アドオン管理がかなり省力化できると思いますし、EKSアドオンのコンセプトは素敵なので、今後の発展に期待したいと思いました。

 

今回は以上です。