A Dockerfile is a configuration file which instructs docker on how to build a customized image from an existing base image and how to get things started. It is useful in building exportable containers for applications which can later be used in creating and deploying containerized nodes.
An application can contain several dockerfiles containing configurations respective for each environment the container is meant for: say like dev, staging and production; while the production dockerfile would be of filename "dockerfile" with no extension.
A dockerfile generally consists of three steps, which are in general the steps in creating a custom image with its own set of contents and runtimes by using a base image.
i/ Specify a base image suitable for the application to be containerized
A base image is required because, a container we’re trying to create contains an application with its own suitable runtime and OS related resources and runs virtually isolated from the host. And in order to provide the application we’re planning to containerize a runtime for itself to be hosted and running, we would require a base image.
ii/ Specify what needs to be copied and where to be placed
We place relavant commands to copy the necessary binaries and supporting assets onto the base image, so that when a container out of this image is created and executed, the copied set of binaries and assets and other files shall also be copied onto the created container and executed; like that of a cloned application. This helps us in rapidly scaling and multiplying application nodes without having to worry about the issues which might arise when the files are being copied onto new replicas. A fastest mode of replication possible.
iii/ Specify what needs to be executed and how it needs to be done, when a container is built and run
As soon as a container is created and run from the image, it boots up with a specific command that comes built-in to it. This is what we override when using docker commands for creating or running containers from images via the toolkit. In general, this command would be something for bootstrapping the application for which the container has been created in the first place.
For example, a node command to startup the nodejs application as soon as a container is started. Or a tcplistener which starts to listen onto a port.
For example, consider building a simplest custom image which is a base linux image and contains an installed node environment for later use. The dockerfile is constructed as below:
# specify a base image to work on FROM alpine # specify what needs to be done RUN apk add --update npm # specify what needs to be done when # a container is run using this image CMD ["npm", "-v"]
This script is saved under a file "Dockerfile" without any extension. And under the directory where the file is saved, to build an image out of the dockerfile instructions we give the below command:
> docker build -t <docker_hub_user_id>/<repository_name>[:version_tag] <path_to_dockerfile>
> docker build -t refebruvuser/mynpm .
Here we specify "." to indicate that we’re running the command on the same directory where the dockerfile exists. While using a dockerfile, we might need to come across adding several other things to the pipeline such as attaching volumes, ports and so on which might make the build command a lot complex than what we’ve used now.
To add more sophistication while building an image without having to write everything within a build command, we can make use of another command which can help us compose the build command in the form of a standalone script and execute the content of the script with a simple command. This we call as docker-compose, which we shall look in the upcoming articles in this series.
This results in an image built and saved under the docker hub with the specified tag of the user. To create and run a new container out of this image, we give the below command:
> docker run -i -t <docker_hub_user_id>/<repository_name>[:version_tag]
here -i indicates that the command shall be interactive which means the command connects to the STDIN of the container created.