Learn Kubernetes using Red Hat Developer Sandbox for OpenShift
Learn Kubernetes in context
Red Hat Developer Sandbox for OpenShift ("Sandbox") is a great platform for learning and experimenting with Red Hat OpenShift. Because OpenShift is built on Kubernetes, the Sandbox is also a great platform for learning and experimenting with Kubernetes.
This activity takes you through the creation of an application using plain Kubernetes instead of OpenShift.

Learn Kubernetes using the Developer Sandbox for Red Hat OpenShift
Introduction
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 sandbox is also a great platform for learning and experimenting with Kubernetes. You can access and treat your sandbox instance like you would any Kubernetes instance. That is to say, you can ignore the fact that it’s OpenShift, and simply use it as plain Kubernetes. This article takes you through the creation of an application using Kubernetes instead of OpenShift.
What will you do?
- Log in to the Developer Sandbox.
- Prepare the local Kubernetes configuration to allow access from the command line.
- Clone the source code repositories.
- Create a back-end program called quotes, including setting environment variables to be used by the code.
- Create a React front-end program called quotesweb.
- Scale the back end to two pods and observing the result in quotesweb.
- Create a Persistent Volume Claim (PVC) to support MariaDB running in Kubernetes.
- Create a Secret to be used with the database.
- Create a MariaDB database, quotesdb, that runs in Kubernetes.
- Create and populate the table quotes in the quotesdb database.
- Update the back-end program quotes to version 2 and observe the results in quotesweb.
- Destroy the MariaDB pod to observe Kubernetes' self-healing capability.
What will you learn?
You will use the following Kubernetes features, which are described in detail on the Kube by example web site:
- Pods
- Labels
- Deployments
- Services
- Service discovery
- Environment variables
- Namespaces
- Volumes
- Persistent volumes
- Secrets
- Logging
How long will this activity take?
Expect to take 60-90 minutes to complete this activity.
Prerequisites
You will need the following in order to complete this activity:
- A Developer Sandbox for Red Hat OpenShift account
- The Kubernetes command-line interface (CLI),
kubectl
, installed on your local PC - Git installed on your local PC
Programming languages
The back-end service is written in Python 3.8. The web interface is written in React. The database is a MariaDB instance.
Note: You do not need knowledge of these languages in order to complete this activity.
Need help?
If you need help, if you get stuck, if something isn’t working, or you simply have questions, you can easily contact us via email at devsandbox@redhat.com.
Personal note from the author
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 your time spent.
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 database, and a front-end application. You'll also be scaling an application and updating another application. Also, when commands are referenced inline, they are shown in a different typeface, e.g., kubectl config view
.
This is hands-on with skills that are 100 percent applicable to a production environment. Thanks for taking to time to trust me with your learning. Enjoy.
Step 1: Log in to the Developer Sandbox
If you’re unsure how to do this, you can find instructions here: Access your Developer Sandbox for Red Hat OpenShift from the command line | Red Hat Developer
Step 2: Prepare the local Kubernetes configuration to allow access from the command line
You don't log in to a Kubernetes cluster. You actually set your local environment to access the API server when issuing kubectl
commands. This part is a bit cumbersome, but it's necessary. You can, of course, automate it. You can also use tools to help moving forward.
Cheat code: If you have the OpenShift command-line interface (oc) installed (not necessary for this tutorial), you can cheat and use the oc login command. If you can install the oc CLI, it makes life a lot easier.
There are three parts in play here:
- Your credentials
- A Kubernetes (or OpenShift) cluster
- A context, i.e., namespace, within the cluster
After establishing those three parts, you use the context you desire. The following commands will take care of this, but first we need to extract some pieces of information from our sandbox. We'll need to get the following items:
- Username, which is represented by {username} in the following commands
- Authorization token {token}
- Name of the cluster {cluster_name}
- Context assigned to us {
context
} - URL of the cluster API server {
api_server_url
}
You'll need to be logged in to your sandbox dashboard to get this information.
Username
Our value for {username
}. This is displayed in the upper right corner of the dashboard. For example:
Given this example, the username would be rhn-engineering-dschenck.
Note: The namespace we'll be using is simply your username with -dev
appended to it, e.g., rhn-engineering-dschenck-dev.
Authorization token
Our value for {token
}. If you click on username, select Copy login command, and log in as DevSandbox, you can see your token. If this is confusing, here is an article that will help.
URL of the cluster API server
Our value for {api_server-url
}. Using the same instructions as the previous section (Authorization token), locate the URL of your API server. (Figure 2)
Name of the cluster
Our value for {cluster_name
}. The cluster name is a modification of the host URL with all periods converted to dashes. Also, the console-openshift-console-apps portion of the host URL is replaced with api. For example, if you navigate to the Topology page of your dashboard, your URL looks something like Figure 3:
Given this, the cluster name will be api-sandbox-x8i5-p1-openshiftapps-com:6443.
Context assigned to us
Our value for {context
}. The context is constructed by combining your username with the name of the cluster in the following format: {username
}-dev/{cluster_name
}/{username
}.
For example, using what we have up to this point, the value for {context
} would be:
rhn-engineering-dschenck-dev/api-sandbox-x8i5-p1-openshiftapps-com:6443/rhn-engineering-dschenck
View and delete your local PC Kubernetes configuration
The command kubectl config view
. will show your configuration. If you wish, you can remove all of your Kubernetes local configuration information by deleting the file "~/.kube/config", e.g., rm ~/.kube/config.
With the information you gathered, run the following four kubectl
commands, substituting your own values where noted:
kubectl config set-credentials {username}/{cluster_name} --token={token}
kubectl config set-cluster {cluster_name} --server={api_server_url}
kubectl config set-context {context} --user={username}/{cluster_name} --namespace={username}-dev --cluster={cluster_name}
kubectl config use-context {context}
Step 3: Clone the source code repositories
Run the following three commands to clone the following three repositories (repos) from GitHub:
git clone https://github.com/redhat-developer-demos/quotesweb.git
git clone https://github.com/redhat-developer-demos/quotemysql.git
git clone https://github.com/redhat-developer-demos/qotd-python.git
Note: For the purposes of this tutorial, the three directories you created will be referenced by their repo name. Where you put them on your local machine is up to you.
What we're creating in this tutorial
This tutorial will guide you through using Kubernetes to create three components:
- Back-end RESTful service
- Front-end ReactJS web page
- MariaDB database
About the back-end RESTful service
A back-end application, quotes, supplies Quote Of The Day-type data via a RESTful API. The endpoints are as follows:
Endpoint: /
HTTP method: GET
Result: Returns the string "qotd" to simply identify the service.
Endpoint: /version
HTTP method: GET
Result: Returns a string denoting the version id of the service, e.g. "2.0.0".
Result: /writtenin
HTTP method: GET
Result: 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.
Endpoint: /quotes
HTTP method: GET
Result: Returns a JSON array from all of the quotes.
Endpoint: /quotes/random
HTTP method: GET
Result: Returns a JSON object of one random quote from among the set of available quotes.
Endpoint: /quotes/{id}
HTTP method: GET
Result: Returns a JSON object of one specific quote within the set of available quotes.
Step 4: Create a back-end program called quotes, then set environment variables to be used by the code
In this step, we will create Kubernetes objects associated with the quotes application: a Deployment, a Service, and a Route (which is similar to the Ingress and Ingress Controller objects). We will also set an Environment Variable that will allow us to change the name of the database service if we want to.
About Route, Ingress, and Ingress Controller
Because the 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 of this 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 want to create the Ingress and Ingress Controller objects.
In the directory where you cloned the qotd-python repo, move into the k8s sub-directory and run the following three commands:
kubectl create -f quotes-deployment.yaml
kubectl create -f service.yaml
kubectl create -f route.yaml
Some examples:
At this point, we have the back-end quotes application running in a pod. It's exposed within Kubernetes as a Service, and the Route allows anyone to access it over the internet. If you wish, you can run the command kubectl get routes
and then use the curl
command with the "/quotes" route URL to see the service serving up data. Figure 5 shows a Linux example.
Note: The PowerShell equivalent is $(curl http://quotes-rhn-engineering-dschenck-dev.apps.sandbox.x8i5.p1.openshiftapps.com/quotes).content
Objects and labels
When you create the objects for the quotes application, Kubernetes will pull the image from the image registry named in the YAML file and create a pod. It will also assign the labels which you specified in the deployment. The pod name is automatically generated from the deployment name, with random characters appended to it.
Viewing the contents of the file quotes-deployment.yaml
, we can see that the containers will be named quotes
(plus the random characters, e.g., quotes-5468c95fc6-5sp9j
), and the labels will be app: quotes
, sandbox: learn-kubernetes
, and learn-kubernetes: quotes
.
kind: Deployment
apiVersion: apps/v1
metadata:
name: quotes
labels:
app: quotes
sandbox: learn-kubernetes
sandbox-learn-kubernetes: quotes
spec:
replicas: 1
selector:
matchLabels:
app: quotes
template:
metadata:
labels:
app: quotes
spec:
containers:
- name: quotes
image: quay.io/donschenck/quotes:v1
imagePullPolicy: Always
ports:
- containerPort: 10000
protocol: TCP
Note: The labels and container names do not need to be the same. Be careful here; this is where good management or poor management can make a big difference. Developing a good standard for labeling objects is important. This subject is addressed in another sandbox activity.
Near the end of the YAML file, we can see that the deployment will use the following image:
quay.io/rhdevelopers/quotes:v1
This is a pre-built Linux image that has data (six "Quote Of The Day"-type entries) hard-coded into the source code. Later, we'll upgrade to version 2, which reads quotes from a MariaDB database (which will also be running in our Kubernetes cluster).
Step 5: Create a React front-end program called quotesweb
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 three commands to create the Deployment, the Service, and the Route:
kubectl create -f quotesweb-deployment.yaml
kubectl create -f quotesweb-service.yaml
kubectl create -f quotesweb-route.yaml
Figure 6 shows an example.
To view the quotesweb app, start by running the following command:
kubectl get routes
Figure 7 shows an example.
Use the route for quotesweb and paste that into your browser. This will take you to the QuoteWeb application in your browser. (Figure 8)
However, you will still need to provide the route to the back-end service (quotes) in order to start retrieving data. In the highlighted text box, enter the URL of the back-end “quotes” service, as shown in Figure 8. (Hint: You retrieved that route earlier in this activity).
Important: You need to prefix the URL with the http://
protocol (sometimes called “scheme”).
Important: Append /quotes/random
to the URL in order to get a random quote every five seconds.
After entering the URL, click Start. (Figure 9)
Step 6: Scale the back-end app to two pods and observe the result in quotesweb
At this point, we have two apps (or Kubernetes services) running in our cluster. As you watch the quotesweb application in your browser, you will notice that the hostname is always the same. That's because we have one pod running our quotes service. We can prove this by running the following command (this is optional):
kubectl get pods
Figure 10 shows an example.
While Kubernetes can be configured to auto-scale (by spinning up additional pods), we can mimic this behavior from the command line and observe the results in our browser. As we increase the number of pods, you'll notice that there are multiple hosts serving quotes.
Run the following command to increase the number of pods to three:
kubectl scale deployments/quotes --replicas=3
In the next section of this tutorial, we'll switch out the hard-coded quotes for quotes stored in a MariaDB database.
Step 7: Create a Persistent Volume Claim (PVC) to support MariaDB running in Kubernetes
While many Kubernetes database solutions offer an ephemeral option, that won't suffice for us. We need to make sure the database files remain intact even when the pods running MariaDB are deleted. This requires a PVC.
In your quotemysql
directory, you'll find the file mysqlvolume.yaml
, and it's 5 GB in size, using the host file system. This is where we'll direct the MariaDB app to place the data files it needs.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysqlvolume
spec:
resources:
requests:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
Run the following command to create the PVC:
kubectl create -f mysqlvolume.yaml
Figure 11 shows an example.
Step 8: Create a Secret to be use with the database
Navigate to the quotemysql
directory on your local PC. You'll find the file mysql-secret.yaml
:
apiVersion: v1
kind: Secret
metadata:
name: mysqlpassword
type: Opaque
data:
password: YWRtaW4=
Run the following command to create the Secret object:
kubectl create -f mysql-secret.yaml
Step 9: Create a MariaDB database, quotesdb, running in Kubernetes
In your quotemysql
directory, you'll find the file mysql-deployment.yaml
. Notice the password name (mysqlpassword
, the Secret we created in Step 8), the persistentVolumeClaim (mysqlvolume
, which we created in Step 7), and the volumeMounts
information. These entries should look familiar; we just created them:
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
labels:
sandbox: learn-kubernetes
learn-kubernetes: quotemysql
spec:
selector:
matchLabels:
app: mysql
tier: database
template:
metadata:
labels:
app: mysql
tier: database
spec:
containers:
- name: mariadb
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysqlpassword
key: password
image: mariadb
resources:
limits:
memory: "512Mi"
cpu: "500m"
ports:
- containerPort: 3306
volumeMounts:
- name: mysqlvolume
mountPath: /var/lib/mysql
volumes:
- name: mysqlvolume
persistentVolumeClaim:
claimName: mysqlvolume
We now have all the pieces to spin up a MariaDB database in our Kubernetes cluster: A Persistent Volume Claim, a Secret, and a Deployment.
Run the following command to create the MariaDB database instance:
kubectl create -f mysql-deployment.yaml
Step 10: Create and populate the table "quotes" in the "quotesdb" database.
Note: You could put all of the following commands into a script. See the file build_database.ps1
for an example.
The following instructions pertain to a PowerShell-based command-line experience. For instructions using Bash Shell, scroll down further in this activity.
PowerShell:
We need the name of the pod running MariaDB. The pod name begins with mysql
.
Run the following command to get the pod name into an environment variable:
$a = (kubectl get pods | select-string 'mysql') -match 'mysql([^\s]+)'; $podname = $matches[0]
This puts the pod name into a variable ($podname
) to be used in the remaining command. In this particulate example, the pod name is mysql-65c8cd6dc6-fs2zj
. Optionally, you can use the PowerShell command $podname
to see the value.
Here’s an example of these commands:
$a = (kubectl get pods | select-string 'mysql') -match 'mysql([^\s]+)'; $podname = $matches[0]
$podname
mysql-749cfb6d5f-qxqwk
Run the following three commands to copy the database creation commands into the pod and execute the script:
kubectl cp ./create_database_quotesdb.sql ${podname}:/tmp/create_database_quotesdb.sql
kubectl cp ./create_database.sh ${podname}:/tmp/create_database.sh
kubectl exec deploy/mysql -- /bin/bash ./tmp/create_database.sh
Run the following three commands to copy the table creation commands into the pod and execute the script:
kubectl cp ./create_table_quotes.sql ${podname}:/tmp/create_table_quotes.sql
kubectl cp ./create_tables.sh ${podname}:/tmp/create_tables.sh
kubectl exec deploy/mysql -- /bin/bash ./tmp/create_tables.sh
Run the following four commands to copy data into the pod and populate the database:
kubectl cp ./populate_table_quotes_POWERSHELL.sql ${podname}:/tmp/populate_table_quotes_POWERSHELL.sql
kubectl cp ./quotes.csv ${podname}:/tmp/quotes.csv
kubectl cp ./populate_tables_POWERSHELL.sh ${podname}:/tmp/populate_tables_POWERSHELL.sh
kubectl exec deploy/mysql -- /bin/bash ./tmp/populate_tables_POWERSHELL.sh
Run the following three commands to query the table to prove that the database is ready:
kubectl cp ./query_table_quotes.sql ${podname}:/tmp/query_table_quotes.sql
kubectl cp ./query_table_quotes.sh ${podname}:/tmp/query_table_quotes.sh
kubectl exec deploy/mysql -- /bin/bash ./tmp/query_table_quotes.sh
Bash
We need the name of the pod running MariaDB. The pod name begins with mysql
.
Run the following command to get the name of the pod running the MariaDB instance into an environment variable:
export PODNAME=$(a=$(kubectl get pods | grep 'mysql') && set -- $a && echo $1)
This puts the pod name into a variable (PODNAME
) to be used in the remaining command. In this particular example, the pod name is mysql-65c8cd6dc6-fs2zj
.
Run the following three commands to copy the database creation commands into the pod and execute the script:
kubectl cp ./create_database_quotesdb.sql $PODNAME:/tmp/create_database_quotesdb.sql
kubectl cp ./create_database.sh $PODNAME:/tmp/create_database.sh
kubectl exec deploy/mysql -- /bin/bash ./tmp/create_database.sh
Run the following three commands to copy the table creation commands into the pod and execute the script:
kubectl cp ./create_table_quotes.sql $PODNAME:/tmp/create_table_quotes.sql
kubectl cp ./create_tables.sh $PODNAME:/tmp/create_tables.sh
kubectl exec deploy/mysql -- /bin/bash ./tmp/create_tables.sh
Run the following four commands to populate the database table:
kubectl cp ./populate_table_quotes_BASH.sql $PODNAME:/tmp/populate_table_quotes_BASH.sql
kubectl cp ./quotes.csv $PODNAME:/tmp/quotes.csv
kubectl cp ./populate_tables_BASH.sh $PODNAME:/tmp/populate_tables_BASH.sh
kubectl exec deploy/mysql -- /bin/bash ./tmp/populate_tables_BASH.sh
Run the following three commands to query the database to prove our work:
kubectl cp ./query_table_quotes.sql $PODNAME:/tmp/query_table_quotes.sql
kubectl cp ./query_table_quotes.sh $PODNAME:/tmp/query_table_quotes.sh
kubectl exec deploy/mysql -- /bin/bash ./tmp/query_table_quotes.sh
DevOps
Write these scripts. Automate. Then, save those scripts in the GitHub repo with the project. Operations can then come along, duplicate what you've done, and improve on the scripts. You're a Developer. You work with Operations. You’re doing DevOps.
Step 11: Update the back-end program to version 2 and observe the results in quotesweb
Before we switch to version 2, we need to do some housekeeping. While Version 1 of our quotes service has values hard-coded into the code, version 2 reads from the database service mysql. The name of this service is not hard-coded into the source code. Instead, it reads the name of the service from the environment variable DB_SERVICE_NAME
. Here's the code snippet where that happens:
try:
conn = mariadb.connect(
user="root",
password="admin",
host=os.environ['DB_SERVICE_NAME'],
database="quotesdb",
port=3306)
The following command will create that environment variable in our deployment. Then, when we update to version 2, it will be available to the Python code:
kubectl set env deployment/quotes DB_SERVICE_NAME=mysql
At this point, we have a front-end application (quotesweb) talking to the back-end app (quotes). We also have a database, running in service mysql. What we need to do is update our back-end app to use our database. Kubernetes will do this on the fly, doing what's called a "rolling update." We already have a version 2 image in an image registry, so all we need to do is change the image in our deployment of quotes to point to version 2. Kubernetes will pull the image, spin up a pod running version 2, and then switch the routing to version 2.
Run the following command to switch to version 2:
kubectl set image deploy quotes quotes=quay.io/donschenck/quotes:v2
After a few seconds—seconds, not minutes—you may need to refresh your browser and re-enter the endpoint URL. At that point, you will notice that there are several more quotes being randomly accessed. Hint: What would happen if you switched back to v1?
What did you do?
- You created a back-end application. You created a front-end application, and connected the two.
- You created a database app running in Kubernetes, and you populated it from your command line.
- You scaled an application with one command.
- You updated an application on-the-fly.
Step 12: Destroy the MariaDB pod to observe Kubernetes' "Self Healing"
Because we use a PVC for our database, instead of an ephemeral database, our data remains intact when a pod falls over. You can prove this by deleting the pod running your MariaDB database. Kubernetes will replace the pod immediately and MariaDB will restart. With no operator intervention. Go ahead, give it a try.
Run the following command to delete the running MariaDB pod:
PowerShell
kubectl delete pod ${podname}
Bash
kubectl delete pod $PODNAME
Guess what? You know Kubernetes
You know Kubernetes! Granted, it's just a start, but you're on your way. Congratulations. We have a virtual ton of information available at developers.redhat.com. I suggest you start at KubernetesByExample.com. See you there.
Move it, remove it, improve it
Now that you know how to create an application using Kubernetes, here are some other ideas to try.
Move
You can download all of the YAML files associated with this application and use them to move it to another OpenShift instance by using the Export Application button in the upper right corner of the OpenShift dashboard.
Remove
You can remove parts of all of this activity by using one of the following commands:
- To remove only the quotes function:
kubectl delete pod $PODNAME
- To remove only the quotesweb application:
kubectl delete all -l learn-kubernetes=quotesweb
- To remove only the MariaDB objects:
kubectl delete all -l learn-kubernetes=quotemysql
- To remove all of the objects associated with this activity:
kubectl delete all -l sandbox=learn-kubernetes
Improve
Some ideas to improve or alter this activity:
- Write your own back-end function in a different language.
- Use a different database engine or a database outside of Kubernetes.
- Write the front end in another language. Use a server-based web engine that reads the URL from an environment variable that doesn’t need to be entered on the screen.
Related articles
Looking for more Kafka?
Learn more about the new Red Hat OpenShift Streams for Apache Kafka.