What is a Dockerfile?
In this article we’ll learn about Dockerfile
and create a simple container based on it!
When I first saw a Docker logo 🐳 few years ago, timeline wise it was after completing graduate school, thought it was cute and clever. It must have been a buzz word then. Tried two tutorials on it just to start making hands dirty. Now have to intermittently come across tasks that involves dockerizing builds. Given I have also spent some time to learn more about it, let’s dive in to get a comprehensive overview of creation of a Dockerfile
and where it stands in the world of containerization.
I am presuming you have some idea on what a Docker is. Give it a quick read if you want a primer or otherwise. Here is the git repository for the tutorial.
Let’s see a sample Dockerfile
:
My current directory structure using command tree .
looks like this:
.├── Dockerfile└── index.html0 directories, 2 files
In the COPY
command, I copied index.html
file from my current directory to container’s /usr/share/nginx/html
directory. We set the path /usr/share/nginx/html
as our working directory using WORKDIR
command. The RUN
command updates and install vim
to the custom container we are making based out of image nginx:1.21.6
. The first command FROM
is the base image we are using. You can see this base nginx:1.21.6
image at the official docker registry. Now, if you look at the Dockerfile
of the nginx:1.21.6
image in the registry, it is using Debian
operating system image version bullseye-slim
.
See, we are basically building on top of some other previous images. In addition we can see history of an image using docker history <image-name>
command.
docker history server-1:1.0.0
General pattern of docker command line is:
docker <command> <subcommand> (options)
Now that we have walked over the custom Dockerfile
, let’s build an image from it:
docker image build -t server-1:1.0.0 .
-t
flag is a tag consisting of name and optional tag in the form <name>:<version>
. Here, we gave server-1:1.0.0
.
.
is a path to a Dockerfile
. Here, the Dockerfile
is present in the same location as where we are running the command.
The output of the command looks something like this:
Now the image is ready, let’s instantiate it and make a container!
docker container run -it -d -p 80:80 --name hello-world server-1:1.0.0
-it
is for interactive and pseudo TTY (terminal interacts with TeleTypewriter)
-d
is for detaching the container
-p
is for port. Digits before :
is of host and after is that of a container. We need to expose the port in Dockerfile
. In our custom Dockerfile
we inherited it from the base nginx:1.21.6
dockerfile.
--name
is for name of the container.
Since nginx is a web-server, if we go to https://127.0.0.1:80
in your browser, you should see your index.html
page rendered:
Now let’s go inside the running container hello-world
and edit the index.html
file.
docker container exec -it hello-world /bin/bash
exec
is for running command in the running container
/bin/bash
is the path to the bash shell.
You would be in the /usr/share/nginx/html
directory, the directory we set from WORKDIR
command. You’ll see index.html
file. Add few line using vi
editor and save it.
Now when you go to the http://127.0.0.1:80
and refresh the page, the newly added contents are seen!
You can exit out of the container using exit
command. You can list running containers using docker container ls
. Let’s stop the container and delete it.
docker container stop hello-world
docker container rm hello-world
It’s been recommended to put the least used command such as LABEL
, ENV
at the top of the Dockerfile while the most used command such as COPY
at the end of the Dockerfile. This way we make re-run commands from where the changes were made to be as small as possible.
In addition to the building images and creation of containers, some other useful commands are:
docker container top <name-of-container>
: List all processes running in the container. You can think of it as a container specific version ofps aux
.docker container inspect <name-of-container>
: Details about the container’s configuration.docker container stats <name-of-container>
: Performance statistics of the container.docker network ls
: show networksdocker network inspect <name-of-network>
: Details about the container
This is just about creating a single container and running it. What if we have multiple containers and have dependencies between them? That’s when we have docker-compose
. In the example above we do not have some kind of persistent data storage service such as postgres. The docker-compose
helps us to integrate these two different images.
For production grade orchestration of these different containers, we have tools like docker’s swarm or kubernetes.
In summary, Dockerfile
is a sequence of commands to create an image. From the image we can create containers.
That’s all for the article. I hope this gave you a basic understanding of a Dockerfile and how it stands in respect to orchestration tools.
Thank you for reading.
Inspiration:
You can support me on Patreon! 🦦