PodにPersistentVolmueをマウントしたいとき、特にオンプレのKubernetesで、都合のいいNFSサーバーがなかったりすると、ノード上のローカルのファイルシステムをマウントしたいことがあります。その場合hostPathが使えますが、localというのもあります。違いがよくわからなかったので調べたメモです。
VolumeとPersistentVolume
わかりにくいですが、VolumeとPersistentVolumeは別です。VolumeとしてのhostPathと、PersistentVolumeとしてのhostPathがあります。
localについては、その説明がVolumeページにあるので混乱しますが、VolumeではなくPersistentVolumeです。以下のAPIドキュメントを確認すると、localというPesistentVolumeはありますが、localというVolumeはないことが確認できます。
hostPath(Volume)
hostPathのVolumeをPodのSpecに直接定義してコンテナにマウントできます。
kind: Pod
apiVersion: v1
metadata:
name: mypod
spec:
containers:
- name: liberty
image: websphere-liberty:18.0.0.4-kernel
volumeMounts:
- mountPath: /logs
name: my-volume
volumes:
- name: my-volume
hostPath:
path: /data
単純なPodではなくDeployment等から管理されるPodの場合、PodのSpecは基本的には同じになるので、上記のようなPodTemplateを書いたとすると、各Podはスケジュールされたノードの/dataをマウントします。PodはnodeSelector等で指定していなければどのノードで稼働するかわかりません。また、1つのノードで複数のPodが稼働すると、同じhostPathをマウントしてしまいます。このためhostPathのVolumeをマウントしても問題がない状況はかなり限定されます。
hostPathのVolumeと相性がよいのは、各ノードで1つのみPodが稼働することが保証されるDaemonSetの場合です。IBM Cloud Privateの実装をみても、DaemonSetとして稼働しているコンテナからhostPathのVolumeをマウントしているケースが多くあります。
(補足)
以下のPersistentVolumeの場合も同様ですが、ノード上の/dataディレクトリーには適切な権限が必要です。上記のLibertyコンテナの場合は1001のUIDで起動するのでchown 1001:0 /dataしてあげる必要があります。
hostPath(PersistentVolume)
PersistentVolumeとしてhostPathはAPIドキュメントに以下のように記載されており、シングルノード環境でのテスト用としてのみ使うように記載されています。VolumeとしてのhostPathのAPIドキュメントにはこのような記載はありません。
This is useful for single-node development and testing only! On-host storage is not supported in any way and WILL NOT WORK in a multi-node cluster.
以下のようにhostPathのPersistentVolumeを定義できます。
kind: PersistentVolume
apiVersion: v1
metadata:
name: my-pv-hostpath
spec:
storageClassName: manual
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
hostPath:
path: /data
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
コンテナからはPersistentVolumeClaimをマウントします。
kind: Pod
apiVersion: v1
metadata:
name: mypod
spec:
containers:
- name: liberty
image: websphere-liberty:18.0.0.4-kernel
volumeMounts:
- mountPath: /logs
name: my-volume
volumes:
- name: my-volume
persistentVolumeClaim:
claimName: my-pvc
単純なPodではなくDeployment/DaemonSetから管理されるPodの場合、全てのPodのSpecは基本的には同じになるので、上記のようなPodTemplateを書いたとすると、各Podが同じPersistentVolumeClaimをマウントしようとしてしまいます。しかしhostPathはReadOnlyManyやReadWriteManyをサポートしていないため問題がありそうです(同じノード上で稼働する複数のPodからマウントするのもOKかもしれません)。このためDeploymentの場合はレプリカ数1でしか使えません。StatufulSetの場合はPersistentVolumeClaimTemplateを使ってPod毎に異なるPersistentVolumeClaimを定義できるので、各Podに異なるPersistentVolumeをアサインできます。
local
localはVolumeではなくPersistentVolumeです。hostPathとの違いはなんでしょうか。ドキュメントによると、hostPathと比べると、Node Affinityを使ってボリュームが実際に存在するノードを指定できるので、Podを手動でスケジュールする必要がないことが書かれています。
Compared to hostPath volumes, local volumes can be used in a durable and portable manner without manually scheduling Pods to nodes, as the system is aware of the volume’s node constraints by looking at the node affinity on the PersistentVolume.
試してみたところ、PersistentVolumeであればlocalだけでなくhostPathでもnfsでもNode Affinityは設定でき機能したので、上記の"Compared to hostPath volumes"という部分は、PersistentVolumeのhostPathではなくVolumeのhostPathを指しているように思われます。
localの場合はNode Affintyの設定が必要です。設定していないと下記のように怒られます。
$ kubectl apply -f pv-local-test.yaml
The PersistentVolume "my-pv-local-test" is invalid: metadata.annotations: Required value: Local volume requires node affinity
$
ただし、Node Affinityで例えば以下のように全てのノードにマッチするような表現を書くことも可能です。
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: Exists
機能的にはNode Affinityの設定が必須であるくらいしか差異はなさそうですが、PersistentVolumeのhostPathはマルチノードがサポートされていないことが明確に書かれています。localのほうはv1.10からのBetaフィーチャーですが、以下のドキュメントに書かれているように、hostPathが持つ課題を解決するための新しい機能であり、精力的に開発がされているようなので、基本的にはこちらを使うべきと思います。
IBM Cloud Privateでの実装をみてみると、ローカルにデータを持つためにStatefulSetとして稼働しているコンテナからPersistentVolumeマウントする場合は、localのPersistentVolumeをマウントしています。
localのPersistentVolumeは以下のように使います。
kind: PersistentVolume
apiVersion: v1
metadata:
name: my-pv-local
spec:
storageClassName: manual
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
local:
path: /data
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- node1
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
kind: Pod
apiVersion: v1
metadata:
name: mypod
spec:
containers:
- name: liberty
image: websphere-liberty:18.0.0.4-kernel
volumeMounts:
- mountPath: /logs
name: my-volume
volumes:
- name: my-volume
persistentVolumeClaim:
claimName: my-pvc
単純なPodではない場合、PersistentVolumeのhostPathの場合と同じく、レプリカ数1のDeploymentか、StatufulSetから使用する必要があります。
まとめ
PersistentVolumeとしてのhostPathとlocalには明確な機能的な違いはなさそうですが、localはBetaフィーチャー(追記:1.14でGAとなりました)である一方、hostPathはマルチノード環境ではサポートされていないことが書かれています。利用方法についてまとめると以下です。
| リソースタイプ | ボリュームタイプ | VolumeNodeAffinity | 主な利用方法 |
|---|---|---|---|
| Volume | hostPath | N/A | DaemonSetを構成するPodから使う |
| PersistentVolume | hostPath | 設定可能 | シングルノード環境でテスト目的でのみ使う |
| PersistentVolume | local | 設定必須 | StatefulSetを構成するPodから使う |