EX180 Series: Deploying Applications to OpenShift

Part 4 of the EX180 series.

The Plan

We are going to have a look at the EX180 exam objectives for OpenShift and perform 4 hands-on tasks to get familiar with an application deployment process.

Exam Objectives: OpenShift

These are official EX180 objectives related to OpenShift (we have covered Podman objectives in the previous article).

You should have basic OpenShift knowledge.

You should be able to create applications in OpenShift:

  1. Create, manage and delete projects from a template, from source code, and from an image.
  2. Customise catalog template parameters.
  3. Specifying environment parameters.
  4. Expose public applications.

Troubleshoot applications in OpenShift

  1. Understand the description of application resources.
  2. Get application logs.
  3. Inspect running applications.
  4. Connecting to containers running in a pod.
  5. Copy resources to/from containers running in a pod.

What is OpenShift?

OpenShift is a Red Hat’s open-source container platform that is built on top of Kubernetes and provides additional services developed by Red Hat that do not come with vanilla Kubernetes, e.g. integrated developer workflow (CI/CD pipelines and s2i), a built-in metrics service, a WebUI, routes to expose services.

Now that we know what OpenShift is, let us get started.

OpenShift Task 1: Deploy a MySQL Application using OpenShift Templates

  1. OpenShift can be accessed on the following URL: https://api.crc.testing:6443.
  2. Login to OpenShift cluster with username developer and password developer.
  3. Create a new project called ex180task1 and use this project to deploy resources for this task.
  4. The OpenShift installer creates several templates by default. Use the mysql-ephemeral template to deploy a MySQL application to OpenShift.
  5. The name of the application should be database.
  6. Inspect the mysql-ephemeral template to work out what variables to pass during deployment.
  7. Set MySQL user to dbuser1.
  8. Set MySQL user password to dbpass1.
  9. Set MySQL database name to db1.
  10. Set MySQL administrator password dbadminpass1.
  11. Verify that the database db1 has been created.

Solution to OpenShift Task 1

Login to the OpenShift cluster and verify:

$ eval $(crc oc-env)
$ oc login -u developer -p developer https://api.crc.testing:6443
$ oc whoami
developer

Create a new project and verify:

$ oc new-project ex180task1
$ oc project
Using project "ex180task1" on server "https://api.crc.testing:6443"

As mentioned in the task definition, OpenShift creates several templates by default in the openshift namespace. List all available templates:

$ oc get templates -n openshift
NAME                                          DESCRIPTION                                                                        PARAMETERS        OBJECTS
cache-service                                 Red Hat Data Grid is an in-memory, distributed key/value store.                    8 (1 blank)       4
cakephp-mysql-example                         An example CakePHP application with a MySQL database. For more information ab...   21 (4 blank)      8
cakephp-mysql-persistent                      An example CakePHP application with a MySQL database. For more information ab...   22 (4 blank)      9
dancer-mysql-example                          An example Dancer application with a MySQL database. For more information abo...   18 (5 blank)      8
dancer-mysql-persistent                       An example Dancer application with a MySQL database. For more information abo...   19 (5 blank)      9
[...output truncated...]
mariadb-ephemeral                             MariaDB database service, without persistent storage. For more information ab...   8 (3 generated)   3
mariadb-persistent                            MariaDB database service, with persistent storage. For more information about...   9 (3 generated)   4
mysql-ephemeral                               MySQL database service, without persistent storage. For more information abou...   8 (3 generated)   3
mysql-persistent                              MySQL database service, with persistent storage. For more information about u...   9 (3 generated)   4
nginx-example                                 An example Nginx HTTP server and a reverse proxy (nginx) application that ser...   10 (3 blank)      5
[...output truncated...]

We see there is a template called mysql-ephemeral that we need to use to deploy a MySQL application.

There are two different ways that I’m aware of to list available parameters from a template. One is to describe a template using oc describe command like this:

$ oc describe template mysql-ephemeral -n openshift | less

Another one is to use oc process command like this:

$ oc process --parameters mysql-ephemeral -n openshift
NAME                    DESCRIPTION                                                             GENERATOR           VALUE
MEMORY_LIMIT            Maximum amount of memory the container can use.                                             512Mi
NAMESPACE               The OpenShift Namespace where the ImageStream resides.                                      openshift
DATABASE_SERVICE_NAME   The name of the OpenShift Service exposed for the database.                                 mysql
MYSQL_USER              Username for MySQL user that will be used for accessing the database.   expression          user[A-Z0-9]{3}
MYSQL_PASSWORD          Password for the MySQL connection user.                                 expression          [a-zA-Z0-9]{16}
MYSQL_ROOT_PASSWORD     Password for the MySQL root user.                                       expression          [a-zA-Z0-9]{16}
MYSQL_DATABASE          Name of the MySQL database accessed.                                                        sampledb
MYSQL_VERSION           Version of MySQL image to be used (8.0-el7, 8.0-el8, or latest).                            8.0-el8

We can have a look at the YAML template definition as well. This gives us some idea of what the DeploymentConfig would do to deploy the application.

$ oc get template mysql-ephemeral -n openshift -o yaml

Deploy MySQL application from the template:

$ oc -n ex180task1 new-app \
  --name=database \
  --template=mysql-ephemeral \
  --param=MYSQL_USER=dbuser1 \
  --param=MYSQL_PASSWORD=dbpass1 \
  --param=MYSQL_DATABASE=db1 \
  --param=MYSQL_ROOT_PASSWORD=dbadminpass1

Expected output:

--> Deploying template "openshift/mysql-ephemeral" to project ex180task1

     MySQL (Ephemeral)
     ---------
     MySQL database service, without persistent storage. For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/mysql-container/blob/master/8.0/root/usr/share/container-scripts/mysql/README.md.
     
     WARNING: Any data stored will be lost upon pod destruction. Only use this template for testing

     The following service(s) have been created in your project: mysql.
     
            Username: dbuser1
            Password: dbpass1
       Database Name: db1
      Connection URL: mysql://mysql:3306/
     
     For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/mysql-container/blob/master/8.0/root/usr/share/container-scripts/mysql/README.md.

     * With parameters:
        * Memory Limit=512Mi
        * Namespace=openshift
        * Database Service Name=mysql
        * MySQL Connection Username=dbuser1
        * MySQL Connection Password=dbpass1
        * MySQL root user Password=dbadminpass1
        * MySQL Database Name=db1
        * Version of MySQL Image=8.0-el8

--> Creating resources ...
    secret "mysql" created
    service "mysql" created
    deploymentconfig.apps.openshift.io "mysql" created
--> Success
    Application is not exposed. You can expose services to the outside world by executing one or more of the commands below:
     'oc expose service/mysql' 
    Run 'oc status' to view your app.

Verify pod status:

$ oc get po
NAME             READY   STATUS      RESTARTS   AGE
mysql-1-deploy   0/1     Completed   0          110s
mysql-1-wwdcl    1/1     Running     0          107s

Verify that the db1 database has been created. Connect to the MySQL pod, when inside the pod, login to MySQL to show databases:

$ oc exec -it po/mysql-1-wwdcl -- /bin/bash

bash-4.4$ mysql -u dbuser1 -pdbpass1 -e "show databases;"
mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------------+
| Database           |
+--------------------+
| db1                |
| information_schema |
+--------------------+
bash-4.4$ exit

The task is complete.

OpenShift Task 2: Deploy a MySQL Application from a Template File

  1. OpenShift can be accessed on the following URL: https://api.crc.testing:6443.
  2. Login to OpenShift cluster with username developer and password developer.
  3. Create a new project called ex180task2 and use this project to deploy resources for this task.
  4. Download OpenShift template from https://raw.githubusercontent.com/openshift/origin/master/examples/db-templates/mysql-persistent-template.json and save it to the mysql-persistent-template.json file.
  5. Create a new OpenShift template from the mysql-persistent-template.json file.
  6. Deploy a new MySQL application with persistent storage using the template file mysql-persistent-template.json.
  7. The name of the application should be persistentdb.
  8. Inspect the mysql-persistent template to work out what variables to pass during deployment.
  9. Set MySQL user to dbuser2.
  10. Set MySQL user password to dbpass2.
  11. Set MySQL database name to db2.
  12. Set MySQL administrator password dbadminpass2.
  13. Set volume space available for MySQL data to 1Gi.
  14. Verify that a persistent volume claim (PVC) has been created.
  15. Copy the /etc/my.cnf file from the MySQL pod to the host directory /tmp.

Solution to OpenShift Task 2

Login to the OpenShift cluster and verify:

$ eval $(crc oc-env)
$ oc login -u developer -p developer https://api.crc.testing:6443
$ oc whoami
developer

Create a new project and verify:

$ oc new-project ex180task2
$ oc project
Using project "ex180task2" on server "https://api.crc.testing:6443"

Download the template using curl and save it to the mysql-persistent-template.json file:

$ curl -sSf -o mysql-persistent-template.json \
  https://raw.githubusercontent.com/openshift/origin/master/examples/db-templates/mysql-persistent-template.json

Create a template from the mysql-persistent-template.json file:

$ oc -n ex180task2 create -f ./mysql-persistent-template.json

By default, the template will be created under the current project:

$ oc get templates
NAME               DESCRIPTION                                                                        PARAMETERS        OBJECTS
mysql-persistent   MySQL database service, with persistent storage. For more information about u...   9 (3 generated)   4

Get environment variables defined in the template:

$ oc process --parameters mysql-persistent
NAME                    DESCRIPTION                                                             GENERATOR           VALUE
MEMORY_LIMIT            Maximum amount of memory the container can use.                                             512Mi
NAMESPACE               The OpenShift Namespace where the ImageStream resides.                                      openshift
DATABASE_SERVICE_NAME   The name of the OpenShift Service exposed for the database.                                 mysql
MYSQL_USER              Username for MySQL user that will be used for accessing the database.   expression          user[A-Z0-9]{3}
MYSQL_PASSWORD          Password for the MySQL connection user.                                 expression          [a-zA-Z0-9]{16}
MYSQL_ROOT_PASSWORD     Password for the MySQL root user.                                       expression          [a-zA-Z0-9]{16}
MYSQL_DATABASE          Name of the MySQL database accessed.                                                        sampledb
VOLUME_CAPACITY         Volume space available for data, e.g. 512Mi, 2Gi.                                           1Gi
MYSQL_VERSION           Version of MySQL image to be used (8.0-el7, 8.0-el8, or latest).                            8.0-el8

We see that the variable used to define volume space is called VOLUME_CAPACITY.

Deploy MySQL application from the template file mysql-persistent-template.json:

$ oc -n ex180task2 new-app \
  --name=persistentdb \
  --file=mysql-persistent-template.json \
  --param=MYSQL_USER=dbuser2 \
  --param=MYSQL_PASSWORD=dbpass2 \
  --param=MYSQL_DATABASE=db2 \
  --param=MYSQL_ROOT_PASSWORD=dbadminpass2 \
  --param=VOLUME_CAPACITY=1Gi

Expected output:

--> Deploying template "ex180task2/mysql-persistent" for "mysql-persistent-template.json" to project ex180task2

     MySQL
     ---------
     MySQL database service, with persistent storage. For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/mysql-container/blob/master/8.0/root/usr/share/container-scripts/mysql/README.md.
     
     NOTE: Scaling to more than one replica is not supported. You must have persistent volumes available in your cluster to use this template.

     The following service(s) have been created in your project: mysql.
     
            Username: dbuser2
            Password: dbpass2
       Database Name: db2
      Connection URL: mysql://mysql:3306/
     
     For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/mysql-container/blob/master/8.0/root/usr/share/container-scripts/mysql/README.md.

     * With parameters:
        * Memory Limit=512Mi
        * Namespace=openshift
        * Database Service Name=mysql
        * MySQL Connection Username=dbuser2
        * MySQL Connection Password=dbpass2
        * MySQL root user Password=dbadminpass2
        * MySQL Database Name=db2
        * Volume Capacity=1Gi
        * Version of MySQL Image=8.0-el8

--> Creating resources ...
    secret "mysql" created
    service "mysql" created
    persistentvolumeclaim "mysql" created
    deploymentconfig.apps.openshift.io "mysql" created
--> Success
    Application is not exposed. You can expose services to the outside world by executing one or more of the commands below:
     'oc expose service/mysql' 
    Run 'oc status' to view your app.

Verify pod status:

$ oc get po
NAME             READY   STATUS      RESTARTS   AGE
mysql-1-deploy   0/1     Completed   0          2m25s
mysql-1-zdks7    1/1     Running     0          2m22s

List persistent volume claims:

$ oc get pvc
NAME    STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mysql   Bound    pv0004   100Gi      RWO,ROX,RWX                   2m50s

Copy /etc/my.cnf from a remote pod to /tmp/my.cnf locally:

$ oc cp mysql-1-zdks7:/etc/my.cnf /tmp/my.cnf

This task is complete.

OpenShift Task 3: Deploy a PHP Application using Source-to-Image (S2I)

  1. OpenShift can be accessed on the following URL: https://api.crc.testing:6443.
  2. Login to OpenShift cluster with username developer and password developer.
  3. Create a new project called ex180task3 and use this project to deploy resources for this task.
  4. Create a new PHP Hello World application using S2I deployment.
  5. The name of the application should be helloworld.
  6. The source code is available from the Git repository at https://github.com/lisenet/DO180-apps/ in the php-helloworld directory.
  7. Use the development branch of the Git repository.
  8. Use the php:7.3 image stream tag.
  9. Expose the PHP application’s service to make the application accessible from a web browser.

Solution to OpenShift Task 3

Login to the OpenShift cluster and verify:

$ eval $(crc oc-env)
$ oc login -u developer -p developer https://api.crc.testing:6443
$ oc whoami
developer

Create a new project and verify:

$ oc new-project ex180task3
$ oc project
Using project "ex180task3" on server "https://api.crc.testing:6443"

Deploy a new PHP application using S2I:

$ oc -n ex180task3 new-app \
  --name helloworld \
  -i php:7.3 https://github.com/lisenet/DO180-apps#development \
  --context-dir php-helloworld

Expected output:

--> Found image 70d4d94 (2 months old) in image stream "openshift/php" under tag "7.3" for "php:7.3"

    Apache 2.4 with PHP 7.3 
    ----------------------- 
    PHP 7.3 available as container is a base platform for building and running various PHP 7.3 applications and frameworks. PHP is an HTML-embedded scripting language. PHP attempts to make it easy for developers to write dynamically generated web pages. PHP also offers built-in database integration for several commercial and non-commercial database management systems, so writing a database-enabled webpage with PHP is fairly simple. The most common use of PHP coding is probably as a replacement for CGI scripts.

    Tags: builder, php, php73, rh-php73

    * The source repository appears to match: php
    * A source build using source code from https://github.com/lisenet/DO180-apps#development will be created
      * The resulting image will be pushed to image stream tag "helloworld:latest"
      * Use 'oc start-build' to trigger a new build

--> Creating resources ...
    imagestream.image.openshift.io "helloworld" created
    buildconfig.build.openshift.io "helloworld" created
Warning: would violate PodSecurity "restricted:v1.24": allowPrivilegeEscalation != false (container "helloworld" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "helloworld" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "helloworld" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container "helloworld" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
    deployment.apps "helloworld" created
    service "helloworld" created
--> Success
    Build scheduled, use 'oc logs -f buildconfig/helloworld' to track its progress.
    Application is not exposed. You can expose services to the outside world by executing one or more of the commands below:
     'oc expose service/helloworld' 
    Run 'oc status' to view your app.

Check the build config:

$ oc get bc
NAME         TYPE     FROM              LATEST
helloworld   Source   [email protected]   1

Check the service:

$ oc get svc
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)             AGE
helloworld   ClusterIP   10.217.4.118   none          8080/TCP,8443/TCP   37s

Check the image stream:

$ oc get is
NAME         IMAGE REPOSITORY                                                                TAGS     UPDATED
helloworld   default-route-openshift-image-registry.apps-crc.testing/ex180task3/helloworld   latest   About a minute ago

Check the pods:

$ oc get po
NAME                          READY   STATUS      RESTARTS   AGE
helloworld-1-build            0/1     Completed   0          3m3s
helloworld-66879f45bc-gfc7h   1/1     Running     0          109s

Note: you can use oc get all command to list all resources.

Expose the PHP application:

$ oc expose svc helloworld

Make sure that the route resource has been created:

$ oc get routes
NAME         HOST/PORT                                PATH   SERVICES     PORT       TERMINATION   WILDCARD
helloworld   helloworld-ex180task3.apps-crc.testing          helloworld   8080-tcp                 None

Access the application with curl:

$ curl http://helloworld-ex180task3.apps-crc.testing
Hello, World! php version is 7.3.33
This is development branch

This task is complete.

OpenShift Task 4: Deploy an HTTP Application from Image

  1. OpenShift can be accessed on the following URL: https://api.crc.testing:6443.
  2. Login to OpenShift cluster with username developer and password developer.
  3. Create a new project called app-from-image and use this project to deploy resources for this task.
  4. Deploy a new web application using the image you have built for the Podman task 4. Use private image quay.io/$USERNAME/httpd:1.0-test, where $USERNAME is your quay.io username.
  5. Create an OpenShift secret called my-quay-credentials for use with a container registry, and use your quay.io credentials. Use this secret to deploy the application from the private registry.
  6. The name of the application should be webserver.
  7. Add the following label to all resources: task=4.
  8. Create the application as a deployment config.
  9. Troubleshoot and identify the reason why the pod is failing to start.
  10. Delete the project from OpenShift.

Solution to OpenShift Task 4

Login to the OpenShift cluster and verify:

$ eval $(crc oc-env)
$ oc login -u developer -p developer https://api.crc.testing:6443
$ oc whoami
developer

Create a new project and verify:

$ oc new-project app-from-image
$ oc project
Using project "app-from-image" on server "https://api.crc.testing:6443".

Check documentation for how to create a new secret:

$ oc create secret --help

Create a new secret to store quay.io credentials:

$ oc -n app-from-image create secret docker-registry my-quay-credentials \
  --docker-server=quay.io \
  --docker-username=YOUR_QUAY_USERNAME \
  --docker-password=YOUR_QUAY_PASSWORD \
  --docker-email=YOUR_QUAY_EMAIL

Verify that the secret has been created:

$ oc get secret my-quay-credentials -o yaml
apiVersion: v1
data:
  .dockerconfigjson: eyJhdXRocyI6eyJxdWF5LmlvIjp7InVzZXJuYW1lIjoiWU9VUl9RVUFZX1VTRVJOQU1FIiwicGFzc3dvcmQiOiJZT1VSX1FVQVlfUEFTU1dPUkQiLCJlbWFpbCI6IllPVVJfUVVBWV9FTUFJTCIsImF1dGgiOiJXVTlWVWw5UlZVRlpYMVZUUlZKT1FVMUZPbGxQVlZKZlVWVkJXVjlRUVZOVFYwOVNSQT09In19fQ==
kind: Secret
metadata:
  creationTimestamp: "2022-11-27T18:50:40Z"
  name: my-quay-credentials
  namespace: app-from-image
  resourceVersion: "161791"
  uid: 918119ae-1f6c-455d-8002-73bcb4bfbef3
type: kubernetes.io/dockerconfigjson

Optionally, we could decode the data by using base64 -d if we wanted to see the credentials.

To use the secret for pulling images for pods, we must add the secret to the default service account, because the service account that the pod uses is the default service account.

Link secrets to a service account:

$ oc secrets link default my-quay-credentials --for=pull

Next we can check documentation for how to add labels to resource and use a deployment config:

$ oc new-app --help | less

Create the webserver application using the httpd image from a private registry and specify the existing secret to use:

$ oc -n app-from-image new-app \
  --name webserver \
  --labels=task=4 \
  --as-deployment-config=true \
  --image=quay.io/lisenet/httpd:1.0-test

Expected output:

--> Found container image 8653efc (12 days old) from quay.io for "quay.io/lisenet/httpd:1.0-test"

    * An image stream tag will be created as "webserver:1.0-test" that will track this image
    * This image will be deployed in deployment config "webserver"
    * Port 80/tcp will be load balanced by service "webserver"
      * Other containers can access this service through the hostname "webserver"
    * WARNING: Image "quay.io/lisenet/httpd:1.0-test" runs as the 'root' user which may not be permitted by your cluster administrator

--> Creating resources with label task=4 ...
    imagestream.image.openshift.io "webserver" created
    deploymentconfig.apps.openshift.io "webserver" created
    service "webserver" created
--> Success
    Application is not exposed. You can expose services to the outside world by executing one or more of the commands below:
     'oc expose service/webserver' 
    Run 'oc status' to view your app.

Get deployment config:

$ oc get dc
NAME        REVISION   DESIRED   CURRENT   TRIGGERED BY
webserver   1          1         1         config,image(webserver:1.0-test)

Get pod status including labels:

$ oc get po
NAME                 READY   STATUS             RESTARTS        AGE   LABELS
webserver-1-deploy   0/1     Completed          0               17m   openshift.io/deployer-pod-for.name=webserver-1
webserver-1-kc22v    0/1     CrashLoopBackOff   7 (4m26s ago)   17m   deployment=webserver-1,deploymentconfig=webserver,task=4

We see that the webserver pod status is CrashLoopBackOff.

Get the cluster events to see if anything is failing:

$ oc get events

Get the webserver pod logs:

$ oc logs po/webserver-1-kc22v
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 10.217.0.173. Set the 'ServerName' directive globally to suppress this message
(13)Permission denied: AH00072: make_sock: could not bind to address [::]:80
(13)Permission denied: AH00072: make_sock: could not bind to address 0.0.0.0:80
no listening sockets available, shutting down
AH00015: Unable to open logs

It looks like the container is trying to bind to a privileged port 80, but is not allowed to do so. The task did not ask us to resolve the problem, but identify the cause.

Delete the project:

$ oc delete project app-from-image

The task is complete.

Leave a Reply

Your email address will not be published. Required fields are marked *