はじめに
- Kubernetes(K8s) クラスタ上のアプリケーションのデータの保全性を確保するために利用するのが永続ボリューム(Persistent Volume = PV)です。
- 外部ストレージシステムと連携することで、データの損失、破損、想定外の変更などからデータ資産を守ることが出来ます。
- 本記事では、K8s の PV を Amazon EKS で利用するための基本事項をまとめ、実際に EBS と EFS での利用を試行します。
K8s で利用可能なボリュームの種類
- K8s クラスタ内部で利用可能なボリュームには、
emptyDir
,hostPath
, 外部ストレージの 3 種類があります。emptyDir
とhostPath
はノード内で利用できる簡便なボリュームです。emptyDir
は同一ポッドのコンテナ間でのみボリュームを共有できます。異なるポッド間での共有はできず、ポッドが終了すると一緒に削除されます。hostPath
では、異なるポッド間でも同じ永続ボリュームとして共有が可能です。ポッドとともに削除されることはありませんが、異なるノードに配置されたポッド間での共有はできません。- 外部ストレージを準備できない簡易的手段として利用できます。
- 外部ストレージサービスと連携する場合、すべてのノードに外部ストレージシステムへのアクセス経路が実装されている必要があります。
- ノード停止時でもポッドを他のノードへ退避すれば、アプリケーションの稼働を継続できます。
EKS で利用可能なストレージシステム
- EKS では以下の AWS のサービスを外部ストレージシステムとして利用することが出来ます。
Amazon Elastic Block Store(EBS)
- 公式サイト
- オンプレで言うところのディスクに相当するものです。
- ブロックレベルのストレージサービスです。
- EC2 と組み合わせることで、NFS サーバとして稼働させることが可能です。
- 単一 AZ の単一 EC2 インスタンスからアクセスが可能です。
- メリットとしては、パフォーマンスの高さ、バックアップ・リストアの容易さ(スナップショット)、オンプレミスから移行の容易さが挙げられます。
- デメリットとしては、EC2 が単一障害点になってしまう点、拡張が少し面倒な点(EBS ボリュームの容量を増やす操作が必要になる)、定額課金である点(コストが確保した、EBS の容量分かかる)が挙げられます。
Amazon Elastic File System(EFS)
- 公式サイト
- オンプレで言うところの NAS に相当するものです。
- 複数 AZ の 1~数千の EC2 インスタンスから同時にアクセスすることが可能です。
- EBS と比較したときのメリットとしては、拡張性の高さ、信頼性の高さ、可用性の高さ、パフォーマンスの高さコスト効率が良さ(従量課金制)が挙げられます。
- デメリットとしては、EBS に比べるとコストが高い点です(EBSの 3 倍)。
EKS で永続ボリュームの利用方法
- EKS で永続ボリュームの利用方法を検証します。
- EC2 に EKS ワーカーノードを作成しておきます。
- 後に作成する試行用アプリケーションのためにノード数を 6 で設定しておきます。
$ eksctl create cluster \ --name [クラスター名] \ --region [リージョン名] \ --nodegroup-name standard-workers \ --node-type t3.medium \ --nodes 6 \ --nodes-min 1 \ --nodes-max 6 \ --ssh-access \ --ssh-public-key my-public-key.pub
EBS を利用する場合
1. ストレージクラスの作成
- 上記のコマンドでクラスターを作成した場合、デフォルトで
gp2
という名前のストレージクラスが作成されていることがわかります。
$ kubectl get storageclass NAME PROVISIONER AGE gp2 (default) kubernetes.io/aws-ebs 11m
- 新たに作成する場合は、Kubernetes の公式ドキュメントを参考に、下記のようなマニフェストを作成してデプロイします。
kind: StorageClass apiVersion: storage.k8s.io/v1 metadata: name: [ストレージクラス名] provisioner: kubernetes.io/aws-ebs parameters: type: gp2 iopsPerGB: "10" fsType: ext4
- デフォルトのストレージクラスを変更する場合は、下記のコマンドで変更します。
- アノテーション
storageclass.kubernetes.io/is-default-class=true
を指定するストレージクラスに付与します。
- アノテーション
$ kubectl patch storageclass [ストレージクラス名] -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
2. IAM ポリシーの作成とアタッチ
- ワーカーノードが Amazon EBS ボリュームを作成および変更できるようにするための IAM ポリシーを作成します。
example-iam-policy.json
の中身
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ec2:AttachVolume", "ec2:CreateSnapshot", "ec2:CreateTags", "ec2:CreateVolume", "ec2:DeleteSnapshot", "ec2:DeleteTags", "ec2:DeleteVolume", "ec2:DescribeInstances", "ec2:DescribeSnapshots", "ec2:DescribeTags", "ec2:DescribeVolumes", "ec2:DetachVolume" ], "Resource": "*" } ] }
$ aws iam create-policy --policy-name Amazon_EBS_CSI_Driver \ --policy-document file://example-iam-policy.json
- 作成した IAM ポリシーを NodeInstanceRole にアタッチします。NodeInstanceRole の ARN は下記のコマンドで取得します。
$ kubectl -n kube-system describe configmap aws-auth Name: aws-auth Namespace: kube-system Labels: <none> Annotations: <none> Data ==== mapRoles: ---- - groups: - system:bootstrappers - system:nodes rolearn: arn:aws:iam::123456789012:role/eksctl-[クラスター名]-nodegroup-standard-w-NodeInstanceRole-XXXXXXXXXXXXX username: system:node:{{EC2PrivateDNSName}} mapUsers: ----
- 上記コマンドの実行結果の
rolearn
のeksctl-
以下の値を利用して、下記のコマンドを実行し、ポリシーをアタッチします。
$ aws iam attach-role-policy \ --policy-arn arn:aws:iam::123456789012:policy/Amazon_EBS_CSI_Driver \ --role-name eksctl-alb-nodegroup-ng-xxxxxx-NodeInstanceRole-xxxxxxxxxx
3. Amazon EBS CSI ドライバーのデプロイ
$ kubectl apply -k "github.com/kubernetes-sigs/aws-ebs-csi-driver/deploy/kubernetes/overlays/stable/?ref=master" serviceaccount/ebs-csi-controller-sa created clusterrole.rbac.authorization.k8s.io/ebs-external-attacher-role created clusterrole.rbac.authorization.k8s.io/ebs-external-provisioner-role created clusterrolebinding.rbac.authorization.k8s.io/ebs-csi-attacher-binding created clusterrolebinding.rbac.authorization.k8s.io/ebs-csi-provisioner-binding created deployment.apps/ebs-csi-controller created daemonset.apps/ebs-csi-node created csidriver.storage.k8s.io/ebs.csi.aws.com created
Amazon EBS CSI ドライバーをテストする
$ git clone https://github.com/kubernetes-sigs/aws-ebs-csi-driver.git
- Amazon EBS ドライバーのテストファイルが含まれているフォルダに移動します。
$ cd aws-ebs-csi-driver/examples/kubernetes/dynamic-provisioning/
- 下記のコマンドで、テストに必要な K8s リソースを作成します。
$ kubectl apply -f specs/
ここで適用されているマニフェストファイルの中身
strorageclass.yml
kind: StorageClass apiVersion: storage.k8s.io/v1 metadata: name: ebs-sc provisioner: ebs.csi.aws.com volumeBindingMode: WaitForFirstConsumer
claim.yml
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: ebs-claim spec: accessModes: - ReadWriteOnce storageClassName: ebs-sc resources: requests: storage: 4Gi
- このポッドでは、5 秒毎にpod.yml
/data/out.txt
に時刻を追記していきます。
apiVersion: v1 kind: Pod metadata: name: app 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: ebs-claim
- 下記のコマンドで永続ボリュームの確認をします。
$ kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pvc-794b3a2c-b276-4e9a-9e0b-7456d94a818a 4Gi RWO Delete Bound default/ebs-claim ebs-sc 80m
- 次のコマンドで、永続ボリュームの詳細を確認できます。
永続ボリュームの詳細確認コマンドとその結果
$ kubectl describe pv pvc-794b3a2c-b276-4e9a-9e0b-7456d94a818a Name: pvc-794b3a2c-b276-4e9a-9e0b-7456d94a818a Labels: <none> Annotations: pv.kubernetes.io/provisioned-by: ebs.csi.aws.com Finalizers: [kubernetes.io/pv-protection external-attacher/ebs-csi-aws-com] StorageClass: ebs-sc Status: Bound Claim: default/ebs-claim Reclaim Policy: Delete Access Modes: RWO VolumeMode: Filesystem Capacity: 4Gi Node Affinity: Required Terms: Term 0: topology.ebs.csi.aws.com/zone in [us-east-2a] Message: Source: Type: CSI (a Container Storage Interface (CSI) volume source) Driver: ebs.csi.aws.com FSType: ext4 VolumeHandle: vol-054704dd57632d9c0 ReadOnly: false VolumeAttributes: storage.kubernetes.io/csiProvisionerIdentity=1589341279006-8081-ebs.csi.aws.com Events: <none>
- 下記のコマンドで、ポッドが正常に動作していることを確認します。
$ kubectl exec -it app -- cat /data/out.txt Wed May 13 06:09:19 UTC 2020 Wed May 13 06:09:24 UTC 2020 Wed May 13 06:09:29 UTC 2020 Wed May 13 06:09:35 UTC 2020 Wed May 13 06:09:40 UTC 2020
- 確認が完了したら、下記のコマンドでアプリを削除します。
$ kubectl delete -f specs/ persistentvolumeclaim "ebs-claim" deleted pod "app" deleted storageclass.storage.k8s.io "ebs-sc" deleted
EFS を利用する場合
1. Amazon EFS CSI ドライバーをデプロイする
$ kubectl apply -k "github.com/kubernetes-sigs/aws-efs-csi-driver/deploy/kubernetes/overlays/stable/?ref=master"
2. Amazon EKS クラスターの VPC ID を確認
- 下記のコマンドを実行します
$ aws eks describe-cluster --name [クラスター名] --query "cluster.resourcesVpcConfig.vpcId" --output text
3. クラスターの VPC の CIDR 範囲を確認
- 下記のコマンドを実行します。
[vpc-id]
は 2. で確認した VPC ID です。
$ aws ec2 describe-vpcs --vpc-ids [vpc-id] --query "Vpcs[].CidrBlock" --output text
4. セキュリティグループの作成
- 下記のコマンドで Amazon EFS マウントポイントのインバウンドネットワークファイルシステム (NFS) トラフィックを許可するセキュリティグループを作成します。
[vpc-id]
は 2. で確認した VPC ID です。
$ aws ec2 create-security-group --description efs-test-sg --group-name efs-sg --vpc-id [vpc-id] { "GroupId": "sg-06ac2d516ee57cd85" }
5. NFS インバウンドルールを追加
- VPC のリソースが EFS と通信できるように、NFS インバウンドルールを追加します。
- 下記のコマンドのうち、
[group-id]
は 4. で出力されたクループ ID、[vpc-cidr]
は 3. で確認した CIDR を指定します。
- 下記のコマンドのうち、
$ aws ec2 authorize-security-group-ingress --group-id [group-id] --protocol tcp --port 2049 --cidr [vpc-cidr]
6. Amazon EKS クラスターに Amazon EFS ファイルシステムを作成
- Amazon Elastic File System コンソールを開き、「ファイルシステムの作成」を選択します。
- 次のページの「VPC」で Amazon EKS クラスターが使用している VPC を選択します(下のスクリーンショットの赤枠部分)。
- また「セキュリティグループ」の欄をクリックし、4.で作成したセキュリティグループを追加していきます(下のスクリーンショットの青枠の部分)。
Amazon EFS CSI ドライバーをテストする
$ git clone https://github.com/kubernetes-sigs/aws-efs-csi-driver.git
- Amazon EFS ドライバーのテストファイルが含まれているフォルダに移動します。
$ cd aws-efs-csi-driver/examples/kubernetes/multiple_pods/
$ aws efs describe-file-systems --query "FileSystems[*].FileSystemId" --output text
specs/pv.yaml
ファイルを編集し、volumeHandle
値を 3. で確認したファイルシステム ID に置き換えます(下記の yaml ファイルの中身も参照)。サンプルアプリケーションをデプロイします。
$ kubectl apply -f specs/
ここで適用されているマニフェストファイルの中身
- EBS のときとの違いは、storageclass.yaml
provisioner
の値です。
kind: StorageClass apiVersion: storage.k8s.io/v1 metadata: name: efs-sc provisioner: efs.csi.aws.com
claim.yaml
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: efs-claim spec: accessModes: - ReadWriteMany storageClassName: efs-sc resources: requests: storage: 5Gi
pv.yaml
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: [ファイルシステム ID]
pod1.yaml
と pod2.yaml
apiVersion: v1 kind: Pod metadata: name: app1 spec: containers: - name: app1 image: busybox command: ["/bin/sh"] args: ["-c", "while true; do echo $(date -u) >> /data/out1.txt; sleep 5; done"] volumeMounts: - name: persistent-storage mountPath: /data volumes: - name: persistent-storage persistentVolumeClaim: claimName: efs-claim
apiVersion: v1 kind: Pod metadata: name: app2 spec: containers: - name: app2 image: busybox command: ["/bin/sh"] args: ["-c", "while true; do echo $(date -u) >> /data/out2.txt; sleep 5; done"] volumeMounts: - name: persistent-storage mountPath: /data volumes: - name: persistent-storage persistentVolumeClaim: claimName: efs-claim
- 下記のコマンドでポッドが 2 つとも「Running」になるまで待ちます。
$ kubectl get pods --watch
- デフォルトの名前空間の永続ボリュームを一覧表示します。
$ kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE efs-pv 5Gi RWX Retain Bound default/efs-claim efs-sc 43s
- 永続ボリュームの詳細を表示します。
永続ボリュームの詳細確認コマンドとその結果
$ kubectl describe pv efs-pv Name: efs-pv Labels: <none> Annotations: pv.kubernetes.io/bound-by-controller: yes Finalizers: [kubernetes.io/pv-protection] StorageClass: efs-sc Status: Bound Claim: default/efs-claim Reclaim Policy: Retain Access Modes: RWX VolumeMode: Filesystem Capacity: 5Gi Node Affinity: <none> Message: Source: Type: CSI (a Container Storage Interface (CSI) volume source) Driver: efs.csi.aws.com FSType: VolumeHandle: [ファイルシステム ID] ReadOnly: false VolumeAttributes: <none> Events: <none>
- ポッドが2つとも正常に動作していることを確認します。
$ kubectl exec -ti app1 -- tail /data/out1.txt Thu May 14 08:33:50 UTC 2020 Thu May 14 08:33:55 UTC 2020 Thu May 14 08:34:00 UTC 2020 Thu May 14 08:34:05 UTC 2020 Thu May 14 08:34:10 UTC 2020 Thu May 14 08:34:15 UTC 2020 Thu May 14 08:34:20 UTC 2020 $ kubectl exec -ti app2 -- tail /data/out1.txt Thu May 14 08:33:50 UTC 2020 Thu May 14 08:33:55 UTC 2020 Thu May 14 08:34:00 UTC 2020 Thu May 14 08:34:05 UTC 2020 Thu May 14 08:34:10 UTC 2020 Thu May 14 08:34:15 UTC 2020 Thu May 14 08:34:20 UTC 2020 Thu May 14 08:34:26 UTC 2020 Thu May 14 08:34:31 UTC 2020
- 確認が完了したら、下記のコマンドでアプリを削除します。
$ kubectl delete -f specs/ persistentvolumeclaim "efs-claim" deleted pod "app1" deleted pod "app2" deleted persistentvolume "efs-pv" deleted storageclass.storage.k8s.io "efs-sc" deleted
- 必要なくなった場合は、クラスター自体も削除しておきます。
$ eksctl delete cluster --name [クラスター名]
まとめ
- K8s のデータの資産を守る永続ボリュームに関する基本事項のまとめと、EKS で利用する試行をしました。
- EBS と EFS での利用にあたって、各サービスの特徴も確認しました。
- 次回、K8s のデプロイメントを利用した高可用性構成を EKS 上でやってみたいと思います。
参考資料
- Amazon EKS で永続的ストレージを使用する
- EBS と EFS を実際に利用する際の手順について参考にしました。
- Kubernetes のストレージについて調べる - Qiita
- K8s のストレージについての基本事項に関して参考にしました。
- EFSとEBS - Qiita
- EFS と EBS の違いについて参考にしました。