Docker, but the goal is to help become more familiar with containerization. Be aware that Docker for macOS and Windows has a licensing fee for commercial use. Docker is one of many other container runtimes and tools such as Podman and Buildah, which are open source.
A container is an isolated process on a host that shares the host operating system’s kernel. However, it runs sequestered from other instances on the host system, using its own binaries and libraries to support whichever application is running on it.
Beginners tend to confuse the concept of virtual machines and containers because they are both instanced environments that function on a physical host. However, virtual machines are made of complete operating systems running over a hypervisor. The virtual machine doesn’t share a kernel with the hypervisor as it has its own, just like a physical host; it is just virtualized.
Virtual machines are resource intensive and take longer to boot. On the other hand, containers don’t need to boot up, load drivers, and have their own kernel. They are much more lightweight, take up fewer resources, and boot quickly. This advantage makes containers perfect for being ephemeral environments for applications to run in when it comes to portability and testing.
Before starting, you must have Docker or Podman installed on your host. It is OK if you do not have Docker installed on your system. I will provide a few links to walk you through the installation process.
Installing Docker is not difficult and should only take a few minutes to do. However, you will need sufficient privileges to install it. Podman is an excellent alternative to Docker because it doesn’t require admin privileges to run. Its front-end API structure is identical to Docker, making Podman manageable to use with a quick learning curve. Docker commands will work for Podman. You prepend the command with podman
instead of docker
; however, if you wish, you can make an alias for podman
and change it to docker
. You will not notice a difference on the front end.
Below are links to the official installation instructions on Docker.com. Docker Desktop requires a license for use by organizations with more than 250 employees or more than $10 million in revenue effective January 1, 2022. On the Windows and macOS operating systems, you can only use Docker Desktop. You may also use Podman as an alternative to Docker if you use Linux or macOS.
First, I will verify that I have Docker installed in my environment with a simple version check. The output may not be the same version displayed below, but if Docker is installed, you should have similar output.
$ docker -v
Docker version 20.10.17, build 100c701
Let‘s clone the application to the directory and go to the working directory of the cloned repository.
$ git clone https://github.com/CiscoLearning/create-container-for-application.git
Cloning into 'create-container-for-application'...
remote: Enumerating objects: 52, done.
remote: Counting objects: 100% (52/52), done.
remote: Compressing objects: 100% (31/31), done.
remote: Total 52 (delta 17), reused 48 (delta 15), pack-reused 0
Receiving objects: 100% (52/52), 20.94 KiB | 5.23 MiB/s, done.
Resolving deltas: 100% (17/17), done.
$ cd tutorial-create-container-for-application/
~/tutorial-create-container-for-application$
If you take a quick look at the contents, you’ll see this repository has a Dockerfile. There are a couple of ways you can instruct Docker on how to assemble a container. This tutorial will focus on using a Dockerfile to build our container. A Dockerfile is a text file containing instructions that tell Docker which commands to run to create the container.
~/tutorial-create-container-for-application$ ls -al
total 64
drwxr-xr-x 13 barweiss staff 416 Aug 1 16:43 .
drwxr-xr-x 14 barweiss staff 448 Aug 1 16:43 ..
-rw-r--r-- 1 barweiss staff 55 Aug 1 16:43 .dockerignore
drwxr-xr-x 12 barweiss staff 384 Aug 1 21:08 .git
-rw-r--r-- 1 barweiss staff 33 Aug 1 16:43 .gitignore
-rw-r--r-- 1 barweiss staff 2731 Aug 1 16:43 Dockerfile
-rw-r--r-- 1 barweiss staff 1070 Aug 1 16:43 LICENSE
-rw-r--r-- 1 barweiss staff 131 Aug 1 16:43 README.md
-rw-r--r-- 1 barweiss staff 469 Aug 1 16:43 main.py
-rw-r--r-- 1 barweiss staff 307 Aug 1 16:43 requirements.txt
drwxr-xr-x 5 barweiss staff 160 Aug 1 16:43 static
-rw-r--r-- 1 barweiss staff 397 Aug 1 16:43 swanson_quote_api.py
drwxr-xr-x 3 barweiss staff 96 Aug 1 16:43 templates
With your favorite code editor, open the Dockerfile to view the contents. I am going to use VS Code in the following examples.
~/tutorial-create-container-for-application$ code Dockerfile
This Dockerfile is very simple but well documented and explains what each command does. (Note: Docker refers to these as instructions.) Take some time to read the Dockerfile and understand what it does. This Dockerfile tells Docker to use the python:3.10-slim-bullseye base image, a simple Python 3.10 shell. Next are a few labels that provide metadata for the image we are creating. In the next step, the Dockerfile creates a new directory in the container called /app and copies the contents in the local directory to the /app file in the new container. The WORKDIR sets the working directory for the application. Pip uses the requirements.txt file to load the Python dependencies into the environment, then opens TCP port 5000 for the container. The application uses TCP port 5000 for all incoming connections. The final line of the Dockerfile instructs Docker to run the command /app/main.py, which starts the application in the container.
In the table below are some more common Dockerfile commands you may encounter. Please see Docker’s Hello Build documentation for more details.
Instruction | Description |
---|---|
ADD | Copies a file from the host system onto the container |
CMD | The command that runs when the container starts |
ENTRYPOINT | Allows you to configure a container that will run as an executable |
ENV | Sets an environment variable in the new container |
RUN | Executes a command and saves the result as a new layer |
USER | Sets the default user within the container |
VOLUME | Creates a shared volume that can be shared among containers or by the host machine |
WORKDIR | Sets the default working directory for the container |
EXPOSE | Informs Docker that the container listens on the specified network ports at runtime |
LABEL | Adds metadata to an image in the form of a key/value pair |
The .dockerignore file is similar to the .gitignore file. Docker will not copy over any files covered in this file during the build process. The .dockerignore file can be used to keep files that may contain private or unique information from being inadvertently added to the container’s environment. A good example is an .env file that may have variables that are unique to a particular environment but are not desirable for the container’s environment.
Once you have your application and Dockerfile configured, the build process is relatively simple to execute. The basics will be covered here, but I encourage you to review the documentation because there are quite a few considerations when going through this process.
First, ensure that you are at the root of your application’s directory. As you recall, in the previous step of this tutorial, we saw the Dockerfile located here. When using the build
command, you will need to tell Docker where the Dockerfile is located, and as you will see, it will be easier for us to run the build
command when we are in the same location.
The name and tag of the image are also needed for the docker build
command. The -t
argument in the docker build
command allows you to add an <image name>:<tag>
to the image, which will then be put into your local container repository. The tag is generally used to denote the version of your container. The naming and tagging conventions should follow a consistent policy, because it can become overwhelming to determine which image is the desired version. There are plenty of resources out there that discuss some of the best practices for container registry versioning and tagging.
Run the following command and be sure that you are running this command in the root of the application’s directory on the host.
~/tutorial-create-container-for-application$ docker build -t tao_of_ron_swanson:1.0 .
Notice the .
at the end of the command; it lets Docker know that the Dockerfile is in this directory. After running the command, Docker will go through the process of building the image. Below is a sample output if the build is successful:
~/create-container-for-application$ docker build -t tao_of_ron_swanson:1.0 .
[+] Building 9.1s (9/9) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 2.82kB 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 95B 0.0s
=> [internal] load metadata for docker.io/library/python:3.10-slim-bullseye 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 78.90kB 0.0s
=> CACHED [1/4] FROM docker.io/library/python:3.10-slim-bullseye 0.0s
=> [2/4] COPY . /app 0.0s
=> [3/4] WORKDIR /app 0.0s
=> [4/4] RUN pip install -r requirements.txt 8.8s
=> exporting to image 0.2s
=> => exporting layers 0.2s
=> => writing image sha256:713d63d683d153a20549599786c652ff89ae0741200c8322644b7c2d70afb90f 0.0s
=> => naming to docker.io/library/tao_of_ron_swanson:1.0
Once the image has been built, verify it is in the local repository.
~/create-container-for-application$ docker images tao_of_ron_swanson:1.0
REPOSITORY TAG IMAGE ID CREATED SIZE
tao_of_ron_swanson 1.0 713d63d683d1 13 minutes ago 140MB
The docker images
command will display all the images in your local container repository. Here, we knew which image to look for, and to make the output easier to read, we added the name and tag of the image to filter in the output.
To run a container, we use the docker run
command (see the documentation for more information). We will use the arguments -dit
to instruct Docker to run the container detached in the background and as an interactive open process; otherwise, it just runs once and exits as soon as the Python main.py script completes. The -t
argument allocates a pseudo-terminal session, which is good if we need to access the container for troubleshooting. The -p
argument is also required to let Docker know we want to externally open TCP port 5000 in this instance. Here, we map the external port 5000 to the container’s port 5000. If we were running multiple containers, the external port number needs to change because port 5000 would already be in use. So, if a second container were running, you would see an argument as -p 5001:5000
, which means map external port 5001 to the container’s port 5000. Finally, you can add an optional name to this running container with the --name
argument; otherwise, Docker will generate a random name for the new container. The image name is added at the very end.
~/create-container-for-application$ docker run -dit -p 5000:5000 --name example_image tao_of_ron_swanson:1.0
ccdf526fafa941db9c4e3e17ba58151ccbad6830df5e8dbfbf62f636d39c4d69
If the command is successful, the output will be assigned a hashed ID in hex for the Docker container instance. The nice thing about Docker is that you can use the first few hash values to identify the container. If no other container has ccdf
as the first four values of its hash, then you can use ccdf
to call on that container. In the example below, we will use the docker ps
command to verify that this container is running. We will use the -f
filter argument again to search by the first four hex values of its ID.
~/create-container-for-application$ docker ps -f id=ccdf
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ccdf526fafa9 tao_of_ron_swanson:1.0 "/bin/sh -c 'python …" 14 minutes ago Up 13 minutes 0.0.0.0:5000->5000/tcp example_image
The output from the docker ps
command provides information about the image’s current state. We can see that the container was created 14 minutes ago and has been up for about 13 minutes. We also see that the external localhost port 5000 is forwarding to 5000/tcp on the container.
Our application is now up and running in a containerized instance. The output of this application can be viewed by opening your browser and going to http://localhost:5000.
This tutorial has just scratched the surface of Docker and containerization. If you would like to learn more, please visit the following links: