# Guided Exercise: Manage non-Shared Storage with Stateful Sets

Deploy a replicated web server by using a deployment and verify that all web server pods share a PV; and deploy a replicated MySQL database by using a stateful set and verify that each database instance gets a dedicated PV.

**Outcomes**

In this exercise, you deploy a web server with a shared persistent volume between the replicas, and a database server from a stateful set with dedicated persistent volumes for each instance.

* Deploy a web server with persistent storage.
    
* Add data to the persistent storage.
    
* Scale the web server deployment and observe the data that is shared with the replicas.
    
* Create a database server with a stateful set by using a YAML manifest file.
    
* Verify that each instance from the stateful set has a persistent volume claim.
    

As the `student` user on the `workstation` machine, use the `lab` command to prepare your system for this exercise.

This command ensures that all resources are available for this exercise.

```plaintext
[student@workstation ~]$ lab start storage-statefulsets
```

**Procedure 5.4. Instructions**

1. Create a web server deployment named `web-server`. Use the [`registry.ocp4.example.com:8443/redhattraining/hello-world-nginx:latest`](http://registry.ocp4.example.com:8443/redhattraining/hello-world-nginx:latest) container image.
    
    1. Log in to the OpenShift cluster as the `developer` user with the `developer` password.
        
        ```plaintext
        [student@workstation]$ oc login -u developer -p developer \
        https://api.ocp4.example.com:6443
        ...output omitted...
        ```
        
    2. Change to the `storage-statefulsets` project.
        
        ```plaintext
        [student@workstation]$ oc project storage-statefulsets
        Now using project "storage-statefulsets" on server ...output omitted...
        ```
        
    3. Create the `web-server` deployment. Ignore the warning message.
        
        ```plaintext
        [student@workstation ~]$ oc create deployment web-server \
          --image registry.ocp4.example.com:8443/redhattraining/hello-world-nginx:latest
        Warning: would violate PodSecurity "restricted:v1.24":
        ...output omitted...
        deployment.apps/web-server created
        ```
        
    4. Verify the deployment status.
        
        ```plaintext
        [student@workstation ~]$ oc get pods -l app=web-server
        NAME                          READY   STATUS    RESTARTS  AGE
        web-server-7d7cb4cdc7-t7hx8   1/1     Running   0         4s
        ```
        
2. Add the `web-pv` persistent volume to the `web-server` deployment. Use the default storage class and the following information to create the persistent volume:
    
    | Field | Value |
    | --- | --- |
    | Name | `web-pv` |
    | Type | `persistentVolumeClaim` |
    | Claim mode | `rwo` |
    | Claim size | `5Gi` |
    | Mount path | `/var/www/html` |
    | Claim name | `web-pv-claim` |
    
    1. Add the `web-pv` persistent volume to the `web-server` deployment. Ignore the warning message.
        
        ```plaintext
        [student@workstation ~]$ oc set volumes deployment/web-server \
          --add --name web-pv --type persistentVolumeClaim --claim-mode rwo \
          --claim-size 5Gi --mount-path /var/www/html --claim-name web-pv-claim
        Warning: would violate PodSecurity "restricted:v1.24":
        ...output omitted...
        deployment.apps/web-server volume updated
        ```
        
        Because a storage class was not specified with the `--claim-class` option, the command uses the default storage class to create the persistent volume.
        
    2. Verify the deployment status. Notice that a new pod is created.
        
        ```plaintext
        [student@workstation ~]$ oc get pods -l app=web-server
        NAME                          READY   STATUS   RESTARTS  AGE
        web-server-64689877c6-qgsvt   1/1     Running  0         5s
        ```
        
    3. Verify the persistent volume status.
        
        ```plaintext
        [student@workstation ~]$ oc get pvc
        NAME           STATUS   VOLUME          CAPACITY   ACCESS MODES   STORAGECLASS   AGE
        web-pv-claim   Bound    pvc-42...63ab   5Gi        RWO            nfs-storage    29s
        ```
        
        The default storage class, `nfs-storage`, provisioned the persistent volume.
        
3. Add data to the PV by using the `exec` command.
    
    1. List pods to retrieve the `web-server` pod name.
        
        ```plaintext
        [student@workstation ~]$ oc get pods
        NAME                          READY   STATUS    RESTARTS   AGE
        web-server-64689877c6-mdr6f   1/1     Running   0          17m
        ```
        
        The pod name might differ in your output.
        
    2. Use the `exec` command to add the pod name that you retrieved from the previous step to the `/var/www/html/index.html` file on the pod. Then, retrieve the contents of the `/var/www/hmtl/index.html` file to confirm that the pod name is in the file.
        
        ```plaintext
        [student@workstation ~]$ oc exec -it pod/web-server-64689877c6-mdr6f \
          -- /bin/bash -c \
          'echo "Hello, World from ${HOSTNAME}" > /var/www/html/index.html'
        ```
        
        ```plaintext
        [student@workstation ~]$ oc exec -it pod/web-server-64689877c6-mdr6f \
          -- cat /var/www/html/index.html
        Hello, World from web-server-64689877c6-mdr6f
        ```
        
4. Scale the `web-server` deployment to two replicas and confirm that an additional pod is created.
    
    1. Scale the `web-server` deployment to two replicas.
        
        ```plaintext
        [student@workstation ~]$ oc scale deployment web-server --replicas 2
        deployment.apps/web-server scaled
        ```
        
    2. Verify the replica status and retrieve the pod names.
        
        ```plaintext
        [student@workstation ~]$ oc get pods
        NAME                          READY   STATUS    RESTARTS   AGE
        web-server-64689877c6-mbj6g   1/1     Running   0          2s
        web-server-64689877c6-mdr6f   1/1     Running   0          17m
        ```
        
        The pod names might differ from your output.
        
5. Retrieve the content of the `/var/www/html/index.html` file on the `web-server` pods by using the `oc exec` command to verify that the file is the same in both pods.
    
    1. Verify that the `/var/www/html/index.html` file is the same in both pods.
        
        ```plaintext
        [student@workstation ~]$ oc exec -it pod/web-server-64689877c6-mbj6g \
          -- cat /var/www/html/index.html
        Hello, World from web-server-64689877c6-mdr6f
        ```
        
        ```plaintext
        [student@workstation ~]$ oc exec -it pod/web-server-64689877c6-mdr6f \
          -- cat /var/www/html/index.html
        Hello, World from web-server-64689877c6-mdr6f
        ```
        
        Notice that both files show the name of the first instance, because they share the persistent volume.
        
6. Create a database server with a stateful set by using the `statefulset-db.yml` file in the `/home/student/DO180/labs/storage-statefulsets` directory. Update the file with the following information:
    
    | Field | Value |
    | --- | --- |
    | [`metadata.name`](http://metadata.name) | `dbserver` |
    | [`spec.selector.matchLabels.app`](http://spec.selector.matchLabels.app) | `database` |
    | [`spec.template.metadata.labels.app`](http://spec.template.metadata.labels.app) | `database` |
    | [`spec.template.spec.containers.name`](http://spec.template.spec.containers.name) | `dbserver` |
    | [`spec.template.spec.containers.volumeMounts.name`](http://spec.template.spec.containers.volumeMounts.name) | `data` |
    | `spec.template.spec.containers.volumeMounts.mountPath` | `/var/lib/mysql` |
    | [`spec.volumeClaimTemplates.metadata.name`](http://spec.volumeClaimTemplates.metadata.name) | `data` |
    | `spec.volumeClaimTemplates.spec.storageClassName` | `lvms-vg1` |
    
    1. Open the `/home/student/DO180/labs/storage-statefulsets/statefulset-db.yml` file in an editor. Replace the `<CHANGE_ME>` objects with values from the previous table:
        
        ```plaintext
        apiVersion: apps/v1
        kind: StatefulSet
        metadata:
          name: dbserver
        spec:
          selector:
            matchLabels:
              app: database
          replicas: 2
          template:
            metadata:
              labels:
                app: database
            spec:
              terminationGracePeriodSeconds: 10
              containers:
              - name: dbserver
                image: registry.ocp4.example.com:8443/redhattraining/mysql-app:v1
                ports:
                - name: database
                  containerPort: 3306
                env:
                - name: MYSQL_USER
                  value: "redhat"
                - name: MYSQL_PASSWORD
                  value: "redhat123"
                - name: MYSQL_DATABASE
                  value: "sakila"
                volumeMounts:
                - name: data
                  mountPath: /var/lib/mysql
          volumeClaimTemplates:
          - metadata:
              name: data
            spec:
              accessModes: [ "ReadWriteOnce" ]
              storageClassName: "lvms-vg1"
              resources:
                requests:
                  storage: 1Gi
        ```
        
    2. Create the database server by using the `oc create -f /home/student/DO180/labs/storage-statefulsets/statefulset-db.yml` command. Ignore the warning message.
        
        ```plaintext
        [student@workstation ~]$ oc create -f \
          /home/student/DO180/labs/storage-statefulsets/statefulset-db.yml
        Warning: would violate PodSecurity "restricted:v1.24":
        ...output omitted...
        statefulset.apps/bdserver created
        ```
        
    3. Wait a few moments and then verify the status of the stateful set and its instances.
        
        ```plaintext
        [student@workstation ~]$ oc get statefulset
        NAME	  READY	  AGE
        dbserver  2/2	  10s
        ```
        
        ```plaintext
        [student@workstation ~]$ oc get pods -l app=database
        NAME	       READY   STATUS	...
        dbserver-0   1/1     Running	...
        dbserver-1   1/1     Running	...
        ```
        
    4. Use the `exec` command to add data to each of the stateful set pods.
        
        ```plaintext
        [student@workstation ~]$ oc exec -it pod/dbserver-0 -- /bin/bash -c \
          "mysql -uredhat -predhat123 sakila -e 'create table items (count INT);'"
        
        mysql: [Warning] Using a password on the command line interface can be insecure.
        ```
        
        ```plaintext
        [student@workstation ~]$ oc exec -it pod/dbserver-1 -- /bin/bash -c \
          "mysql -uredhat -predhat123 sakila -e 'create table inventory (count INT);'"
        
        mysql: [Warning] Using a password on the command line interface can be insecure.
        ```
        
7. Confirm that each instance from the `dbserver` stateful set has a persistent volume claim. Then, verify that each persistent volume claim contains unique data.
    
    1. Confirm that the persistent volume claims have a `Bound` status.
        
        ```plaintext
        [student@workstation ~]$ oc get pvc -l app=database
        NAME		          STATUS  ...  CAPACITY	 ACCESS MODE  ...
        data-dbserver-0   Bound   ...  1Gi	     RWO          ...
        data-dbserver-1   Bound   ...  1Gi	     RWO	        ...
        ```
        
    2. Verify that each instance from the `dbserver` stateful set has its own persistent volume claim by using the `oc get pod pod-name -o json | jq .spec.volumes[0].persistentVolumeClaim.claimName` command.
        
        ```plaintext
        [student@workstation ~]$ oc get pod dbserver-0 -o json | \
          jq .spec.volumes[0].persistentVolumeClaim.claimName
        "data-dbserver-0"
        ```
        
        ```plaintext
        [student@workstation ~]$ oc get pod dbserver-1 -o json | \
          jq .spec.volumes[0].persistentVolumeClaim.claimName
        "data-dbserver-1"
        ```
        
    3. Application-level clustering is not enabled for the `dbserver` stateful set. Verify that each instance of the `dbserver` stateful set has unique data.
        
        ```plaintext
        [student@workstation ~]$ oc exec -it pod/dbserver-0 -- /bin/bash -c \
          "mysql -uredhat -predhat123 sakila -e 'show tables;'"
        mysql: [Warning] Using a password on the command line interface can be insecure.
        ------------------
        | Tables_in_sakila |
        ------------------
        | items            |
        ------------------
        ```
        
        ```plaintext
        [student@workstation ~]$ oc exec -it pod/dbserver-1 -- /bin/bash -c \
          "mysql -uredhat -predhat123 sakila -e 'show tables;'"
        mysql: [Warning] Using a password on the command line interface can be insecure.
        ------------------
        | Tables_in_sakila |
        ------------------
        | inventory        |
        ------------------
        ```
        
8. Delete a pod in the `dbserver` stateful set. Confirm that a new pod is created and that the pod uses the PVC from the previous pod. Verify that the previously added table exists in the `sakila` database.
    
    1. Delete the `dbserver-0` pod in the `dbserver` stateful set. Confirm that a new pod is generated for the stateful set. Then, confirm that the `data-dbserver-0` PVC still exists.
        
        ```plaintext
        [student@workstation ~]$ oc delete pod dbserver-0
        pod "dbserver-0" deleted
        ```
        
        ```plaintext
        [student@workstation ~]$ oc get pods -l app=database
        
        NAME         READY   STATUS    RESTARTS   AGE
        dbserver-0   1/1     Running   0          4s
        dbserver-1   1/1     Running   0          5m
        ```
        
        ```plaintext
        [student@workstation ~]$ oc get pvc -l app=database
        NAME		          STATUS  ...  CAPACITY	 ACCESS MODE  ...
        data-dbserver-0   Bound   ...  1Gi	     RWO          ...
        data-dbserver-1   Bound   ...  1Gi	     RWO	        ...
        ```
        
    2. Use the `exec` command to verify that the new `dbserver-0` pod has the `items` table in the `sakila` database.
        
        ```plaintext
        [student@workstation ~]$ oc exec -it pod/dbserver-0 -- /bin/bash -c \
          "mysql -uredhat -predhat123 sakila -e 'show tables;'"
        mysql: [Warning] Using a password on the command line interface can be insecure.
        ------------------
        | Tables_in_sakila |
        ------------------
        | items            |
        ------------------
        ```
        

**Finish**

On the `workstation` machine, use the `lab` command to complete this exercise. This step is important to ensure that resources from previous exercises do not impact upcoming exercises.

```plaintext
[student@workstation ~]$ lab finish storage-statefulsets
```
