【IBM Cloud k8s検証メモ】ポッドから永続ストレージのマウント


Bluemix の CFコンテナからブロック・ストレージをマウント出来ないため、CMS等の様にクラスタ構成時にファイル共用が必須なケースでは、本番運用に対応できず残念な点でした。 一方 Bluemix k8sでは、Bluemix Infrastructure の ファイル・ストレージをマウントできる様になりました。これにより、前述の問題も解決できる様になり、コンテナ・ベースの本格的な利用ができると思います。

そこで、簡単な構成ですが、次の図の様なコンテナ間のファイル共有する方法について確認しました。


![スクリーンショット 2017-09-14 19.45.20.png](https://qiita-image-store.s3.amazonaws.com/0/42829/b881c50e-a501-b071-386f-bb087ac8415d.png "スクリーンショット 2017-09-14 19.45.20.png")

### 図の説明

* 外部からアクセスするDNS名は、k8sの標準クラスタ作成時に、Bluemixから付与されるDNS名を利用します。 
* HTTPSでアクセスできる様に、ロードバランサーに、Ingressコントローラを選択します。
* VIPは、Bluemix Infrastructure の ポータブルサブネットから割り当てられるIPアドレスです。
* アプリケーションのコンテナは、ポッドとして複数のノード（ワーカー）にデプロイされ、ハードウェア障害に対応します。
* コンテナから IBM Cloud のストレージ・サービスをNFSマウントして、Read/Write します。


## 永続ストレージの作成方法

永続ストレージの作成と利用方法には、２つの方法があり、それぞれ、Bluemix Docs に記載があります。 この二つの方法について検証してみたいと思います。

* [IBM Bluemix Container Service における Kubernetes での既存の NFS ファイル共有の使用](https://console.bluemix.net/docs/containers/cs_cluster.html?pos=2#cs_cluster_volume_create)

* [IBM Bluemix Container Service での永続ストレージの作成](https://console.bluemix.net/docs/containers/cs_apps.html#cs_apps_volume_claim)


前者を「既存のNFSファイル共有の使用」のパートで、後者を「kubectlを利用した永続ストレージ作成」のパートに書いていきます。



## 既存のNFSファイル共有の使用
ここでは、[IBM Bluemix Container Service における Kubernetes での既存の NFS ファイル共有の使用](https://console.bluemix.net/docs/containers/cs_cluster.html?pos=2#cs_cluster_volume_create) の検証を進めていきます。



### 永続ストレージを準備する場所の確認

既存の状態を再現するために、Bluemix Infrastructure の画面から、先に永続ストレージを作成します。　その前に、何処のデータセンターに永続ストレージを作るべきかを確認するため、k8sクラスタが作成されたデータセンターを確認するために、デバイス・リストの画面を表示します。 kubeから始まるサーバー名が、k8s用に作成されたサーバーのホスト名です。 この例では、ロケーションが Dalas12 となっていますので、同じ場所に、永続ストレージを作成することにします。

![スクリーンショット 2017-09-15 14.46.59.png](https://qiita-image-store.s3.amazonaws.com/0/42829/41035003-3ae1-7d82-c1e5-339be16d75a6.png "スクリーンショット 2017-09-15 14.46.59.png")

NFSやiSCSIなど、ネットワークを介してアクセスするストレージ・サービスは、パケットがネットワークを伝わる時間の影響を受けやすく、サーバーやコンテナとストレージが、遠く離れていると、距離によりパケットの到着時間が長くなる影響を受けて、アクセス速度が遅くなる問題があります。 このため、コンテナとストレージは、可能な限り近い場所に置くことが望ましいと言えます。


### 永続ストレージの作成

k8sと接続できるストレージのサービスは、ファイル・ストレージ（NFS）となっており、２つの系統があります。 一つは容量と性能の比例型ストレージで、容量が増えるほどIOPS性能が、比例して良くなるタイプのエンデュランス・ストレージ、そして、性能指定型のパフォーマンス・ストレージです。 時間課金を選べるパフォーマンス・ストレージを選択します。

![スクリーンショット 2017-09-15 15.03.58.png](https://qiita-image-store.s3.amazonaws.com/0/42829/5efefc34-de52-e42d-237a-95562eba23ed.png "スクリーンショット 2017-09-15 15.03.58.png")

ウェブ画面のガイドに従って、先へ進み、オーダーを確定します。


### 永続ストレージの作成確認

2〜3分で作成が完了して、ボリューム名の先頭から時計マークが消えます。 そしたら、後にYAMLファイルに設定する項目を得るために、ボリューム名をクリックして詳細を表示します。

![スクリーンショット 2017-09-15 15.16.48.png](https://qiita-image-store.s3.amazonaws.com/0/42829/6f8288d5-0b34-7721-7ca8-9fa68318694e.png "スクリーンショット 2017-09-15 15.16.48.png")

次の詳細画面で、ホスト名とマウント・ポイントのパス部分が、kubectlに与えるyamlファイルで必要になりますので、エディタ等にメモしておきます。

![スクリーンショット 2017-09-15 15.17.34.png](https://qiita-image-store.s3.amazonaws.com/0/42829/387e8a28-1696-0595-602f-ea837e9961ae.png "スクリーンショット 2017-09-15 15.17.34.png")


### 大切なもう一つの操作、アクセス権設定

もう一つ、k8sのWorkerノードへのアクセス権を付与する必要があります。 この設定が入っていないと、コンテナが何時迄もスタートできない状態になりますので、忘れいない様に注意ですね。 オレンジの矢印の先をクリックして、許可ホストにWokerノードを追加します。 

![スクリーンショット 2017-09-15 15.29.32.png](https://qiita-image-store.s3.amazonaws.com/0/42829/4f62d50c-fbda-1bac-42cd-aa073ec86de7.png "スクリーンショット 2017-09-15 15.29.32.png")

もう一つの方法として、サブネットを登録する方法があります。 この方法であれば、Workerノードを追加した時にも、追加設定が不要なので、こちらの方が望ましいですね。

![スクリーンショット 2017-09-15 15.39.51.png](https://qiita-image-store.s3.amazonaws.com/0/42829/493cf15e-40b6-3c36-8e47-989d73ff07f8.png "スクリーンショット 2017-09-15 15.39.51.png")


これで、永続ストレージの準備完了です。

### k8sクラスタ

前回の[[Bluemix k8s検証メモ] アプリをHTTPSとドメイン名でアクセスする](http://qiita.com/MahoTakara/items/34565e3c4095b6701a6a)で作成したk8sクラスタのコンテナに、永続ディスクをマウントしたいと思います。 標準クラスタが作れていれば、この時点で、デプロイメントが無くても、読み進めれば、永続ストレージをマウントしていけますので、ご参照いただければ幸いです。 

次のコマンドで、現在の状態を確認します。 po/express-app-188xxxx の PODの各コンテナへ、永続ストレージをマウントします。

~~~bash
$ kubectl get all
NAME                              READY     STATUS    RESTARTS   AGE
po/express-app-1882574706-pfhjv   1/1       Running   0          4d
po/express-app-1882574706-pqmf2   1/1       Running   0          4d
po/express-app-1882574706-xlgr4   1/1       Running   0          4d

NAME              CLUSTER-IP    EXTERNAL-IP   PORT(S)          AGE
svc/express-svc   10.10.10.57   <nodes>       3000:31514/TCP   4d
svc/kubernetes    10.10.10.1    <none>        443/TCP          4d

NAME                 DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deploy/express-app   3         3         3            3           4d

NAME                        DESIRED   CURRENT   READY     AGE
rs/express-app-1882574706   3         3         3         4d
~~~


### 永続ボリュームへのマッピング

エディタで次のファイルを作成します。 ここで、server, path の部分は、前述のストレージ画面のメモを利用します。 パスの部分は、ホスト名部分を除外してパスだけをセットします。　各項目の説明は、[IBM Bluemix Container Service における Kubernetes での既存の NFS ファイル共有の使用](https://console.bluemix.net/docs/containers/cs_cluster.html?pos=2#cs_cluster_volume_create)を参照願います。 また、[Kubernetesドキュメント Persistent Volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) も参考にすると、理解が深まります。


~~~file:t050_stg.yml
apiVersion: v1
kind: PersistentVolume
metadata:
 name: mypv
spec:
 capacity:
   storage: "20Gi"
 accessModes:
   - ReadWriteMany
 nfs:
   server: "fsf-dal1201a-fz.adn.networklayer.com"
   path: "/IBM02SV289550_1/data01"
~~~   

yamlファイルを反映させて、永続ボリュームを作成済みの永続ストレージへマッピングします。

~~~bash
$ kubectl apply -f t050_stg.yml 
persistentvolume "mypv" created
~~~

永続ボリュームが出来ていることを確認します。

~~~bash
$ kubectl get pv
NAME      CAPACITY   ACCESSMODES   RECLAIMPOLICY   STATUS      CLAIM     REASON    AGE
mypv      20Gi       RWX           Retain          Available                       11s
~~~

詳細に見るには、次のコマンドを利用します。

~~~bash
$ kubectl describe pv mypv
Name:		mypv
Labels:		<none>
StorageClass:	
Status:		Bound
Claim:		default/mypvc
Reclaim Policy:	Retain
Access Modes:	RWX
Capacity:	20Gi
Message:	
Source:
    Type:	NFS (an NFS mount that lasts the lifetime of a pod)
    Server:	fsf-dal1201a-fz.adn.networklayer.com
    Path:	/IBM02SV289550_1/data01
    ReadOnly:	false
No events.
~~~



### 永続ボリュームクレームの設定

今度は、コンテナにマウントする際に利用するクレームを定義します。 前述同様に、個々の項目の説明は避けますが、ポイントだけ補足します。
`storage:` と `accessModes` の値が、前述の永続ボリュームと一致する様にします。


~~~file:t051_stg.yml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
 name: mypvc
 annotations:
   volume.beta.kubernetes.io/storage-class: ""
spec:
 accessModes:
   - ReadWriteMany
 resources:
   requests:
     storage: "20Gi"
~~~

yamlを反映します。

~~~bash
$ kubectl apply -f t051_stg.yml 
persistentvolumeclaim "mypvc" created
~~~

永続ボリュームクレームを確認します。 永続ボリュームとバインドされている状態になっています。

~~~bash
$ kubectl get pvc
NAME      STATUS    VOLUME    CAPACITY   ACCESSMODES   AGE
mypvc     Bound     mypv      20Gi       RWX           12s
~~~

同様に詳細も見ておきます。

~~~bash
$ kubectl describe pvc mypvc
Name:           mypvc
Namespace:      default
StorageClass:   
Status:         Bound
Volume:         mypv
Labels:         <none>
Capacity:       20Gi
Access Modes:   RWX
No events.
~~~



### コンテナの再作成と永続ボリュームクレームのマウント

前回、アプリ・コンテナとIngressをデプロイした時の YAMLファイルがあれば、それを利用して一度、削除します。

~~~bash
$ kubectl delete -f publish.yml
~~~

もう一つの方法としては、以下のコマンドを実行して削除します。

~~~
$ kubectl delete deployment express-app
$ kubectl delete service express-svc
$ kubectl delete ingress express-ingress
~~~


前回のpublish.ymlを修正して、次のファイルを準備します。 主な違いは、'volumeMount:' と 'volumes:' を加えたことです。 コンテナをビルドした時に利用した Dockerfileを編集する必要はありません。 次のYAMLファイルだけで、コンテナに永続ストレージをマウントできる様になります。 

~~~file:t043_cluster_disk.yml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: express-app
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: express-app
    spec:
      containers:
      - name: express-container
        image: registry.ng.bluemix.net/takara/express-app
        ports:
        - containerPort: 3000
        volumeMounts:
        - mountPath: /app/public
          name: myvol
      volumes:
      - name: myvol
        persistentVolumeClaim:
          claimName: mypvc
---
apiVersion: v1
kind: Service
metadata:
  name: express-svc
spec:
  type: NodePort
  selector:
    app: express-app
  ports:
  - protocol: TCP
    port: 3000
    nodePort: 31514
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: express-ingress
spec:
  tls:
  - hosts:
    - mycluster3.us-south.containers.mybluemix.net
    secretName: mycluster3
  rules:
  - host: mycluster3.us-south.containers.mybluemix.net
    http:
      paths:
      - path: /
        backend:
          serviceName: express-svc
          servicePort: 3000
~~~


次のコマンドで、アプリ・コンテナとIngressをデプロイします。

~~~
$ kubectl create -f t043_cluster_disk.yml
~~~


### コンテナでのマウント状態の確認

`kubectl get pod で、podのリストを取得して、その中の一つにログインして、マウント状態を確認した結果が以下です。 

外からのYAMLファイルだけの操作で、コンテナにファイルシステムをマウントできる点は、とっても便利ですね。

~~~bash
$ kubectl exec -it express-app-943621608-0k35n /bin/bash
root@express-app-943621608-0k35n:/# df
Filesystem                                                   1K-blocks    Used Available Use% Mounted on
overlay                                                      101330012 6063956  95249672   6% /
tmpfs                                                          2048016       0   2048016   0% /dev
tmpfs                                                          2048016       0   2048016   0% /sys/fs/cgroup
/dev/xvda2                                                   101330012 6063956  95249672   6% /etc/hosts
fsf-dal1201a-fz.adn.networklayer.com:/IBM02SV289550_1/data01  20971264       0  20971264   0% /app/public
shm                                                              65536       0     65536   0% /dev/shm
tmpfs                                                          2048016      12   2048004   1% /run/secrets/kubernetes.io/serviceaccount
tmpfs                                                          2048016       0   2048016   0% /sys/firmware
~~~




## kubectlを利用した永続ストレージ作成

永続ストレージを利用するためのもう一つの方法について、[IBM Bluemix Container Service での永続ストレージの作成](https://console.bluemix.net/docs/containers/cs_apps.html#cs_apps_volume_claim) の内容を検証していきます。



~~~
$ kubectl get storageclasses
NAME                         TYPE
default                      ibm.io/ibmc-file   
ibmc-file-bronze (default)   ibm.io/ibmc-file   
ibmc-file-custom             ibm.io/ibmc-file   
ibmc-file-gold               ibm.io/ibmc-file   
ibmc-file-retain-bronze      ibm.io/ibmc-file   
ibmc-file-retain-custom      ibm.io/ibmc-file   
ibmc-file-retain-gold        ibm.io/ibmc-file   
ibmc-file-retain-silver      ibm.io/ibmc-file   
ibmc-file-silver             ibm.io/ibmc-file   
~~~


~~~
$ kubectl describe storageclasses ibmc-file-bronze 
Name:		ibmc-file-bronze
IsDefaultClass:	Yes
Annotations:	kubectl.kubernetes.io/last-applied-configuration={"kind":"StorageClass","apiVersion":"storage.k8s.io/v1beta1","metadata":{"name":"ibmc-file-bronze","creationTimestamp":null,"labels":{"addonmanager.kubernetes.io/mode":"Reconcile","kubernetes.io/cluster-service":"true"},"annotations":{"storageclass.beta.kubernetes.io/is-default-class":"true"}},"provisioner":"ibm.io/ibmc-file","parameters":{"iopsPerGB":"2","reclaimPolicy":"Delete","sizeRange":"20Gi,40Gi,80Gi,100Gi,250Gi,500Gi,1000Gi,2000Gi,4000Gi,8000Gi,12000Gi","type":"Endurance"}},storageclass.beta.kubernetes.io/is-default-class=true
Provisioner:	ibm.io/ibmc-file
Parameters:	iopsPerGB=2,reclaimPolicy=Delete,sizeRange=20Gi,40Gi,80Gi,100Gi,250Gi,500Gi,1000Gi,2000Gi,4000Gi,8000Gi,12000Gi,type=Endurance
Events:		<none>
~~~



次のYAMLファイルで、Bluemix Infrastructure エンデュランス・ストレージ（月額）を作成して、コンテナにマウントできる状態になります。 このケースでは、永続ボリュームを作ったり、Bluemix Infrastructure のウェブ画面から、ファイル・ストレージを作成する必要はありません。

~~~file:t053_stg.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mypvc2
  annotations:
    volume.beta.kubernetes.io/storage-class: "ibmc-file-bronze"
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 20Gi
~~~

適用します。

~~~
$ kubectl apply -f t053_stg.yml
persistentvolumeclaim "mypvc2" created
~~~

2〜3分すると、mypvc2 の列の様に、永続ボリューム・クレームが利用できる様になります。

~~~
$ kubectl get pvc
NAME      STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE
mypvc     Bound     mypv                                       20Gi       RWX           7h
mypvc2    Bound     pvc-70563956-9949-11e7-8559-9694aad01573   20Gi       RWX           2m
~~~


永続ボリュームも確認すると、次の様になっています。 RECLAIMPOLICY が違うことに要注意です。

~~~
$ kubectl get pv
NAME                                       CAPACITY   ACCESSMODES   RECLAIMPOLICY   STATUS    CLAIM            REASON    AGE
mypv                                       20Gi       RWX           Retain          Bound     default/mypvc              7h
pvc-70563956-9949-11e7-8559-9694aad01573   20Gi       RWX           Delete          Bound     default/mypvc2             1m
~~~



#### 永続ボリュームクレームのマウント

次のYAMLファイルで、前述の方法で作成した永続ボリューム・クレームをマウントするコンテナを作成します。

~~~file:t044_cluster_endurance.yml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: express-app2
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: express-app2
    spec:
      containers:
      - name: express-container2
        image: registry.ng.bluemix.net/takara/express-app
        ports:
        - containerPort: 3000
        volumeMounts:
        - mountPath: /app/public
          name: myvol
      volumes:
      - name: myvol
        persistentVolumeClaim:
          claimName: mypvc2
~~~

kubectl を実行して、内容を反映させます。

~~~
$ kubectl create -f t044_cluster_endurance.yml
deployment "express-app2" created
service "express-svc2" created
~~~

PODを確認すると、exporess-app2-* という名前で、PODが稼働していることが解ります。

~~~
kubectl get pod
NAME                            READY     STATUS    RESTARTS   AGE
express-app-943621608-3bfps     1/1       Running   0          21h
express-app-943621608-70z2s     1/1       Running   0          21h
express-app-943621608-7l6tm     1/1       Running   0          21h
express-app2-3198649982-14p53   1/1       Running   0          13s
mypod                           1/1       Running   0          1d
~~~

実際にログインして、dfコマンドでマウント状態を確認します。 /app/publicのファイル・システムは、Bluemix IaaSのファイル・システムであることが確認できます。

~~~
$ kubectl exec -it express-app2-3198649982-14p53 /bin/bash
root@express-app2-3198649982-14p53:/# df
Filesystem                                                    1K-blocks    Used Available Use% Mounted on
overlay                                                       101330012 5718424  95595204   6% /
tmpfs                                                           2048016       0   2048016   0% /dev
tmpfs                                                           2048016       0   2048016   0% /sys/fs/cgroup
fsf-dal1201b-fz.adn.networklayer.com:/IBM02SEV289550_1/data01  20971328       0  20971328   0% /app/public
/dev/xvda2                                                    101330012 5718424  95595204   6% /etc/hosts
shm                                                               65536       0     65536   0% /dev/shm
tmpfs                                                           2048016      12   2048004   1% /run/secrets/kubernetes.io/serviceaccount
tmpfs                                                           2048016       0   2048016   0% /sys/firmware
~~~


ファイルが入っていませんが、/app/public で ls を実行して、ファイルシステムが動作していることが確認できます。

~~~
root@express-app2-3198649982-14p53:/# ls -al /app/public
total 8
drwxr-xr-x 2 nobody 4294967294 4096 Sep 14 12:37 .
drwxr-xr-x 7 root   root       4096 Sep  9 16:10 ..
root@express-app2-3198649982-14p53:/# 
~~~


## まとめ

Bluemix Infrastructure ファイル・サービス で提供される NFSサーバーを kubectl を使って コンテナの内容を変えることなく、マウントできることが確認できました。  また、kubectl から Bluemix Infrastructure のエンデュランス・ストレージを作成して、同様にマウントできることも確認できました。  これで、複数のコンテナ間でファイルが共有でき、コンテナで、永続的な情報としてファイルへ書き込みことができる事が確認できました。




## 参考資料
* [IBM Bluemix Container Service における Kubernetes での既存の NFS ファイル共有の使用](https://console.bluemix.net/docs/containers/cs_cluster.html?pos=2#cs_cluster_volume_create)
* [IBM Bluemix Container Service での永続ストレージの作成](https://console.bluemix.net/docs/containers/cs_apps.html#cs_apps_volume_claim)
* [Kubernetesドキュメント Persistent Volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) 
* [THE TWELVE-FACTOR APP](https://12factor.net/ja/)