Docker is a containerization system which enables developers and teams to handle application deployment and scaling in a much easier way than before.
Just as the name suggests, we can think of a container as an isolated system, that contains everything that is needed to run a certain application given a certain setup.
Images can be pre-built, retrieved from registries, created from already existing ones, combined together and started via a common network, etc.
We will see the difference between an image and a container, how they relate to each other, and how these two concepts allow us to make an application self-contained, and ready to be deployed.
Images and containers
We can think of images and containers as being two different states of the same underlying concept. Essentially, we can say that a container is a running instance of an image that packages and represents a certain application. For the sake of an analogy, going back to Java classes and objects, a container can be seen as an object, and an image can be seen as class.
Images are used to start-up containers, and, from running containers, we can get images, and all of this can be composed together to form a very powerful, system-agnostic way of packaging and deploying applications. I like to think of it almost as "shipping your localhost". Why? Since Docker requires that all the configurations needed to run an application are contained and installed within an image, creating an image is essentially replicating current running state of both the environment and the app, and bundling it together, making it available in the cloud.
So, as a recap:
Images can be retrieved from registries, that contain multiple pre-built images to be used by developers to build more complex images
Containers can be seen as running instances of images. It is possible to orchestrate multiple containers to be started and stopped as a "bundled service", effectively allowing entire applications to be structured as a cluster of related docker containers
Dockerfile - the way to dockerize an application
In order to dockerize an application, the flow we follow is to create a docker image that builds on top of an already existing one and that contains our application jar or the startup script for a Python app, as its start-up command.
A start-up command for a container means that this is the command that will be executed when the container is started. So, essentially, the running instance of our image becomes the container running our application.
Dockerfile is the specification that we use to build a new container from an already pre-built image and add our custom logic to startup our application. From a
Dockerfile, we use the
docker build command to build an image.
Dockerfile works by layers, where the first layer starts with the
FROM keyword, and defines which pre-built image to use to build our own image. Afterwards, we can defined more things like user permissions, which files to copy into the container from the local system (think for example, the
requirements.txt from a Python web app) and which start-up scripts to execute. Below is an example of a
FROM python:3 WORKDIR /usr/src/app COPY requirements.txt ./ RUN pip install --no-cache-dir -r requirements.txt COPY . . CMD [ "python", "./your-daemon-or-script.py" ]
Let's go over the contents of this simple Dockerfile and see what's happening.
The first line
FROM python:3 is declaring that the image we are going to build will expand on the base image that contains all the necessary runtime to run Python 3 based applications.
All the commands after this one, will be applied on top of the already existing Python 3 image, and, as a result, we will create our own Python 3 custom image, that contains our application.
WORKDIR defines the working directory, so the directory that will be assumed for any other command that follows in the Dockerfile, and where our application will be. In the
COPY commands below, the
. directory reference refers to the
COPY command is used to copy files from our local filesystem where the Dockerfile is, into the container, so that these files are readily available in our image.
RUN command is self-descriptive and simply executes a certain command, which in this example is installing our dependencies.
CMD command defines the start-up command to run when the container starts, and, in this case, we will simply start our application.
These base concepts are the foundation upon which Docker builds further capabilites and, if you know these basic concepts you can learn anything else. With these concepts you can now build our own images and will be able to understand what you'll see when you encounter a Dockerfile in your work's or in your friend's projects.
Last, but not least, you can find many official, pre-built images in Docker Hub.
Next, we will see how to package a simple Springboot application with Docker, and we'll introduce the
docker-compose command and the docker-compose file.