DRBDのFlexVolume driverを作ってみる。

# TL;DR

- KubernetesのFlexVolume driverを自作してみた。
- DRBDボリュームをマウントするだけの簡単なdriver、bashで作成。
- Volume周りの動きが分かるようになり、CSIの理解にもつながる。

# 検証の前に
これまでの投稿で書いているように、PostgreSQL on Kubernetesをする際に[Rook](https://rook.io)（というかCeph）を使ってきました。しかし、Cephは**分散**に重きをおいており、ストレージ・ノードが少ない環境では十分な性能が出るものではないようです。
*※私のCeph RBDのチューニングスキルが低いことも付け加えておきます。*

すると、今回考えているKubernetesを用いたHAのDBクラスタ構成で性能も同時に向上させたい場合、**データ・ローカリティ**の観点が重要になることに気付きます。つまり、

- DBMSのPodと同一ノードにデータが配置され、KubernetesがPodを動かした際にはデータもその先に準備されている

という状態を作ることで、DBからみたIOレイテンシの改善を図ることが可能です。

Kubernetesという文脈からは外れますが、データ・ローカリティについて分かりやすいのはNutanixについての[こちらの解説](https://www.nicpartners.co.jp/report/44170/)で、以下の特徴が挙げられています。

- Readでは単一ノードで、ディスクアクセスのみ行う。
- Writeでは最小限のノードで、データ冗長化のために通信を行う。

# DRBDによる永続ボリュームの冗長化
データ・ローカリティの観点で注目したのが[DRBD](https://drbd.jp/wd_features/)です。DRBD（Distributed Replicated Block Device）は、LinuxでHA構成を作る際にお馴染みのソフトウェアで、ネットワーク越しに複数サーバのHDD（パーティション）を同期してくれます。

現在のバージョンは**DRBD9**で、シンプルなレプリケーションだけでなくSDS機能を合わせ持ちます。そして、**<font color="red">コンテナやKubernetesへの対応も進められています</font>**。

## DRBDをKubernetesから利用する方法
DRBDをKubernetesから使う方法はサイオステクノロジー（旧サードウェア）の[DRBD Tech Info](https://blog.3ware.co.jp/)というブログに良くまとまっています。

やり方は大きく2つあり、KubernetesのFlexVolumeプラグインとして利用する方法とCSIプラグインを使用する方法が紹介されています。Kubernetes 1.13でCSIがGAとなり、今後はそちらがメインとなりますが、それぞれの特徴に簡単に触れてみます。

#### LINSTOR FlexVolume Provisioner
[こちらのブログ](https://blog.3ware.co.jp/2018/08/k8s-persistent-volume-by-drbd/)に具体的な構築方法が記述されていますが、概要としては以下のようになります。

- k8sの各ノードにDRBDとLINSTORというモジュール導入が必要。
- [LINSTOR](https://www.3ware.co.jp/solution/sds/linstor)は、KubernetesからDRBDを永続ボリュームとして扱う際の管理・制御を行う。
- LINSTOR External ProvisionerがMatsterノードに配置されてコントローラとして稼動、ボリュームの作成と削除を行う。
- LINSTOR FlexVolume Provisionerが各ノードに配置され、ボリュームの接続/切断、マウント/アンマウント等を行う。これがdriverの実体である。
 
但し、githubリポジトリは2019年4月時点でアーカイブされており、[DRBD9の公式ドキュメント](https://blog.3ware.co.jp/drbd-users-guide-9.0/drbd-users-guide.html)でもFlexVolume関連の記述はなくなっています。

#### linstor-csi plugin
こちらの構築方法も[同じブログ](https://blog.3ware.co.jp/2019/02/kubernetes-linstor-csi-plugin/)で紹介されています。

- linstor-csi-controllerというStatefulSetとlinstor-csi-nodeというDaemonSetがデプロイされる。
- 後はk8sのボリューム割当の手順（StorageClass、PVC、PV）を使ってボリュームをプロビジョニング可能。

こちらは当ポストとは別に検証を行う予定です。

## FlexVolume driverの試作
ここまで見てきて、DRBDをシンプルにKubernetesから使いたいだけなのに、意外と手順が面倒なことに気付きます。「各ノードにDRBDとLINSTORのモジュールを入れて、ノード登録も事前に行っておく」という部分があり、それをするならプロビジョニング（つまりボリュームの作成）も手動でやってしまえば、残りはPodからDRBDボリュームをマウントするだけです。

ということで、今回のKubernetesからのDRBD利用を以下2つの作業に分解します。

1. 各ノードにDRBDとLINSTORをインストールし、ボリュームとファイルシステム作成まで行う（ここまではホストOSレベルの作業）
2. 上記で準備したボリュームのマウントをk8sのPodから行うために、FlexVolumeのdriverを自作する。

FlexVolumeのdriver自体は難しいことを考えなければシェルでも実装が可能で、こうした[記事](http://leebriggs.co.uk/blog/2017/03/12/kubernetes-flexvolumes.html)でも紹介されています。それを踏まえ、下図の構成を作るに当たって「FlexVolume driver」の箇所をbashで書いてみることにします。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/54853/5fe90ed8-ba16-8b96-f28c-bdf3182bea77.png)

### DRBDデバイスをマウント／アンマウントするプラグイン
LVMのボリュームを操作するFlexVolume driverのサンプルが[ここ](https://github.com/sysoperator/kube-lvm-flexvolume)にありますので、これを参考にします。

上記にある[FlexVolume driver](https://github.com/sysoperator/kube-lvm-flexvolume/blob/master/lvm)を読むと、bashで必要なドライバのIFを実装しています。実際にどのようなIFが必要かはバージョンによって異なるため、自身のk8s環境とあわせる必要がありますが、

- Kubernetesのpkg/volume/flexvolume/driver-call.go

にドライバで実装すべきコマンドが記述されていますので、それを参照するのが最も確実でしょう。

今回の検証ではKubernetes v1.11（Rancher v2.1で使われているもの）を使っているので、そちらを参考にドライバを書いてみたものが以下となります。

```shell:mount関連を一部抽出
#!/bin/bash
domount() {
	MNTPATH=$1
	DMDEV=$2
	FSTYPE=$(echo $3|jq -r '.["kubernetes.io/fsType"]')

	if [ ! -b "${DMDEV}" ]; then
		err "{\"status\": \"Failure\", \"message\": \"${DMDEV} does not exist\"}"
		exit 1
	fi

	if [ $(ismounted) -eq 1 ] ; then
		log "{\"status\": \"Success\"}"
		exit 0
	fi

	VOLFSTYPE=`blkid -o udev ${DMDEV} 2>/dev/null|grep "ID_FS_TYPE"|cut -d"=" -f2`
	if [ "${VOLFSTYPE}" == "" ]; then
		mkfs -t ${FSTYPE} ${DMDEV}
		if [ $? -ne 0 ]; then
			err "{ \"status\": \"Failure\", \"message\": \"Failed to create fs ${FSTYPE} on device ${DMDEV}\"}"
			exit 1
		fi
	fi

	mkdir -p ${MNTPATH} &> /dev/null

	mount ${DMDEV} ${MNTPATH} &> /dev/null
	if [ $? -ne 0 ]; then
		err "{ \"status\": \"Failure\", \"message\": \"Failed to mount device ${DMDEV} at ${MNTPATH}\"}"
		exit 1
	fi
	log "{\"status\": \"Success\"}"
	exit 0
}

```
（開発中のドライバ全文は[こちら](https://github.com/tzkoba/postgres-on-k8s/blob/develop/postgres-install/pg-drbd/drbd.sh)）

### driverのデプロイとkubeletの設定変更
FlexVolumeのdriverが出来たら、それをKubernetesから使えるように設定していきます。具体的には以下の手順になります。

- kubeletに```--enable-controller-attach-detach=false```を設定して再起動
- 先述のbashをKubernetes各ノードに、```/usr/libexec/kubernetes/kubelet-plugins/volume/exec/<vendor~driver>/drbd```としてデプロイして実行権限を付与

kubeletのパラメータ修正やFlexVolumeプラグインの配置方法は環境によって異なるため、次の投稿で解説したいと思います。

# まとめ
ドライバの動作確認までを当投稿に書くつもりでしたが、思ったより分量が多いため、Rancher+Kubernetes環境へのデプロイと動作確認は次記事に譲りたいと思います。

FlexVolumeのdriverを書いてみようと考えたのは海外のブログポストを見つけたこと、そして、KubernetesのMeetupで「CNIのプラグインを自作してみた」という発表を見つけたのがきっかけになっています。

今回はFlexVolumeについてですが、今後の主流となるCSIのプラグインについても既存のものを読んでみたり、必要であれば書いていきたいと思いますので、よろしくお願いします。