docker devops linux

Playing with docker is something funny, it allows you to create, destroy, modify and play around with a lot of tools and applications, which are mostly available on Docker Hub but what if you decide to biuld your own docker image?

As we have seen in a past article ( How to run a Docker container ) docker images are the core of our container, that contain all is needed to our application to run smoothly, but it can happen that you want to build up a custom Docker image, because you need to install a certificate, or you need a particular config file standing in a particular folder of the resulting virtual fs of the image.

What is a Dockerfile?

Yeah, you are right, let’s start from the beginning, you can think of a Dockerfile as a recipe that is needed to build a docker image. Technically speaking, it is a file that contains all the directives needed by docker build command to build up your container, in the way you want to.

Just a quick example before going deep in the details of the possible commands that you can input in your Dockerfile.

The following snippet is a very simple and basic Dockerfile that allows you to run a base linux image and it will install jq (json parsing tool).

Let’s build our first Docker container

Keep in mind that docker build command syntax requires a Dockerfile, wherever it is (containing folder path/URL/STDIN) and you can specify various options, we will just consider “-t” to tag our image, and “–rm” to remove intermediate containers needed for the build, in order to save space, if you want to discover other options I suggest you reading docker build documentation from official Docker reference

Getting back to our snippet, let’s try to build the docker image:

Run it to see it in action:

As you can see, using the -it option of the docker run command, I managed to create an interaction between my terminal and the container, where i had the chance to run the ‘jq‘ command.

What about the recipe?

It works, but let’s see the details of the commands in the snippet, and we will later try to enrich our image with some stuff.

Trying to follow the analogy to a recipe, I’m gonna list the basic directives that are found in a Dockerfile.

FROM image[:tag]

The base ingredient of your image, imagine of it as a pizza dough. It is where all the magic begins, the base image, usually minimalist linux images with the basic functions to run a shell and some commands, like packet managers, etc. Tag is an optional parameter, it will assume ‘latest’ by default, but if you need a specific version just use the tag.

COPY src dst

COPY directive allows the user to copy one or more resources from the filesystem to the docker image filesystem. Source path MUST BE RELATIVE to Dockerimage folder.

USER user

Used to specify a different user than root to run the container.

WORKDIR path

Sets the working directory where the container will start in. This will be the working directory for RUNCMDENTRYPOINTCOPY and ADD instructions.

ADD src dst

Similar to COPY, but it is a bit more advanced, as it allows remote paths (URLs) to be used as a source, decompressing archives, if the image supports it and has the right packages installed.

RUN command | ["executable”, "parameter1”, … ]

The key command to run custom scripts, execute package installation with base image packet manager (yum, apt, …), run linux commands and so on. Be careful when playing around with it, since RUN, ADD and COPY commands work creating a “layer” starting from the snapshot of the image at that point, caching commands, this means that if you need a package and you put this in your dockerfile:

And then you decide to add another package:

What happens here is that the apt-get update command is cached, so is not executed and you could end up with an outdated version of the packages.

ENV key=value

This directive sets environment variable to the indicated value, and this will be available as soon as you start the container. Also, environment variables, once defined, can be reused internally in the Dockerfile in the subsequent commands.

VOLUME path

Let’s open a little parenthesis about VOLUME directive, as it works differently from the “-v” flag in docker run command.

Indicating a particular volume with VOLUME directive means that you want the container to store data from that particular volume directly on the host’s disk. Upon evaluating VOLUME statement, Docker will create the corresponding directory in the container, and it will persist even if you remove the container and relative image.

Volumes in linux are usually located (depending on the installation) in /var/lib/docker/volumes

The difference with “-v” flag (as we saw) is that the flag allows you to ‘mount’ a folder at a particular path on the host in the container. This could cause issues when porting an image on different systems, if the specified path does not exist.

EXPOSE port(s)

With EXPOSE directive you are declaring which are the ports the container will work with. This is slightly different from “-p” flag of docker run, since the flag publishes a given container port on the corresponding host port.

ENTRYPOINT [“["command”]n style="color: #4bb566;">/CMD [“co["command”, "parameter1”, … ]p>They are used to indicate a command/program to be ran as soon as the container is ran.

Both indicate a command to execute as soon as the container is ready, but as you can see, CMD takes with itself the parameters of the command, while ENTRYPOINT doesn’t. This is because when entrypoint is specified, you need to specify parameters inside a separate CMD, i.e.:

They can be overridden with docker run:

Overriding ENTRYPOINT

Overriding CMD

How to enrich a Docker image and build it

Now that many of the directives are clear, let’s add them to our ‘recipe’ to make it do something more!

In the following Dockerfile I am going to:

  • Set an environment variable and use it
  • Create a volume and set it as the working directory
  • Copy a file from my repo to that volume
  • List the volume folder
  • Execute jq over the downloaded file

To build it, since I’m using a different name for the Dockerfile, I use the -f flag:

And this is the result:

You ran your first custom docker image!

Combinations can be infinite, you can do whatever you want before building the image, just practice and if you need further details check the Documentation from Docker website

If you like the article, feel free to share it with your friends and colleagues!