Kubernetes CSIとは?コンテナストレージ連携の基本と仕組みを徹底解説

Kubernetes CSIとは?コンテナストレージ連携の基本と仕組みを徹底解説

はじめに:Kubernetesとストレージの課題

今日のITインフラストラクチャにおいて、コンテナ技術、特にKubernetesはデファクトスタンダードとして広く普及しています。アプリケーションの高速なデプロイ、スケーリング、高可用性を実現するKubernetesは、ステートレスなマイクロサービスアーキテクチャにおいてその真価を発揮します。しかし、データベース、メッセージキュー、ファイルサーバーといった永続的なデータを必要とする「ステートフル」なアプリケーションをKubernetes上で運用しようとすると、ストレージ管理が大きな課題として浮上します。

コンテナは、その性質上、一時的で使い捨て可能な設計が基本です。コンテナが停止したり再起動したりすると、内部に保存されたデータは失われます。これは、永続性を必要としないアプリケーションには適していますが、データベースのようにデータを永続的に保持する必要があるアプリケーションには致命的です。

従来の仮想マシン(VM)環境では、VMに直接ストレージをアタッチしたり、NFSやiSCSIといった共有ストレージをマウントしたりすることが一般的でした。しかし、Kubernetesのような動的なコンテナオーケストレーション環境では、VMベースのストレージ管理手法では以下のような問題に直面します。

  • 動的なプロビジョニングの難しさ: アプリケーションの要求に応じて、動的にストレージを割り当てたり、拡張したりすることが困難です。
  • ポータビリティの欠如: 特定のストレージシステムに密結合してしまい、異なる環境(オンプレミス、クラウド)へのアプリケーションの移行が難しくなります。
  • ベンダーロックイン: ストレージベンダー固有のAPIやツールに依存することで、将来的な選択肢が狭まります。
  • 管理の複雑性: 多数のコンテナがそれぞれ異なるストレージ要件を持つ場合、手動での管理は非常に煩雑になります。

これらの課題を解決し、Kubernetesエコシステム内でストレージをシームレスに利用するための標準化されたインターフェースが求められていました。そこで登場したのが、CSI(Container Storage Interface)です。

CSI登場以前:Kubernetesのストレージ連携の歴史と課題

CSIが誕生する以前、Kubernetesはどのようにストレージと連携していたのでしょうか?その歴史を振り返ることで、CSIの必要性がより明確になります。

1. インツリー(In-tree)ボリュームプラグイン

Kubernetesの初期バージョンでは、主要なストレージプロバイダー(AWS EBS, GCE Persistent Disk, Azure Disk, Ceph RBDなど)への連携機能が、Kubernetes本体のコードベースに直接組み込まれていました。これを「インツリー(In-tree)ボリュームプラグイン」と呼びます。

メリット:
* Kubernetesのインストールと同時に利用可能
* 主要なクラウドプロバイダーのストレージと簡単に連携

デメリット:
* Kubernetes本体への密結合: 新しいストレージシステムをサポートしたり、既存のプラグインに機能を追加したりするたびに、Kubernetes本体のコードを修正し、新しいバージョンをリリースする必要がありました。これはKubernetesの開発サイクルを遅延させる要因となりました。
* ベンダーロックイン: 特定のストレージベンダーの機能がKubernetesのリリースサイクルに依存するため、最新の機能を利用するためにはKubernetesのアップデートが必要でした。また、ベンダー固有の実装がKubernetesのコアロジックに散在するため、コードベースの保守性が低下しました。
* 拡張性の欠如: マイナーなストレージシステムや、Kubernetesが公式にサポートしていない新しいストレージ技術を導入することが困難でした。
* セキュリティリスク: ストレージ管理に必要な権限がKubernetesのコアコンポーネントに集中し、潜在的なセキュリティリスクを高める可能性がありました。

2. FlexVolume

インツリープラグインの課題を解決するための一歩として、「FlexVolume」が登場しました。FlexVolumeは、Kubernetesのノード上で実行される外部スクリプトを呼び出すことで、ストレージの連携を可能にするメカニズムです。これにより、Kubernetes本体のコードを変更することなく、新しいストレージタイプをサポートできる柔軟性が生まれました。

メリット:
* インツリープラグインと比較して、拡張性が向上
* Kubernetes本体の再ビルドなしに、新しいストレージの追加が可能

デメリット:
* 複雑性とセキュリティ: FlexVolumeはスクリプトベースであり、各ノードに手動でスクリプトを配置する必要がありました。これはデプロイと管理を複雑にし、スクリプトの実行権限を適切に管理しないとセキュリティ上の問題を引き起こす可能性がありました。
* 限定的な機能: ボリュームのアタッチ/デタッチ、マウント/アンマウントといった基本的な操作は可能でしたが、ボリュームのスナップショット、リサイズ、クローンといった高度なストレージ機能はサポートされていませんでした。
* 標準化の欠如: 特定のストレージベンダーが独自のFlexVolume実装を提供するため、異なるベンダー間でAPIや操作方法に一貫性がなく、標準化されたインターフェースとは言えませんでした。

これらの課題を踏まえ、Kubernetesコミュニティは、ベンダー中立で、拡張性が高く、よりリッチなストレージ機能を提供できる、全く新しいストレージインターフェースの必要性を強く認識しました。その結果、CSIが開発され、Kubernetes v1.9でアルファ版として導入され、v1.13でGA(一般公開)となりました。

CSI(Container Storage Interface)とは?

CSIは、コンテナオーケストレーター(CO)とストレージプロバイダー(SP)が連携するための標準インターフェースです。このインターフェースを介して、Kubernetesなどのコンテナオーケストレーターは、様々なストレージシステムに対して、ボリュームのプロビジョニング、アタッチ、マウントといった操作を統一された方法で実行できるようになります。

CSIの定義と目的

CSIは、コンテナオーケストレーションシステムが、ブロックストレージやファイルストレージといったエンタープライズレベルのストレージシステムと連携するために、一連のgRPCベースのAPIを定義しています。このAPI仕様に準拠していれば、どのようなストレージシステムでもKubernetesと連携できるようになります。

CSIの主要な目的は以下の通りです。

  1. 標準化とベンダー中立性: ストレージベンダーは、CSI仕様に準拠した単一の「CSIドライバー」を開発するだけで、Kubernetesを含む複数のコンテナオーケストレーター(例: Docker Swarm, Mesos)と連携できるようになります。これにより、CO側はストレージ固有のロジックを持つ必要がなくなり、ストレージベンダー側は自社製品を幅広いCOに提供しやすくなります。
  2. 拡張性と柔軟性: Kubernetesのコアコードからストレージロジックが分離されるため、新しいストレージ技術や機能を迅速に導入できます。ストレージベンダーは、Kubernetesのリリースサイクルに縛られることなく、独自のCSIドライバーを開発・リリースできます。
  3. 開発の迅速化: Kubernetes開発チームは、ストレージ関連の複雑なコードを管理する必要がなくなり、コア機能の開発に集中できます。ストレージベンダーは、Kubernetesの内部実装の詳細を知らなくても、CSI仕様に沿ってドライバーを開発できます。
  4. セキュリティの向上: ストレージドライバーが独立したコンポーネントとして動作することで、必要な権限を最小限に抑え、セキュリティリスクを低減できます。

CSIが解決する問題

CSIは、前述のインツリープラグインやFlexVolumeが抱えていた問題を根本的に解決します。

  • ベンダーロックインからの解放: 特定のクラウドプロバイダーやオンプレミスストレージベンダーに依存することなく、Kubernetesクラスタで任意のCSI準拠ストレージを利用できます。これにより、マルチクラウド戦略やハイブリッドクラウド戦略が容易になります。
  • 動的プロビジョニングの標準化: StorageClassとPersistentVolumeClaim(PVC)というKubernetesの抽象化されたストレージリソースと連携し、アプリケーションの要求に応じてストレージを自動的に動的プロビジョニングする仕組みを標準化します。
  • 高度なストレージ機能のサポート: ボリュームのスナップショット、クローン、リサイズといった、ステートフルアプリケーションの運用に不可欠な高度なストレージ操作をKubernetesのAPIを通じて実行できるようになります。
  • Kubernetesの安定性向上: ストレージ関連のバグや問題がKubernetesコアに影響を与えるリスクが低減され、Kubernetes自体の安定性が向上します。

CSIは、Kubernetesにおけるストレージ管理を抽象化し、デカップリングすることで、Kubernetesエコシステムの成熟度を飛躍的に高めました。今や、Kubernetes上でステートフルアプリケーションを運用する際には、CSIドライバーが不可欠な存在となっています。

CSIのアーキテクチャと主要コンポーネント

CSIは、Kubernetesと連携する際に複数のコンポーネントが協調して動作する複雑なシステムです。その中心にあるのは、各ストレージベンダーが提供するCSIドライバーです。CSIドライバーは、大きく分けてController ServiceNode Serviceの2つの主要なサービスを提供します。

さらに、これらのサービスをKubernetes上で効果的に運用するために、Kubernetesコミュニティは標準的なサイドカーコンテナを提供しています。

KubernetesとCSIドライバーの関係

Kubernetesクラスタは、CSIドライバーと直接通信するのではなく、CSIドライバーが提供するUNIXドメインソケットまたはTCPソケットを通じてgRPC呼び出しを行います。この抽象化されたインターフェースにより、Kubernetesはストレージの種類を意識することなく、標準的なAPIを通じてストレージ操作を指示できます。

CSIドライバーの構成要素

CSIドライバーは、通常、Kubernetesクラスタ内でDaemonSetやDeploymentとしてデプロイされます。

  1. CSI Controller Service (Controller Server)
    このサービスは、Kubernetesクラスタのコントロールプレーン上で動作します。主に、クラスタレベルのストレージ管理操作を担当します。通常、Deploymentとしてデプロイされ、単一のインスタンスまたは高可用性のために複数のインスタンスが動作します。

    • Provisioner: PersistentVolumeClaim (PVC) の要求に基づいて、ストレージシステムの新しいボリュームをプロビジョニング(作成)します。また、PVが削除された際に、対応するストレージボリュームを削除します。
    • Attacher: Kubernetesノードに対して、プロビジョニングされたボリュームをアタッチ(接続)する役割を担います。これは、ストレージシステムの特性に応じて、SCSI接続やNFSエクスポートなどを構成する操作です。
    • Snapshotter: ボリュームのスナップショットを作成したり、既存のスナップショットからボリュームをリストアしたりする機能を提供します。
    • Resizer: 既存のボリュームの容量を変更(リサイズ)する機能を提供します。
  2. CSI Node Service (Node Server)
    このサービスは、Kubernetesクラスタ内の各ワーカーノード上で動作します。主に、ノードローカルなストレージ操作を担当します。通常、DaemonSetとしてデプロイされ、各ノードに1つのインスタンスが動作します。

    • NodeStageVolume: ボリュームをノードに「ステージング」します。これは、アタッチされたボリュームをノード上の共有パス(例: /var/lib/kubelet/plugins/kubernetes.io/csi/pv/pv_name/globalmounts)にマウントする操作です。複数のPodが同じボリュームを共有する場合に効率的です。ファイルシステムがフォーマットされていない場合は、この段階でフォーマットされることもあります。
    • NodePublishVolume: ステージングされたボリュームを、Podが利用する特定のコンテナパス(例: /var/lib/kubelet/pods/pod_uid/volumes/kubernetes.io~csi/pv_name/mount)に「パブリッシュ(マウント)」します。これはPodのライフサイクルに密接に関連します。
    • NodeUnstageVolume: NodeStageVolumeの逆操作で、ノードからボリュームの共有マウントを解除します。
    • NodeUnpublishVolume: NodePublishVolumeの逆操作で、Podからボリュームのマウントを解除します。Podが停止または削除された際に実行されます。
  3. サイドカーコンテナ
    CSIドライバーは、上記のController ServiceとNode Serviceという、ストレージベンダーが実装する中核ロジックに加えて、Kubernetesが提供する汎用的なヘルパーコンテナ(サイドカーコンテナ)と組み合わせてデプロイされます。これらのサイドカーコンテナは、CSI仕様に準拠したドライバーとKubernetes APIサーバーの間で、様々なオーケストレーションタスクを処理します。

    主なサイドカーコンテナは以下の通りです。

    • external-provisioner: Kubernetes APIサーバーが作成したPersistentVolumeClaimイベントを監視し、CSIドライバーのControllerPublishVolume RPCを呼び出して新しいストレージボリュームをプロビジョニングします。プロビジョニングが完了すると、対応するPersistentVolumeオブジェクトを作成します。
    • external-attacher: PersistentVolumeがPodにバインドされ、ノードにアタッチされる必要があることを監視し、CSIドライバーのControllerPublishVolume RPCを呼び出してボリュームをノードにアタッチします。
    • external-resizer: PersistentVolumeClaimのリサイズ要求を監視し、CSIドライバーのControllerExpandVolume RPCを呼び出してストレージボリュームの容量を拡張します。
    • external-snapshotter: VolumeSnapshotおよびVolumeSnapshotClassオブジェクトを監視し、CSIドライバーのCreateSnapshot RPCを呼び出してボリュームのスナップショットを作成・管理します。
    • node-driver-registrar: CSIドライバーが提供するソケットを監視し、ドライバーの情報をKubernetesのNodeオブジェクトに登録します。これにより、KubeletはどのCSIドライバーが利用可能かを知ることができます。各ノードでDaemonSetの一部として実行されます。
    • livenessprobe: CSIドライバーのコンポーネントが正常に動作しているか(ヘルスチェック)を監視します。

これらのコンポーネントが連携することで、Kubernetesはストレージシステムの種類を意識することなく、抽象化されたAPIを通じて永続ボリュームを管理できるようになります。

gRPCインターフェースの役割

CSI仕様は、すべての通信をgRPCというRPC(Remote Procedure Call)フレームワークを使用して定義しています。gRPCは、Googleが開発したオープンソースの高性能RPCフレームワークで、Protocol Buffersをインターフェース記述言語として使用します。

gRPCのメリット:
* 効率性: HTTP/2上で動作し、Protocol Buffersを使用するため、JSON/RESTベースのAPIよりも効率的で高速です。
* 言語中立性: 複数のプログラミング言語でクライアントとサーバーを生成できるため、異なる言語でCSIドライバーを開発できます。
* ストリーミング: 一方向および双方向のストリーミングをサポートし、より複雑な通信パターンに対応できます。

CSIドライバーは、これらのgRPCサービスを実装し、KubernetesのサイドカーコンテナがgRPCクライアントとしてこれらのサービスを呼び出すことで、ストレージ操作が実行されます。

CSIによるストレージ操作のライフサイクル

CSIが実際にどのように動作し、Kubernetesのストレージリソースがどのように管理されるのかを、具体的なライフサイクルを通じて見ていきましょう。

1. 永続ボリュームの動的プロビジョニング (Dynamic Provisioning)

ステートフルアプリケーションの運用において、最も重要な機能の一つが「動的プロビジョニング」です。これは、アプリケーションが必要とするタイミングで、自動的にストレージボリュームが作成される仕組みです。

  1. StorageClassの作成:
    まず、クラスタ管理者はStorageClassオブジェクトを定義します。これは、ストレージの種類、性能特性、プロビジョニング方法などを抽象化するテンプレートです。
    yaml
    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
    name: my-csi-storage
    provisioner: csi.my-storage.example.com # 使用するCSIドライバーの識別子
    parameters:
    type: ssd # ストレージプロバイダー固有のパラメータ
    replication: "3"
    reclaimPolicy: Delete # PVが解放されたときの動作 (DeleteまたはRetain)
    volumeBindingMode: Immediate # ボリュームバインディングモード (ImmediateまたはWaitForFirstConsumer)

    provisionerフィールドには、CSIドライバーの一意な識別子(例: disk.csi.azure.comebs.csi.aws.com)を指定します。parametersには、ストレージプロバイダーがサポートする任意のオプション(SSD/HDD、レプリケーション数、IOPSなど)を指定できます。

  2. PersistentVolumeClaim (PVC) の作成:
    アプリケーション開発者は、必要なストレージの要件(容量、アクセスモード)を記述したPersistentVolumeClaim (PVC) オブジェクトを作成します。
    yaml
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: my-app-data
    spec:
    accessModes:
    - ReadWriteOnce # ボリュームのアクセスモード
    resources:
    requests:
    storage: 10Gi # 必要なストレージ容量
    storageClassName: my-csi-storage # 使用するStorageClassの名前

    PVCは、特定のストレージシステムを直接指定するのではなく、抽象化された要求を表現します。

  3. CSI Controller ServiceによるPVの作成とストレージプロビジョニング:

    • Kubernetesのexternal-provisionerサイドカーは、Kubernetes APIサーバーを監視し、新しいPVCが作成されたことを検知します。
    • external-provisionerは、PVCのstorageClassNameに対応するCSIドライバー(csi.my-storage.example.com)のController Serviceに対して、CreateVolumegRPC呼び出しを行います。この呼び出しには、PVCで指定された容量やStorageClassのパラメータが含まれます。
    • CSIドライバーのProvisionerコンポーネントは、この要求を受け取り、バックエンドのストレージシステム(例: AWS EBS, Azure Disk, Ceph)に対して、実際にボリュームを作成するAPIコールを実行します。
    • ストレージボリュームが正常に作成されると、ProvisionerはCreateVolumeの応答として、作成されたボリュームのIDなどの情報(volume_id, volume_context)をexternal-provisionerに返します。
    • external-provisionerは、この情報をもとに、Kubernetesクラスタ内に対応するPersistentVolume (PV) オブジェクトを作成します。このPVは、作成された物理ボリュームの詳細情報(ID、容量、CSIドライバー情報など)を含みます。
    • Kubernetesは、新しく作成されたPVと既存のPVCを、要求された容量とアクセスモードに基づいて「バインド」します。これにより、PVCは特定のPVに紐付けられ、Podが利用できるようになります。
  4. PodからのPVC参照:
    アプリケーションのPod定義では、作成されたPVCを参照してボリュームを利用します。
    yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: my-app
    spec:
    template:
    spec:
    containers:
    - name: my-container
    image: busybox
    command: ["sh", "-c", "echo Hello from Pod > /data/hello.txt && tail -f /dev/null"]
    volumeMounts:
    - name: my-volume
    mountPath: /data
    volumes:
    - name: my-volume
    persistentVolumeClaim:
    claimName: my-app-data # 作成したPVCの名前

    Podがスケジュールされると、Kubernetesは対応するPVとそれを管理するCSIドライバーを特定します。

2. ボリュームのアタッチとマウント

Podが特定のノードにスケジュールされると、そのノード上でボリュームが利用可能になるための操作が行われます。

  1. external-attacherによるボリュームのアタッチ:

    • Kubernetesのexternal-attacherサイドカーは、PVがPodにバインドされ、特定のノードにアタッチされる必要があることを検知します。
    • external-attacherは、CSIドライバーのController Serviceに対して、ControllerPublishVolumegRPC呼び出しを行います。この呼び出しには、ボリュームのIDとターゲットノードのIDが含まれます。
    • CSIドライバーのAttacherコンポーネントは、この要求を受け取り、バックエンドのストレージシステムに対して、指定されたボリュームをターゲットノードにアタッチするAPIコールを実行します。この操作は、クラウド環境ではディスクをVMに接続する操作に相当し、オンプレミス環境ではiSCSI接続を確立したり、NFSエクスポートを許可したりする操作に相当します。
    • アタッチが完了すると、AttacherはControllerPublishVolumeの応答をexternal-attacherに返します。
  2. NodeStageVolumeとNodePublishVolumeによるステージングとマウント:

    • アタッチが完了した後、KubernetesのKubelet(ノードエージェント)は、そのノード上で動作するCSIドライバーのNode Serviceを呼び出します。
    • まず、KubeletはCSIドライバーのNode Serviceに対してNodeStageVolumegRPC呼び出しを行います。
      • CSIドライバーのNode Serviceは、アタッチされたストレージデバイスをノード上の共通パス(例: /var/lib/kubelet/plugins/kubernetes.io/csi/pv/pv_name/globalmounts)にマウントします。
      • この段階で、必要に応じてファイルシステムがフォーマットされます。
    • 次に、KubeletはCSIドライバーのNode Serviceに対してNodePublishVolumegRPC呼び出しを行います。
      • CSIドライバーのNode Serviceは、ステージングされたボリュームを、Podがコンテナ内で利用する最終的なマウントパス(例: /var/lib/kubelet/pods/pod_uid/volumes/kubernetes.io~csi/pv_name/mount)にマウントします。
      • このマウントは通常、シンボリックリンクやバインドマウントとして行われ、Podのコンテナにボリュームが公開されます。
      • アクセスモード(ReadWriteOnceなど)やサブパスなどのPod固有の設定がここで適用されます。

これにより、Podのコンテナ内からmountPathで指定されたパスを通じて、永続ボリュームにアクセスできるようになります。

3. ボリュームのデタッチとアンマウント

Podが停止または削除されると、そのボリュームはノードからデタッチされ、アンマウントされます。

  1. NodeUnpublishVolumeとNodeUnstageVolume:

    • Podが終了すると、KubeletはCSIドライバーのNode Serviceに対してNodeUnpublishVolumegRPC呼び出しを行います。
      • CSIドライバーのNode Serviceは、Podのマウントパスからボリュームをアンマウントします。
    • ノード上のそのボリュームを参照するすべてのPodが終了すると、KubeletはCSIドライバーのNode Serviceに対してNodeUnstageVolumegRPC呼び出しを行います。
      • CSIドライバーのNode Serviceは、ノードの共通ステージングパスからボリュームをアンマウントします。
  2. external-attacherによるボリュームのデタッチ:

    • ノード上のそのボリュームを参照するすべてのPVが解放されると、external-attacherサイドカーは、CSIドライバーのController Serviceに対してControllerUnpublishVolumegRPC呼び出しを行います。
    • CSIドライバーのAttacherコンポーネントは、バックエンドのストレージシステムから、指定されたボリュームをノードからデタッチするAPIコールを実行します。

4. ボリュームの削除

PVのreclaimPolicyDeleteに設定されている場合、PVCが削除されると、対応するPVとバックエンドのストレージボリュームも削除されます。

  • PVCが削除されると、external-provisionerはCSIドライバーのController Serviceに対してDeleteVolumegRPC呼び出しを行います。
  • CSIドライバーのProvisionerコンポーネントは、バックエンドのストレージシステムから、指定されたボリュームを削除するAPIコールを実行します。

5. スナップショットとリストア

CSIは、ボリュームのスナップショット機能もサポートします。

  1. VolumeSnapshotClass:
    スナップショットを作成するためのVolumeSnapshotClassを定義します。これはStorageClassと同様に、スナップショットの特性やプロバイダー固有のパラメータを指定します。
    yaml
    apiVersion: snapshot.storage.k8s.io/v1
    kind: VolumeSnapshotClass
    metadata:
    name: my-csi-snapshot-class
    driver: csi.my-storage.example.com
    deletionPolicy: Delete # スナップショットが削除されたときの動作

  2. VolumeSnapshot:
    既存のPVCからスナップショットを作成するためにVolumeSnapshotオブジェクトを作成します。
    yaml
    apiVersion: snapshot.storage.k8s.io/v1
    kind: VolumeSnapshot
    metadata:
    name: my-app-snapshot
    spec:
    volumeSnapshotClassName: my-csi-snapshot-class
    source:
    persistentVolumeClaimName: my-app-data # スナップショットを作成するPVC

    • external-snapshotterサイドカーは、VolumeSnapshotオブジェクトの作成を検知します。
    • external-snapshotterは、CSIドライバーのController Serviceに対してCreateSnapshotgRPC呼び出しを行います。
    • CSIドライバーのSnapshotterコンポーネントは、バックエンドのストレージシステムに対してスナップショット作成のAPIコールを実行し、結果を返します。
    • external-snapshotterは、VolumeSnapshotContentオブジェクトを作成し、VolumeSnapshotとバインドします。
  3. PVCからのリストア:
    作成したスナップショットから新しいPVCをリストアできます。
    yaml
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: my-app-restored-data
    spec:
    accessModes:
    - ReadWriteOnce
    resources:
    requests:
    storage: 10Gi
    storageClassName: my-csi-storage
    dataSource:
    name: my-app-snapshot # リストア元のVolumeSnapshotの名前
    kind: VolumeSnapshot
    apiGroup: snapshot.storage.k8s.io

    このPVCが作成されると、external-provisionerは、スナップショットからのボリューム作成をCSIドライバーのProvisionerに指示します(CreateVolume RPCにスナップショット情報を含める)。

6. ボリュームのリサイズ

CSIは、既存のボリュームのオンライン/オフラインリサイズもサポートします。

  • 既存のPVCのspec.resources.requests.storageを編集し、容量を増やします。
  • external-resizerサイドカーは、PVCの容量変更を検知します。
  • external-resizerは、CSIドライバーのController Serviceに対してControllerExpandVolumegRPC呼び出しを行います。
  • CSIドライバーのResizerコンポーネントは、バックエンドのストレージシステムに対してボリュームのリサイズAPIコールを実行します。
  • リサイズが完了すると、external-resizerはPVオブジェクトの容量を更新します。
  • その後、Kubeletがノード上でファイルシステムのリサイズ(NodeExpandVolume)を行います。

7. CSIボリュームの各種アクセスモード

CSIボリュームは、KubernetesのPersistentVolume(PV)で定義される以下のアクセスモードをサポートします。CSIドライバーがどのアクセスモードをサポートするかは、そのストレージシステムの特性に依存します。

  • ReadWriteOnce (RWO): 単一のノードから読み書き可能。最も一般的で、ブロックストレージ(EBS, Azure Disk, GCE PDなど)でサポートされます。
  • ReadOnlyMany (ROM): 複数のノードから読み込みのみ可能。ファイルストレージ(NFS, Azure Filesなど)やオブジェクトストレージの読み込みアクセスなどでサポートされます。
  • ReadWriteMany (RWM): 複数のノードから読み書き可能。NFSベースのファイルストレージや、分散ファイルシステム(CephFS, GlusterFSなど)でサポートされます。ステートフルなPodの水平スケーリングを可能にする重要なモードです。

CSIのメリットとデメリット

CSIはKubernetesにおけるストレージ管理を大きく進化させましたが、全てにおいて完璧というわけではありません。メリットとデメリットを理解することは、適切なCSIドライバーを選択し、運用する上で重要です。

メリット

  1. ベンダー独立性/ポータビリティ:
    CSIの最も大きなメリットは、特定のストレージベンダーやクラウドプロバイダーに依存しない標準インターフェースであることです。これにより、Kubernetesクラスタを異なる環境(オンプレミス、AWS、Azure、GCPなど)に移行する際に、ストレージ層の変更を最小限に抑えることができます。アプリケーションコードを変更することなく、必要に応じてストレージの種類を変更できる柔軟性が得られます。

  2. 拡張性/柔軟性:
    新しいストレージ技術やベンダーが、Kubernetesのコアコードを変更することなく、CSIドライバーを開発・提供できます。これにより、最新のストレージ機能がKubernetesエコシステムに迅速に取り込まれるようになります。また、特定のニーズに合わせたカスタムストレージドライバーを作成することも可能です。

  3. 開発の迅速化:
    Kubernetesのストレージ関連の開発サイクルとストレージベンダーの製品開発サイクルが分離されます。Kubernetes開発者はコアオーケストレーション機能に集中でき、ストレージベンダーは自社の製品ロードマップに合わせてドライバーを開発・リリースできます。

  4. セキュリティの向上:
    CSIドライバーは、通常のKubernetesワークロードと同様にPodとして実行されます。これにより、ストレージ操作に必要な特権(例: ホストのファイルシステムにアクセスする権限)を、ドライバーのPodにのみ付与し、他のKubernetesコンポーネメントから分離できます。これは、最小権限の原則に合致し、セキュリティリスクを低減します。

  5. 機能の豊富さ:
    CSIは、単なるボリュームのプロビジョニングとマウントだけでなく、ボリュームのスナップショット、クローン、リサイズ、動的なトポロジー認識(Topology-aware scheduling)など、ステートフルアプリケーションの運用に不可欠な高度なストレージ機能を提供します。

  6. Kubernetesコアの安定性向上:
    ストレージロジックがKubernetes本体から切り離されたことで、ストレージ関連のバグや脆弱性がKubernetesのコア機能に直接影響を与えるリスクが低減され、Kubernetes自体の安定性と信頼性が向上します。

デメリット

  1. 初期学習コスト:
    CSIのアーキテクチャは、複数のコンポーネント(CSIドライバー、サイドカー、StorageClass、PVC、PVなど)が連携して動作するため、初めてKubernetesのストレージを扱うユーザーにとっては学習曲線が急に感じられるかもしれません。各コンポーネメントの役割と連携方法を理解するまでに時間がかかります。

  2. CSIドライバーの品質依存:
    CSIドライバーは各ストレージベンダーが開発・提供するため、その品質、安定性、機能の豊富さはドライバーによって異なります。バグがあったり、特定の機能が実装されていなかったりするドライバーも存在します。導入前に、選択するCSIドライバーのコミュニティサポートや成熟度を確認することが重要です。

  3. トラブルシューティングの複雑化:
    複数のコンポーネントが連携しているため、ストレージ関連の問題が発生した場合、どのレイヤー(Kubernetes API、CSIサイドカー、CSIドライバーのController/Node Service、バックエンドストレージ)で問題が発生しているのかを特定するのが複雑になることがあります。ログの確認箇所が増え、デバッグに手間がかかる可能性があります。

  4. ストレージプロバイダー固有の制約:
    CSIは標準インターフェースですが、バックエンドのストレージシステムが提供する機能や性能に制約されます。例えば、RWMアクセスモードをサポートしないブロックストレージでは、そのCSIドライバーもRWMを提供できません。CSIはあくまで「インターフェース」であり、基盤となるストレージの物理的な特性を超えることはできません。

これらのデメリットは、CSI自体の欠陥というよりも、分散システムを運用する上での一般的な複雑性に起因するものが多いです。適切なドキュメント、コミュニティサポート、そして何よりも十分なテストを通じて、これらの課題は克服可能です。

主要なCSIドライバーの紹介

CSIは、その標準性のおかげで、多種多様なストレージベンダーやクラウドプロバイダーによって採用され、多くのCSIドライバーが提供されています。ここでは、その一部を紹介します。

1. クラウドプロバイダー系CSIドライバー

主要なクラウドプロバイダーは、自社のIaaSストレージサービスとKubernetesを連携させるためのCSIドライバーを提供しています。これらは通常、そのクラウド環境でKubernetesを実行する際に推奨されるストレージ連携方法です。

  • AWS EBS CSI Driver (Amazon Elastic Block Store):
    AWS EC2インスタンスにアタッチされるブロックストレージであるEBSをKubernetesで利用するためのCSIドライバー。RWOアクセスモードをサポートし、スナップショット、ボリュームリサイズなどが可能です。
  • GCE PD CSI Driver (Google Compute Engine Persistent Disk):
    Google CloudのCompute Engineで使用される永続ディスクをKubernetesで利用するためのCSIドライバー。EBSと同様にRWOブロックストレージを提供し、スナップショット、リサイズに対応します。
  • Azure Disk CSI Driver:
    Microsoft AzureのManaged Disks(ブロックストレージ)をKubernetesで利用するためのCSIドライバー。RWOアクセスモード、スナップショット、リサイズをサポートします。
  • Azure File CSI Driver:
    Microsoft AzureのAzure Files(NFS/SMBベースの共有ファイルストレージ)をKubernetesで利用するためのCSIドライバー。ReadWriteMany (RWM) アクセスモードをサポートし、複数のPodからの同時書き込みが可能です。

2. オープンソース系CSIドライバー

クラウドに依存しない、オープンソースの分散ストレージシステムや、ローカルストレージを管理するためのCSIドライバーも多数存在します。

  • Ceph RBD CSI Driver (Rook-Ceph):
    Cephは、オブジェクトストレージ、ブロックストレージ(RBD)、ファイルストレージ(CephFS)を提供する分散ストレージシステムです。RookはKubernetes上でCephをデプロイ・管理するためのOperatorで、その一部としてRBDとCephFSのCSIドライバーを提供します。RBDはRWOブロックストレージ、CephFSはRWMファイルストレージを提供します。
  • OpenEBS CSI Driver:
    OpenEBSは、Kubernetes上で動作するオープンソースのコンバージドストレージソリューションです。Podのローカルストレージ(HostPath)や、ノードに接続されたディスクをPVとして利用できるようにし、レプリケーション機能も提供します。ブロックストレージ、ファイルストレージのCSIドライバーを提供します。
  • Longhorn CSI Driver:
    Longhornは、Rancher Labsが開発した軽量で分散型のブロックストレージシステムです。各ノードのローカルディスクを使用してストレージプールを形成し、ボリュームのレプリケーション、スナップショット、バックアップ/リストアなどの機能を提供します。主にRWOブロックストレージとして利用されます。
  • HostPath CSI Driver:
    これは主に開発やテスト用途で使用されるCSIドライバーで、Kubernetesノードのローカルファイルシステム上のパスをボリュームとして提供します。プロダクション環境での永続データには向きません(ノードが落ちるとデータが失われるため)。

3. エンタープライズ系CSIドライバー

NetApp, Pure Storage, Dell EMCなどの既存のエンタープライズストレージベンダーも、自社製品をKubernetesと連携させるためのCSIドライバーを提供しています。これにより、既存の高性能なストレージインフラをKubernetesアプリケーションで活用できます。

  • NetApp ONTAP CSI Driver:
    NetAppのONTAPストレージシステム(NFS, iSCSI, FC)をKubernetesで利用するためのCSIドライバー。NFS/iSCSI/FCプロトコルをサポートし、ボリュームのプロビジョニング、スナップショット、クローン、リサイズ、クォータ管理など、ONTAPの豊富な機能をKubernetesから操作できます。
  • Pure Storage CSI Driver:
    Pure StorageのFlashArray(オールフラッシュブロックストレージ)やFlashBlade(高性能ファイル/オブジェクトストレージ)をKubernetesで利用するためのCSIドライバー。高いIOPSと低遅延を必要とするワークロードに適しています。

これらのCSIドライバーは、それぞれ異なるバックエンドストレージの特性を活かし、Kubernetesクラスタに様々なストレージ機能を提供します。ユーザーは、自身の要件(性能、可用性、コスト、既存のインフラなど)に合わせて最適なCSIドライバーを選択することが可能です。

CSIのデプロイと運用

CSIドライバーのデプロイと運用には、いくつかの基本的なステップと考慮事項があります。

CSIドライバーのインストール方法

ほとんどのCSIドライバーは、Kubernetesの標準的なデプロイツール(YAMLファイルやHelmチャート)を通じてインストールされます。

  1. YAMLファイルの適用:
    CSIドライバーのGitHubリポジトリや公式ドキュメントには、必要なDeployment、DaemonSet、ServiceAccount、ClusterRole、ClusterRoleBinding、ConfigMapなどのYAMLファイル群が提供されています。これらをkubectl apply -f <directory_or_file>コマンドで適用することで、ドライバーがクラスタにデプロイされます。
    例: kubectl apply -k https://github.com/kubernetes-csi/csi-driver-host-path/deploy/kubernetes/overlays/stable/?ref=release-1.5

  2. Helmチャートの利用:
    多くのCSIドライバーはHelmチャートとして提供されており、デプロイ、設定、アップグレード、アンインストールが容易に行えます。Helmは、Kubernetesアプリケーションのパッケージマネージャーであり、複雑なアプリケーションのデプロイを簡素化します。
    例: helm install my-aws-ebs-csi-driver stable/aws-ebs-csi-driver

インストール後、kubectl get pods -n <csi-driver-namespace>kubectl get storageclassで、CSIドライバーのPodとStorageClassが正常に作成されているかを確認します。

StorageClassの設定例

CSIドライバーがデプロイされると、多くの場合、デフォルトのStorageClassが自動的に作成されるか、ユーザーが手動で作成する必要があります。以下は、AWS EBS CSIドライバーを使用するStorageClassの例です。

yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gp2 # StorageClassの名前
provisioner: ebs.csi.aws.com # CSIドライバーの識別子
parameters:
type: gp2 # EBSのボリュームタイプ (汎用SSD)
fsType: ext4 # ファイルシステムタイプ
reclaimPolicy: Delete # PVC削除時にPVと物理ボリュームも削除
allowVolumeExpansion: true # ボリュームのオンライン拡張を許可
volumeBindingMode: Immediate # PVC作成時にPVを即時プロビジョニング

parametersセクションは、プロビジョナー固有の設定を含みます。fsTypeは、ボリュームがマウントされる際にフォーマットされるファイルシステムを指定します。allowVolumeExpansion: trueは、ボリュームのリサイズを許可するかどうかを制御します。

PVC/Podの設定例

前述のライフサイクルでも示しましたが、アプリケーションはPVCを通じてストレージを要求し、PodはPVCを参照して利用します。

“`yaml

PersistentVolumeClaim (PVC)

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-app-pvc
spec:
accessModes:
– ReadWriteOnce # シングルノードからの読み書き
resources:
requests:
storage: 5Gi # 5GBのストレージを要求
storageClassName: gp2 # 上で定義したStorageClassを参照


Deployment (PodがPVCを利用)

apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-data-app
spec:
replicas: 1
selector:
matchLabels:
app: nginx-data
template:
metadata:
labels:
app: nginx-data
spec:
containers:
– name: nginx
image: nginx
ports:
– containerPort: 80
volumeMounts:
– name: nginx-persistent-storage
mountPath: /usr/share/nginx/html # Nginxがデータを読み書きするパス
volumes:
– name: nginx-persistent-storage
persistentVolumeClaim:
claimName: my-app-pvc # 上で作成したPVCを参照
“`

トラブルシューティングのヒント

CSI関連の問題が発生した場合、以下の場所を確認することで、原因を特定しやすくなります。

  • イベントの確認:
    kubectl get eventsまたはkubectl describe pvc <pvc-name>, kubectl describe pod <pod-name>, kubectl describe pv <pv-name>を実行し、関連するイベントログを確認します。プロビジョニング、アタッチ、マウントの各フェーズで発生したエラーが記録されています。
  • CSIドライバーPodのログ:
    CSIドライバーの各コンポーネント(external-provisioner, external-attacher, CSI Controller Pod, CSI Node DaemonSet)のログを確認します。
    kubectl logs <pod-name> -n <csi-driver-namespace>
    特に、問題が発生している操作(プロビジョニング、アタッチなど)を担当するサイドカーと、実際のCSIドライバーのログを重点的に確認します。
  • ノードのログ:
    Kubeletのログ(journalctl -u kubelet)を確認し、NodeStageVolumeNodePublishVolume関連のエラーがないか確認します。
  • CSIドライバーのステータス:
    kubectl get pod -n <csi-driver-namespace>で、CSIドライバーのPodがRunning状態であること、再起動を繰り返していないことなどを確認します。
  • バックエンドストレージの監視:
    利用しているクラウドプロバイダーのコンソールや、オンプレミスのストレージ管理ツールで、実際にボリュームが作成されているか、アタッチされているか、エラーが発生していないかを確認します。

CSIとセキュリティ

CSIドライバーの運用においては、セキュリティも重要な考慮事項です。

  • RBAC (Role-Based Access Control):
    CSIドライバーのPodは、Kubernetes APIと連携するために適切なRBAC権限(ServiceAccount, ClusterRole, ClusterRoleBinding)を必要とします。通常、これらはCSIドライバーのYAMLに含まれていますが、デプロイ前にその権限が適切であるか確認することが重要です。
  • Pod Security Admission (PSA):
    Pod Security Admissionコントローラーは、Podのセキュリティコンテキストを強制し、特権コンテナやホストパスへのアクセスなどを制限します。CSIドライバーのPodは、多くの場合、ホストパスへのアクセスや特権的な機能が必要になるため、PSAポリシーと衝突しないように注意が必要です。CSIドライバーのNamespaceを特定のPSAプロファイル(例: privileged)に設定する必要がある場合があります。
  • シークレット管理:
    ストレージシステムの認証情報(APIキー、ユーザー名/パスワードなど)は、KubernetesのSecretとして安全に管理し、CSIドライバーのPodにマウントされるように設定する必要があります。

CSIの発展と今後の展望

CSIはKubernetesのストレージエコシステムにおいて中核的な存在となり、現在も活発な開発が続けられています。CSIのバージョンアップとともに、様々な新機能が追加され、Kubernetes上でのステートフルアプリケーションの運用がより堅牢かつ柔軟になっています。

新しい機能の追加

  • ボリューム拡張のオンライン化:
    初期のCSIでは、ボリュームをリサイズする際にPodの再起動が必要な場合がありましたが、多くのCSIドライバーで、Podが稼働したままでボリュームを拡張できる「オンライン拡張」がサポートされるようになりました。
  • Topology-aware scheduling:
    Kubernetesスケジューラーが、CSIドライバーが報告するストレージのトポロジー情報(例: 特定のゾーンやラックでのみ利用可能)を考慮してPodをスケジュールする機能です。これにより、データへの近接性を最適化し、パフォーマンスと可用性を向上させることができます。
  • Ephemeral Inline Volumes:
    Podのライフサイクルに完全に紐付けられた一時的なボリュームを、Podの定義内で直接指定できるCSI機能です。これは、特定のファイルシステムやボリューム特性を必要とする一時的なスクラッチスペースなどに利用されます。
  • CSI Volume Health Monitoring:
    CSIドライバーがバックエンドストレージの健全性情報をKubernetesに報告し、Kubernetesがそれを監視できるようにする機能です。これにより、ストレージ障害を早期に検知し、対応することが可能になります。
  • Volume Cloning:
    既存のPVから、データをコピーして新しいPVを作成する機能です。データベースのテスト環境を迅速に立ち上げたり、開発用のサンドボックス環境を提供したりするのに役立ちます。

CSIからKubernetesへのフィードバック

CSIは単なる「Kubernetesの機能拡張」に留まらず、CSIの開発を通じて得られた知見がKubernetesコアのストレージ抽象化をさらに改善する原動力となっています。CSIの成功は、Kubernetesが外部コンポーネントとのインターフェースを標準化することの重要性を示す好例であり、同様のアプローチが他の領域(ネットワーク、デバイスプラグインなど)でも採用されています。

マルチクラウド/ハイブリッドクラウド戦略におけるCSIの重要性

現代の企業は、特定のクラウドベンダーに縛られず、複数のクラウド環境やオンプレミス環境を組み合わせて利用するマルチクラウドやハイブリッドクラウド戦略を志向しています。CSIは、異なる環境で同じアプリケーション(特にステートフルなもの)を実行する際のストレージ層の障壁を取り除く上で不可欠な役割を果たします。CSI準拠のストレージドライバーを使用することで、アプリケーションは基盤となるインフラストラクチャの違いを意識することなく、どこでも同じようにストレージを利用できるようになります。

まとめ

Kubernetes CSI(Container Storage Interface)は、Kubernetesにおけるストレージ管理を革新し、ステートフルアプリケーションの運用を現実のものにした、極めて重要な技術です。CSIが登場する以前は、Kubernetesのストレージ連携はベンダーロックイン、拡張性の欠如、管理の複雑性といった課題に直面していました。

CSIは、コンテナオーケストレーターとストレージプロバイダーが連携するための標準的なgRPCベースのインターフェースを定義することで、これらの問題を解決しました。CSIドライバーは、Controller Service(プロビジョニング、アタッチ、スナップショットなど)とNode Service(ステージング、パブリッシュなど)から構成され、Kubernetesのサイドカーコンテナと協調して動作することで、永続ボリュームの動的なプロビジョニング、アタッチ、マウント、そして高度なストレージ機能(スナップショット、リサイズなど)をシームレスに提供します。

CSIの主なメリットは、ベンダー独立性、高い拡張性、開発の迅速化、セキュリティの向上、そしてKubernetesコアの安定性向上にあります。一方で、初期学習コストやCSIドライバーの品質依存といったデメリットも存在しますが、これらはCSIの持つ大きな恩恵と比較すれば十分に許容範囲内と言えるでしょう。

AWS EBS, Azure Disk/File, Google PDといったクラウドネイティブなストレージから、Ceph, OpenEBS, Longhornといったオープンソースの分散ストレージ、さらにはNetApp, Pure Storageなどのエンタープライズストレージに至るまで、多様なCSIドライバーが利用可能であり、ユーザーは自身の要件に最適な選択肢を選ぶことができます。

CSIは現在も進化を続けており、オンラインボリューム拡張、トポロジー認識スケジューリング、ボリュームヘルスモニタリングなど、より高度な機能が継続的に追加されています。これにより、Kubernetes上でのステートフルアプリケーションのデプロイと運用は、今後ますます容易で堅牢なものとなるでしょう。

Kubernetesとコンテナ技術がITインフラの未来を形作る中で、CSIは、単なる一時的な実行環境であったコンテナを、企業の中核を担うミッションクリティカルなステートフルアプリケーションの実行基盤へと昇華させるための、不可欠な橋渡し役を果たしているのです。Kubernetesを深く理解し、その真価を引き出すためには、CSIの基本と仕組みを徹底的に理解することが、今日のエンジニアにとって必須の知識となっています。

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール