I would like to share my (perhaps a bit unusual) configuration of glusterFS cluster.

Warning: This text should not be treated as  ‘how to’, precisely and literally describing all steps necessary to carry out the whole installation. I have tried to describe all important steps, but I focused on more on the idea behind them, so there may be certain simplifications in the text.

I have considered a highly available storage for quite a while now. Choosing gluster resulted from, first of all, low ‘treshold of entry’. In comparison to a potential alternative - ceph definitely less equipment is needed to startup a minimal reasonable configuration.

I needed a gluster for 2 reasons:

  • as capacious (not necessarily very efficient, but with high availability) data warehouse (e.g. installation figures of systems/applications, repository/archive for builds of software created in a company)
  • in a time perspective, I was about to run a local OKD installation , for which I needed durable storage for containers.

Such requirements caused project decisions: integration with OKD enforced the use of heketi (through it, OKD integrates with the gluster), applications outside OKD, in turn, enforced “non-container” setup of the gluster on machines separate from the OKD.

Equipment

I used 4 machines in similar configurations to build it:

  • 2U housing with room for 12 3.5" hard drives, with 11 8T or 10T disks + 2 sata ssd 2.5" drives per system (in separate slots at the back of the server)
  • Supermicro X11-SSL-F mainboard (I really like this board – in the area of 'economic servers', in which it’s not necessary to have many PCI-E lines and lots of CPU cores)
  • Intel Xeon E3-12xx v6 processor
  • DDR4 ECC (2x 16GB) memory
  • RAID controller with cache and battery backup/flash (different models - Microsemi(Adaptec) or Broadcom(LSI))
  • additional network card 2x10Gbit

I configured system drives as mirror, with the use of software-raid on the mainboard, I connected 2 disks for the main storage to the raid and I configured RAID-10 on them, with one spare disk.

I chose CentOS 7 in quite a minimal configuration as the operating system, located it on dedicated SSD drives.

On 2 network cards embedded on the main board, I configured bond0 interface, used mainly for administrative purposes, on 10Gbit interfaces I configured the second interface bond1 for the needs of SAN/gluster network.

Each machine is visible as glusterN.softax (1Gbit network) and glusterN.san (10Gbit network).

Install the necessary cluster software:

# yum install centos-release-gluster6
# yum install glusterfs-server nfs-ganesha-gluster glusterfs-resource-agents
# yum install heketi heketi-client

A default cluster solution proposed together with glusterfs is ctdb. I did not quite like this solution (perhaps because I do not know it very well), it is not also vital for me to balance the load on the level of SMB between the machines of the cluster (where using CTDB was profitable). I definitely preferred to use pacemaker, which I know very well too and which I have been using for many years in different applications.

Unfortunately, the choice of pacemaker results in the fact that we cannot use an ‘automagic’ storhaug tool for configurating glusterfs cluster, but everything will have to be manually configured.

# yum install corosync pacemaker pacemaker-cli

To (re)configure pacemaker clusters, my favourite tool is crmsh

# rpm -ihv crmsh-3.0.0-6.2.noarch.rpm python-parallax-1.0.1-29.1.noarch.rpm

Because heketi likes to be self sustained within the whole device/partition it will receive, on the main data storage I created a partition for different than heketi needs. For now, I will use only a small part of it, but it is easier to leave a bit of space than struggle later on with resizing of the partition.

The final effect of partitioning of the main storage (not mentioning the system discs) is as follows:

# parted /dev/sdc
  GNU Parted 3.1
  Using /dev/sdc
  Welcome to GNU Parted! Type 'help' to view a list of commands.
  (parted) print
  Model: ASR8160 storage (scsi)
  Disk /dev/sdc: 50.0TB
  Sector size (logical/physical): 512B/512B
  Partition Table: gpt

  Disk Flags: 
  Number  Start   End     Size    File system  Name   Flags 
  1       1049kB  137GB   137GB   infra        lvm 
  2       137GB   50.0TB  49.8TB  data         lvm

Partition sdc2 will be for the needs of heketi – I do not configure anything manually on it.
On sdc1 partition I set up a volume group vgclusterinfoN and gluster_shared_storage volumen,  which I next mount in directory /var/lib/glusterd/ss_brick :

# pvcreate /dev/sdc1
# vgcreate vgclusterinfoN /dev/sdc1
# lvcreate --type thin-pool -l100%free -n thinpool vgclusterinfoN
# lvcreate -V1G -n gluster_shared_storage vgclusterinfoN/thinpool
# mkfs.xfs -i size=512 /dev/vgclusterinfoN/gluster_shared_storage
# mount /dev/vgclusterinfoN/gluster_shared_storage /var/lib/glusterd/ss_brick

(of course, we also must remember to add a new entry to /etc/fstab)

After making the above configurations on all 4 machines, we can start gluster configuration.

We have got 4 machines, and for making quorum an odd number is much better, so that is why I added the 5th machine to the 4 (superstorage server, used for other purposes) – I will not run any services/volumes on it, it will only be used while setting quorum.

We create a gluster cluster (we connect the machines with one another):

# gluster peer probe gluster2.san
# gluster peer probe gluster3.san
# gluster peer probe gluster4.san
# gluster peer probe superstorage.san

The command gluster pool list should at this moment show us all the 5 connected machines.

We prepare configuration for corosync (/etc/corosync/corosync.conf ):

totem {
    version: 2
    cluster_name: gluster
    transport: udpu
    secauth: on
}

nodelist {
    node {
        ring0_addr: gluster1.san
        nodeid: 1
    }
    node {
        ring0_addr: gluster2.san
        nodeid: 2
    }
    node {
        ring0_addr: gluster3.san
        nodeid: 3
    }
    node {
        ring0_addr: gluster4.san
        nodeid: 4
    }
    node {
        ring0_addr: superstorage.san
        nodeid: 99
    }

}

quorum {
    provider: corosync_votequorum
}

logging {
    to_logfile: yes
    logfile: /var/log/cluster/corosync.log
    to_syslog: yes
}

We start corosync first, and then pacemaker on all machines.
Having waited long enough for the cluster  to constitute, we have a look at crm-a and we make the initial configuration:
- We have not got shared disk resources, we can safely switch off stonith.
- We do not want any cluster services to be run on superstorage.

# crm
  crm(live) configure
  crm(live) property stonith-enabled=false
  crm(live) commit
  crm(live) cd
  crm(live) node standby superstorage.san

As a result we should get a cluster ready for services configuration:

# crm node list
  gluster1.san(1): normal
  standby=off
  gluster2.san(2): normal
  standby=off
  gluster3.san(3): normal
  standby=off
  gluster4.san(4): normal
  standby=off
  superstorage.san(99): normal
  standby=on

Next, we make a volume for cluster configurations:

# gluster volume create  gluster_shared_storage replica 4 gluster1.san:/var/lib/glusterd/ss_brick gluster2.san:/var/lib/glusterd/ss_brick gluster3.san:/var/lib/glusterd/ss_brick gluster4.san:/var/lib/glusterd/ss_brick

And next on all machines:

# mount -t glusterfs -o _netdev gluster.san /vol/gluster_shared_storage

(plus a corresponding to the above listing  in /etc/fstab)

This way in the path /vol/gluster_shared_storage we get filesystem shared between all nodes of the cluster.

I move to it (replacing the original files/catalogues with links):

  • corosync (/etc/corosync) configuration
  • samba (/etc/samba) configuration
  • nfs-ganesha (/etc/ganesha) configuration
  • heketi (/etc/heketi/heketi.json) configuration

At this moment we can configure application services for the cluster – in this case they will be:

  • virtual IP for both networks
  • heketi
  • samba and nfs-ganesha services
# crm configure
  crm(live) primitive gluster_san_trigger ganesha_trigger
  crm(live) primitive gluster_softax_trigger ganesha_trigger
  crm(live) primitive heketi systemd:heketi op monitor interval=60
  crm(live) primitive nfs-ganesha systemd:nfs-ganesha op monitor interval=61
  crm(live) primitive smb systemd:smb op monitor interval=59
  crm(live) primitive vip_gluster_san IPaddr2 params ip=1.1.1.2 cidr_netmask=22 nic=bond1 \
  op monitor interval=60
  crm(live) primitive vip_gluster_softax IPaddr2 params ip=2.2.2.180 cidr_netmask=24 nic=bond0 \
  op monitor interval=60
  crm(live) group gluster_san vip_gluster_san gluster_san_trigger heketi
  crm(live) group gluster_softax vip_gluster_softax gluster_softax_trigger vol_images smb
  crm(live) clone nfs-ganesha-clone nfs-ganesha meta notify=true target-role=Started
  crm(live) commit

(I ‘borrowed’ resource ganesh_trigger from storhaug – it ensures grace-period service after switching nfs server address to another machine)

The last element that requires configuring is heketi.

Heketi is a service enabling automation of glusterfs cluster management – after configuring resources it is going to manage (cluster machines, disks for storage) operations such as creating or deleting a volume are available (via REST API and  the command-line client) as single actions. All necessary technical operations (e.g. in the case of setting up a new volume: the choice of machines, setting up volumes on the level of local LVMs, formatting  filesystems, creating bricks of glusterfs volume and finally creating glusterfs volume) happen ‘in the background’.

The main configuration of heketi etc/heketi/heketi.json looks like this:

{
    "port": "8080",
    "use_auth": true,
    "jwt": {
        "admin": {
            "key": "<AdminSecret>"
        },
        "user": {
            "key": "<UserSecret>"
        }
    },
    "glusterfs": {
        "executor": "ssh",
        "sshexec": {
            "keyfile": "/vol/gluster_shared_storage/data/heketi/ssh/id_rsa",
            "user": "root"
        },
        "db": "/vol/gluster_shared_storage/data/heketi/heketi.db",
        "_loglevel_comment": [
            "Set log level. Choices are:",
            "  none, critical, error, warning, info, debug",
            "Default is warning"
        ],
        "loglevel": "info"
    }
}

To make heketi work, it is also necessary to generate ssh key, to which we refer in the configuration and copying the public key to  /root/.ssh/authorized_key  on all glusterX  machines - using this key heketi will log onto particular servers and execute commands managing disk resources of servers.

To facilitate the use of heketi-cli,  I defined on the user from which I run this tool  HEKETI_CLI_USER and HEKETI_CLI_KEY environment variables,

(indicating the user and the key according to heketi included in the configuration) and HEKETI_CLI_SERVER=http://gluster.san:8080.

Configuration of the cluster and nodes in heketi:

$ heketi-cli cluster create --block=false --file=true
  Id:cc6615faec3607dd6680f637f65ce920
$ heketi-cli node add --zone=1 --cluster=cc6615faec3607dd6680f637f65ce920 --management-host-name=gluster1.softax --storage-host-name=gluster1.san
  Id:<...>
$ heketi-cli node add --zone=2 --cluster=cc6615faec3607dd6680f637f65ce920 --management-host-name=gluster2.softax --storage-host-name=gluster2.san
  Id:<...>
$ heketi-cli node add --zone=3 --cluster=cc6615faec3607dd6680f637f65ce920 --management-host-name=gluster3.softax --storage-host-name=gluster3.san
  Id:<...>
$ heketi-cli node add --zone=4 --cluster=cc6615faec3607dd6680f637f65ce920 --management-host-name=gluster4.softax --storage-host-name=gluster4.san
  Id:<...>
$ heketi-cli device add --name=/dev/sdc2 --node=<gluster1 node id>
$ heketi-cli device add --name=/dev/sdc2 --node=<gluster2 node id>
$ heketi-cli device add --name=/dev/sdc2 --node=<gluster3 node id>
$ heketi-cli device add --name=/dev/sdc2 --node=<gluster4 node id>

Initially, I tried to use, instead of the above, "heketi-cli topology load", but I got discouraged - precise documentation of the file format with topology is poor, and errors returned by heketi-cli gently speaking did not facilitate error diagnosis and it was easier and faster for me to create infrastructure configuration in heketi with single commands.

And this finishes configuration of the whole gluster infrastructure.
From now on we can create new volumens from heketi-cli level, the example of which is below:

$ heketi-cli volume create --replica 3 --size 1T --name=n_imagesId:36e5608e8d85472822f0c60ee19515bf 

Check what has happened:

$ gluster volume info n_images
  Volume Name: n_images
  Type: Replicate
  Volume ID: 209975a3-eb56-458e-a702-daf1c66cd37b
  Status: Started
  Snapshot Count: 0
  Number of Bricks: 1 x 3 = 3
  Transport-type: tcp
  Bricks:
  Brick1: gluster4.san:/var/lib/heketi/mounts /vg_d37a8ea80f7ceb5512427f4aae9c826c/brick_77ecb6b56a12ae7be6d3aed808a0a6f7/brick
  Brick2: gluster1.san:/var/lib/heketi/mounts/vg_d511c3f47c665e45449df6c5f3636f95/brick_615c967efe3fe8f77010d4314262b4fb/brick
  Brick3: gluster2.san:/var/lib/heketi/mounts/vg_059e336ddf97785018aaa3b5126217db/brick_c73e55a25326dcfa041d44cf2f57a028/brick
  Options Reconfigured:
  nfs.disable: on
  transport.address-family: inet
  features.barrier: disable
  cluster.enable-shared-storage: enable
  auto-delete: enable

Success!

Volumes created like this can be shared directly (mounting them on the target machine with glusterfs client), or by using samba or NFS operating on glusterfs cluster machines.

The integration of gluster cluster configured like this with OKD (Openshift Origin) has brought about some more difficulties and surprises, but I will write about it in the next article, in which I will describe my struggling to run OKD on the physical infrastructure in Softax.