概要
KubernetesでPersistentVolume(永続ストレージ)のDynamic Provisionerを使って、外部ストレージをコンテナにmountする。
Dynamic Provisionerを使わない方法については以下を参照。
このページを読む前に、以下の内容を理解しておいたほうがスムーズかと思われる。
【KubernetesのPersistentVolume】その1- 外部ストレージをPodにmountする
目次
前提知識
Dynamic Provisionerとは
Dynamic Provisionerを使うことで、PersistentVolumeを動的に生成することが可能となる。
そのため、予めPersistentVolumeを作成(定義)しておく必要がない。
具体的な設定方法の話をすると、PersistentVolumeを定義せずに、PersistentVolumeClaimを定義するだけで、Volumeを生成しPodにmountすることができる。
- PersistentVolumeClaimを定義
- Podを定義し、PersistentVolumeClaimと関連付ける
- Podを起動 -> Volumeが自動生成されmountされる
Provisionerについて
Dynamic Provisionerでmountするには、Provisionerというものを利用する。
このProvisionerはいくつかの種類があり、ストレージの種類ごと(AWS, GCPなど)にそれぞれ使い分ける。
また、デフォルトでいくつかのProvisionerが用意されている。
利用可能なPorvisionerは以下を参照。
https://kubernetes.io/docs/concepts/storage/storage-classes/#provisioner
ただし、今回利用するNFSサーバーには、デフォルトのporvisionerが用意されていない。
そこで、external provisionersというものを利用する。
external provisionersとは
external provisionersは、デフォルトで用意されていないProvisionerをカバーするもので、NFS用のものも用意されている。今回はここで用意されているnfs-clientを利用する。
external provisionersの仕組み
external provisionersの仕組みを簡単に説明すると、NFSの場合は、nfs-clientの下のnfs-client/cmd/nfs-client-provisioner/provisioner.goによって作られるDockerコンテナ(nfs-client/docker/x86_64/Dockerfile)が、NFSのmountなどをやってくれるprovisionerとして動作してくれるようである。
mountされる仕組み
Provisionerを使ったmountの仕組みを理解するのに少し時間がかかったが、以下の用であると理解した。
- nfs-provisioner用のPodを作成
- サービス用のPodを作成
- サービス用Podがnfs-provisioner用のPodを介してNFSサーバーをmountする
ハンズオン
それでは実際に環境構築をする。
環境
- CentOS 7.6
- Minikube v1.1.1
- Kubernetes v1.14.3
ここではMinikubeで構築した環境のPodに、NFSサーバーをmountする。
関連記事:Minikubeを使ってローカルにkubernetes環境を構築
NFSサーバーの構築
まずはmountするためのNFSサーバーを構築する。
手順については以下を参照。
nfs-utilsのインストール
クラスタのホストOS(ここではminikube)に以下をインストール。
$ sudo yum install -y rpcbind nfs-utils
nfs-provisionerの作成
external provisionersを利用して、nfs-provisionerを作成する。
StorageClass
ストレージの種類を定義する。
provisioner
という箇所があるが、デフォルトで用意されているProvisionerを利用する場合は、こちらからmountするストレージに合ったProvisionerを指定する。
- e.g.
- AWSの場合:
provisioner: kubernetes.io/aws-ebs
- Azureの場合:
provisioner: kubernetes.io/azure-file
- AWSの場合:
ただし、今回の用にexternal provisionersを利用する場合は、何でも良いので適当に名前を付ける。
そして、後述のDeploymentの定義のspec.template.spec.containers.env.nameのvalueと同じ名前にして、関連付けられるようにする。
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
# persitent-volumne-claim.ymlのspec.storageClassNameと合わせる
name: nfs
# deployment.ymlのspec.template.spec.containers.env.nameのvalueと合わせる
provisioner: lab.hoge.jp/nfs
# Volumeが削除されたときの挙動を定義
# Delete or Retain。デフォルトはDelete。
reclaimPolicy: Retain
PersistentVolumeClaim
PersistentVolumeClaimで、Podが要求するVolumeのspecを定義する。
Dynamic Provisionerを利用する場合は、Volumeは動的に生成されるため、PersistentVolumeを定義する必要はない。
参考:VolumeとPersistentVolumeとPersistentVolumeClaimの違い
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
# PVC(PersistentVolumeClaim)のための任意の名前を指定
name: sample-pvc
annotations:
# storage-class.ymlのmetadata.nameと同じ値
volume.kubernetes.io/storage-class: "nfs"
spec:
# アクセス権を設定
accessModes:
- ReadWriteMany
# storage-class.ymlのmetadata.nameと合わせる
# storageClassNameを省略するとHostPathでホストOS上にmountされる
storageClassName: nfs
resources:
requests:
# 割り当てるstorageの容量
storage: 1Gi
RBAC
RBAC(Role-based access control)とは、役割ベースのアクセス制御機能。
NFSのprovisionerに与える権限を定義する。
参考:KubernetesのRBACについて
kind: ServiceAccount
apiVersion: v1
metadata:
# pod.ymlのspec.serviceAccountNameと合わせる
name: nfs-client-provisioner
---
# 許可する操作を定義
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-client-provisioner-runner
rules:
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["list", "watch", "create", "update", "patch"]
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
# RoleもしくはClusterRoleを関連付けるUserAccountもしくはServiceAcctountを定義
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: default
roleRef:
kind: ClusterRole
name: nfs-client-provisioner-runner
apiGroup: rbac.authorization.k8s.io
Deployment
nfs-porovisionerのDeploymentの定義。
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
name: nfs-client-provisioner
spec:
replicas: 1
# https://qiita.com/MahoTakara/items/a776286e5c4385662df4#%E3%82%B9%E3%83%88%E3%83%A9%E3%83%86%E3%82%B8
# 古いポッドを新しいポッドに置き換えるための方法
# defaultはRollingUpdate
# Recreate 既存のポッドはすべて削除されます。
strategy:
type: Recreate
# nfs-provisionerのPodの定義
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
# https://tech.uzabase.com/entry/2018/03/14/200512
# これを指定することで、全てのリソースに対する参照all-readerができるようにしています。
# ちなみに、サービスアカウントを作成するとsecretにca.crt、tokenというデータが作成されます。
# このsecretはポッドが起動した際に、自動的に/var/run/secrets/kubernetes.io/serviceaccount の配下にマウントされます。
serviceAccountName: nfs-client-provisioner
# nfs-provisionerのコンテナの定義
containers:
- name: nfs-client-provisioner
image: quay.io/external_storage/nfs-client-provisioner:latest
# コンテナ内のmountの定義
volumeMounts:
# 以下のspec.template.spec.volumes.nameと合わせる
- name: nfs-client-root
# コンテナ内でNFSをmountさせるPATH
mountPath: /persistentvolumes
env:
# storage-class.ymlのprovisionerと同じ値にする
- name: PROVISIONER_NAME
value: lab.hoge.jp/nfs
# NFSサーバーのアドレス
- name: NFS_SERVER
value: xxx.xxx.xxx.xxx
# NFSサーバーのmountするPATH
- name: NFS_PATH
value: /var/share/nfs
# コンテナ外のmountの定義
volumes:
- name: nfs-client-root
nfs:
# NFSサーバーのアドレス
server: xxx.xxx.xxx.xxx
# NFSサーバーのmountするPATH
path: /var/share/nfs
nfs-provisionerを作成
これでnfs-provisioner用のPodが作成される。
$ sudo kubectl apply -f storage-class.yml \
-f persitent-volumne-claim.yml \
-f rbac.yml \
-f deployment.yml
storageclass.storage.k8s.io/nfs created
persistentvolumeclaim/sample-pvc created
serviceaccount/nfs-client-provisioner created
clusterrole.rbac.authorization.k8s.io/nfs-client-provisioner-runner created
clusterrolebinding.rbac.authorization.k8s.io/run-nfs-client-provisioner created
deployment.extensions/nfs-client-provisioner created
nfs-provisioner作成後確認
Pod, Deployment, ReplicaSetを確認
nfs-provisionerのPod, Deployment, ReplicaSetが作成されたことを確認。
$ sudo kubectl get all
NAME READY STATUS RESTARTS AGE
+pod/nfs-client-provisioner-657f86599c-xhfsm 1/1 Running 0 11s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 97m
NAME READY UP-TO-DATE AVAILABLE AGE
+deployment.apps/nfs-client-provisioner 1/1 1 1 11s
NAME DESIRED CURRENT READY AGE
+replicaset.apps/nfs-client-provisioner-657f86599c 1 1 1 11s
StorageClass, PersistentVolumeClaim, PersistentVolumeを確認
以下の用になっていることを確認
- StorageClassが作成された。
- PersistentVolumeClaimは作成されているがSTATUSがPendingのまま。
- PersistentVolumeは作成されていない。
$ sudo kubectl get sc,pv,pvc
NAME PROVISIONER AGE
+storageclass.storage.k8s.io/nfs lab.hoge.jp/nfs 4m9s
storageclass.storage.k8s.io/standard (default) k8s.io/minikube-hostpath 19m
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
+persistentvolumeclaim/sample-pvc Pending nfs 4m9s
mountされているか確認
nfs-provisionerコンテナ内から、NFSサーバーのmountしたディレクトリが見えるか確認。
NFSサーバーのmountする場所にテスト用ファイルを作成する。
$ sudo echo 'This is a sample.' > /var/share/nfs/sample.txt
作成されたnfs-client-provisionerのPodのNFS_PATHの中を確認すると、sample.txtが見える。
$ sudo kubectl exec -ti nfs-client-provisioner-657f86599c-xhfsm ls /persistentvolumes
sample.txt
$ sudo kubectl exec -ti nfs-client-provisioner-657f86599c-xhfsm cat /persistentvolumes/sample.txt
This is a sample.
これでnfs-provisionerの作成は完了。
Podを作成してNFSサーバーをmountする
次に、Podを作成して、nfs-provisionerを介してNFSサーバーの領域をmountする。
NFSサーバーのmountされるディレクトリのpermissionを変更
nfs-provisionerが、NFSサーバーのmountディレクトリの下に、nfsnobody
というユーザーでディレクトリを作成するため、権限を与える必要がある。
$ sudo chmod 777 /var/share/nfs
権限を与えないと、mount時にmkdirできずに以下のエラーが発生する。
$ sudo kubectl describe persistentvolumeclaim/sample-pvc
# **snip**
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Provisioning 101s (x4 over 3m26s) lab.hoge.jp/nfs_nfs-client-provisioner-657f86599c-5mp5r_91c30fd0-922e-11e9-9954-0242c0a8f904 External provisioner is provisioning volume for claim "default/sample-pvc"
Warning ProvisioningFailed 101s (x4 over 3m26s) lab.hoge.jp/nfs_nfs-client-provisioner-657f86599c-5mp5r_91c30fd0-922e-11e9-9954-0242c0a8f904
failed to provision volume with StorageClass "nfs": unable to create directory to provision new pv:
mkdir /persistentvolumes/default-sample-pvc-pvc-8ff59f6d-922e-11e9-afb4-fa163e42f6bd: permission denied
Normal ExternalProvisioning 9s (x16 over 3m29s) persistentvolume-controller waiting for a volume to be created, either by external provisioner "lab.hoge.jp/nfs" or manually created by system administrator
Mounted By: <none>
Podを定義
kind: Pod
apiVersion: v1
metadata:
name: nginx-pod
spec:
# rbac.ymlのmetadata.nameと合わせる
# これでRBACと関連付けられる
serviceAccountName: nfs-client-provisioner
containers:
# Dockerコンテナの名前
- name: nginx-container
# 利用するDockerイメージ
image: nginx
# 公開するDockerコンテナのポート
ports:
- containerPort: 80
# Dockerコンテナ内のmountポイント
volumeMounts:
# 以下のspec.volumes.nameと合わせる
- name: nfs-pvc
mountPath: "/mnt"
# Never or OnFailure
restartPolicy: OnFailure
# mountする外部ストレージの設定
volumes:
# 上記のspec.containers.volumeMounts.nameと合わせる
- name: nfs-pvc
persistentVolumeClaim:
# persitent-volumne-claim.ymlのmetadatal.nameと合わせる
# これでPersistentVolumeClaimと関連付けられる
claimName: sample-pvc
Podを作成
$ sudo kubectl apply -f pod.yml
pod/nginx-pod created
Pod確認
nginx-podというPodが作成されたことを確認。
$ sudo kubectl get pod
NAME READY STATUS RESTARTS AGE
nfs-client-provisioner-657f86599c-xhfsm 1/1 Running 0 19m
+nginx-pod 1/1 Running 0 13s
PersistentVolumeClaim, PersistentVolumeを確認
以下の用になっていることを確認。
- PersistentVolumeClaimのSTATUSがBoundになった。
- PersistentVolumeが作成された。
$ sudo kubectl get pvc,pv
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
+persistentvolumeclaim/sample-pvc Bound pvc-b3ee5521-9253-11e9-b851-fa163e42f6bd 1Gi RWX nfs 20m
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
+persistentvolume/pvc-b3ee5521-9253-11e9-b851-fa163e42f6bd 1Gi RWX Retain Bound default/sample-pvc nfs 20m
describeすることでどこにmountされているのか分かる。
$ sudo kubectl describe persistentvolume/pvc-b3ee5521-9253-11e9-b851-fa163e42f6bd
Name: pvc-b3ee5521-9253-11e9-b851-fa163e42f6bd
Labels: <none>
Annotations: pv.kubernetes.io/provisioned-by: lab.hoge.jp/nfs
Finalizers: [kubernetes.io/pv-protection]
StorageClass: nfs
Status: Bound
Claim: default/sample-pvc
Reclaim Policy: Retain
Access Modes: RWX
VolumeMode: Filesystem
Capacity: 1Gi
Node Affinity: <none>
Message:
Source:
Type: NFS (an NFS mount that lasts the lifetime of a pod)
Server: xxx.xxx.xxx.xxx
Path: /var/share/nfs/default-sample-pvc-pvc-b3ee5521-9253-11e9-b851-fa163e42f6bd
ReadOnly: false
Events: <none>
mountされているか確認
NFSサーバーでの作業
NFSサーバーに以下のディレクトリが作成されている。
$ ls -l /var/share/nfs/
drwxrwxrwx 2 nfsnobody nfsnobody 6 Jun 19 14:33 default-sample-pvc-pvc-b3ee5521-9253-11e9-b851-fa163e42f6bd
作成されたディレクトリの下にテスト用ファイルを作成。
$ cd /var/share/nfs/default-sample-pvc-pvc-b3ee5521-9253-11e9-b851-fa163e42f6bd
$ echo 'This is a sample in PersistenVolume.' > persistent-volume.txt
Podを確認
Pod内のmountされたディレクトリを確認する。
NFSサーバーの/var/share/nfs/default-sample-pvc-pvc-b3ee5521-9253-11e9-b851-fa163e42f6bd
の中と同じファイルが見えていればOK。
$ sudo kubectl exec -it nginx-pod ls /mnt
persistent-volume.txt
$ sudo kubectl exec -it nginx-pod cat /mnt/persistent-volume.txt
This is a sample in PersistenVolume.