Learning Docker – Hello World!

Welcome to my tutorials on how to leverage Docker to deploy your application! This is meant as much to help me learn, so it doubles as a “what worked for me” series.

Before diving in, I want to give a brief overview of Docker and some of the concepts we will need to apply. Docker works by providing an environment, called a container, for applications to run in, regardless of the underlying OS. Each container runs an image. There are many images available free for use, especially those listed on Docker Hub at https://hub.docker.com.

However, if you want your application to run in Docker, or any other container platform that uses Docker containers such as Kubernetes, you will need to make your own images. Every image can be used as the basis for a new image, eg the Tomcat images are based on Java images. So a good way to start building your own image is to identify an image that already exists with tools that you will need, and then add to it. This also means that you can make your own “starter images” containing your common software that you need for multiple applications.

Last, as a note, is is good development practice to isolate the top-level running programs that run to make up your application into separate images that run as individual containers, eg building a Python program and the RabbitMQ broker that it uses as separate images. This enables you to update each component separately and can cut down on development time. You also don’t have to follow this guidance, but you may discover that you can reuse that RabbitMQ image in another application.

First, we will create the dreaded, “Hello world” image. There is one on Docker Hub, but that image comes with a binary that is run, and the application primarily confirms that your Docker installation is working. My goal is to build my first image.

To build our “Hello World” image (or any image), we need to create a Dockerfile file. This file tells Docker how to build your image. There is a reference on the commands that you can place into a Dockerfile (and a good best practices guide to boot) at https://docs.docker.com/develop/develop-images/dockerfile_best-practices/. However, we only need two commands for your Dockerfile: FROM and CMD.

Make a directory on your computer, and create a file called Dockerfile with the following content:

FROM busybox
CMD ["sh", "-c", "echo Hello world!"]

The FROM statement identifies which image name (repository) and optional tag you want to use as the starting point for your image. By default, this will pull from Docker Hub. However, you can attach other registries (including your own) and get your images from there. We chose the “busybox” image, as it is small (1.22MB) and provides a shell that we can run a command against. “busybox” identifies no tags, so it assumes that we want the “busybox:latest” The people who built the busybox image can also tag different versions of their image, using different architectures, libraries, and versions. At the time of this writing, the image contained busybox 1.31.1 and could be compiled using one of three different C libraries: musl, glibc, and uclibc. The image maintainers identified the tags “busybox:musl”, “busybox:glibc”, and “busybox:uclibc” so that you as the image developer could pick which kind of busybox binary you want. It also happens that the “busybox:latest” tag is also assigned to the “busybox:musl” tagged image, so that is the busybox version we are using.

The CMD statement is the program to run inside the docker container. In that case of Apache HTTPD or Tomcat, we would supply the command we would type at a shell prompt to run the server. For our “Hello World” image, we are only going to call the sh shell (which happens to be busybox) and pass it the -c and “echo Hello world!” arguments, which causes sh to write “Hello world!” to the console.

Now that we have a working Dockerfile file, we can bake our first image! From a shell prompt, change to the directory you created for your Dockerfile and run this command:

docker build -t helloworld .

Docker will spew out a bit of information regarding its progress, and will download the busybox image we specified in the Dockerfile if it is not already on your system. If it is successful, your should see the following two lines:

The first line gives you the image identifier, and this number is unique for every image on your system. The number it displayed for you is YOUR OWN image identifier. You can use this identifier to specify an image to manipulate when using docker image manipulation commands. The second line indicates the tag applied to your image. The -t argument for docker build specifies the tag you want to give the image. You can also add your own subtag, like “helloworld:v1”, “helloworld:busybox”, or even “helloworld:im_awesome”. You can also use a tag to identify an image to manage with the docker image commands.

If you get a “SECURITY WARNING: You are building a Docker image from Windows against a non-Windows Docker host” message, don’t worry about this for now. We will cover this in the next lesson.

You have your image. Let’s try it out! Run the following Docker command:

docker run -it --rm helloworld

If all is well, you will see “Hello world!” in your shell. If not, the Dockerfile is probably mistyped. The -it tells docker to run interactive and to use the console for input and output. Otherwise you won’t see a thing. The --rm tells Docker to destroy the container it creates to run your image when it is finished running. For now, we don’t need these littering your disks.

So now you have your first Docker image. But it is using the output that I provided you. Edit your Dockerfile so that it outputs your own personalized message, save it, and rerun the docker build command from above. Once you have your “Successfully built”, then run the docker run command from above. Now you have built and run your first customized docker image.

Now as you begin building your own images, what happens to the old ones? Did you change the tag name in your docker build command? If not, what happened to the old image? Let’s find out. Run the following:

docker image ls

Your output should look something like this:

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
helloworld          latest              0021c983fd24        About an hour ago   1.22MB
<none>              <none>              9cde4988b759        About an hour ago   1.22MB
busybox             latest              1c35c4412082        2 days ago          1.22MB

I didn’t change the tag in my build command, and docker will only allow one image to have a given tag. The last image made gets the tag. So then what can I do with the image with no tag? I can do a few things. I can run it:

docker run -it 9cde4988b759

I can also delete it:

docker image rm 9cde4988b759

If you get a message that there are stopped containers using the image, you can purge stopped containers:

docker container purge

So to wrap up, we briefly covered what a Docker container and an image are. We created a simple Dockerfile that writes “Hello world!” using the latest busybox image off Docker hub and talked about tags. We built an image from the Dockerfile and ran it. We made a second image with a custom message and ran that one, too. And we listed the images on our system and deleted an unused image. I hope you found this helpful, and for the next lesson, we will add custom files into our images.

Tagged :