Skip to main contentCloud Native Bootcamp

Lab - Docker


In this lab, you will learn about how to use docker and how to run applications using docker. This lab will not explicitly give you the commands to progress through these exercises, but will show you a similar expected output. It’s your goal to develop the commands (shown as < command > at each step) to finish the lab.


  • Create a Quay account. This account is needed to push images to a container registry. Follow the tutorial to get familiar with interacting with Quay
  • You need to install Docker in your environment. Follow the instructions here to install it on Mac and here to install it on Windows.

Working with docker

Before proceeding, make sure docker is properly installed on your system.

  1. Please verify your Docker by looking up the version.

If it is installed, you will see a version number something similar to below.

$ <command>
Docker version 19.03.0-beta3, build c55e026

Running a hello-world container

Let us start with a hello-world container.

  1. run a hello-world container.

If it is successfully run, you will see something like below.

$ <command>
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
1b930d010525: Pull complete
Digest: sha256:41a65640635299bab090f783209c1e3a3f11934cf7756b09cb2f1e02147c6ed8
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.

Since, hello-world image is not existing locally, it is pulled from library/hello-world. But if it is already existing, docker will not pull it every time but rather use the existing one.

This image is pulled from Docker hub is a repository used to store docker images. Similarly, you can use your own registries to store images. For example, IBM Cloud provides you a container registry.

Verifying the hello-world image

  1. Now verify if an image is existing in your system locally.

You will then see something like below.

$ <command>
hello-world latest fce289e99eb9 5 months ago 1.84kB

Get the sample application

To get the sample application, you will need to clone it from github.

# Clone the sample app
git clone
# Go to the project's root folder
cd cloudnative_sample_app/

Run the application on Docker

Build the docker image

Let’s take look at the docker file before building it.

FROM maven:3.3-jdk-8 as builder
COPY . .
RUN mvn clean install
FROM openliberty/open-liberty:springBoot2-ubi-min as staging
COPY --chown=1001:0 --from=builder /target/cloudnativesampleapp-1.0-SNAPSHOT.jar /config/app.jar
RUN springBootUtility thin \
  • Using the FROM instruction, we provide the name and tag of an image that should be used as our base. This must always be the first instruction in the Dockerfile.
  • Using COPY instruction, we copy new contents from the source filesystem to the container filesystem.
  • RUN instruction executes the commands.

This Dockerfile leverages multi-stage builds, which lets you create multiple stages in your Dockerfile to do certain tasks.

In our case, we have two stages.

  • The first one uses maven:3.3-jdk-8 as its base image to download and build the project and its dependencies using Maven.
  • The second stage uses openliberty/open-liberty:springBoot2-ubi-min as its base image to run the compiled code from the previous stage.

The advantage of using the multi-stage builds approach is that the resulting image only uses the base image of the last stage. Meaning that in our case, we will only end up with the openliberty/open-liberty:springBoot2-ubi-min as our base image, which is much tinier than having an image that has both Maven and the JRE.

By using the multi-stage builds approach when it makes sense to use it, you will end up with much lighter and easier to maintain images, which can save you space on your Docker Registry. Also, having tinier images usually means less resource consumption on your worker nodes, which can result cost-savings.

Once, you have the docker file ready, the next step is to build it. The build command allows you to build a docker image which you can later run as a container.

  1. Build the docker file with the image_name of greeting and give it a image_tag of v1.0.0 and build it using the current context.

You will see something like below:

$ <command>
Sending build context to Docker daemon 22.17MB
Step 1/6 : FROM maven:3.3-jdk-8 as builder
---> 9997d8483b2f
Step 2/6 : COPY . .
---> c198e3e54023
Step 3/6 : RUN mvn clean install
---> Running in 24378df7f87b
[INFO] Scanning for projects...
  1. Next, verify your newly built image

The output will be as follows.

$ <command>
greeting v1.0.0 89bd7032fdee 51 seconds ago 537MB
openliberty/open-liberty springBoot2-ubi-min bcfcb2c5ce16 6 days ago 480MB
hello-world latest f9cad508cb4c 5 months ago 1.84kB

Run the docker container

Now let’s try running the docker container. Run it with the following parameters:

  1. Expose port 9080. Run it in the background in detached mode. Give the container the name of greeting.

Once done, you will have something like below.

$ <command>

Also, docker cannot create two containers with the same name. If you try to run the same container having the same name again, you will see something like below.

$ <command>
docker: Error response from daemon: Conflict. The container name "/greeting" is already in use by container "a74b91789b29af6e7be92b30d0e68eef852bfb24336a44ef1485bb58becbd664". You have to remove (or rename) that container to be able to reuse that name.
See 'docker run --help'.

It is a good practice to name your containers. Naming helps you to discover your service easily.

  1. List all the running containers.

You will see something like below.

$ <command>
bc2dc95a6bd1 greeting:v1.0.0 "/opt/ol/helpers/run…" 18 minutes ago Up 18 minutes>9080/tcp, 9443/tcp greeting
  1. Let’s inspect the running container.

By inspecting the container, you can access detailed information about the container. By using this command, you get to know the details about network settings, volumes, configs, state etc.

If we consider our container, it is as follows. You can see lot of information about the greeting container.

$ <command>
"Id": "bc2dc95a6bd1f51a226b291999da9031f4443096c1462cb3fead3df36613b753",
"Created": "2019-08-30T16:56:40.2081539Z",
"Path": "/opt/ol/helpers/runtime/",
"Args": [
  1. Get the logs of the greeting container.

It helps you to access the logs of your container. It allows you to debug the container if it fails. It also lets you to know what is happening with your application.

At the end, you will see something like below.

. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
:: Spring Boot :: (v2.1.7.RELEASE)
2019-08-30 16:57:01.494 INFO 1 --- [ecutor-thread-5] application.SBApplication : Starting SBApplication on bc2dc95a6bd1 with PID 1 (/opt/ol/wlp/usr/servers/defaultServer/dropins/spring/thinClinic.jar started by default in /opt/ol/wlp/output/defaultServer)
2019-08-30 16:57:01.601 INFO 1 --- [ecutor-thread-5] application.SBApplication : No active profile set, falling back to default profiles: default

This shows that the Spring Boot application is successfully started.

Access the application

  • To access the application, open the browser and access http://localhost:9080/greeting?name=John.

You will see something like below.

{"id":2,"content":"Welcome to Cloudnative bootcamp !!! Hello, John :)"}

Container Image Registry

Container Image Registry is a place where you can store the container images. They can be public or private registries. They can be hosted by third party as well. In this lab, we are using Quay.

Pushing an image to a Registry

Let us now push the image to the Quay registry. Before pushing the image to the registry, one needs to login.

  1. Login to Quay using your credentials.

Once logged in we need to take the image for the registry.

  1. Tag your image for the image registry using the same name and tag from before. Be sure to include the host name of the target image registry in the destination tag (e.g. NOTE: the tag command has both the source tag and repository destination tag in it.

  2. Now push the image to the registry. This allows you to share images to a registry.

If everything goes fine, you will see something like below.

$ <command>
The push refers to repository [<repository_name>/greeting]
2e4d09cd03a2: Pushed
d862b7819235: Pushed
a9212239031e: Pushed
4be784548734: Pushed
a43c287826a1: Mounted from library/ibmjava
e936f9f1df3e: Mounted from library/ibmjava
92d3f22d44f3: Mounted from library/ibmjava

Once the push is successful, your image will be residing in the registry.

Clean Up

  1. Stop the greeting container.

  2. Remove the container.

  3. Remove the image. (NOTE: You will need the image_id to remove it.)

Pulling an image from the registry

Sometimes, you may need the images that are residing on your registry. Or you may want to use some public images out there. Then, we need to pull the image from the registry.

  1. Pull the image greeting from the registry,

If it successfully got pulled, we will see something like below.

ddcb5f219ce2: Pull complete
e3371bbd24a0: Pull complete
49d2efb3c01b: Pull complete
Digest: sha256:21c2034646a31a18b053546df00d9ce2e0871bafcdf764f872a318a54562e6b4
Status: Downloaded newer image for <repository_name>/greeting:v1.0.0<repository_name>/greeting:v1.0.0


You have successfully completed this lab! Let’s take a look at what you learned and did today:

  • Learned about Dockerfile.
  • Learned about docker images.
  • Learned about docker containers.
  • Learned about multi-stage docker builds.
  • Ran the Greetings service on Docker.

Congratulations !!!

Check out the Solution