# Guided Exercise: Select a Storage Class for an Application

Deploy a MySQL database with persistent storage based on block storage by selecting block storage instead of a default file storage.

**Outcomes**

You should be able to deploy applications with persistent storage and create volumes from a storage class. The storage class must meet the application storage requirements.

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-classes
```

**Procedure 5.3. Instructions**

1. Log in to the OpenShift cluster as the `developer` user with the `developer` password. Use the `storage-classes` project.
    
    1. Log in to the OpenShift cluster.
        
        ```plaintext
        [student@workstation ~]$ oc login -u developer -p developer \
          https://api.ocp4.example.com:6443
        Login successful.
        ...output omitted...
        ```
        
    2. Set the `storage-classes` project as the active project.
        
        ```plaintext
        [student@workstation ~]$ oc project storage-classes
        ...output omitted...
        ```
        
2. Examine the available storage classes on the cluster. Identify an appropriate storage class to use for a database application.
    
    1. Use the `get` command to retrieve a list of storage classes in the cluster. You can use the `storageclass` short name, `sc`, in the command.
        
        ```plaintext
        [student@workstation ~]$ oc get sc
        NAME                    PROVISIONER                                   ...
        nfs-storage (default)   k8s-sigs.io/nfs-subdir-external-provisioner   ...
        lvms-vg1                topolvm.io                                    ...
        ```
        
        Because an administrator can change the default storage, applications must specify a storage class that meets the application requirements.
        
    2. Use the `oc describe` command to view the details of the `lvms-vg1` storage class.
        
        ```plaintext
        [student@workstation ~]$ oc describe sc lvms-vg1
        Name:                  lvms-vg1
        IsDefaultClass:        No
        Annotations:           description=Provides RWO and RWOP Filesystem & Block volumes
        Provisioner:           topolvm.io
        Parameters:            csi.storage.k8s.io/fstype=xfs,topolvm.io/device-class=vg1
        AllowVolumeExpansion:  True
        MountOptions:          <none>
        ReclaimPolicy:         Delete
        VolumeBindingMode:     WaitForFirstConsumer
        Events:                <none>
        ```
        
        The description annotation states that the storage class provides support for block volumes. For some applications, such as databases, block volumes can provide a performance advantage over file system volumes. In the `lvms-vg1` storage class, the `AllowVolumeExpansion` field is set to `True`. With volume expansion, cluster users can edit their PVC objects and specify a new size for the PVC. Kubernetes then uses the storage back end to automatically expand the volume to the requested size. Kubernetes also expands the file system of pods that use the PVC. Enabling volume expansion can help to protect an application from failing due to the data growing too fast. With these features, the `lvms-vg1` storage class is a good choice for the database application.
        
3. Use the [`registry.ocp4.example.com:8443/rhel8/mysql-80`](http://registry.ocp4.example.com:8443/rhel8/mysql-80) container image to create a MySQL deployment named `db-pod`. Add the missing environment variables for the pod to run.
    
    1. Create the `db-pod` deployment. Ignore the warning message.
        
        ```plaintext
        [student@workstation ~]$ oc create deployment db-pod --port 3306 \
          --image registry.ocp4.example.com:8443/rhel8/mysql-80
        Warning: would violate PodSecurity "restricted:v1.24":
        ...output omitted...
        deployment.apps/db-pod created
        ```
        
    2. Add the environment variables. Ignore the warning message.
        
        ```plaintext
        [student@workstation ~]$ oc set env deployment/db-pod \
          MYSQL_USER=user1 \
          MYSQL_PASSWORD=mypa55w0rd \
          MYSQL_DATABASE=items
        Warning: would violate PodSecurity "restricted:v1.24":
        ...output omitted...
        deployment.apps/db-pod updated
        ```
        
    3. Verify that the pod is running.
        
        ```plaintext
        [student@workstation ~]$ oc get pods
        NAME                    READY   STATUS    RESTARTS   AGE
        db-pod-b4ccfb74-nn4s5   1/1     Running   0          5s
        ```
        
    4. Expose the `db-pod` deployment to create a service.
        
        ```plaintext
        [student@workstation ~]$ oc expose deployment/db-pod
        service/db-pod exposed
        ```
        
4. Add a 1 Gi, RWO PVC named `db-pod-odf-pvc` to the deployment. Specify the volume name as `odf-lvm-storage`, and set the `/var/lib/mysql` directory as the mount path. Use the `lvms-vg1` storage class to create a block mode volume.
    
    1. Use the `oc set volume` command to create a PVC for the deployment. Ignore the warning message.
        
        ```plaintext
        [student@workstation ~]$ oc set volumes deployment/db-pod \
          --add --name odf-lvm-storage --type pvc \
          --claim-mode rwo --claim-size 1Gi --mount-path /var/lib/mysql \
          --claim-class lvms-vg1 \
          --claim-name db-pod-odf-pvc
        ...output omitted...
        deployment.apps/db-pod volume updated
        ```
        
        The `claim-class` option specifies a non-default storage class.
        
    2. Use the `oc get pvc` command to view the status of the PVC. Identify the name of the PV, and confirm that the PVC uses the `lvms-vg1` non-default storage class.
        
        ```plaintext
        [student@workstation ~]$ oc get pvc
        NAME             STATUS   VOLUME          CAPACITY   ACCESS MODES   STORAGECLASS  ...
        db-pod-odf-pvc   Bound    pvc-72...035a   1Gi        RWO            lvms-vg1   ...
        ```
        
    3. Use the `oc describe pvc` command to inspect the details of the `db-pod-odf-pvc` PVC.
        
        ```plaintext
        [student@workstation ~]$ oc describe pvc db-pod-odf-pvc
        Name:          db-pod-odf-pvc
        Namespace:     storage-classes
        StorageClass:  lvms-vg1
        Status:        Bound
        Volume:        pvc-b3084a46-3f32-435e-987b-4ad3b9026021
        Labels:        <none>
        Annotations:   pv.kubernetes.io/bind-completed: yes
                       pv.kubernetes.io/bound-by-controller: yes
                       volume.beta.kubernetes.io/storage-provisioner: topolvm.io
                       volume.kubernetes.io/selected-node: master01
                       volume.kubernetes.io/storage-provisioner: topolvm.io
        Finalizers:    [kubernetes.io/pvc-protection]
        Capacity:      1Gi
        Access Modes:  RWO
        VolumeMode:    Filesystem
        Used By:       db-pod-568888457d-qmxn9
        Events:
        ...output omitted...
        ```
        
        The `Used By` attribute confirms that the PVC is bound to a pod.
        
5. Connect to the database to verify that it is working.
    
    ```plaintext
    [student@workstation ~]$ oc run query-db -it --rm \
      --image registry.ocp4.example.com:8443/rhel8/mysql-80 \
      --restart Never --command \
      -- /bin/bash -c "mysql -uuser1 -pmypa55w0rd --protocol tcp \
      -h db-pod -P3306 items -e 'show databases;'"
    mysql: [Warning] Using a password on the command line interface can be insecure.
    +--------------------+
    | Database           |
    +--------------------+
    | information_schema |
    | items              |
    | performance_schema |
    +--------------------+
    pod "query-db" deleted
    ```
    
6. Observe how the `oc set volume` command changed the deployment.
    
    1. Use the `oc get deployment` command to view the deployment.
        
        ```plaintext
        [student@workstation ~]$ oc get deployment/db-pod -o yaml
        apiVersion: apps/v1
        kind: Deployment
        ...output omitted...
           spec:
              containers:
        ...output omitted...
                volumeMounts:
                - mountPath: /var/lib/mysql
                  name: odf-lvm-storage
        ...output omitted...
              volumes:
              - name: odf-lvm-storage
                persistentVolumeClaim:
                  claimName: db-pod-odf-pvc
        ...output omitted...
        ```
        
7. Delete the `db-pod` deployment and the `db-pod-odf-pvc` PVC.
    
    1. Delete the `db-pod` deployment.
        
        ```plaintext
        [student@workstation ~]$ oc delete all -l app=db-pod
        pod "db-pod-568888457d-fvklp" deleted
        service "db-pod" deleted
        deployment.apps "db-pod" deleted
        replicaset.apps "db-pod-568888457d" deleted
        ```
        
    2. Verify that the PVC still exists without the deployment.
        
        ```plaintext
        [student@workstation ~]$ oc get pvc
        NAME            STATUS  VOLUME         CAPACITY  ACCESS MODES  STORAGECLASS ...
        db-pod-odf-pvc  Bound   pvc-72...035a  1Gi       RWO           lvms-vg1     ...
        ```
        
    3. Delete the `db-pod-odf-pvc` PVC.
        
        ```plaintext
        [student@workstation ~]$ oc delete pvc db-pod-odf-pvc
        persistentvolumeclaim "db-pod-odf-pvc" deleted
        ```
        
8. Create a PVC for an application that requires a shared storage volume from the `nfs-storage` storage class.
    
    1. Create a PVC YAML manifest file named `nfs-pvc.yaml` with the following contents:
        
        ```plaintext
        apiVersion: v1
        kind: PersistentVolumeClaim
        metadata:
          name: nfs-pvc
        spec:
          accessModes:
          - ReadWriteOnce
          resources:
            requests:
              storage: 1Gi
          storageClassName: nfs-storage
        ```
        
    2. Create the PVC by using the `oc create -f` command and the YAML manifest file.
        
        ```plaintext
        [student@workstation ~]$ oc create -f nfs-pvc.yaml
        persistentvolumeclaim/nfs-pvc created
        ```
        
    3. Use the `oc describe pvc` command to view the details of the PVC resource.
        
        ```plaintext
        [student@workstation ~]$ oc describe pvc nfs-pvc
        Name:          nfs-pvc
        Namespace:     storage-classes
        StorageClass:  nfs-storage
        Status:        Bound
        Volume:        pvc-9f462124-d96e-43e5-96a7-f96dd9375579
        Labels:        <none>
        Annotations:   pv.kubernetes.io/bind-completed: yes
                       pv.kubernetes.io/bound-by-controller: yes
                       volume.beta.kubernetes.io/storage-provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
                       volume.kubernetes.io/storage-provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
        Finalizers:    [kubernetes.io/pvc-protection]
        Capacity:      1Gi
        Access Modes:  RWO
        VolumeMode:    Filesystem
        Used By:       <none>
        Events:
        ...output omitted...
        ```
        
        The `Used By: <none>` attribute shows that no pod is using the PVC. The `Status: Bound` value and the `Volume` attribute assignment confirm that the storage class has its `VolumeBindingMode` set to `Immediate`.
        
9. Use the [`registry.ocp4.example.com:8443/ubi9/httpd-24:1-215`](http://registry.ocp4.example.com:8443/ubi9/httpd-24:1-215) container image to create a web application deployment named `web-pod`.
    
    1. Create the `web-pod` deployment. Ignore the warning message.
        
        ```plaintext
        [student@workstation ~]$ oc create deployment web-pod --port 8080 \
          --image registry.ocp4.example.com:8443/ubi8/httpd-24:1-215
        Warning: would violate PodSecurity "restricted:v1.24":
        ...output omitted...
        deployment.apps/web-pod created
        ```
        
    2. Create a service for the `web-pod` application.
        
        ```plaintext
        [student@workstation ~]$ oc expose deployment web-pod
        service/web-pod exposed
        ```
        
    3. Expose the service to create a route for the `wep-pod` application. Specify [`web-pod.apps.ocp4.example.com`](http://web-pod.apps.ocp4.example.com) as the hostname.
        
        ```plaintext
        [student@workstation ~]$ oc expose svc web-pod \
          --hostname web-pod.apps.ocp4.example.com
        route.route.openshift.io/web-pod exposed
        ```
        
10. Verify that the route that is assigned to the `web-pod` application is accessible.
    
    1. View the route that is assigned to the `web-pod` application.
        
        ```plaintext
        [student@workstation ~]$ oc get routes
        NAME      HOST/PORT                       PATH   SERVICES   PORT   ...
        web-pod   web-pod.apps.ocp4.example.com          web-pod    8080   ...
        ```
        
    2. Use the `curl` command to view the index page of the `web-pod` application.
        
        ```plaintext
        [student@workstation ~]$ curl http://web-pod.apps.ocp4.example.com/
        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
        
        <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
        	<head>
        		<title>Test Page for the HTTP Server on Red Hat Enterprise Linux</title>
        ...output omitted...
        ```
        
11. Add the `nfs-pvc` PVC to the `web-pod` deployment.
    
    1. Use the `oc set volume` command to add the PVC to the deployment. Specify the volume name as `nfs-volume`, and set the mount path to the `/var/www/html` directory. Ignore the warning message.
        
        ```plaintext
        [student@workstation ~]$ oc set volumes deployment/web-pod \
          --add --name nfs-volume \
          --claim-name nfs-pvc \
          --mount-path /var/www/html
        Warning: would violate PodSecurity "restricted:v1.24":
        ...output omitted...
        deployment.apps/web-pod volume updated
        ```
        
        The volume `mount-path` is set to the `/var/www/html` directory. The server uses this path to serve HTML content.
        
12. Use the [`registry.ocp4.example.com:8443/redhattraining/do180-roster`](http://registry.ocp4.example.com:8443/redhattraining/do180-roster) container image to create a custom web application deployment named `app-pod`.
    
    1. Create the `app-pod` deployment and specify port `9090` as the target port. Ignore the warning message.
        
        ```plaintext
        [student@workstation ~]$ oc create deployment app-pod --port 9090 \
          --image registry.ocp4.example.com:8443/redhattraining/do180-roster
        Warning: would violate PodSecurity "restricted:v1.24":
        ...output omitted...
        deployment.apps/app-pod created
        ```
        
    2. Create a service for the `app-pod` application.
        
        ```plaintext
        [student@workstation ~]$ oc expose deployment app-pod
        service/app-pod exposed
        ```
        
    3. Expose the service to create a route for the `app-pod` application. Use [`app-pod.apps.ocp4.example.com`](http://app-pod.apps.ocp4.example.com) for the hostname.
        
        ```plaintext
        [student@workstation ~]$ oc expose svc app-pod \
          --hostname app-pod.apps.ocp4.example.com
        route.route.openshift.io/app-pod exposed
        ```
        
13. Add the `nfs-pvc` PVC to the `app-pod` application deployment.
    
    1. Use the `oc set volume` command to add the PVC to the deployment. Set the volume name to `nfs-volume` and the mount path to the `/var/tmp` directory. Ignore the warning message.
        
        ```plaintext
        [student@workstation ~]$ oc set volumes deployment/app-pod \
          --add --name nfs-volume \
          --claim-name nfs-pvc \
          --mount-path /var/tmp
        Warning: would violate PodSecurity "restricted:v1.24":
        ...output omitted...
        deployment.apps/app-pod volume updated
        ```
        
        At this point, the `web-pod` and the `app-pod` applications are sharing a PVC. Kubernetes does not have a mechanism to prevent data conflicts between the two applications. In this case, the `app-pod` application is a writer and the `web-pod` application is a reader, and thus they do not have a conflict. The application implementation, not Kubernetes, prevents data corruption from the two applications that use the same PVC. The RWO access mode does not protect data integrity. The RWO access mode means that a single node can mount the volume as read/write, and pods that share the volume must exist on the same node.
        
14. Use the `app-pod` application to add content to the shared volume.
    
    1. Open a web browser to the [`http://app-pod.apps.ocp4.example.com/`](http://app-pod.apps.ocp4.example.com/) page
        
        ![](https://rol.redhat.com/rol/static/static_file_cache/do180-4.12/storage/classes/assets/app-pod-screenshot.png align="center")
        
    2. In the form, enter your information and then click **save**. The application adds your information to the list after the form.
        
    3. Click **push** to create the `/var/tmp/People.html` file on the shared volume.
        
15. Open another tab on the browser and navigate to the [`http://web-pod.apps.ocp4.example.com/People.html`](http://web-pod.apps.ocp4.example.com/People.html) page. The `web-pod` application displays the `People.html` file from the shared volume.
    

**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-classes
```
