コンテナからICOSのバケットをマウントする方法

ICOS（IBM Cloud Object Storage)は、AWS S3と互換性のあるオブジェクト・ストレージです。このストレージを IKS (IBM Cloud Kubernetes Service) のポッド内コンテナのファイルシステムにマウントして利用する方法をみていきます。 ここでは簡単にセットアップするために、[IBM Cloud Object Storage Plug-in](https://cloud.ibm.com/kubernetes/solutions/helm-charts/ibm-charts/ibm-object-storage-plugin)のHELMを使ってインストールします。

![スクリーンショット 2019-05-12 21.50.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42829/6e32fcb9-74c9-f6ff-b9cd-f09e7f166305.png)


## 前提条件

出来るだけ本文を短くするために、以下の前提条件は完了している処から、始めたいと思います。

* IBM Cloud のアカウントを持っている
* IBM Cloud のアカウントで Kubernetes を作成している
* IBM Cloud CLI がインストールされている
* IBM Cloud にログインして、ibmcloudとkubectlコマンドが利用できる
* HELMクライアントが、PCにインストールされている
* HELM tiller がクラスタにインストールされている。


## ICOSのオーダーとサービス資格情報の取得

ここでは、ICOSをオーダーして、アクセス資格情報を作成して、apikey と resource_instance_id を取得します。

ICOSのオーダーは、IBM Cloudのウェブ画面のメニューから、カタログ -> ストレージ -> オブジェクト・ストレージへと進んで、どちらのプランを選んで、「作成」ボタンをクリックします。

バケットはKubernetesの永続ボリュームのプロビジョナで作成するので、バケットを作らずに、サービス資格情報を作成しておきます。

ICOSのメニューから、サービス資格情報 -> 「新規資格情報」ボタンをクリックします。次に、資格情報を画面に表示して、apikey と resource_instance_id の二つの行を、メモ帳などにコピペしておきます。

以上で、ICOSの準備は完了です。


## RBACの設定

名前空間(Namespace) icos-client を作成して、下記のYAMLを順次適用して、オブジェクト・ストレージのプラグインへ、アクセス権を設定します。

~~~
$ kubectl create namespace icos-client
$ kubectl apply -f customPodSecurityPolicy.yaml
$ kubectl apply -f clusterRole.yaml
$ kubectl apply -f clusterRoleBinding.yaml
~~~

順番に、ポッドセキュリティポリシーを作成します。これによって、ポッドの実行時の条件を指定します。

~~~file:customPodSecurityPolicy.yaml
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: ibm-object-storage-plugin-psp
spec:
  allowPrivilegeEscalation: false
  requiredDropCapabilities:
  - ALL
  hostNetwork: true
  volumes:
  - 'hostPath'
  - 'secret'
  seLinux:
    rule: RunAsAny
  supplementalGroups:
    rule: MustRunAs
    ranges:
      - min: 1
        max: 65535
  runAsUser:
    rule: RunAsAny
  fsGroup:
    rule: MustRunAs
    ranges:
      - min: 1
        max: 65535
~~~

ICOSをアクセスするロールをクラスタ全体に設定します。

~~~file:clusterRole.yaml
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: ibmcloud-object-storage-plugin-psp-user
rules:
  - apiGroups: ['policy']
    resources: ['podsecuritypolicies']
    verbs:     ['use']
    resourceNames:
    - ibm-object-storage-plugin-psp
~~~


名前空間 icos-client に、先に作成した役割、すなわち、権限を対応付けます。

~~~file:clusterRoleBinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
    name: ibmcloud-object-storage-plugin-psp-user
    namespace: icos-client
roleRef:
    apiGroup: rbac.authorization.k8s.io
    kind: ClusterRole
    name: ibmcloud-object-storage-plugin-psp-user
subjects:
  - kind: ServiceAccount
    name: ibmcloud-object-storage-plugin
    namespace: icos-client
  - kind: ServiceAccount
    name: ibmcloud-object-storage-driver
    namespace: icos-client
~~~

これで、RBACの設定は完了です。


## ICOSのアクセス資格情報を保存するシークレットの作成

シークレット(Secret)は、秘匿性が必要な情報を、名前空間に保存するためのAPIオブジェクトです。前述の apikey と resource_instance_id の値をbase64でエンコードして、下記のYAMLを完成させます。シークレット名は icos01として、名前空間 icos-client に作成します。

~~~file:secret.yaml
apiVersion: v1
kind: Secret
type: ibm/ibmc-s3fs
metadata:
  name: icos01
  namespace: icos-client
data:
  api-key: <base64_apikey>
  service-instance-id: <base64_resource_instance_id>
~~~

Base64エンコーディングは、次の様にして画面に表示された文字列を前述のYAMLファイルにコピペします。

~~~
$ echo -n "<key_value>" | base64
~~~

シークレットを作成するマニフェストの適用

~~~
$ kubectl apply -f secret.yaml
~~~


## HELMチャートのカスタマイズ

ここでは、リポジトリにあるICOSプラグインのHELMチャートを少し修正して適用するために、一旦、ローカルにダウンロードして、編集したチャートを適用します。次は、チャートをダウンロードして、ディレクトリへ移動するコマンドです。

~~~
$ helm fetch --untar iks-charts/ibm-object-storage-plugin && cd ibm-object-storage-plugin
$ cd ./templates && ls
$ vi provisioner-sa.yaml
~~~


デフォルトでは、ICOSプラグインはクラスタ内のすべてのシークレットにアクセスすることを許可されています。そこで、プラグインを制限して、指定したシークレットだけにアクセスできるようにします。
そこで、provisioner-sa.yamlの以下の部分を編集します。

~~~file:provisioner-sa.yaml抜粋
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: ibmcloud-object-storage-secret-reader
rules:
- apiGroups: [""]
  resources: ["secrets"]
  resourceNames: ["icos01"]  ## <<-- 編集箇所、シークレット名を設定する
  verbs: ["get"]
~~~

## ICOSプラグインのインストール

引き続き、ローカルにダウンロードした HELMチャートのディレクトリで作業をします。　次のコマンドで、HELMチャートのディレクトリから、HELMクライアントにプラグインを追加します。

~~~
$ helm plugin install ./helm-ibmc
~~~

次のコマンドで、helm コマンドに、ICOSプラグインがインストールされたか確認ができます。

~~~
$ helm ibmc --help
~~~

おそらく実行権がなくて動作しないので、以下のコマンドで、実行権を付与します。

~~~
chmod +x  ~/.helm/plugins/helm-ibmc/ibmc.sh
~~~

これで、このibmcサブコマンドを使って、k8sクラスタへインストールを実行します。

~~~
helm ibmc install ./ --name ibm-object-storage-plugin
~~~

次のコマンドでICOSをマウントするためのドライバーとプラグイン、そして、ストレージクラスが作成しられた事を確認します。

~~~
$ kubectl get pod -n icos-client -o wide | grep object
$ kubectl get storageclass | grep 'ibmc-s3fs'
~~~

これで、ICOSをコンテナからマウントするための準備は完了です。次に、コンテナからマウントを実施します。


## ポッド内コンテナからICOSのバケットをマウントする。

最初に永続ボリューム要求(PersistentVolumeClaim)を作成します。アノテーションにICOSを利用する際の条件や、アクセス許可情報が入ったシークレットを指定します。

~~~file:pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: icos-pvc
  namespace: icos-client
  annotations:
    ibm.io/auto-create-bucket: "true"   ## バケットを作成する
    ibm.io/auto-delete-bucket: "false"  ## バケットを消さない
    ibm.io/bucket: "takara-bucket-123"  ## バケット名
    #ibm.io/object-path: "/var/backups" ## 既存のバケットを利用する時は有効化
    ibm.io/secret-name: "icos01"        ## サービス資格情報のシークレット名
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 8Gi # Enter a fictitious value
  storageClassName: ibmc-s3fs-standard-regional
~~~

次は、上記のPVCをアクセスするデプロイメントです。Nginxコンテナの /var/backups に、ICOSのバケット /var/backups をマウントします。

~~~file:deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: icos-client
  namespace: icos-client
  labels:
    app: icos-client
spec:
  selector:
    matchLabels:
      app: icos-c
  template:
    metadata:
      labels:
        app: icos-c
    spec:
      containers:
      - image: nginx
        name: nginx
        securityContext:
          runAsUser: 0
        volumeMounts:
        - name: icos-bucket
          mountPath: /var/backups  ## マウント先のパス
      volumes:
      - name: icos-bucket
        persistentVolumeClaim:
          claimName: icos-pvc      ## ICOS の PVC名
~~~

以上のマニフェストをデプロイします。

~~~
$ kubectl apply -f pvc.yaml
$ kubectl apply -f deployment.yaml
~~~


## 動作確認

Kubernetesクラスタのポッドに入って、ICOSのマウントしている状態を確認します。
まず、ポッド名を取得します。

~~~
$ kubectl get pod -n icos-client
NAME                           READY   STATUS    RESTARTS   AGE
icos-client-75c8bd5cb5-54shq   1/1     Running   0          143m
~~~
上記のポッドに対話型シェルを起動して、ファイルシステムのマウント状態を確認します。
「s3fs」としてマウントされているのが確認できます。

~~~
$ kubectl exec -it icos-client-75c8bd5cb5-54shq sh  -n icos-client
imac:IBM_Cloud_Object_Storage maho$ kc exec -it icos-client-75c8bd5cb5-54shq sh  -n icos-client
# df
Filesystem                 1K-blocks    Used    Available Use% Mounted on
overlay                    103079200 2374536     95445504   3% /
tmpfs                          65536       0        65536   0% /dev
tmpfs                        2022516       0      2022516   0% /sys/fs/cgroup
s3fs                    274877906944       0 274877906944   0% /var/backups
<以下省略>
~~~

コンテナ内のシェルから、ICOSへの書き込みテストを行ってみます。

~~~
# cd /var/backups
# ls -al
total 5
drwxrwxr-x 1 root root    0 Jan  1  1970 .
drwxr-xr-x 1 root root 4096 May  6 00:00 ..
# ls -lR / > ls-lR.txt
# ls -la
total 4849
drwxrwxr-x 1 root root       0 Jan  1  1970 .
drwxr-xr-x 1 root root    4096 May  6 00:00 ..
-rw-r--r-- 1 root root 4960369 May 12 09:54 ls-lR.txt
# ps -ax > ps-ax.txt
sh: 8: ps: not found
# tar cf nginx-container.tar /usr
tar: Removing leading `/' from member names
tar: Removing leading `/' from hard link targets
# ls -la
total 89140
drwxrwxr-x 1 root root        0 Jan  1  1970 .
drwxr-xr-x 1 root root     4096 May  6 00:00 ..
-rw-r--r-- 1 root root  4960369 May 12 09:54 ls-lR.txt
-rw-r--r-- 1 root root 86312960 May 12 09:56 nginx-container.tar
-rw-r--r-- 1 root root        0 May 12 09:54 ps-ax.txt
~~~

コンテナで書き込んだファイルは、ICOSのウェブの管理画面からオブジェクトとしてリストを見ることができます。

![スクリーンショット 2019-05-12 21.28.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42829/183a87d7-d46a-2ee9-a158-04baeb13d10a.png)



## まとめ

手軽に従量課金のICOSというS3互換のオブジェクト・ストレージのバケットを、ポッド内コンテナからマウントして、ファイルシステムのようにアクセスできることが確認できました。　ポッドとICOSの間は、HTTPSですから、ランダムアクセスは不得意ですが、画像や動画などサイズの大きなファイルなどを、保存する先として有用なものと思います。



## 参考資料
[1] IBM Object Storage Plugin, https://cloud.ibm.com/kubernetes/solutions/helm-charts/ibm-charts/ibm-object-storage-plugin
[2] IBM Cloud Object Storage へのデータの保管, https://cloud.ibm.com/docs/containers?topic=containers-object_storage#add_cos
[3] Qiita Helmの使い方を3分で理解する, https://qiita.com/MahoTakara/items/3c509235cc18bd407f07