TL;DR
- KubernetesのFlexVolume driverを自作してみた。
- DRBDボリュームをマウントするだけの簡単なdriver、bashで作成。
- Volume周りの動きが分かるようになり、CSIの理解にもつながる。
検証の前に
これまでの投稿で書いているように、PostgreSQL on Kubernetesをする際にRook(というかCeph)を使ってきました。しかし、Cephは分散に重きをおいており、ストレージ・ノードが少ない環境では十分な性能が出るものではないようです。
※私のCeph RBDのチューニングスキルが低いことも付け加えておきます。
すると、今回考えているKubernetesを用いたHAのDBクラスタ構成で性能も同時に向上させたい場合、データ・ローカリティの観点が重要になることに気付きます。つまり、
- DBMSのPodと同一ノードにデータが配置され、KubernetesがPodを動かした際にはデータもその先に準備されている
という状態を作ることで、DBからみたIOレイテンシの改善を図ることが可能です。
Kubernetesという文脈からは外れますが、データ・ローカリティについて分かりやすいのはNutanixについてのこちらの解説で、以下の特徴が挙げられています。
- Readでは単一ノードで、ディスクアクセスのみ行う。
- Writeでは最小限のノードで、データ冗長化のために通信を行う。
DRBDによる永続ボリュームの冗長化
データ・ローカリティの観点で注目したのがDRBDです。DRBD(Distributed Replicated Block Device)は、LinuxでHA構成を作る際にお馴染みのソフトウェアで、ネットワーク越しに複数サーバのHDD(パーティション)を同期してくれます。
現在のバージョンはDRBD9で、シンプルなレプリケーションだけでなくSDS機能を合わせ持ちます。そして、コンテナやKubernetesへの対応も進められています。
DRBDをKubernetesから利用する方法
DRBDをKubernetesから使う方法はサイオステクノロジー(旧サードウェア)のDRBD Tech Infoというブログに良くまとまっています。
やり方は大きく2つあり、KubernetesのFlexVolumeプラグインとして利用する方法とCSIプラグインを使用する方法が紹介されています。Kubernetes 1.13でCSIがGAとなり、今後はそちらがメインとなりますが、それぞれの特徴に簡単に触れてみます。
LINSTOR FlexVolume Provisioner
こちらのブログに具体的な構築方法が記述されていますが、概要としては以下のようになります。
- k8sの各ノードにDRBDとLINSTORというモジュール導入が必要。
- LINSTORは、KubernetesからDRBDを永続ボリュームとして扱う際の管理・制御を行う。
- LINSTOR External ProvisionerがMatsterノードに配置されてコントローラとして稼動、ボリュームの作成と削除を行う。
- LINSTOR FlexVolume Provisionerが各ノードに配置され、ボリュームの接続/切断、マウント/アンマウント等を行う。これがdriverの実体である。
但し、githubリポジトリは2019年4月時点でアーカイブされており、DRBD9の公式ドキュメントでもFlexVolume関連の記述はなくなっています。
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つの作業に分解します。
- 各ノードにDRBDとLINSTORをインストールし、ボリュームとファイルシステム作成まで行う(ここまではホストOSレベルの作業)
- 上記で準備したボリュームのマウントをk8sのPodから行うために、FlexVolumeのdriverを自作する。
FlexVolumeのdriver自体は難しいことを考えなければシェルでも実装が可能で、こうした記事でも紹介されています。それを踏まえ、下図の構成を作るに当たって「FlexVolume driver」の箇所をbashで書いてみることにします。
DRBDデバイスをマウント/アンマウントするプラグイン
LVMのボリュームを操作するFlexVolume driverのサンプルがここにありますので、これを参考にします。
上記にあるFlexVolume driverを読むと、bashで必要なドライバのIFを実装しています。実際にどのようなIFが必要かはバージョンによって異なるため、自身のk8s環境とあわせる必要がありますが、
- Kubernetesのpkg/volume/flexvolume/driver-call.go
にドライバで実装すべきコマンドが記述されていますので、それを参照するのが最も確実でしょう。
今回の検証ではKubernetes v1.11(Rancher v2.1で使われているもの)を使っているので、そちらを参考にドライバを書いてみたものが以下となります。
#!/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
}
(開発中のドライバ全文はこちら)
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のプラグインについても既存のものを読んでみたり、必要であれば書いていきたいと思いますので、よろしくお願いします。