Intro to Docker - Containers, images and the Dockerfile
Tags: docker, devops
Published: 2020-04-12 08:40:51

**Introduction**

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.

A `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.

**A `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 `Dockerfile`:

```
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 `CMD` and `COPY` commands below, the `.` directory reference refers to the `WORKDIR`.

After, 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.

The `RUN` command is self-descriptive and simply executes a certain command, which in this example is installing our dependencies.

Finally, the `CMD` command defines the start-up command to run when the container starts, and, in this case, we will simply start our application.

**Conclusion**

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](https://hub.docker.com/). 

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.