The Developer Sandbox for Red Hat OpenShift is a great platform for learning and experimenting with Red Hat OpenShift. Because OpenShift is built on Kubernetes, the Developer Sandbox is also a great platform for learning and experimenting with Kubernetes.

This article is the first article in a two-part series that takes you through the creation of an application using plain Kubernetes instead of OpenShift.

Where commands differ between Bash and PowerShell, I'll note the difference. Otherwise, the commands work at either command line.

A personal note from the author

Sit down, relax, and be prepared to spend some quality time with this tutorial. We cover a lot of ground, and I've attempted to mimic a real-life situation in order to bring the most value to the time you spend on the tutorial. If you're new to Kubernetes, you'll go from zero to deploying applications in this guide. You'll be rolling out a back-end application, a front-end application, and (in part 2) a database. You'll also be scaling an application and updating an application. You'll be doing hands-on experimentation, gaining skills that are 100% applicable to a production environment. Thanks for taking to time to trust me with your learning. Enjoy.


It should take less than half an hour to set up all the following tools if you don't have them already. For most of the tools, I have not provided URLs because they are different for different operating systems, but they can all be found easily through web searches.

  • A free Developer Sandbox account.
  • The Kubernetes command-line interface (CLI), kubectl, installed on your local PC.
  • GitHub, installed on your local PC.
  • An image registry that you can use: is a good option.
  • The ability to build an image on your local PC using either Podman or Docker.

Steps in this article

The following steps build and deploy the tutorial's Kubernetes application in the Developer Sandbox:

  1. Log into the sandbox.
  2. Create a back-end program called quotes.
  3. Create a React front-end program called quotesweb.
  4. View quotesweb in your browser.
  5. Scale the back end to two pods and observe the result in quotesweb.
  6. Create a persistent volume claim to host MariaDB (a popular spin-off from the MySQL database) running in Kubernetes.
  7. Create a Secret to use with the database.
  8. Create a MariaDB database, quotesdb, running in Kubernetes.
  9. Create and populate the quotes table in the quotesdb database.
  10. Destroy the MariaDB pod to observe Kubernetes's "self-healing."
  11. Update the quotes back end to version 2 and observe the results in quotesweb.

The Kubernetes features we use, as described at the Kubernetes by example web site, are:

  • Pods
  • Labels
  • Deployments
  • Services
  • Service discovery
  • Environment variables
  • Namespaces
  • Volumes
  • Persistent volumes
  • Secrets
  • Logging


Download or clone three repositories (repos) from GitHub using the following commands. You can choose any directory on your system to hold these repos:

git clone
git clone
git clone

This tutorial refers to the three directories you just created by their repo names: quotesweb, quotemysql, and qotd-python.

Logging into the Developer Sandbox

You don't actually "log in to" a Kubernetes cluster. Instead, you set your local environment to connect to your cluster when you issue kubectl commands. This procedure is a bit cumbersome but necessary. You can, of course, automate it. You can also use tools to help you. Finally, if you have the OpenShift CLI (the oc command) installed, you can cheat and use the oc login command—it does make life a lot easier.

Three elements go into setting up your environment:

  • Your credentials (username, etc.)
  • A Kubernetes (or OpenShift) cluster
  • A context: i.e., a namespace within the cluster

After establishing those three elements, you use the context that has been created.

Before establishing the context, you need to extract the following information from your sandbox:

  • Username, which is represented by {username} in the commands in this article
  • Authorization token, represented by {token}
  • Name of the cluster, represented by {cluster}
  • Context assigned to you, represented by {context}
  • URL of the cluster API server, represented by {server}

The following sections show how to to get this information, which requires you to log in to your sandbox.


This is displayed in the upper right corner of the dashboard in a red box and looks like Figure 1.

The usernmae assigned to you by the Developer Sandbox is at the upper right corner of the dashboard.
Figure 1: Username for the Developer Sandbox user.

Given this example, your username would be rhn-engineering-dschenck. Insert the name from your dashboard wherever {username} appears in this article.

Note: The Kubernetes namespace (which OpenShift calls a project) you'll be using is simply your username with -dev appended: e.g., rhn-engineering-dschenck-dev.

Authorization token

The procedure to get this token is a bit complicated and is explained in the article Access your Developer Sandbox for Red Hat OpenShift from the command line. Find the DevSandbox option to get the authorization token, and insert it wherever {token} appears in this article.

Name of the cluster

The cluster name is a modification of the host URL with all dots converted to dashes. You must also change the console-openshift-console-apps portion of the host URL to that of the API server. For example, if you navigate to the Topology page of your dashboard, your URL looks something like Figure 2.

The Topology page shows a URL representing your cluster, which requires some changes to be used as your cluster name.
Figure 2: String that is the basis for a cluster name, from the Topology page.

Given this string, the cluster name is api-sandbox-x8i5-p1-openshiftapps-com:6443.

Insert the cluster name you derived from your Topology page wherever {cluster} appears in this article.


The context is constructed by combining your username with the name of the cluster in the following format: {username}-dev/{cluster}/{username}.

For example, using what we have up to this point, your context would be:


Insert your context wherever {context} appears in this article.

URL of the cluster API server

This is almost the same as the cluster name, but it keeps the dots. For example, given what we have up to this point, the URL would be :

Insert your API server's URL wherever {server} appears in this article.

Viewing and deleting your local PC's Kubernetes configuration

The kubectl config view command shows your configuration. If you wish, you can remove all of your local Kubernetes configuration information by deleting the ~/.kube/config file.

Use this command in Bash:

rm ~/.kube/config

Use this command in PowerShell:

Remove-Item -Recurse -Force ~/.kube/config

Connecting to your Kubernetes cluster

Using the information you have obtained from your sandbox, issue the following commands and substitute your own values where noted:

kubectl config set-credentials {username}/{cluster} --token={token}
kubectl config set-cluster {cluster} --server={server}
kubectl config set-context {context} --user={username}/{cluster} /
  --namespace={username}-dev --cluster={cluster}
kubectl config use-context {context}

What we're creating in this tutorial

This tutorial guides you through using Kubernetes to create three components:

  • RESTful back-end service.
  • React front-end web page.
  • MariaDB database (in part 2).

Figure 3 shows a typical display from the complete application.

The application displays a page saying "QuoteWeb" and an Animated GIF showing a quote that changes at regular intervals.
Figure 3: Animated GIF displayed by the application created in this article.

About the RESTful back-end service

A back-end application named quotes, written in Python 3.8, supplies strings resembling a "quote of the day" via a RESTful API. The endpoints are described in Table 1.

Table 1: Endpoints offered by the RESTful API.
Endpoint URL HTTP method Action
/ GET Returns the string "qotd" simply to identify the service.
/version GET Returns a string denoting the version ID of the service, "2.0.0" for instance.
/writtenin GET Returns the programming language in which the service is written. In this case, it is "Python," but this same service is available in several different programming languages.
/quotes GET Returns a JSON array containing all of the quotes.
/quotes/random GET Returns a JSON object containing one randomly chosen quote from among the set of available quotes.
/quotes/{id} GET Returns a JSON object of one specific quote matching the {id} within the set of available quotes.

Creating the back-end quotes app

This step creates Kubernetes objects associated with the quotes application: a deployment, a service, and a route (which is similar to Ingress and Ingress Controller objects in Kubernetes). We will also set an environment variable that will allow us to change the name of the database service if we want to.

About the route, Ingress, and Ingress controller

Because Developer Sandbox for Red Hat OpenShift is administered by Red Hat, you do not have administrator access to the Kubernetes cluster. One of the limitations created by this access restriction is that you are not granted rights to create Ingress and Ingress controller objects.

OpenShift has its own built-in Ingress-like object, the Route. For this tutorial, we're going to "cheat" and use the Route object. Be aware that we are using this workaround. In your own Kubernetes cluster you'll create the Ingress and Ingress controller objects.

The blog posting Kubernetes Ingress vs OpenShift Route offers more historical background and details on the role of the route.

Creating the route

In the directory where you cloned the qotd-python repo, move into the k8s sub-directory and run the following commands:

kubectl apply -f quotes-deployment.yaml
kubectl apply -f service.yaml
kubectl apply -f route.yaml

For example:

PS C:\Users\dschenck\src\github\qotd-python\k8s> kubectl apply -f .\quotes-deployment.yaml
deployment.apps/quotes created
PS C:\Users\dschenck\src\github\qotd-python\k8s> kubectl apply -f .\service.yaml
service/quotes created
PS C:\Users\dschenck\src\github\qotd-python\k8s> kubectl apply -f .\route.yaml created
PS C:\Users\dschenck\src\github\qotd-python\k8s>

At this point, you have the back-end quotes application running in a Kubernetes pod. It's exposed within Kubernetes as a service, and the route allows anyone to access it over the internet. Therefore, you can run the kubectl get routes command to get the route URL and then issue a curl command with the route URL to see the service serving up data. Here's an example:

PS C:\Users\dschenck\src\github\qotd-python\k8s> kubectl get routes
NAME     HOST/PORT                                                                    PATH   SERVICES   PORT        TERMINATION   WILDCARD
quotes          quotes     10000-tcp                 None
PS C:\Users\dschenck\src\github\qotd-python\k8s> curl
    "author": "Don Schenck",
    "hostname": "quotes-8649f468d4-jhnkd",
    "id": 0,
    "quotation": "It is not only what you do, but also the attitude you bring to it, that makes you a success."
    "author": "Francis Bacon",
    "hostname": "quotes-8649f468d4-jhnkd",
    "id": 1,
    "quotation": "Knowledge is power."
    "author": "Confucius",
    "hostname": "quotes-8649f468d4-jhnkd",
    "id": 2,
    "quotation": "Life is really simple, but we insist on making it complicated."
    "author": "William Shakespeare",
    "hostname": "quotes-8649f468d4-jhnkd",
    "id": 3,
    "quotation": "This above all, to thine own self be true."
    "author": "Will Ferrell",
    "hostname": "quotes-8649f468d4-jhnkd",
    "id": 4,
    "quotation": "I got a fever, and the only prescription is more cowbell."
    "author": "Andrew Hendrixson",
    "hostname": "quotes-8649f468d4-jhnkd",
    "id": 5,
    "quotation": "Anyone who has ever made anything of importance was disciplined."

Pods and labels

When you create the deployment, Kubernetes pulls the image for your application from the image registry named in the YAML file and creates a pod. Kubernetes also assigns the labels that you specified in the deployment. The pod name is automatically generated from the deployment name, with random characters appended to it.

The quotes-deployment.yaml file shows that our pods will be named quotesweb (plus the random characters, e.g., "quotesweb-5468c95fc6-5sp9j"), while the label will be app=quotesweb:

kind: Deployment
apiVersion: apps/v1
  name: quotesweb
    app: quotesweb
  replicas: 1
      app: quotesweb
        app: quotesweb
        - name: quotes
          imagePullPolicy: Always
            - containerPort: 3000
              protocol: TCP

Note that the pod name and application name can differ. Be careful here, because this is where good or poor management can make a big difference.

The YAML file also shows that the deployment (the quotes-deployment.yaml file) uses the following image:

This image is a Linux image that has data (six "quote of the day" type entries) hard-coded into the source code. The second article in this series upgrades the image to version 2, which reads quotes from a MariaDB database running in your Kubernetes cluster.

Creating the front-end web application

Before you create the React front-end program, you need to change some code in the back-end application, build an image, and push the image to a publicly available registry from which you can pull it into your Kubernetes cluster.

First, change the source code for the quotesweb application to point to the route you created to the quotes service running in the Kubernetes cluster. You can find the URL of this route by running the following command:

kubectl get routes

For example:

PS C:\Users\dschenck\src\github> kubectl get routes
NAME     HOST/PORT                                                                    PATH   SERVICES   PORT        TERMINATION   WILDCARD
quotes          quotes     10000-tcp                 None
PS C:\Users\dschenck\src\github>

The URL, plus the endpoint you need (/quotes/random), needs to be specified in the quotesweb application. The URL looks somewhat like the following:


To alter the code, change into your quotesweb/src/components directory and edit the quotes.js file. Substitute your URL for your-url-goes-here in line 26 of the code (Figure 4):

Insert the URL for your quotes service into the fetch statement in quotes.js.
Figure 4: Inserting the proper URL for the quotes service.

Save this change.

Move back into your quotesweb directory where the Dockerfile file is located and build your image. You will need to use your own naming pattern based on your own image registry. For example, if you are using Docker Hub as your image registry, and your username there is janedoe, you'd enter the command:

docker build -t .

I'm using Red Hat's image registry, so I entered the command:

docker build -t .

No matter what image registry you use, you'll need to log in to it: e.g., run docker login.

With the image built, push it to your image registry. For example:

docker push

The name of the image you create (such as will be used when you alter the deployment file, quote-deployment.yaml. This deployment file is in the k8s subdirectory of quotesweb. Find and change the following line, replacing the image name with your own image.


This change (Figure 5) will direct Kubernetes to pull your custom-built image to create the quotesweb front-end application:

Insert the path to your image into the deployment configuration file for the front-end application.
Figure 5: Updated deployment file for the front-end application.

Optionally, you can leave the deployment file unchanged, and use the image that I've built.

Why use the external, publicly available route?

When you want one service in Kubernetes to communicate with another Kubernetes service, you use the internal service name. For example, the URL to communicate with the quotes service might be http://quotes/quotes/random. However, because you are using a React application, this won't work. React sends a JavaScript payload to the browser where the JavaScript executes. Because this code, communicating with the quotes application's RESTful API, is running in the browser, it is outside Kubernetes, and therefore must use a public URL to reach your back-end quotes service.

"But can't the whole world access this?" you ask.

Yes. If this were your actual production architecture, you would implement a form of authorization here or use a secure API service such as Red Hat 3scale API Management.

Running the front-end application

It's time to get our front-end quotesweb application up and running in our Kubernetes cluster.

In your quotesweb/k8s directory on your local machine, run the following commands to create the deployment, service, and route:

kubectl apply -f quotesweb-deployment.yaml
kubectl apply -f quotesweb-service.yaml
kubectl apply -f quotesweb-route.yaml

Here's an example from my own machine:

PS C:\Users\dschenck\src\github\quotesweb\k8s> kubectl apply -f .\quotesweb-deployment.yaml
deployment.apps/quotesweb created
PS C:\Users\dschenck\src\github\quotesweb\k8s> code .\quotesweb-route.yaml
PS C:\Users\dschenck\src\github\quotesweb\k8s> kubectl apply -f .\quotesweb-route.yaml created
PS C:\Users\dschenck\src\github\quotesweb\k8s> kubectl apply -f .\quotesweb-service.yaml
service/quotesweb created

Get the URL with which you can view quotesweb in your browser:

kubectl get routes

Here's an example from my own machine:

PS C:\Users\dschenck\src\github\quotesweb\k8s> kubectl get routes
NAME        HOST/PORT                                                                       PATH   SERVICES    PORT        TERMINATION   WILDCARD
quotes             quotes      10000-tcp                 None
quotesweb          quotesweb   3000-tcp                  None
PS C:\Users\dschenck\src\github\quotesweb\k8s>

When you paste the "quotesweb" URL into your browser, you can see the QuoteWeb application running (Figure 6).

The application displays "QuoteWeb" and a rotating quote.
Figure 6: QuoteWeb application running in a browser.

Scale to meet demand

At this point, we have two applications (or Kubernetes services) running in our cluster. As you watch the QuoteWeb application in your browser, you will notice that the hostname is always the same. That's because you have only one pod running our quotes service. You can prove this with the following command:

kubectl get pods

Here's an example:

PS C:\Users\dschenck\src\github\quotesweb\k8s> kubectl get pods
NAME                         READY   STATUS    RESTARTS   AGE
quotes-8649f468d4-jhnkd      1/1     Running   0          24m
quotesweb-5468c95fc6-krgxw   1/1     Running   0          4m40s
PS C:\Users\dschenck\src\github\quotesweb\k8s>

Although Kubernetes can be configured to autoscale by spinning up additional pods, you can mimic this behavior from the command line and observe the results in your browser. Use the following command to increase the number of pods to 3:

kubectl scale deployments/quotesweb --replicas=3

If you now run kubectl get pods, you'll notice that there are more pods serving quotes.

What you accomplished so far

If you actually performed the steps we've gone through in this article and successfully exposed your applications, you learned the following tasks:

  • Creating back-end and front-end applications and connecting them.
  • Scaling an application with one command.

In the next article of the series, you'll create and populate a database running in Kubernetes to serve your application. You'll also see how easy it is to update an application.

Last updated: March 6, 2023