kubernetesが外部のGlusterFSを使えるようにして、データを永続化できるようにする

Kubernetes界隈だと近頃の永続化ストレージはRook+Cephを扱った記事が多いようですが、[こちら](https://qiita.com/ozota/items/3b6e4374688cc989ca12)でCephは障害が多そうとか、Cephは最低3台必要でちょっとマシンリソース的に厳しいとかで、GlusterFSにしました。前の会社で、ちょっとGlusterFS+Heketiに因縁もあったし。

で、ベアメタル?オンプレミス?環境だから、GlusterFSをPodで動かすより、Kubernetes外部で動かして、データにアクセスしやすくしてみました。GlusterFSも推奨は3台ですが、最低2台でもいけるので、物理ホストと、新しい仮想サーバ(glusterfs-node1)でクラスタを構成しました。KVMの調子が悪くなっても物理ホストにデータが残るように。

![](http://www.plantuml.com/plantuml/png/XP4_IyD06CRt-nGtrY52Rt8efIBuXo8A1QSkr-Ge9PSxIIueI9SSg5saWvlWuc8HwI0EWk27UOT-3U-QD4TYYbloFE_pU_zSBr4qLcd4aP01mwlPOZM_1Vq4sHVe-rQOZbWic6B9BfS-vJkuqpbWGfqDkbryWp3sPNGfXL7m_la9w7VGtw1deEUW5w0_8Fiq2J6UY6iJe0dcaWOZogdmMOnCVjqLzBFjb_s0ViW1xQ5eXNk95z54iRXjs_OlJdD4eQtRJxSZI0L16X2aZbZQPFRsPueh6OVrAGhjFmIf84W3matXSva6k5-UV2WWCxn5lfXDEAy8LLxWc8LCJOoKA1dJCSD7FBLx7WvMZw-Wxo1xMQEM5ZNElNO-UbbRsRseyMo1sxvCkzaHmfMNnPwtUQQj3a5uKsD5jrgfaxfU7jrB6mrIDupZ_h3dBfH_s79t2AS6fKeGcg8U4q4QyLy0)


### 物理ホスト
##### GlusterFSとHeketiのインストール
```shell-session
# 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ノードの作成
```ruby
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
```

```shell-session
# vagrant up
# echo "192.168.122.6 gluster-node1" >> /etc/hosts
```

##### GlusterFS用ディスクの作成
Glusterノードは、追加したストレージが/dev/vdbに割当たるので、それをそのまま使用します。
物理サーバは、構築時に残しておいた領域から論理ボリュームを作成します。

```shell-session
# 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をコピーしたりしておきます。
そして起動。

```shell-session
# systemctl start heketi
# systemctl enable heketi
```

次にHeketiが管理するGlusterFSクラスタを設定します。
以下のようなノードとデバイスの組み合わせをJSON形式で定義してコマンドで登録します。

```shell-session
# 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を利用するストレージクラスを登録します。

```shell_session
# 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
```

これで環境としては構築できたので、実際にストレージを要求してみます。

```shell_session
# 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

```