Kubernetes界隈だと近頃の永続化ストレージはRook+Cephを扱った記事が多いようですが、こちらでCephは障害が多そうとか、Cephは最低3台必要でちょっとマシンリソース的に厳しいとかで、GlusterFSにしました。前の会社で、ちょっとGlusterFS+Heketiに因縁もあったし。
で、ベアメタル?オンプレミス?環境だから、GlusterFSをPodで動かすより、Kubernetes外部で動かして、データにアクセスしやすくしてみました。GlusterFSも推奨は3台ですが、最低2台でもいけるので、物理ホストと、新しい仮想サーバ(glusterfs-node1)でクラスタを構成しました。KVMの調子が悪くなっても物理ホストにデータが残るように。
物理ホスト
GlusterFSとHeketiのインストール
# yum install -y centos-release-gluster
# yum install -y glusterfs-server heketi heketi-client
# setsebool -P nis_enabled on
# firewall-cmd --add-service=glusterfs --permanent
# firewall-cmd --add-port=8080/tcp --permanent
# firewall-cmd --reload
- 8080はHeketiサーバ用のポート。
Glusterノードの作成
Vagrant.configure(2) do |config|
config.ssh.insert_key = false
$nodes = [
{ name: "gluster-node1", cpu: "2", memory: "4096", ip: "192.168.122.6", disk: "50G"}
]
$nodes.each do |param|
config.vm.define param[:name] do |node|
node.vm.box = "centos/7"
node.vm.hostname = param[:name]
node.vm.provider :libvirt do |v|
v.cpus = param[:cpu]
v.memory = param[:memory]
v.storage :file, :size => param[:disk], :type => "raw"
end
node.vm.network :private_network, ip: param[:ip], :libvirt__network_name => "default"
node.vm.provision :shell do |s|
ssh_insecure_key = File.read("#{Dir.home}/.vagrant.d/insecure_private_key")
s.inline = <<-SHELL
mkdir -p /root/.ssh
echo "#{ssh_insecure_key}" >> /root/.ssh/id_rsa
chmod 400 /root/.ssh/id_rsa
cp /home/vagrant/.ssh/authorized_keys /root/.ssh/authorized_keys
echo "#{ssh_insecure_key}" >> /home/vagrant/.ssh/id_rsa
chown vagrant /home/vagrant/.ssh/id_rsa
chmod 400 /home/vagrant/.ssh/id_rsa
SHELL
end
node.vm.provision :shell, inline: $script
end
end
end
$script = <<-SCRIPT
yum update -y
# SELinuxの無効化
setenforce 0
sed -i 's/^SELINUX=.*/SELINUX=disabled/g' /etc/selinux/config
# GlusterFSのインストール
yum install -y centos-release-gluster
yum install -y glusterfs-server
# GlusterFSの起動
systemctl start glusterd
systemctl enable glusterd
SCRIPT
# vagrant up
# echo "192.168.122.6 gluster-node1" >> /etc/hosts
GlusterFS用ディスクの作成
Glusterノードは、追加したストレージが/dev/vdbに割当たるので、それをそのまま使用します。
物理サーバは、構築時に残しておいた領域から論理ボリュームを作成します。
# fdisk /dev/sda
The device presents a logical sector size that is smaller than
the physical sector size. Aligning to a physical sector (or optimal
I/O) size boundary is recommended, or performance may be impacted.
Welcome to fdisk (util-linux 2.23.2).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.
コマンド (m でヘルプ): n
Partition type:
p primary (2 primary, 0 extended, 2 free)
e extended
Select (default p):
Using default response p
パーティション番号 (3,4, default 3):
最初 sector (475414528-584843263, 初期値 475414528):
初期値 475414528 を使います
Last sector, +sectors or +size{K,M,G} (475414528-584843263, 初期値 584843263):
初期値 584843263 を使います
Partition 3 of type Linux and of size 52.2 GiB is set
コマンド (m でヘルプ): t
パーティション番号 (1-3, default 3):
Hex code (type L to list all codes): 8e
Changed type of partition 'Linux' to 'Linux LVM'
コマンド (m でヘルプ): w
パーティションテーブルは変更されました!
ioctl() を呼び出してパーティションテーブルを再読込みします。
WARNING: Re-reading the partition table failed with error 16: デバイスもしくはリソースがビジー状態です.
The kernel still uses the old table. The new table will be used at
the next reboot or after you run partprobe(8) or kpartx(8)
ディスクを同期しています。
# partprobe /dev/sda
# pvcreate /dev/sda3
Physical volume "/dev/sda3" successfully created.
# vgcreate gluster /dev/sda3
# lvcreate --name kubernetes --size 52.1GB gluster
- 最初、パーティションのままHeketiに登録しようとしたらエラーになって途方に暮れたが、設定例で論理ボリュームを指定してるものがあって助かった。
GlusterFSクラスタの作成
後述するHeketiで、GlusterFSクラスタを作成してくれるのですが、今回の環境だとちょっと不都合が発生しました。
物理サーバからはプールを確認すると、、、
# gluster pool list
UUID Hostname State
35d6e969-b8cb-4f81-9a36-f0556fea7c5c 192.168.122.6 Connected
06c5039c-53d4-4ceb-a7a6-25c4bf090968 localhost Connected
で問題ないですが、Glusterノード側からは、、、
# gluster pool list
UUID Hostname State
06c5039c-53d4-4ceb-a7a6-25c4bf090968 192.168.122.1 Connected
35d6e969-b8cb-4f81-9a36-f0556fea7c5c localhost Connected
物理サーバのアドレスが、KVMネットワークのゲートウェイになってしまいます。GlusterFSのコマンドにはホスト名を指定するので、各ノードが認識しているホスト名とズレているとエラーになります。
- これは物理ホストから手動でgluster peer probe 192.168.122.6としたのと同じ状態です。
- ちなみにGlusterノード側からgluster peer probe ${物理ホストIPアドレス}とした場合には、物理ホスト側から見たGlusterノードはvagrant-libvirt管理ネットワークで割り振られたIPアドレス(192.168.121.*)になってしまいました。Glusterノードのデフォルトゲートウェイが管理ネットワークの方なのが悪いのだと思います。
ネットワークの設定を見直すべきなのかもしれませんが、ここでは/var/lib/glusterd/peers配下にあるファイル中のホスト名を修正してglusterdを再スタートすることで回避しました。
Heketiサーバの設定と起動
/etc/heketi/heketi.jsonを修正。
- "executor": "mock", ⇒ "executor": "ssh",
- "sshexec"配下を適切に設定。
"keyfile": "/var/lib/heketi/.ssh/id_rsa",
"user": "root",
"port": "22",
"fstab": "/etc/fstab"
heketiユーザで実行するHeketiサーバが、sshの公開鍵認証で各ノードにアクセスするので、Vagrantが生成した秘密鍵をheketiユーザホームに置いたり、物理ホスト側にも仮想サーバからauthorized_keysをコピーしたりしておきます。
そして起動。
# systemctl start heketi
# systemctl enable heketi
次にHeketiが管理するGlusterFSクラスタを設定します。
以下のようなノードとデバイスの組み合わせをJSON形式で定義してコマンドで登録します。
# cat <<EOF > topology.json
{
"clusters": [{
"nodes": [{
"node": {
"hostnames": {
"manage": ["物理サーバのIP"],
"storage": ["物理サーバのIP"]
},
"zone": 1
},
"devices": [{
"name": "/dev/mapper/gluster-kubernetes",
"destroydata": false
}]
}, {
"node": {
"hostnames": {
"manage": ["192.168.122.6"],
"storage": ["192.168.122.6"]
},
"zone": 1
},
"devices": [{
"name": "/dev/vdb",
"destroydata": false
}]
}]
}]
}
EOF
# heketi-cli topology load --json topology.json
Creating cluster ... ID: 0627d2d2996d2f903434b296fd89c4c0
Allowing file volumes on cluster.
Allowing block volumes on cluster.
Creating node 物理サーバのIP ... ID: 115f74b5a300266ff99cb940a3f18ce3
Adding device /dev/mapper/gluster-kubernetes ... OK
Found node 192.168.122.6 on cluster 556644d30e71710cf76f3e74e15b251b
Found device /dev/vdb
ストレージクラスの登録
Kubernetesに、作成したHeketiを利用するストレージクラスを登録します。
# cat <<EOF > gluster.yaml
kind: StorageClass
apiVersion: storage.k8s.io/v1beta1
metadata:
name: gluster-dyn
provisioner: kubernetes.io/glusterfs
parameters:
resturl: "http://kubernetes.local:8080"
restauthenabled: "false"
volumetype: "replicate:2"
EOF
# kubectl create -f gluster.yaml
これで環境としては構築できたので、実際にストレージを要求してみます。
# cat <<EOF > gluster-test.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: gluster-dyn-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
storageClassName: gluster-dyn
EOF
# kubectl create -f gluster-test.yaml
# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
gluster-dyn-pvc Bound pvc-9fc01705-0f69-4798-b072-5a1d9ec270fe 1Gi RWX gluster-dyn 11m