[アップデート] Fargate for EKSがEFSを正式サポートしました
みなさん、こんにちは!
AWS事業本部の青柳@福岡オフィスです。
Amazon EKS (Amazon Elastic Kubernetes Service) から利用できるAWSのストレージサービスとしては、以下のものがあります。
- Amazon EBS (Elastic Block Store)
- Amazon EFS (Elastic File System)
- Amazon FSx for Lustre (※FSx for Lustre用のドライバーは現在ベータ版です)
これらのうち EFS は、これまではEC2ワーカーノード上のポッドからのみ利用可能だったのですが、今回のアップデートで Fargate上のポッドから利用可能になりました!!
AWS Fargate 上の Amazon EKS が Amazon EFS ファイルシステムのサポートを開始
以下のサイトで設定手順・利用方法が公開されていますので、これらを参考に実際に試してみました。
AWSブログ:
新機能 – AWS Fargate for Amazon EKS が新たに Amazon EFS をサポート | Amazon Web Services ブログ
AWSドキュメント:
Amazon EFS CSI driver - Amazon EKS
(※ 日本語版ドキュメントはまだ更新されていませんので、英語版を参照してください)
GitHubリポジトリ:
https://github.com/kubernetes-sigs/aws-efs-csi-driver
これらの中で特に AWSブログの記事 は「EKSとEFSを組み合わせることの利点」や「Kubernetesのストレージ利用におけるPV、PVC、StoregeClassの役割」などについて非常に分かり易く解説されていると思いました。
是非、一読されることをお勧めします。
前提条件
今回のアップデートを試すにあたって、特にツール類のバージョンは指定されていません。
とは言え、念のために最新バージョンにアップデートしておきましょう。
今回は、以下の各ツールのバージョンを使用しました。
$ eksctl version 0.25.0 $ kubectl version --client --short Client Version: v1.18.8 $ aws --version aws-cli/2.0.40 Python/3.7.3 Linux/4.19.104-microsoft-standard exe/x86_64.ubuntu.18
準備
EKSクラスターの作成
EKSクラスターを作成します。
作成手段は問われませんが、今回は「eksctl」コマンドを使ってサクッと作成しました。
$ eksctl create cluster \ --name eks-example \ --fargate
--fargate
オプションを付けることで、EC2ワーカーノードは作成されず、Fargate専用のEKSクラスターが作成されます。
セキュリティグループの作成
EFSファイルシステムを作成する前に、EKSの「ポッド」からEFSファイルシステムに対するアクセスを許可するためのセキュリティグループを作成します。
eksctlコマンドでEKSクラスターを作成した場合、3つのアベイラビリティゾーンに「Public Subnet」「Private Subnet」が各1個ずつ、合計6個のサブネットが作成されます。
EKSでFargateを使用する場合はプライベートサブネットに展開されますので、Fargate上で起動するポッドは3個のプライベートサブネットのCIDR範囲のIPアドレスを持つことになります。
したがって、下図のように3個のプライベートサブネットからの「NFSプロトコル (TCP:2049)」のインバウンドアクセスを許可するセキュリティグループを作成します。
(ドキュメントでは「EKSクラスターが展開されるVPCのCIDR範囲を指定」となっていますが、パブリックサブネットからのアクセスが無いことが判っていますので、今回はプライベートサブネットのみに範囲を絞って指定しました)
EFSファイルシステムの作成
EKSクラスターからアクセスするEFSファイルシステムは、以下のいずれかのVPCに作成する必要があります。
- EKSクラスターと同じVPC上に作成する
- EKSクラスターとは別のVPCに作成して、EKSクラスターのVPCとのピアリング接続を行う
今回は前者の「EKSクラスターと同じVPC上に作成する」を採用することにします。
EFSファイルシステムを作成する際に「カスタマイズ」を選択して、「ステップ2 ネットワークアクセス」の設定画面で以下のように設定します。
- VPC: EKSクラスターと同じVPCを指定
- マウントターゲット
- 各アベイラビリティゾーン毎に、プライベートサブネットを選択
- セキュリティグループは、初期設定されている「default」を削除し、前の手順で作成したものを選択
その他の設定は全てデフォルトのままで、ファイルシステムを作成します。
EKSクラスターへのCSIドライバーの組み込み
ここからは、EKSクラスター上での設定を行っていきます。
EKSクラスターがストレージをマウントしてアクセスできるようにするために、「CSI (Container Storage Interface)」という仕組みを使います。
(かつては「in-tree plugin」という形で提供されてきましたが、徐々にCSIへ移行しつつあります)
今回使用する「Amazon EFS CSI Driver」をEKSクラスターへ組み込みます。
以下のYAMLファイルを作成して、kubectl apply
コマンドを実行します。
apiVersion: storage.k8s.io/v1beta1 kind: CSIDriver metadata: name: efs.csi.aws.com spec: attachRequired: false
$ kubectl apply -f csidriver.yaml csidriver.storage.k8s.io/efs.csi.aws.com created
組み込まれたCSIドライバーを確認します。
$ kubectl get csidrivers NAME CREATED AT efs.csi.aws.com 2020-08-19T10:10:28Z
次に、「ストレージクラス」の定義を行います。
「ストレージクラス」とは、Kubernetes上でストレージを利用する方法について「性能」「冗長性」などのパラメーターの組み合わせを「Standard」「Premium」などの名前を付けて事前定義しておき、利用時に選択できるようにしておく仕組みです。
例えば、EBSの場合には「Type (io1/gp2/sc1/st1)」や「IOPS (io1の場合)」などのパラメーターを指定することができます。
しかし、EFSでは指定できるパラメーターが無いため、パラメーター無しのストレージクラスを一つだけ定義します。
kind: StorageClass apiVersion: storage.k8s.io/v1 metadata: name: efs-sc provisioner: efs.csi.aws.com
$ kubectl apply -f storageclass.yaml storageclass.storage.k8s.io/efs-sc created
これで、EKSクラスターからEFSファイルシステムを利用する準備ができました。
Persistent Volume (PV) および Persistent Volume Claim (PVC) の作成
ここからは、EFSファイルシステムをマウント・利用する都度、設定する必要がある手順となります。
まず、作成したEFSファイルシステムをマウント可能にするための定義として「Persistent Volume (PV)」を作成します。
apiVersion: v1 kind: PersistentVolume metadata: name: efs-pv spec: capacity: storage: 5Gi volumeMode: Filesystem accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain storageClassName: efs-sc csi: driver: efs.csi.aws.com volumeHandle: fs-XXXXXXXX
代表的なパラメーターを説明します:
spec.capacity.storage
: 用意するボリュームのサイズを指定します。ただし、EFSは完全にエラスティックなファイルシステムであり事前にサイズを確保する必要が無いため、ここで指定した値は意味がなく、無視されます。(Kubernetesの仕様上、ダミー値として指定する必要があります)spec.accessModes
: マウントしたボリュームに対するアクセス方法を以下から選択します。- ReadWriteOnce: 単一のノードから「読み取り/書き込み可能」としてマウント可能
- ReadOnlyMany : 複数のノードから「読み取り専用」としてマウント可能
- ReadWriteMany: 複数のノードから「読み取り/書き込み可能」としてマウント可能
spec.persistentVolumeReclaimPolicy
: ボリュームがマウントから解放された時の振る舞いを以下から選択します。- Delete: ボリュームの中身は削除される
- Retain: ボリュームの中身は削除されず、次回にマウントされた時に再利用される
spec.storageClassName
: 定義したストレージクラスから利用するものを指定します。spec.csi.volumeHandle
: EFSファイルシステムの「ファイルシステムID」を指定します。(ご自身の環境に合わせて書き換えてください)
以下のコマンドで Persistent Volume を作成します。
$ kubectl apply -f pv.yaml persistentvolume/efs-pv created
次に、ポッドがEFSファイルシステムのマウントを要求する際に指定する「Persistent Volume Claim (PVC)」を作成します。
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: efs-claim spec: accessModes: - ReadWriteMany storageClassName: efs-sc resources: requests: storage: 5Gi
パラメーターは基本的に Persistent Volume に合わせて記述します。
以下のコマンドで Persistent Volume Claim を作成します。
$ kubectl apply -f claim.yaml persistentvolumeclaim/efs-claim created
ポッドからEFSファイルシステムをマウントしてみる
では、いよいよポッドからEFSファイルシステムをマウントしてみましょう。
ポッドのマニフェストを以下のように記述します。
apiVersion: v1 kind: Pod metadata: name: sample spec: containers: - name: sample image: busybox 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: efs-claim
spec.volumes
には、ポッドが利用しようとするボリュームについての記述を行います。
具体的には、作成した「Persistent Volume Claim」の名前を指定して、「Persistent Volume」で定義されているボリュームへのマウントを要求します。
そして、spec.containers.volumeMounts
には、コンテナの設定情報の一つとして「ボリュームのマウント情報」を指定します。
マウント元はname
で示したボリューム定義、マウント先はmountPath
で指定したコンテナ上のパスです。
準備ができましたら、ポッドをデプロイします。
$ kubectl apply -f pod.yaml pod/sample created
Fargateが起動するのに1~2分程度「Pending」の状態になり、問題なく起動してボリュームへのマウントが成功すると「Running」になります。
$ kubectl get pods NAME READY STATUS RESTARTS AGE sample 0/1 Pending 0 32s $ kubectl get pods NAME READY STATUS RESTARTS AGE sample 1/1 Running 0 57s
上手くいった場合: EFSファイルシステムへ書き込まれたファイルを確認してみましょう
ポッドの起動に成功したら、以下のコマンドでポッド内のコンテナにログインします。
$ kubectl exec -it sample -- /bin/sh
マウントされたEFSファイルシステムのボリュームを確認します。
# ls -l /data total 4 -rw-r--r-- 1 root root 1590 Aug 19 12:23 out.txt
ポッドによってテキストファイルが書き込まれていますね。
中身をダンプしてみます。
# cat /data/out.txt Wed Aug 19 12:22:32 UTC 2020 Wed Aug 19 12:22:37 UTC 2020 Wed Aug 19 12:22:42 UTC 2020 Wed Aug 19 12:22:47 UTC 2020 Wed Aug 19 12:22:52 UTC 2020 Wed Aug 19 12:22:57 UTC 2020 Wed Aug 19 12:23:02 UTC 2020 Wed Aug 19 12:23:07 UTC 2020 Wed Aug 19 12:23:12 UTC 2020 Wed Aug 19 12:23:17 UTC 2020
ちゃんとポッドからテキストファイルに対して書き込まれていることが確認できました。
上手くいかない場合: ポッドの状態を確認してみましょう
もし、「Running」とならずに「ContainerCreating」の状態のままになってしまう場合は、まずはポッドのイベントを確認してみましょう。
$ kubectl describe pod/sample (中略) Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled <unknown> fargate-scheduler Successfully assigned default/sample to fargate-ip-192-168-184-116.ap-northeast-1.compute.internal Warning FailedMount 1s (x2 over 18s) kubelet, fargate-ip-192-168-184-116.ap-northeast-1.compute.internal MountVolume.SetUp failed for volume "efs-pv" : kubernetes.io/csi: mounter.SetupAt failed: rpc error: code = Internal desc = Could not mount "fs-b6485e97:/" at "/var/lib/kubelet/pods/341c9fda-1ffa-4a01-a04d-a7e2bb69dbe0/volumes/kubernetes.io~csi/efs-pv/mount": mount failed: exit status 32 Mounting command: mount Mounting arguments: -t efs -o tls fs-b6485e97:/ /var/lib/kubelet/pods/341c9fda-1ffa-4a01-a04d-a7e2bb69dbe0/volumes/kubernetes.io~csi/efs-pv/mount Output: Could not start amazon-efs-mount-watchdog, unrecognized init system "supervisord" mount.nfs4: Connection reset by peer
このように、EFSへのマウントに関して「Failed」と出力されている場合は、ここまでの設定でどこかが間違っている可能性が高いです。
例えば、以下のような点を確認すると良いでしょう。
- セキュリティグループの設定は正しいか?
- 手順の漏れは無いか? (例えば「ストレージクラスを定義していない」など)
- PV、PVCの名前の対応は間違っていないか?
上記とは異なる出力 (例えば「コンテナイメージのpullに失敗した」) がされている場合は、内容に応じて問題個所を見直してみてください。
複数のポッドからの書き込みを試してみる
EFSは「共有ファイルシステム」ですので、複数のポッドからの読み込みや書き込みにも当然対応しています。
Deploymentを定義して、複数のポッドからEFSへの書き込みを試してみましょう。
apiVersion: apps/v1 kind: Deployment metadata: name: sample2 spec: replicas: 3 selector: matchLabels: app: sample2 template: metadata: labels: app: sample2 spec: containers: - name: sample2 image: busybox command: ["/bin/sh"] args: ["-c", "while true; do echo $(date -u) >> /data/$(uname -n).txt; sleep 5; done"] volumeMounts: - name: persistent-storage mountPath: /data volumes: - name: persistent-storage persistentVolumeClaim: claimName: efs-claim
先ほどの「ポッド単体」の時と、定義内容はほとんど変わりません。
spec.replicas: 3
でポッドを3つデプロイするようにしています。
また、EFSファイルシステムへの書き込みは、同一のディレクトリに対してコンテナ自身のホスト名をファイル名として書き込むようにします。
準備ができましたら、デプロイします。
$ kubectl apply -f deployment.yaml deployment.apps/sample2 created
しばらく待って、ポッドが3つ起動することを確認します。
$ kubectl get pods NAME READY STATUS RESTARTS AGE sample2-6bdcb7dd85-dk4k6 1/1 Running 0 100s sample2-6bdcb7dd85-njb5q 1/1 Running 0 100s sample2-6bdcb7dd85-pf6k9 1/1 Running 0 100s
起動したポッドの一つにログインして、/data
ディレクトリを確認します。
$ kubectl exec -it sample2-6bdcb7dd85-dk4k6 -- /bin/sh # ls -l /data total 20 -rw-r--r-- 1 root root 4548 Aug 19 12:31 out.txt -rw-r--r-- 1 root root 319 Aug 19 12:34 sample2-6bdcb7dd85-dk4k6.txt -rw-r--r-- 1 root root 551 Aug 19 12:34 sample2-6bdcb7dd85-njb5q.txt -rw-r--r-- 1 root root 580 Aug 19 12:34 sample2-6bdcb7dd85-pf6k9.txt
各コンテナからファイルに書き込まれていることが確認できました。
(もちろん、中身もちゃんと書き込まれていることも確認しましょう!)
おわりに
FargateからのEFS利用については、既にECSでは今年4月にリリースされていました。
EKSでの設定方法はECSとは全く異なりますが、Kubernetesにおけるストレージ利用方法について理解を進めつつ試してみると、そこまで難しくはないのではないかと思いました。(使いこなそうとすると、やっぱり難しいかもしれませんけどね・・・)
今回、ECSに加えてEKSにおいても「FargateからのEFS利用」が実現したことで、AWSのコンテナオーケストレーションにおけるストレージ利用の選択肢が広がったのではないかと思います。
みなさんも是非試してみてください。