Lab 04 - Shared Volumes
In this section, we will create a Portworx volume (PVC) for NGINX.
Why NGINX?
In this lab, we are using NGINX to demonstrate how shared volumes can be utilized to deploy and scale stateless applications effectively. NGINX is an ideal example for testing the behavior of shared volumes, as its data must remain consistent across multiple instances. This approach will help us understand how Portworx shared volumes work in an OpenShift environment.
Create StorageClass
Take a look at the StorageClass definition for Portworx and create the storage class.
cat <<EOF | oc apply -f -
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: px-shared-sc
provisioner: pxd.portworx.com
parameters:
repl: "3"
sharedv4: "true"
EOF
The parameters are declarative policies for your storage volume. See here for a full list of supported parameters. In our case, the key parameter is sharedv4 = true
.
Create PersistentVolumeClaim
Take a look at the PersistentVolumeClaim and create it.
cat <<EOF | oc apply -f -
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: px-shared-pvc
spec:
storageClassName: px-shared-sc
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
EOF
Now that we have the volume created, let’s deploy a few NGINX instances and see how the shared volume works!
In this step, we will deploy the NGINX application using the PersistentVolumeClaim
created before.
Notice in the specification below, we have set the |
Deploy 3 Instances of NGINX
cat <<EOF | oc apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp1
labels:
app: webapp1
spec:
selector:
matchLabels:
app: webapp1
replicas: 1
template:
metadata:
labels:
app: webapp1
group: webapp
spec:
securityContext:
runAsNonRoot: true
seLinuxOptions:
level: "s0:c1,c0"
seccompProfile:
type: RuntimeDefault
containers:
- name: webapp1
securityContext:
allowPrivilegeEscalation: false
seLinuxOptions:
level: "s0:c1,c0"
capabilities:
drop: ["ALL"]
image: nginxinc/nginx-unprivileged
ports:
- containerPort: 8080
volumeMounts:
- mountPath: /usr/share/nginx/html
name: shared-data
volumes:
- name: shared-data
persistentVolumeClaim:
claimName: px-shared-pvc
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp2
labels:
app: webapp2
spec:
selector:
matchLabels:
app: webapp2
replicas: 1
template:
metadata:
labels:
app: webapp2
group: webapp
spec:
securityContext:
runAsNonRoot: true
seLinuxOptions:
level: "s0:c1,c0"
seccompProfile:
type: RuntimeDefault
containers:
- name: webapp2
securityContext:
allowPrivilegeEscalation: false
seLinuxOptions:
level: "s0:c1,c0"
capabilities:
drop: ["ALL"]
image: nginxinc/nginx-unprivileged
ports:
- containerPort: 8080
volumeMounts:
- mountPath: /usr/share/nginx/html
name: shared-data
volumes:
- name: shared-data
persistentVolumeClaim:
claimName: px-shared-pvc
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp3
labels:
app: webapp3
spec:
selector:
matchLabels:
app: webapp3
replicas: 1
template:
metadata:
labels:
app: webapp3
group: webapp
spec:
securityContext:
runAsNonRoot: true
seLinuxOptions:
level: "s0:c1,c0"
seccompProfile:
type: RuntimeDefault
containers:
- name: webapp3
securityContext:
allowPrivilegeEscalation: false
seLinuxOptions:
level: "s0:c1,c0"
capabilities:
drop: ["ALL"]
image: nginxinc/nginx-unprivileged
ports:
- containerPort: 8080
volumeMounts:
- mountPath: /usr/share/nginx/html
name: shared-data
volumes:
- name: shared-data
persistentVolumeClaim:
claimName: px-shared-pvc
---
apiVersion: v1
kind: Service
metadata:
name: webapp1-svc
labels:
app: webapp1
spec:
ports:
- port: 80
targetPort: 8080
selector:
app: webapp1
---
apiVersion: v1
kind: Service
metadata:
name: webapp2-svc
labels:
app: webapp2
spec:
ports:
- port: 80
targetPort: 8080
selector:
app: webapp2
---
apiVersion: v1
kind: Service
metadata:
name: webapp3-svc
labels:
app: webapp3
spec:
ports:
- port: 80
targetPort: 8080
selector:
app: webapp3
EOF
Observe the volumeMounts
and volumes
sections where we mount the PVC.
Verify NGINX Pods Are Ready
Run the command below and wait until all three NGINX pods are in a ready state.
watch oc get pods -l group=webapp -o wide
When all three pods are in the Running
state, press ctrl-c
to clear the screen. Be patient. If they stay in the Pending
state for a while, it is because each node must fetch the Docker image.
Inspect the Portworx Volume
Portworx ships with a pxctl command line tool that can be used to manage Portworx.
Below, we will use pxctl
to inspect the underlying volume for our PVC.
pxctl volume inspect $(oc get pvc | grep px-shared-pvc | awk '{print $3}')
-
Status
: Indicates that the volume is attached and shows the node on which it is attached. For shared volumes, this is the transaction coordinator node that all other nodes use to write data. -
HA
: Displays the number of configured replicas for this volume (shared volumes can also be replicated; you can test this by modifying the storage class in step 2). -
Shared
: Shows if the volume is shared. -
IO Priority
: Displays the relative priority of the volume’s IO (high, medium, or low). -
Volume consumers
: Shows which pods are accessing the volume.
With our shared volume successfully created and mounted across all three NGINX containers, we can now write data into the html
folder of NGINX and verify that all three containers can read the data.
Confirm Our NGINX Servers Are Up
Run the following command:
oc run test-webapp1 --image nginx --restart=Never --rm -ti -- curl webapp1-svc
You should see the following:
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/xxx</center>
</body>
</html>
Create index.html
in the NGINX HTML Folder on webapp1
Copy index.html
into the webapp1
pod:
cat <<"EOF" > /tmp/index.html
/$$$$$$$ /$$
| $$__ $$ | $$
| $$ \ $$ /$$$$$$ /$$$$$$ /$$$$$$ /$$ /$$ /$$ /$$$$$$ /$$$$$$ /$$ /$$
| $$$$$$$//$$__ $$ /$$__ $$|_ $$_/ | $$ | $$ | $$ /$$__ $$ /$$__ $$| $$ /$$/
| $$____/| $$ \ $$| $$ \__/ | $$ | $$ | $$ | $$| $$ \ $$| $$ \__/ \ $$$$/
| $$ | $$ | $$| $$ | $$ /$$| $$ | $$ | $$| $$ | $$| $$ >$$ $$
| $$ | $$$$$$/| $$ | $$$$/| $$$$$/$$$$/| $$$$$$/| $$ /$$/\ $$
|__/ \______/ |__/ \___/ \_____/\___/ \______/ |__/ |__/ \__/
EOF
POD=$(oc get pods -l app=webapp1 | grep Running | awk '{print $1}')
oc cp /tmp/index.html $POD:/usr/share/nginx/html/index.html
Now, let’s access all three URLs and verify that the "Hello World" message appears on each. This happens because all three containers are attached to the same volume, so any updates made to one are reflected across all.
oc run test-webapp1 --image nginx --restart=Never --rm -ti -- curl webapp1-svc
oc run test-webapp2 --image nginx --restart=Never --rm -ti -- curl webapp2-svc
oc run test-webapp3 --image nginx --restart=Never --rm -ti -- curl webapp3-svc
Summary
Congratulations! You have successfully created a shared volume using Portworx and deployed multiple NGINX instances to demonstrate how data consistency is maintained across multiple pods. We verified that each instance of NGINX can access the same shared data, showcasing the power of shared storage in an OpenShift environment.
With our shared volumes successfully created and mounted across all three NGINX containers, we were able to modify the html folder of webapp1, and see the changes reflected in all NGINX instances. This demonstrated how shared volumes facilitate consistent data across multiple application pods.