Complete Example Using GlusterFS
Overview
This topic provides an end-to-end example of how to use an existing Containerized GlusterFS, External GlusterFS, or standalone GlusterFS cluster as persistent storage for OKD. It is assumed that a working GlusterFS cluster is already set up. For help installing Containerized GlusterFS or External GlusterFS, see Persistent Storage Using GlusterFS.
For an end-to-end example of how to dynamically provision GlusterFS volumes, see Complete Example Using GlusterFS for Dynamic Provisioning.
All |
Prerequisites
To access GlusterFS volumes, the mount.glusterfs
command must be available on all schedulable nodes. For RPM-based systems, the glusterfs-fuse package must be installed:
# yum install glusterfs-fuse
If glusterfs-fuse is already installed on the nodes, ensure that the latest version is installed:
# yum update glusterfs-fuse
By default, SELinux does not allow writing from a pod to a remote GlusterFS server. To enable writing to GlusterFS volumes with SELinux on, run the following on each node running GlusterFS:
$ sudo setsebool -P virt_sandbox_use_fusefs on (1)
$ sudo setsebool -P virt_use_fusefs on
1 | The -P option makes the boolean persistent between reboots. |
The |
If you use Atomic Host, the SELinux booleans are cleared when you upgrade Atomic Host. When you upgrade Atomic Host, you must set these boolean values again. |
Static Provisioning
To enable static provisioning, first create a GlusterFS volume. See the GlusterFS Administration Guide for information on how to do this using the
gluster
command-line interface or the heketi project site for information on how to do this usingheketi-cli
. For this example, the volume will be namedmyVol1
.Define the following Service and Endpoints in
gluster-endpoints.yaml
:---
apiVersion: v1
kind: Service
metadata:
name: glusterfs-cluster (1)
spec:
ports:
- port: 1
---
apiVersion: v1
kind: Endpoints
metadata:
name: glusterfs-cluster (1)
subsets:
- addresses:
- ip: 192.168.122.221 (2)
ports:
- port: 1 (3)
- addresses:
- ip: 192.168.122.222 (2)
ports:
- port: 1 (3)
- addresses:
- ip: 192.168.122.223 (2)
ports:
- port: 1 (3)
1 These names must match. 2 The ip
values must be the actual IP addresses of a GlusterFS server, not hostnames.3 The port number is ignored. From the OKD master host, create the Service and Endpoints:
$ oc create -f gluster-endpoints.yaml
service "glusterfs-cluster" created
endpoints "glusterfs-cluster" created
Verify that the Service and Endpoints were created:
$ oc get services
NAME CLUSTER_IP EXTERNAL_IP PORT(S) SELECTOR AGE
glusterfs-cluster 172.30.205.34 <none> 1/TCP <none> 44s
$ oc get endpoints
NAME ENDPOINTS AGE
docker-registry 10.1.0.3:5000 4h
glusterfs-cluster 192.168.122.221:1,192.168.122.222:1,192.168.122.223:1 11s
kubernetes 172.16.35.3:8443 4d
Endpoints are unique per project. Each project accessing the GlusterFS volume needs its own Endpoints.
In order to access the volume, the container must run with either a user ID (UID) or group ID (GID) that has access to the file system on the volume. This information can be discovered in the following manner:
$ mkdir -p /mnt/glusterfs/myVol1
$ mount -t glusterfs 192.168.122.221:/myVol1 /mnt/glusterfs/myVol1
$ ls -lnZ /mnt/glusterfs/
drwxrwx---. 592 590 system_u:object_r:fusefs_t:s0 myVol1 (1) (2)
1 The UID is 592. 2 The GID is 590. Define the following PersistentVolume (PV) in
gluster-pv.yaml
:apiVersion: v1
kind: PersistentVolume
metadata:
name: gluster-default-volume (1)
annotations:
pv.beta.kubernetes.io/gid: "590" (2)
spec:
capacity:
storage: 2Gi (3)
accessModes: (4)
- ReadWriteMany
glusterfs:
endpoints: glusterfs-cluster (5)
path: myVol1 (6)
readOnly: false
persistentVolumeReclaimPolicy: Retain
1 The name of the volume. 2 The GID on the root of the GlusterFS volume. 3 The amount of storage allocated to this volume. 4 accessModes
are used as labels to match a PV and a PVC. They currently do not define any form of access control.5 The Endpoints resource previously created. 6 The GlusterFS volume that will be accessed. From the OKD master host, create the PV:
$ oc create -f gluster-pv.yaml
Verify that the PV was created:
$ oc get pv
NAME LABELS CAPACITY ACCESSMODES STATUS CLAIM REASON AGE
gluster-default-volume <none> 2147483648 RWX Available 2s
Create a PersistentVolumeClaim (PVC) that will bind to the new PV in
gluster-claim.yaml
:apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: gluster-claim (1)
spec:
accessModes:
- ReadWriteMany (2)
resources:
requests:
storage: 1Gi (3)
1 The claim name is referenced by the pod under its volumes
section.2 Must match the accessModes
of the PV.3 This claim will look for PVs offering 1Gi or greater capacity. From the OKD master host, create the PVC:
$ oc create -f gluster-claim.yaml
Verify that the PV and PVC are bound:
$ oc get pv
NAME LABELS CAPACITY ACCESSMODES STATUS CLAIM REASON AGE
gluster-pv <none> 1Gi RWX Available gluster-claim 37s
$ oc get pvc
NAME LABELS STATUS VOLUME CAPACITY ACCESSMODES AGE
gluster-claim <none> Bound gluster-pv 1Gi RWX 24s
PVCs are unique per project. Each project accessing the GlusterFS volume needs its own PVC. PVs are not bound to a single project, so PVCs across multiple projects may refer to the same PV. |
Using the Storage
At this point, you have a dynamically created GlusterFS volume bound to a PVC. You can now utilize this PVC in a pod.
Create the pod object definition:
apiVersion: v1
kind: Pod
metadata:
name: hello-openshift-pod
labels:
name: hello-openshift-pod
spec:
containers:
- name: hello-openshift-pod
image: openshift/hello-openshift
ports:
- name: web
containerPort: 80
volumeMounts:
- name: gluster-vol1
mountPath: /usr/share/nginx/html
readOnly: false
volumes:
- name: gluster-vol1
persistentVolumeClaim:
claimName: gluster1 (1)
1 The name of the PVC created in the previous step. From the OKD master host, create the pod:
# oc create -f hello-openshift-pod.yaml
pod "hello-openshift-pod" created
View the pod. Give it a few minutes, as it might need to download the image if it does not already exist:
# oc get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
hello-openshift-pod 1/1 Running 0 9m 10.38.0.0 node1
oc exec
into the container and create an index.html file in themountPath
definition of the pod:$ oc exec -ti hello-openshift-pod /bin/sh
$ cd /usr/share/nginx/html
$ echo 'Hello OpenShift!!!' > index.html
$ ls
index.html
$ exit
Now
curl
the URL of the pod:# curl http://10.38.0.0
Hello OpenShift!!!
Delete the pod, recreate it, and wait for it to come up:
# oc delete pod hello-openshift-pod
pod "hello-openshift-pod" deleted
# oc create -f hello-openshift-pod.yaml
pod "hello-openshift-pod" created
# oc get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
hello-openshift-pod 1/1 Running 0 9m 10.37.0.0 node1
Now
curl
the pod again and it should still have the same data as before. Note that its IP address may have changed:# curl http://10.37.0.0
Hello OpenShift!!!
Check that the index.html file was written to GlusterFS storage by doing the following on any of the nodes:
$ mount | grep heketi
/dev/mapper/VolGroup00-LogVol00 on /var/lib/heketi type xfs (rw,relatime,seclabel,attr2,inode64,noquota)
/dev/mapper/vg_f92e09091f6b20ab12b02a2513e4ed90-brick_1e730a5462c352835055018e1874e578 on /var/lib/heketi/mounts/vg_f92e09091f6b20ab12b02a2513e4ed90/brick_1e730a5462c352835055018e1874e578 type xfs (rw,noatime,seclabel,nouuid,attr2,inode64,logbsize=256k,sunit=512,swidth=512,noquota)
/dev/mapper/vg_f92e09091f6b20ab12b02a2513e4ed90-brick_d8c06e606ff4cc29ccb9d018c73ee292 on /var/lib/heketi/mounts/vg_f92e09091f6b20ab12b02a2513e4ed90/brick_d8c06e606ff4cc29ccb9d018c73ee292 type xfs (rw,noatime,seclabel,nouuid,attr2,inode64,logbsize=256k,sunit=512,swidth=512,noquota)
$ cd /var/lib/heketi/mounts/vg_f92e09091f6b20ab12b02a2513e4ed90/brick_d8c06e606ff4cc29ccb9d018c73ee292/brick
$ ls
index.html
$ cat index.html
Hello OpenShift!!!