Amazon EFSを使ってEKSに永続ボリュームを導入した|sitateru tech blog

シタテルの技術やエンジニアの取り組みを紹介するテックブログです。

2021年10月14日木曜日

Amazon EFSを使ってEKSに永続ボリュームを導入した

AWS EKSで動かしているアプリケーション用にEFSを使って永続ボリュームを用意してみたので、その方法をまとめてみようと思います。

とあるテスト用Kubernetes環境でMySQLのイメージを使ってデータベースを動かしているのですが、何かの拍子にpodが一旦削除などされてしまうとデータベースの中身のデータが全て無くなってしまいます。
そこで永続ボリュームを確保しておいて、MySQLデータ用のディレクトリをマウントしたボリュームにすればOKというわけです。

手順はほぼこのドキュメントどおりですが、一部変えないと動かなかった箇所があるのでその点も触れつつ書いてみようと思います。
Amazon EFS CSI ドライバー

ドキュメントに従っても動かないよ!という件のissueはこちら。
Missing permission in the example IAM policy file · Issue #489 · kubernetes-sigs/aws-efs-csi-driver · GitHub

また、今回やってみた環境は以下のようになっています。
Kubernetes v1.21
EKSプラットフォーム eks.2
EFS CSI ドライバー v1.3.4


  1. Amazon EFS CSI ドライバー

まずはIAMポリシーを作成します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "elasticfilesystem:DescribeAccessPoints",
                "elasticfilesystem:DescribeFileSystems",
                "elasticfilesystem:DescribeMountTargets",
                "ec2:DescribeAvailabilityZones"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "elasticfilesystem:CreateAccessPoint"
            ],
            "Resource": "*",
            "Condition": {
                "StringLike": {
                    "aws:RequestTag/efs.csi.aws.com/cluster": "true"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": "elasticfilesystem:DeleteAccessPoint",
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "aws:ResourceTag/efs.csi.aws.com/cluster": "true"
                }
            }
        }
    ]
}

AmazonEKS_EFS_CSI_Driver_Policy というポリシーを作成してこのjsonでアクセス権限を設定します。

AWSのドキュメントからリンクされているポリシーと比べると"elasticfilesystem:DescribeMountTargets", "ec2:DescribeAvailabilityZones"が増えているのですが、これを追加しないと権限不足のエラーが出てドライバーが動きませんでした。🤔

このポリシーでサービスアカウントを作成します。<cluster-name><Account ID>, <region>はお使いの環境に合わせて置き換えてください。

$ eksctl create iamserviceaccount \
    --name efs-csi-controller-sa \
    --namespace kube-system \
    --cluster <cluster-name> \
    --attach-policy-arn arn:aws:iam::<Account ID>:policy/AmazonEKS_EFS_CSI_Driver_Policy \
    --approve \
    --override-existing-serviceaccounts \
    --region <region>
$ eksctl create iamserviceaccount \
    --name efs-csi-node-sa \
    --namespace kube-system \
    --cluster <cluster-name> \
    --attach-policy-arn arn:aws:iam::<Account ID>:policy/AmazonEKS_EFS_CSI_Driver_Policy \
    --approve \
    --override-existing-serviceaccounts \
    --region <region>

Helmを使ってドライバーをクラスターにインストールします。
クラスターのリージョンによってimage.repositoryは変えないといけないようなので↓を参照してください。
Amazon EKS アドオンコンテナイメージのアドレス - Amazon EKS

$ helm repo add aws-efs-csi-driver https://kubernetes-sigs.github.io/aws-efs-csi-driver/
$ helm repo update
$ helm upgrade -i aws-efs-csi-driver aws-efs-csi-driver/aws-efs-csi-driver \
    --namespace kube-system \
    --set image.repository=602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/eks/aws-efs-csi-driver \
    --set controller.serviceAccount.create=false \
    --set controller.serviceAccount.name=efs-csi-controller-sa \
    --set node.serviceAccount.create=false \
    --set node.serviceAccount.name=efs-csi-node-sa

AWSのドキュメントでは efs-csi-node-sa は出てこないのですが、こちらも試した環境ではこのサービスアカウントを作成してhelmで指定しないと動きませんでした。🤔


  1. EFS ファイルシステム作成

ここはインフラ管理の都合上terraformで行いました。
セキュリティグループ、ファイルシステム、マウントターゲット(AWSコンソールでは「ネットワーク」という表示名になっています)を作成します。
VPCに複数のプライベートサブネットがある場合は aws_efs_mount_target も同様に複数作っておきましょう。

resource "aws_security_group" "efs" {
  name   = "eks-${var.cluster_name}-efs"
  # ↓クラスターがあるVPCのid
  vpc_id = var.vpc_id
  ingress {
    from_port   = 2049
    to_port     = 2049
    protocol    = "tcp"
    # ↓VPCのCIDR
    cidr_blocks = [var.vpc_cidr]
  }
}
resource "aws_efs_file_system" "fs" {
  creation_token = "eks-${var.cluster_name}-fs"
  tags = {
    "Name" = "eks-${var.cluster_name}-fs"
  }
}
resource "aws_efs_mount_target" "az" {
  file_system_id  = aws_efs_file_system.fs.id
  # ↓プライベートサブネットのID
  subnet_id       = var.subnet_private.id
  security_groups = [aws_security_group.efs.id]
}

  1. クラスターでStorageClassを追加

まずはストレージクラスの宣言です。StorageClassはnamespaceに属していないので、一度applyしておけばOKです。
parametersについてはこちらを参考にしてください👇

aws-efs-csi-driver/examples/kubernetes/dynamic_provisioning at master · kubernetes-sigs/aws-efs-csi-driver · GitHub

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: efs-sc
provisioner: efs.csi.aws.com
parameters:
  provisioningMode: efs-ap
  # EFSで作成したファイルシステムのID
  fileSystemId: fs-00abcdef123456789
  directoryPerms: "700"

  1. アプリケーションで永続ボリュームを確保、マウント

これで準備ができたので、PersistentVolumeを使うアプリケーションでのyamlを書いていきます。

まずはPVC(PersistentVolumeClaim)です。
storageClassName は3.で宣言したStorageClassのnameですね。

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: efs-claim
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: efs-sc
  resources:
    requests:
      storage: 2Gi

このPVCで確保したボリュームをコンテナにマウントします。
以下のyamlはdeploymentでやる場合のvolumeに関するところを書き出したものです。 claimName がPVCリソースのnameですね。

apiVersion: apps/v1
kind: Deployment
spec:
  template:
    spec:
      containers:
        - name: mysql
          image: mysql:8.0.25
          volumeMounts:
            - name: persistent-storage
              mountPath: /var/lib/mysql
      volumes:
        - name: persistent-storage
          persistentVolumeClaim:
            claimName: efs-claim

と、このような作業で永続ボリュームを利用できました。

冒頭で触れたドキュメント不完全問題もあって少し手間取りましたが、振り返ってまとめてみるとそう難しいことはなさそうですね。

EKSで永続ボリュームが欲しくなったときはお試しください👐

, , ,