Kubernetes 1.17 と Ceph を CSI で連携を検証

ROOKを利用すれば、Kubernetesクラスタ上にCephなどを簡単に構築することができる。Kubernetesクラスタ上に作ることは、運用管理面で効率化が期待できる。一方、Cephを直接サーバーにインストールすれば、仮想サーバーや物理サーバーにインストールして、KubernetesクラスタをCSI(Conainer Storage Interface)を使って連携することもできる。

ダイレクトにCephを利用するメリットは、積み重ねるスタックが少なくて済み、Kubernetesのバージョンアップなどの影響を受けない点である。永続データそのものは、ストレージに格納されるため、RAIDを組むなどの対策があれば、遜色ないように思われる。しかし、失うことが許されないデータは、ソフトウェアスタックの少ない専用基盤の上に保存されることに安定感を感じる。

今後、ROOKを使っていくことになると思うが、今回は、CephクラスタとKubernetesと連携させることも顧慮できるように検証する。
検証の環境は、いつものように、アップストリームのKubernetesとCephである。


## サンドボックスディレクトの作成

これには解説は要らないと思うが、Vagrantのファイル一式などを、このディレクト下に配置する。

~~~
mkdir sandbox-1
cd sandbox-1
~~~

## Cephの起動

Cephクラスタの起動は、[Vagrant仮想マシンでCephクラスタをAnsibleで構築してみた](https://qiita.com/MahoTakara/items/468ae7ef7c0945c7fe36)で開発した [GitHub takara9/vagrant-ceph](https://github.com/takara9/vagrant-ceph) を利用する。

~~~
git clone https://github.com/takara9/vagrant-ceph ceph
cd ceph
vagrant up
~~~

## Cephの初期設定

基本は、Cephクラスタを構築するAnsibleのプレイブックに記述してあるが、ここでは、Kubenetesクラスタを接続するための設定を実施する。

Cephクラスタのマスターノードへログインして、設定を実施する。

~~~
vagramt ssh master
sudo -s
~~~

以下の５行をコピーして、ターミナルへペーストすることで、一気に入力を終えることができる。

~~~
ceph version
ceph osd pool create kubernetes 20
rbd pool init kubernetes
ceph auth get-or-create client.kubernetes mon 'profile rbd' osd 'profile rbd pool=kubernetes' mgr 'profile rbd'
ceph mon dump
~~~

以下は実際に実行した例で、結果として表示される key と fsid は、後に使用するのでメモしておく。

~~~
vagrant@master:~$ sudo -s
root@master:~# ceph version
ceph version 14.2.6 (f0aa067ac7a02ee46ea48aa26c6e298b5ea272e9) nautilus (stable)
root@master:~# ceph osd pool create kubernetes 20
pool 'kubernetes' created
root@master:~# rbd pool init kubernetes
root@master:~# ceph auth get-or-create client.kubernetes mon 'profile rbd' osd 'profile rbd pool=kubernetes' mgr 'profile rbd'
[client.kubernetes]
	key = AQARTCRe8YzyIBAAyILadDjDkabJ7qYBBMzfXg==  <--- クライアント認証のキー
root@master:~# ceph mon dump
dumped monmap epoch 1
epoch 1
fsid aca2c93e-e75c-4fc3-b0b0-eecba959270c　<--- クラスタID
last_changed 2020-01-19 12:26:07.410539
created 2020-01-19 12:26:07.410539
min_mon_release 14 (nautilus)
0: [v2:172.20.1.30:3300/0,v1:172.20.1.30:6789/0] mon.master
1: [v2:172.20.1.31:3300/0,v1:172.20.1.31:6789/0] mon.node1
2: [v2:172.20.1.32:3300/0,v1:172.20.1.32:6789/0] mon.node2
3: [v2:172.20.1.33:3300/0,v1:172.20.1.33:6789/0] mon.node3
~~~

ここまで完了すると、ブラウザからCephのコンソール画面を参照できる。URLアドレスは、以下のどちらかになり

* https://172.20.1.30:8443/   パソコン内LAN経由でアクセスする場合のURL
* https://192.168.1.90:8443/  パソコン外からアクセスする時のURL

このブラウザ画面は、自己署名証明書でTLS化されているので、アクセスするブラウザを選ぶ。筆者はFirefoxを利用してログインした。
ユーザーIDとパスワードは、admin/password が初期設定されている。

## Kubernetesクラスタの起動

最新のバージョンは、ブランチに作ってテストしてある。マイナーバージョンがリリースされて安定したので、1.17を使用する。

~~~
git clone -b 1.17 https://github.com/takara9/vagrant-kubernetes k8s
cd k8s
vagrant up
~~~

環境変数を設定して、仮想マシンのホストから、kubectlコマンドの接続を確認する。

~~~
tkr@luigi:~/sandbox-1/k8s$ export KUBECONFIG=`pwd`/kubeconfig/config
tkr@luigi:~/sandbox-1/k8s$ kubectl get no
NAME     STATUS   ROLES    AGE    VERSION
master   Ready    master   111s   v1.17.1
node1    Ready    <none>   80s    v1.17.1
node2    Ready    <none>   80s    v1.17.1
~~~

Ceph用の名前空間`ceph-csi`とコンテキスト`cc`を作成して、切り替える。

~~~
kubectl create ns ceph-csi
kubectl config set-context cc --namespace=ceph-csi --cluster=kubernetes --user=kubernetes-admin
kubectl config use-context cc
kubectl config get-contexts
~~~

## K8sとCeph連携の環境設定

K8sの名前空間上のポッドが、Cephクラスタにアクセスできるようにコンフィグマップを作成する。

~~~
cat <<EOF > csi-config-map.yaml
---
apiVersion: v1
kind: ConfigMap
data:
  config.json: |-
    [
      {
        "clusterID": "fsidで置き換える",
        "monitors": [
          "172.20.1.30:6789",
          "172.20.1.31:6789",
          "172.20.1.32:6789",
          "172.20.1.33:6789"
        ]
      }
    ]
metadata:
  name: ceph-csi-config
EOF
kubectl apply -f csi-config-map.yaml
~~~


次に、アクセスのためのキーを格納したシークレットを作成する。


~~~
cat <<EOF > csi-rbd-secret.yaml
---
apiVersion: v1
kind: Secret
metadata:
  name: csi-rbd-secret
  namespace: ceph-csi
stringData:
  userID: kubernetes
  userKey: keyで置き換える
EOF
kubectl apply -f csi-rbd-secret.yaml
~~~

## CSI実行のための権限設定(RBAC)

最初に、CSIを動かすための権限設定(RBAC)を適用する。ここで、前述のように名前空間を変更したので、エディタで開いて名前空間名を`ceph-csi`へ変更する。

~~~
curl -O https://raw.githubusercontent.com/ceph/ceph-csi/release-v1.2.0/deploy/rbd/kubernetes/v1.14%2B/csi-provisioner-rbac.yaml
vi csi-provisioner-rbac.yaml
kubectl apply -f csi-provisioner-rbac.yaml
~~~

同様に、名前空間を変更する。

~~~
curl -O https://raw.githubusercontent.com/ceph/ceph-csi/release-v1.2.0/deploy/rbd/kubernetes/v1.14%2B/csi-nodeplugin-rbac.yaml
vi csi-nodeplugin-rbac.yaml
kubectl apply -f csi-nodeplugin-rbac.yaml
~~~

## CSIのポッドを起動

この中には、名前空間が設定されていないので、そのまま、実行する。

~~~
kubectl apply -f https://raw.githubusercontent.com/ceph/ceph-csi/release-v1.2.0/deploy/rbd/kubernetes/v1.14%2B/csi-rbdplugin-provisioner.yaml
kubectl apply -f https://raw.githubusercontent.com/ceph/ceph-csi/release-v1.2.0/deploy/rbd/kubernetes/v1.14%2B/csi-rbdplugin.yaml
~~~

ポッドのSTATUSが、Running に変わることを確認する。

~~~
kubectl get po -w
~~~

## ストレージクラスの設定

ストレージクラスを設定して、PVCからPVが作成できるようにする。

~~~
cat <<EOF > csi-rbd-sc.yaml
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
   name: csi-rbd-sc
provisioner: rbd.csi.ceph.com
parameters:
   clusterID: ***fsidで置き換える***  <<--- 注意
   pool: kubernetes
   csi.storage.k8s.io/provisioner-secret-name: csi-rbd-secret
   csi.storage.k8s.io/provisioner-secret-namespace: default
   csi.storage.k8s.io/node-stage-secret-name: csi-rbd-secret
   csi.storage.k8s.io/node-stage-secret-namespace: default
reclaimPolicy: Delete
mountOptions:
   - discard
EOF
$ kubectl apply -f csi-rbd-sc.yaml
~~~

それから、デフォルトのストレージクラスへ変更する。

~~~
kubectl patch storageclass csi-rbd-sc -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
kubectl get sc
~~~

これで、Kubernetesクラスタから、Cephのブロック・ストレージをダイナミック・プロビジョニングできるようになった。


## ブロックデバイスとしてマウントの確認

ブロックデバイスとして、/dex/xvda を Cephのブロックストレージをマッピングする。
最初にPVCを作成する。この中で `spec.volumeMode: Block` を指定することで、ブロックデバイスがコンテナのでデバイスとして利用できる。

~~~
cat <<EOF > raw-block-pvc.yaml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: raw-block-pvc
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Block
  resources:
    requests:
      storage: 1Gi
  storageClassName: csi-rbd-sc
EOF
kubectl apply -f raw-block-pvc.yaml
~~~

ポッドのマニフェストで`devicePath: /dev/xvda`が設定され、ファイルシステムとしてマウントすることができない。
筆者が試したところ、このデバイスは、mkfs.ext4を実行して、ファイルシステムを書き込むことができるが、マウントはできない。
もちろん、RAWデバイスとして、読み書きする分には問題がない。　

~~~
cat <<EOF > raw-block-pod.yaml
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-with-raw-block-volume
spec:
  containers:
    - name: fc-container
      image: fedora:26
      command: ["/bin/sh", "-c"]
      args: ["tail -f /dev/null"]
      volumeDevices:
        - name: data
          devicePath: /dev/xvda
  volumes:
    - name: data
      persistentVolumeClaim:
        claimName: raw-block-pvc
EOF
kubectl apply -f raw-block-pod.yaml
~~~


## ブロックストレージをファイルシステムとしてマウント

PVCのマニフェストに`spec.volumeMode: Filesystem`を設定することで、ブロックデバイスをファイルシステムでフォーマットされたボリュームとしてマウントできる。

~~~
cat <<EOF > pvc.yaml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: rbd-pvc
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 1Gi
  storageClassName: csi-rbd-sc
EOF
kubectl apply -f pvc.yaml
~~~

こちらは、コンテナにストレージをマウントした状態で起動する。

~~~
cat <<EOF > pod.yaml
---
apiVersion: v1
kind: Pod
metadata:
  name: csi-rbd-demo-pod
spec:
  containers:
    - name: web-server
      image: nginx
      volumeMounts:
        - name: mypvc
          mountPath: /var/lib/www/html
  volumes:
    - name: mypvc
      persistentVolumeClaim:
        claimName: rbd-pvc
        readOnly: false
EOF
kubectl apply -f pod.yaml
~~~

## クリーンナップ

以下のコマンドで、仮想サーバーごと消去することできる。一時的に止めておきたい場合は、`vagrant halt`を利用すると良い。

~~~
cd sandbox-1/k8s
vagrant destroy -f
cd ../ceph
vagrant destroy -f
~~~

## まとめ

Cephを数台のサーバーにインストールして、ストレージを構築して、Kubernetesクラスタへ接続することを想定した検証を実施してみた。Glusterfsは、その名の通り、分散ファイルシステムとしての利用になるが、Cephはブロックストレージとして、RAWとファイルシステムとして利用可能なので、データベースのストレージとしても利用も期待できる。

プールの作成などで、Cephクラスタを操作する必要も出てくるが、論理ボリュームの割り当てや開放など、PVCを利用して実施できるのは素晴らしい。次はROOKを検証してみたい。


参考URL
https://docs.ceph.com/docs/master/rbd/rbd-kubernetes/#block-devices-and-kubernetes
https://kubernetes-csi.github.io/docs/secrets-and-credentials-storage-class.html