Deploy Heketi-Glusterfs for dynamic storage

5 minute read

In this post, I will show you how to deploy Heketi-Glusterfs on Kubernetes cluster for dynamic storage. Here I assumed that you already have a Kubernetes cluster, if not, please follow my previous blog to build one.

Cluster Status

Master nodes:

  • k8s-cluster-k8s-master-1 192.168.147.6

Worker nodes with volumn attached (/dev/vdb):

  • k8s-cluster-k8s-node-nf-1 192.168.147.13 /dev/vdb
  • k8s-cluster-k8s-node-nf-2 192.168.147.17 /dev/vdb
  • k8s-cluster-k8s-node-nf-3 192.168.147.25 /dev/vdb
  • k8s-cluster-k8s-node-nf-4 192.168.147.16 /dev/vdb

Volumn setup

Note:

  • You just need to create the volumns (‘/dev/vdb’) and attach them to the worker nodes, please don’t do anything else to the volumns.

Requirements

First of all, we need to install glusterfs and load lvm kernel modules in all of our nodes. We can use ansible-playbook to achieve it. Create a ‘prepare_heketi.yml’ file with content as below.


- hosts: all
  become: yes
  tasks:
  - name: "Load lvm kernel modules"
    with_items:
      - "dm_snapshot"
      - "dm_mirror"
      - "dm_thin_pool"
    modprobe:
      name: "{{ item }}"
      state: "present"

  - name: add latest glusterfs YUM repository
    yum_repository:
      name: glusterfs
      description: glusterfs repository
      baseurl: http://mirror.centos.org/centos/7/storage/x86_64/gluster-7
      gpgcheck: no

  - name: install glusterfs-fuse
    yum:
      name: glusterfs-fuse
      state: present
      update_cache: true

  - name: install glusterfs
    yum:
      name: glusterfs
      state: present
      update_cache: true


Then run 
```bash
ansible-playbook --become -i hosts prepare_heketi.yml

Note:

  • Using above setup, we will install glusterfs>=7, which is matched with the gluster versions we will deploy in the pods.

Remove taint of the master node.

kubectl taint node k8s-cluster-k8s-master-1 node-role.kubernetes.io/master:NoSchedule-

Download files for deployment

wget https://raw.githubusercontent.com/heketi/heketi/master/extras/kubernetes/glusterfs-daemonset.json
wget https://raw.githubusercontent.com/heketi/heketi/master/extras/kubernetes/heketi-bootstrap.json
wget https://raw.githubusercontent.com/heketi/heketi/master/extras/kubernetes/heketi-deployment.json
wget https://raw.githubusercontent.com/heketi/heketi/master/extras/kubernetes/heketi-service-account.json
wget https://raw.githubusercontent.com/heketi/heketi/master/extras/kubernetes/heketi.json
wget https://raw.githubusercontent.com/heketi/heketi/master/extras/kubernetes/topology-sample.json

Deploy glusterfs

Label the nodes which will used to deploy glusterfs

kubectl label node k8s-cluster-k8s-node-nf-1 storagenode=glusterfs
kubectl label node k8s-cluster-k8s-node-nf-2 storagenode=glusterfs
kubectl label node k8s-cluster-k8s-node-nf-3 storagenode=glusterfs
kubectl label node k8s-cluster-k8s-node-nf-4 storagenode=glusterfs

Modified ‘glusterfs-daemonset.json’, as

{
    "kind": "DaemonSet",
    "apiVersion": "apps/v1",
    "metadata": {
        "name": "glusterfs",
        "labels": {
            "glusterfs": "deployment"
        },
        "annotations": {
            "description": "GlusterFS Daemon Set",
            "tags": "glusterfs"
        }
    },
    "spec": {
        "selector": {
            "matchLabels": {
                "glusterfs-node": "daemonset"
            }
        },
        "template": {
            "metadata": {
                "name": "glusterfs",
                "labels": {
                    "glusterfs-node": "daemonset"
                }
            },
...

Deploy the glusterfs-daemonset by

kubectl create -f glusterfs-daemonset.json

Deploy heketi server

Create a keketi service account and grant it access to control the gluster pods.

kubectl create -f heketi-service-account.json
kubectl create clusterrolebinding heketi-gluster-admin --clusterrole=edit --serviceaccount=default:heketi-service-account

Create a Kubernetes secret to save the configuration of our heketi instance. Let’s modify the heketi.json as

{
  "_port_comment": "Heketi Server Port Number",
  "port": "8080",

  "_use_auth": "Enable JWT authorization. Please enable for deployment",
  "use_auth": true,

  "_jwt": "Private keys for access",
  "jwt": {
    "_admin": "Admin has access to all APIs",
    "admin": {
      "key": "adminkey"
    },
    "_user": "User only has access to /volumes endpoint",
    "user": {
      "key": "My Secret"
    }
  },

  "_glusterfs_comment": "GlusterFS Configuration",
  "glusterfs": {
    "_executor_comment": "Execute plugin. Possible choices: mock, kubernetes, ssh",
    "executor": "kubernetes",

    "_db_comment": "Database file name",
    "db": "/var/lib/heketi/heketi.db",

    "kubeexec": {
      "rebalance_on_expansion": true
    },

    "sshexec": {
      "rebalance_on_expansion": true,
      "keyfile": "/etc/heketi/private_key",
      "fstab": "/etc/fstab",
      "port": "22",
      "user": "root",
      "sudo": false
    }
  },

  "_backup_db_to_kube_secret": "Backup the heketi database to a Kubernetes secret when running in Kubernetes. Default is off.",
  "backup_db_to_kube_secret": true
}

Then we can create the secret by execuating

kubectl create secret generic heketi-config-secret --from-file=./heketi.json

Deploy the heketi container. To do so, you need to modify ‘heketi-bootstrap.json’ as follows.

{
  "kind": "List",
  "apiVersion": "v1",
  "items": [
    {
      "kind": "Service",
      "apiVersion": "v1",
      "metadata": {
        "name": "deploy-heketi",
        "labels": {
          "glusterfs": "heketi-service",
          "deploy-heketi": "support"
        },
        "annotations": {
          "description": "Exposes Heketi Service"
        }
      },
      "spec": {
        "selector": {
          "name": "deploy-heketi"
        },
        "ports": [
          {
            "name": "deploy-heketi",
            "port": 8080,
            "targetPort": 8080
          }
        ]
      }
    },
    {
      "kind": "Deployment",
      "apiVersion": "apps/v1",
      "metadata": {
        "name": "deploy-heketi",
        "labels": {
          "glusterfs": "heketi-deployment",
          "deploy-heketi": "deployment"
        },
        "annotations": {
          "description": "Defines how to deploy Heketi"
        }
      },
      "spec": {
        "selector": {
          "matchLabels": {
            "name": "deploy-heketi"
           }
         }, 
       "replicas": 1,
        "template": {
          "metadata": {
            "name": "deploy-heketi",
            "labels": {
              "name": "deploy-heketi",
              "glusterfs": "heketi-pod",
              "deploy-heketi": "pod"
            }
          },
          "spec": {
            "serviceAccountName": "heketi-service-account",
            "containers": [
              {
                "image": "heketi/heketi:9",
                "imagePullPolicy": "Always",
                "name": "deploy-heketi",
                "env": [
                  {
                    "name": "HEKETI_EXECUTOR",
                    "value": "kubernetes"
                  },
                  {
...

Then execute

kubectl create -f heketi-bootstrap.json

Configure heketi client

Download the heketi client and make it ready to use. (Note that the heketi client and sever should have the same version, which is 9.0.0 in this tutorial)

wget https://github.com/heketi/heketi/releases/download/v9.0.0/heketi-client-v9.0.0.linux.amd64.tar.gz
tar zxvf heketi-client-v9.0.0.linux.amd64.tar.gz
sudo cp heketi-client/bin/heketi-cli /usr/local/bin/

Modify the ‘topology-sample.json’ file to configure your cluster status (replace the ip addresses and node names with your own), e.g.,

{
    "clusters": [
        {
            "nodes": [
                {
                    "node": {
                        "hostnames": {
                            "manage": [
                                "k8s-cluster-k8s-node-nf-1"
                            ],
                            "storage": [
                                "192.168.147.15"
                            ]
                        },
                        "zone": 1
                    },
                    "devices": [
                        {
                            "name": "/dev/vdb"
                        }
                    ]
                },
                {
                    "node": {
                        "hostnames": {
                            "manage": [
                                "k8s-cluster-k8s-node-nf-2"
                            ],
                            "storage": [
                                "192.168.147.5"
                            ]
                        },
                        "zone": 1
                    },
                    "devices": [
                        {
                            "name": "/dev/vdb"
                        }
                    ]
                },
                {
                    "node": {
                        "hostnames": {
                            "manage": [
                                "k8s-cluster-k8s-node-nf-3"
                            ],
                            "storage": [
                                "192.168.147.8"
                            ]
                        },
                        "zone": 1
                    },
                    "devices": [
                        {
                            "name": "/dev/vdb"
                        }
                    ]
                },
                {
                    "node": {
                        "hostnames": {
                            "manage": [
                                "k8s-cluster-k8s-node-nf-4"
                            ],
                            "storage": [
                                "192.168.147.14"
                            ]
                        },
                        "zone": 1
                    },
                    "devices": [
                        {
                            "name": "/dev/vdb"
                        }
                    ]
                }
            ]
        }
    ]
}

Initialize the cluster

Set up envirnment variables for Heketi.

export HEKETI_CLI_SERVER=http://10.233.29.138:8080
export HEKETI_CLI_USER=admin
export HEKETI_CLI_KEY=adminkey

Note that ‘10.233.29.138’ is the ClusterIP of heketi service, which you can obtain by executing kubectl get svc|grep heketi.

Create the cluster

heketi-cli  topology load --json=topology-sample.json

Now you may need to install device-mapper* if you want to have the heketi have persistent volumns.

You may need go back your local system and try to install device-mapper* on all the nodes.

ansible -i inventory/my-kube/hosts -m yum -a "name=device-mapper*" -u centos --become all

Generate ‘heketi-storage.json’ by running

heketi-cli setup-openshift-heketi-storage

In ‘heketi-storage.json’ file, we need to change the “heketi/heketi:dev” to “heketi/heketi:9”,

sed -i 's/heketi:dev/heketi:9/g' heketi-storage.json

Then

kubectl create -f heketi-storage.json

After the pod, ‘heketi-storage-copy-job’, is completed, you can delete the temporary heketi deployment used for setting up.

kubectl delete all,svc,jobs,deployment,secret --selector="deploy-heketi"

Finally, you can deploy the persistent heketi. Modify ‘heketi-deployment.json’,

......
"image": "heketi/heketi:9",
......
"spec": {
          "selector": {
            "matchLabels": {
                "name": "heketi"
            }
        },
......

Then create the deployment by

kubectl create -f heketi-deployment.json 

Check the new ClusterIP for heketi by kubectl get svc|grep heketi and set it up again.

export HEKETI_CLI_SERVER=http://10.233.42.189:8080

We can check the HEKETI_CLI_SERVER is accessible or not, as follows.

[centos@k8s-cluster-k8s-master-1 build_heketi]$ curl $HEKETI_CLI_SERVER/hello
Hello from Heketi

Check the cluster id which will be used for storageclass-clusterid.

[centos@k8s-cluster-k8s-master-1 build_heketi]$ heketi-cli cluster list
Clusters:
Id:5108ea8a533e7366b26755470cf6d242 [file][block]

Finally, using the heketi clusters id, ‘5108ea8a533e7366b26755470cf6d242’, and resturl, “http://10.233.42.189:8080”, we can create our storageclass as follows,

kind: StorageClass
apiVersion: storage.k8s.io/v1beta1
metadata:
  name: gluster-heketi
provisioner: kubernetes.io/glusterfs
parameters:
  resturl: "http://10.233.42.189:8080"
  restuser: "admin"
  restuserkey: "adminkey"
  restauthenabled: "true"
  clusterid: "5108ea8a533e7366b26755470cf6d242"
allowVolumeExpansion: true
kubectl create -f storageclass.yml