AWS EBS CSI Driver 8.0 以降は gp3 がデフォルトになるのでアップグレードには注意が必要


AWSが提供するKubernetesのマネージドサービスであるEKSでは、デフォルトのストレージクラスとしてgp2が定義されていますが、EBS CSI driver をインストールすると、ボリュームのスナップショットやリサイズなどが行えるようになります。
https://github.com/kubernetes-sigs/aws-ebs-csi-driver

AWS EBS CSI Driver 8.0 以降から gp3 がデフォルトに

Amazon EBS では、汎用ボリュームとして gp2gp3 が選べます。
gp2 に比べて gp3 ではコストが安価になり、小容量でもIOPSやスループットを引き上げることができるようになりました。

詳しくはこの辺りを参考にしてください。

それに伴って、AWS EBS CSI Driver 8.0 以降からボリュームタイプのデフォルトが gp2 から gp3 に変更されています。
そのため、ボリュームタイプを指定せずにドライバーのバージョンをアップグレードしてしまうとエラーが発生してしまいます。

また、ややこしい事にアップグレード時にエラーは発生しないのですが、Podが再起動して別のノードに移動したときなど、割り当てたボリュームの付け替えが発生した際に下記のようなエラーになりPodの起動がうまくいかなくなります。

Warning FailedAttachVolume 71s attachdetach-controller Multi-Attach error for volume "pvc-xxxx-xxxx-xxxx-xxx-xxxxxxxxx" Volume is already exclusively attached to one node and can't be attached to another

自分も開発環境のKubernetesでドライバーをアップグレードした際、後になってエラーが出て原因特定まで時間がかかりました。。。

再現実験

テスト用にほぼデフォルトの状態のEKSを作成して、エラーの再現をしてみます。

まずはデフォルトがgp2の古いバージョンのドライバーをインストール。
Helm で EBS CSI Driver 7.1 をインストール

$ helm upgrade --install \
       aws-ebs-csi-driver \
       https://github.com/kubernetes-sigs/aws-ebs-csi-driver/releases/download/v0.7.1/helm-chart.tgz

CSIドライバーの Storage Class の作成

storage.yaml
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: ebs
provisioner: ebs.csi.aws.com
reclaimPolicy: Retain
volumeBindingMode: WaitForFirstConsumer
$ kubectl apply -f storageclass.yaml

テスト用のPersistent Volume Claimの作成

pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: test-pvc
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: ebs
  resources:
    requests:
      storage: 10Gi
$ kubectl apply -f pvc.yaml

テスト用のDeployment の作成

deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: test-app
spec:
  selector:
    matchLabels:
      app: test-app
  template:
    metadata:
      labels:
        app: test-app
    spec:
      containers:
      - name: test-app
        image: centos
        command: ["/bin/sh"]
        args: ["-c", "while true; do echo $(date -u) >> /data/out.txt; sleep 5; done"]
        volumeMounts:
        - name: test-data
          mountPath: /data
      volumes:
      - name: test-data
        persistentVolumeClaim:
          claimName: test-pvc
kubectl --kubeconfig ./kubeconfig apply -f deployment.yaml

EBSのボリュームタイプがgp2のボリュームが付与されたテスト用のPodが作成されました。

次に、AWS EBS CSI Driver 最新版 (v9.8) にアップグレード。
ボリュームタイプは指定しなかったのでデフォルトの gp3 になっています。

$ helm repo add aws-ebs-csi-driver https://kubernetes-sigs.github.io/aws-ebs-csi-driver
$ helm upgrade aws-ebs-csi-driver aws-ebs-csi-driver/aws-ebs-csi-driver

(7.1と9.8でインストールが異なるのは、8.0あたりからHelmレポジトリが提供されるようになったからです)

この状態でテスト用Podが動いているノードを強制的に停止して、別ノードにテスト用Podを移動させることでボリュームの付け替えを発生させます。

やはり予想通り、ボリュームタイプが異なるとボリュームのアタッチに失敗して起動しません。

$ kubectl get pods
NAME                        READY   STATUS              RESTARTS   AGE
test-app-7886db5c8d-sqrzz   0/1     ContainerCreating   0          4m41s

エラーメッセージは以下の通りです。

Warning FailedAttachVolume 3m38s attachdetach-controller
Multi-Attach error for volume "pvc-xxxxx-xxxxx-xxxx-xxxxxxx" Volume is already exclusively at tached to one node and can't be attached to another Warning FailedMount 95s kubelet, ip-xxx-xxx-xxx-xxx.ap-northeast-1.compute.internal Unable to attach or mount volumes: unmounted volumes=[test-data], unattached volumes=[test-data default-tken-qmjpg[]: timed out waiting for the conditio

以前、開発環境でエラーを発生させてしまった時は、ドライバーのボリュームタイプのパラメーターをgp2に設定して解決させましたが、今回はEBSのボリュームタイプの方をgp3に変更した場合の挙動を確認してみます。

AWSのコンソール画面からボリュームタイプをgp3に変更します。

予想としてはgp3への変換が完了したタイミングでドライバーが正しくボリュームを認識してテスト用Podが正常起動すると思っていたのですが、実際は変換途中でドライバー側からはgp3のボリュームとして正しく認識していて、変換途中の段階でテスト用Podは正常起動しました。
もしかするとコンソール画面で変更処理を実行したタイミングでgp3として認識されるのかもしれません。

下記のようにgp3へ変換している途中ですが、

テスト用のPodは正常に起動していました。

kubectl get pods
NAME                        READY   STATUS    RESTARTS   AGE
test-app-7886db5c8d-tfrsb   1/1     Running   0          4m9s

この挙動はちょっと意外でした。

まとめ

アップグレード方法としてはストレージタイプのパラメーターを明示的に指定する場合は以下のようになると思います。

$ helm upgrade --set type=gp2 \
       aws-ebs-csi-driver \
       aws-ebs-csi-driver/aws-ebs-csi-driver

割り当てられているEBSボリュームがそれほど多くなく、ある程度のダウンタイムが発生しても許容してくれるようなシステムの場合、EBSのストレージタイプをgp2からgp3に変更する方法を検討してもいいかもしれません。
今回の実験でもgp3として認識するのは変換が完了する前なので、gp3への変換中にノードが停止するような事になっても、問題なく切り替わる可能性は高いと思います。

注意することがあるとすれば、EKSではスポットインスタンスを使うことが多いと思いますが、gp3では旧世代のインスタンスには未対応なため、対象のインスタンスタイプにそれらが含まれていないようにする必要があります。

また、ドキュメントを見る限り、gp2gp3の両方を扱うことは無理っぽいのでどちらかに統一する必要があるが悩ましいところです。