この記事はなむゆの個人ブログにもマルチポストしています。
はじめに
Kubernetes の Pod のデータの永続化において、Kubernetes が動いているマシン上にデータを置く Volume のタイプには local と hostPath、emptyDir とありますが、それぞれ似たり寄ったりした概念で若干混乱してきたので、比較して纏めてみようという回です。
hostPath
hostPath は Pod からマウントできる Volume のタイプの一つで、その Pod をホストしている node のファイルシステムを Pod からマウントするものです。
これによって、node のファイルシステム上にあるファイルを利用できるようにしたり、そのファイルシステム上で追加または上書きしたファイルを Pod の削除後も維持できるようにしたりできます。
使い道としては、ホストしている node 内にある Docker 関連のファイルが欲しいときのために /var/lib/docker/
ディレクトリをマウントしたり、cAdviser(Google 開発の監視ツール)でメトリックを取るために/sys
などのディレクトリをマウントしたりといったものが挙げられています。
あまり書き込み関連のユースエースが見られませんが、それは後に開設するセキュリティ上の理由があります。
永続ボリュームのページでは「マルチノードクラスターでは動作しません」とありますが、マルチノードクラスター上でも hostPath を volume として設定した Pod を作成することはできます。
ただ、その Pod がスケジュールされる先の Node は Pod の nodeAffinity 等によって決められるため、意図しない Node に Pod が展開されて意図しないディレクトリにマウントしてしまう可能性があります。
回避方法としてはこのタイプの Volume を PersistentVolume として設定して、nodeAffinity を設定することが挙げられます。
そういった意味において「マルチノードでは(Pod の nodeAffinity で設定しない限りどの node のファイルシステムをマウントするか分からないので)動作しません」の意味の動作しないということなのかなと個人的には解釈しています。
ただ、hostPath を使うと Pod の securityContext の設定次第ではホストのファイルシステム上の高い権限がなければ弄れないディレクトリを弄ることもできてしまうため、なるべく使わない、最悪使う場合でもできる限り ReadOnly で利用することが推奨されています。
また、そのようなセキュリティ上の危険性や制約のため、hostPath を使うのは単一ノードでのテスト時のみに限り、できる限り後述の local Volume を使うことを推奨されています。
使い方としては、PersistetnVolume のリソースのマニフェストに書くこともできるのですが、Pod リソースのマニフェストのspec.volumes
下に直接下記のように書き込むことで Pod から直接マウントすることもできます。
hostPath:
path: /data
type: Directory
local
local という Volume のタイプも、基本の振る舞いとしては hostPath と同じで、その Pod をホストしている node のファイルシステムを Pod からマウントできるようにします。
hostPath との違いは、このタイプの Volume は PersistentVolume というリソースで静的にしか作ることができないという点です。
hostPath は前述のとおり Pod のマニフェストの volume の設定に直接書き込んで使用することができますが、local の場合はまず PersistentVolume のリソースを設定し、その際に指定した storageClass を PersistentVolumeClaim で指定して、その PersistentVolumeClaim を Pod で指定することで使用できるようになります。
PersistentVolume で静的に local の Volume を設定する際にはパラメータとして nodeAffinity の設定が必須になっており、これによってこの PersistentVolume を使用して作成する Pod がスケジュールされる先の node を指定する必要があります。
これによって、この Volume で使用する node を固定でき(ただし nodeAffinity の条件に当てはまる node は 1 台だけになるようにする必要があります)、Pod によって予期しない異なる node の local のディレクトリを参照する可能性をなくすことができます。
基本的に Pod からその Pod がスケジュールされている node 上にあるディレクトリを参照する必要がある場合は local の Volume タイプを使用することが推奨されているようです。
apiVersion: v1
kind: PersistentVolume
metadata:
name: example-pv
spec:
capacity:
storage: 100Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Delete
storageClassName: local-storage
local:
path: /sample/html
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: node-role.kubernetes.io/control-plane
operator: Exists
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 8Gi
storageClassName: example-pv
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: myfrontend
image: nginx
volumeMounts:
- mountPath: "/var/www/html"
name: mypd
volumes:
- name: mypd
persistentVolumeClaim:
claimName: myclaim
emptyDir
emptyDir は、Pod の生成と同時に Node 上に作られる、中身が空っぽの Volume です。
他の二つの Volume のタイプとは違い、Pod が削除されると同時に削除されます。
なので、コンテナ内で生成されたデータを永続化する目的で使うことはできません。
これだけだと、特に何も Volume を設定しないときのコンテナ内の記憶領域と何ら変わらないように見えます。
一方で、Pod ではなくその内部のコンテナが再起動した場合でも Volume の中身は削除されないという特徴があり、それによって起動中保持していてほしいファイルをコンテナ再起動で失わないようにすることができます。
emptyDir に設定していない場合のデータは全部コンテナ内で管理されているので、コンテナが再起動するとそのタイミングで削除されてしまいます。
ユースケースとして挙げられている例としては、ディスクに一時的にデータを保存しながら行うソート処理のデータ保存場所や長い時間かかる処理のチェックポイントファイル置き場、また、Web ページにおいてコンテンツマネージャーとして動いているコンテナが Web サーバーから引っ張っていたデータの一時保存先が挙げられています。
また、パラメータの中にmedium
というものがあり、これをMemory
に設定していると、メモリ上にこの Volume のファイルシステムを作って動作してくれるので、ファイルシステムとしての動作が高速になります。
ただし、こちらは node が再起動するとメモリが揮発してディレクトリが空になってしまうので、その点には注意が必要です。
このように、emptyDir はデータの永続化ではなく一時的なファイルの作成先、特にコンテナのリセットでもそれらのデータを失わないために使うもののようです。
emptyDir:
medium: ""
sizeLimit: 1Gi
使い分け
これまでの説明をまとめると使い分けとしてはこのようになるかと思います。
- Node マシン上のファイル取得をしたいのであれば local
- コンテナ再起動でも消えてほしくない一時ファイルの保存先に emptyDir
- hostPath は基本的には使わない、開発用途に限定