第6章 Kubernetes におけるストレージのプロビジョニング
6.1. 概要
このセクションでは、Kubernetes でのストレージのプロビジョニング方法について説明します。
本書の演習を開始する前に、Kubernetes をセットアップしておく必要があります。
Kubernetes がセットアップされていない場合は、「Kubernetes によるコンテナーのオーケストレーション」の指示に従ってください。
6.2. Kubernetes 永続ボリューム
このセクションでは、Kubernetes の永続ボリュームについて概観します。以下の例では、nginx
を使用して永続ボリュームからコンテンツを提供する方法について説明します。
このセクションでは、読者が Kubernetes を基本的に理解されており、Kubernetes クラスターを稼働しておられることを想定しています。
Kubernetes の永続ボリューム (PV) は、インフラストラクチャーの基礎となるストレージ容量の実際の構成要素を表しています。Kubernetes を使用してマウントを実行する前に、まずマウントするストレージを作成する必要があります。クラスター管理者は、Kubernetes でマウントできるよう GCE ディスクを作成し、NFS 共有をエクスポートする必要があります。
永続ボリュームは、GCE 永続ディスク、NFS 共有、および AWS ElasticBlockStore ボリュームなどの「ネットワークボリューム」用に設計されています。HostPath が開発およびテストを容易にするために組み込まれています。以下の例では、ローカルの HostPath を作成します。
重要! HostPath を機能させるには、単一ノードクラスターを実行する必要があります。Kubernetes は現時点ではホスト上のローカルストレージをサポートしません。Pod は HostPath のある正しいノードに常に必ず配置される訳ではありません。
// this will be nginx's webroot $ mkdir /tmp/data01 $ echo 'I love Kubernetes storage!' > /tmp/data01/index.html
物理ボリュームを API サーバーに提示して作成します。
$ kubectl create -f examples/persistent-volumes/volumes/local-01.yaml $ kubectl get pv NAME LABELS CAPACITY ACCESSMODES STATUS CLAIM pv0001 map[] 10737418240 RWO Available
6.2.1. ストレージの要求
Kubernetes のユーザーは、Pod 用に永続ストレージを要求します。基礎となるプロビジョニングの性質については、ユーザーが理解している必要はありません。ただし、ユーザーはストレージの要求を使用でき、ストレージを使用する数多くの Pod とは別にそのストレージのライフサイクルを管理できることを知っておく必要があります。
要求は、それらを使用する Pod と同じ名前空間に作成される必要があります。
$ kubectl create -f examples/persistent-volumes/claims/claim-01.yaml $ kubectl get pvc NAME LABELS STATUS VOLUME myclaim-1 map[]
バックグラウンドプロセスは、この要求をボリュームに一致させることを試行します。要求の状態は最終的には以下のようになります。
$ kubectl get pvc NAME LABELS STATUS VOLUME myclaim-1 map[] Bound f5c3a89a-e50a-11e4-972f-80e6500a981e $ kubectl get pv NAME LABELS CAPACITY ACCESSMODES STATUS CLAIM pv0001 map[] 10737418240 RWO Bound myclaim-1 / 6bef4c40-e50b-11e4-972f-80e6500a981e
6.2.2. 要求をボリュームとして使用する
要求は Pod のボリュームとして使用されます。Kubernetes は要求を使用してそれがバインドされた PV を検索します。次に、PV は Pod に公開されます。
$ kubectl create -f examples/persistent-volumes/simpletest/pod.yaml $ kubectl get pods POD IP CONTAINER(S) IMAGE(S) HOST LABELS STATUS CREATED mypod 172.17.0.2 myfrontend nginx 127.0.0.1/127.0.0.1 <none> Running 12 minutes> $ kubectl create -f examples/persistent-volumes/simpletest/service.json $ kubectl get services NAME LABELS SELECTOR IP PORT(S) frontendservice <none> name=frontendhttp 10.0.0.241 3000/TCP kubernetes component=apiserver,provider=kubernetes <none> 10.0.0.2 443/TCP kubernetes-ro component=apiserver,provider=kubernetes <none> 10.0.0.1 80/TCP
6.2.3. 次のステップ
nginx が提供しているコンテンツを表示するためにサービスエンドポイントを照会します。"forbidden" エラーが出た場合は、SELinux (# setenforce 0) を無効にします。
# curl 10.0.0.241:3000 I love Kubernetes storage!
6.3. ボリューム
Kubernetes は各種のストレージ機能を「ボリューム」として抽象化します。
ボリュームは、Pod 定義の volumes セクションで定義されます。ボリュームにあるデータのソースは (1) リモート NFS 共有、(2) iSCSI ターゲット、(3) 空のディレクトリー、または (4) ホスト上のローカルディレクトリーのいずれかになります。
Pod 定義の volumes セクションでは複数のボリュームを定義することができます。各ボリュームは、マウント手順の実行時に Pod 内の固有 ID として使用される (Pod のコンテキスト内の) 固有名を持つ必要があります。
これらのボリュームは、いったん定義されると Pod 定義の containers セクションに定義されるコンテナーにマウントできます。各コンテナーでは複数のボリュームをマウントできます。一方、単一ボリュームを複数のコンテナーにマウントすることもできます。コンテナー定義の volumeMounts セクションでは、ボリュームをマウントする必要のある場所を指定します。
6.3.1. 例
apiVersion: v1beta3 kind: Pod metadata: name: nfs-web spec: volumes: # List of volumes to use, i.e. *what* to mount - name: myvolume < volume details, see below > - name: mysecondvolume < volume details, see below > containers: - name: mycontainer volumeMounts: # List of mount directories, i.e. *where* to mount # We want to mount 'myvolume' into /usr/share/nginx/html - name: myvolume mountPath: /usr/share/nginx/html/ # We want to mount 'mysecondvolume' into /var/log - name: mysecondvolume mountPath: /var/log/
6.4. Kubernetes および SELinux パーミッション
Kubernetes が適切に機能するには、ホストとコンテナー間で共有されるディレクトリーへのアクセスがなければなりません。SELinux はデフォルトでは、Kubernetes がその共有ディレクトリーにアクセスすることをブロックします。通常このブロックは得策と言えます。脆弱なコンテナーがホストにアクセスしてダメージを加えることは誰もが阻止したいことであるためです。この状況では、SELinux の共有の阻止に向けた介入なしに、ディレクトリーをホストと Pod 間で共有されるようにする必要があります。
以下は一例になります。ディレクトリー /srv/my-data を Atomic Host と Pod で共有する必要がある場合、/srv/my-data を SELinux ラベルの svirt_sandbox_file_t で、明示的に再度ラベル付けする必要があります。(ホストにある) このディレクトリーにこのラベルが付けられることにより、SELinux はコンテナーに対し、ディレクトリーの読み取りおよびディレクトリーへの書き込みを許可します。以下は、svirt_sandbox_file_t ラベルを /srv/my-data ディレクトリーに割り当てるコマンドです。
$ chcon -R -t svirt_sandbox_file_t /srv/my-data
以下の例は、この手順を実行するためのステップを示しています。
ステップ 1
ホストから /srv/my-data を使用するコンテナーを HTML ルートとして定義します。
{ "apiVersion": "v1beta3", "kind": "Pod", "metadata": { "name": "host-test" }, "spec": { "containers": [ { "name": "host-test", "image": "nginx", "privileged": false, "volumeMounts": [ { "name": "srv", "mountPath": "/usr/share/nginx/html", "readOnly": false } ] } ], "volumes": [ { "name": "srv", "hostPath": { "path": "/srv/my-data" } } ] } }
ステップ 2
コンテナーホストで以下のコマンドを実行し、SELinux が nginx コンテナーの/srv/my-data への読み取りアクセスを拒否していることを確認します。
$ mkdir /srv/my-data $ echo "Hello world" > /srv/my-data/index.html $ curl <IP address of the container>
以下の出力が表示されます。
<html> <head><title>403 Forbidden</title></head> ...
ステップ 3
ラベル svirt_sandbox_file_t をディレクトリー /srv/my-data に適用します。
$ chcon -R -t svirt_sandbox_file_t /srv/my-data
ステップ 4
curl を使用してコンテナーにアクセスし、ラベルが有効になったことを確認します。
$ curl <IP address of the container> Hello world
curl コマンドが "Hello world" を返す場合、SELinux ラベルは適切に適用されていることになります。
詳細は、この点についての情報を追跡している BZ#1222060[ を参照してください。
6.5. NFS
以下のシナリオをテストするには、NFS 共有が事前に準備されている必要があります。この例では、NFS 共有を Pod にマウントします。
以下の例では、NFS 共有を /usr/share/nginx/html/ にマウントし、nginx webserver を実行します。
ステップ 1
nfs-web.yaml という名前のファイルを作成します。
apiVersion: v1beta3 kind: Pod metadata: name: nfs-web spec: volumes: - name: www nfs: # Use real NFS server address here. server: 192.168.100.1 # Use real NFS server export directory. path: "/www" readOnly: true containers: - name: web image: nginx ports: - name: web containerPort: 80 protocol: tcp volumeMounts: # 'name' must match the volume name below. - name: www # Where to mount the volume. mountPath: "/usr/share/nginx/html/"
ステップ 2
Pod の起動:
$ kubectl create -f nfs-web.yaml
Kubernetes は 192.168.100.1:/www
を nginx コンテナー内の /usr/share/nginx/html/`
にマウントし、これを実行します。
ステップ 3
webserver が NFS 共有からデータを受信することを確認します。
$ curl 172.17.0.6 Hello from NFS
トラブルシューティング
403 Forbidden error: webserver から "403 Forbidden" の応答を受信する場合、以下のコマンドを実行し、SELinux が Docker コンテナーに対して NFS 経由のデータの読み取りを許可していることを確認します。
$ setsebool -P virt_use_nfs 1
6.6. iSCSI
ステップ 1 iSCSI ターゲットが適切に設定されていることを確認します。すべての Kubernetes ノードに iSCSl ターゲットから LUN を割り当てる十分な特権があることを確認します。
ステップ 2 以下の Pod 定義を含む iscsi-web.yaml
という名前のファイルを作成します。
apiVersion: v1beta3 kind: Pod metadata: name: iscsi-web spec: volumes: - name: www iscsi: # Address of the iSCSI target portal targetPortal: "192.168.100.98:3260" # IQN of the portal iqn: "iqn.2003-01.org.linux-iscsi.iscsi.x8664:sn.63b56adc495d" # LUN we want to mount lun: 0 # Filesystem on the LUN fsType: ext4 readOnly: false containers: - name: web image: nginx ports: - name: web containerPort: 80 protocol: tcp volumeMounts: # 'name' must match the volume name below. - name: www # Where to mount he volume. mountPath: "/usr/share/nginx/html/"
ステップ 3
Pod の作成:
$ kubectl create -f iscsi-web.yaml
ステップ 4
Kubernetes は iSCSI ターゲットにログインし、LUN 0 を割り当て (通常は /dev/sdXYZ
)、指定のファイルシステム (本書の例では ext4) を nginx コンテナー内の /usr/share/nginx/html/
にマウントして、これを実行します。
ステップ 5
web サーバーが iSCSI ボリュームのデータを使用していることを確認します。
$ curl 172.17.0.6 Hello from iSCSI
6.7. Google Compute Engine
Google Compute Engine 永続ディスク (GCE PD)
クラスターを Google Compute Engine で実行している場合、永続ディスクを永続ストレージソースとして使用できます。以下の例では、GCE PD から html コンテンツを提供する Pod を作成します。
ステップ 1
GCE SDK がセットアップされている場合、以下のコマンドを使用して永続ディスクを作成します。
$ gcloud compute disks create --size=250GB {Persistent Disk Name}
または、GCE web インターフェースでディスクを作成することもできます。GCE SDK をセットアップする必要がある場合は、こちら の指示に従ってください。
ステップ 2
gce-pd-web.yaml
という名前のファイルを作成します。
apiVersion: v1beta3 kind: Pod metadata: name: gce-web spec: containers: - name: web image: nginx ports: - name: web containerPort: 80 protocol: tcp volumeMounts: - name: html-pd mountPath: "/usr/share/nginx/html" volumes: - name: html-pd gcePersistentDisk: # Add the name of your persistent disk below pdName: {Persistent Disk Name} fsType: ext4
ステップ 3
Pod の作成:
$ kubectl create -f gce-pd-web.yaml
Kubernetes は Pod を作成し、ディスクを割り当てますが、これをフォーマットしたり、マウントしたりすることはできません。これはバグによる問題ですが、Kubernetes の今後のバージョンで修正される予定です。この問題を回避するために、次のステップに進んでください。
ステップ 4
永続ディスクをフォーマットし、マウントします。
ステップ 5
ディスクは仮想マシンに割り当てられ、デバイスは scsi-0Google_PersistentDisk_{Persistent Disk Name}
という名前で /dev/disk/by-id/`
に表示されます。このディスクがすでにフォーマットされており、データが含まれている場合は、次のステップに進みます。そうでない場合は root で以下のコマンドを実行し、これをフォーマットします。
$ mkfs.ext4 /dev/disk/by-id/scsi-0Google_PersistentDisk_{Persistent Disk Name}
ステップ 6
ディスクがフォーマットされている場合、これを Kubernetes が予想する場所にマウントします。以下のコマンドを root として実行します。
# mkdir -p /var/lib/kubelet/plugins/kubernetes.io/gce-pd/mounts/{Persistent Disk Name} && mount /dev/disk/by-id/scsi-0Google_PersistentDisk_{Persistent Disk Name} /var/lib/kubelet/plugins/kubernetes.io/gce-pd/mounts/{Persistent Disk Name}
注意: mkdir コマンドおよび mount コマンドは上記のように連続して実行される必要があります。Kubernetes の削除により、何もマウントされていないことが確認されるとディレクトリーが削除されるためです。
ステップ 7
ディスクがマウントされており、正しい SELinux コンテキストが付与されているはずです。root で以下を実行します。
$ sudo chcon -R -t svirt_sandbox_file_t /var/lib/kubelet/plugins/kubernetes.io/gce-pd/mounts/{Persistent Disk Name}
ステップ 8
webserver が提供するデータを作成します。
$ echo "Hello world" > /var/lib/kubelet/plugins/kubernetes.io/gce-pd/mounts/{Persistent Disk Name}/index.html
ステップ 9
Pod から HTML を取得できるはずです。
$ curl {IP address of the container} Hello World!
Comments