A Guide on Docker Dockerfile and Example to Build Image

January 9, 2017 | By in TRENDING
| Reply More

The current article will walkthrough one of the most used concept in docker ecosystem which is the Dockerfile. We will explore the building blocks of Dockerfile to automate the building of docker images for our applications and services.

Dockerfile is basically a text file that contains a set of instructions or commands aim at assembling a docker image. Therefore, calling docker build command docker read the instructions from Dockerfile and bundle the docker image.

What we will learn

  • Dockerfile instructions
  • Examples of building docker images

The article is organized as follows

  • Dockerfile usage
  • Dockerfile structure and common instructions
  • Dockerfile examples
  • Conclusion

Dockerfile usage

Mainly, the command to build a docker image is as mentioned above docker build ${CONTEXT}; ${CONTEXT} is the context of our image, which can be either a PATH of a folder in the host machine files system or URL to a git repository; roughly speaking the ${CONTEXT} is all the files needed by the Dockerfile during the building phase.

The build is run by the docker daemon not the docker CLI. All the ${CONTEXT} is sent recursively to the docker daemon, thus, is highly recommended to use ‘/’ as PATH of the image context as everything belonging to ‘/’ will be sent. Dockerfile must be located in the root of the ${CONTEXT}.

Docker allows to use .dockerignore file to specify all the files and folders which must be excluded from the build, therefore, they will not be sent to the docker daemon and by doing so we will increase the building performance.

Let’s create a simple Dockerfile that contains only one instruction that I’ll explain later on:

➜  docker-dockerfile cat Dockerfile
FROM node:latest

The simplest way to build a docker from this Dockerfile is by running docker build and giving the current directory as ${CONTEXT}:

➜  docker-dockerfile docker build .
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM node:latest
---> 36dc1bb7a52b
Successfully built 36dc1bb7a52b

It’s possible to specify the path of the Dockerfile by adding –f argument like below:

➜  Code docker build -f docker-dockerfile/Dockerfile docker-dockerfile/

One of the most important feature of docker image building is the ability to tag our images so that we can find them by tags (mainly names). Thus, to tag an image we must add the argument –t and the tag name; we can also assign a version of the image by adding –t ${tag_name}:${version}

➜  docker-dockerfile docker build -t linoxide/nodejs .

➜  docker-dockerfile docker build -t linoxide/nodejs:0.0.1 .

We can check if the built image:

➜  docker-dockerfile docker images linoxide/nodejs

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
linoxide/nodejs     0.0.1               36dc1bb7a52b        2 weeks ago         655.5 MB
linoxide/nodejs     latest              36dc1bb7a52b        2 weeks ago         655.5 MB

Dockerfile structure

The format/structure of Dockerfile is very straightforward, which is basically like below:

# Comment goes here

INSTRUCTION argument1 argument2 argument3 …

Instruction is non-case-sensitive but as convention developers are used to write instructions in uppercase format. The order of the instructions in Dockerfile is important, which the same order followed at the image build time.

.dockerignore file

Below is an example of .dockeringore file that illustrate all to forms allowed to exclude files or folders from the docker image build:

➜  docker-dockerfile cat .dockerignore
# this is a comment
*/temp*
*/*/temp*
temp?
node_modules/
.git/

  • */temp*:    exclude all files or directories whose name start with temp and that are subdirectories of the root, for instance /parent_folder/temp.txt /parent_folder/tempo/ etc.
  • */*/temp*: same as the first but with two levels from the root
  • temp?: concerns files or directories that have one character after temp like tempo etc.
  • node_modules/: exclude folder node_modules/ in the current folder from the build.
  • .git/: exclude the hidden folder .git/ in the current folder from the build.

All the lines that start with # are considered as comments so they are ignored at the build time.

Dockerfile structure and common instructions

1) FROM

FROM instruction sets the base image for the subsequent instructions. Basically, a valid Dockerfile must have in his first line a FROM instruction without considering comments.  Docker daemon will first check if the image exist in the host machine otherwise it will pull it from docker hub. This instruction has three forms:

FROM <image>
FROM <image:tag>
FROM <image>@<digest>

FROM:

  • Must be first non-comment line
  • Can appears multiple times in the same Dockerfile
  • <image>@<digest> and <tag> are optional if specified docker will pull the exact image otherwise it will pull the latest image.

In our first example (see above) we use nodejs image by using: FROM node:latest

2) MAINTAINER

This instruction allows us to specify the author of the built image; like so:

MAINTAINER <name>

# Example 2:
FROM node:latest
MAINTAINER linoxide

3) RUN

This instruction aims at executing commands. The command will be executed in a new layer on the top of the image and commit the result, which will be used as an image for next instructions and so on. RUN has two forms, a shell form and an exec from:

  • RUN <command> : shell form, the command is run in a shell, by default /bin/sh –c for linux or cmd /S /C for windows.
  • RUN [“executable”, “argument1”, “argument2”, “argument3” …]

Adding RUN command into our Dockerfile example:

# Example 1
# ----------
FROM node:latest
# Example 2
# ----------
MAINTAINER linoxide
# Example 3
# ----------
RUN /bin/sh -c 'echo "*** hello Dockerfile!! ***"'
RUN ["npm", "--version"]

Let’s build the image to see this example in action, notice the that commands 'echo "*** hello Dockerfile!! ***" and npm –version have been executed.

➜  docker-dockerfile docker build -t linoxide/nodejs:0.0.3 .

Step 3 : RUN /bin/sh -c 'echo "*** hello Dockerfile!! ***"'
 ---> Running in 6ebdd85f7687
*** hello Dockerfile!! ***

Step 4 : RUN npm --version
 ---> Running in bc9dbc7cf8c1
3.10.10

4) LABEL

This instruction add metadata to the image as a key-value pairs. the format is as below:

LABEL <key>=<value> <key>=<value> <key>=<value> ...

  • We can use multiple lines of LABEL:

LABEL "com.example.image "="Node Js image LABEL example"

LABEL version="0.0.4"

LABEL description=" label-values \

can span multiple lines."

  • We can use one line of LABEL with multiple key-value pairs:

LABEL "com.example.image"="Node Js image LABEL example" version="0.0.4" description=" label-values \

can span multiple lines."

Let’s build the image:

➜  docker-dockerfile docker build -t linoxide/nodejs:0.0.3 . --no-cache=true

Inspect the image:

➜  docker-dockerfile docker inspect linoxide/nodejs:0.0.3

"Labels": {
                "com.example.image": "Node Js image LABEL example",
                "description": " label-values can span multiple lines.",
                "version": "0.0.4"
            }

5) EXPOSE

EXPOSE <port> [<port>...]

This instruction informs Docker that the container listens on the specified network ports at runtime. It does not make the ports of the container accessible to the host. To do that, you must use either the -p flag to publish a range of ports or the -P flag to publish all of the exposed ports.

# Example 1
# ----------
FROM node:latest
# Example 2

# Example 5
# ----------
EXPOSE 3000

6) ENV

Follow the same format as LABEL, but to define environment variables.


# Example 6
# ----------
ENV mysql_root_password test dsf
ENV src_folder ./src/app

Build the image and inspect it to check the Env variables:

➜  docker-dockerfile docker build -t linoxide/nodejs:0.0.3 . --no-cache=true
➜  docker-dockerfile docker inspect linoxide/nodejs:0.0.3

"Env": [             "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "NPM_CONFIG_LOGLEVEL=info",
                "NODE_VERSION=7.2.1",
                "mysql_root_password=test  dsf",
                "src_folder=./src/app"
            ]

7) ADD

This instruction copies new files, directories or remote file URLs from <src> and adds them to the filesystem of the image at the path <dest>. The format is:

ADD <src>... <dest> but if the paths containing whitespace we need to use ADD ["<src>",... "<dest>"]

...
# Example 7

# ----------
ADD . /src/app/

This example copies the current directory content into /src/app/ in the image.

  • If<dest> does not end with a trailing slash, it will be considered as a regular file and the contents of <src> will be written at <dest>.
  • If<dest> doesn’t exist, it is created along with all missing directories in its path.
  • Basic wildcards are allowed in <src> path for instance, dist* readm?.txt etc.

If the <src> is an archive (tar, gzip, bzip2 or xz) ADD will automatically unpack it in the <dest>

8) COPY

This instruction is very similar to ADD and pretty much do the same thing. COPY has the same form as ADD, it copies files/directories from <src> into <dest>. In fact, unlike ADD, COPY does not except a URL as <src> and it does not unpack an archive <src> file.

9) ENTRYPOINT

This command allows us to specify a startup command that will always be executed when a container is started.

...

# Example 8
# ----------
ENTRYPOINT ["/bin/echo", "Hello linoxide"]

Rebuilding the docker image:

➜  docker-dockerfile docker build -t linoxide/nodejs:0.0.3 . --no-cache=true

Run a container from the built image:

➜  docker-dockerfile docker run -it --name nodejs-dockerfile linoxide/nodejs:0.0.3
Hello linoxide

Please notice that only one ENTRYPOINT by Dockerfile is allowed.

10) CMD

The command CMD is pretty much similar to the command ENTRYPOINT, also is one command is allowed by Dockerfile. The main difference between ENTRYPOINT and CMD is that the CMD is omitted once a command is specified within docker run …

...
# Example 8

# ----------
ENTRYPOINT ["/bin/echo", "Hello linoxide"]

# Example 9
# ----------
CMD  ["Yo World!!"]

Rebuilding the image:

➜  docker-dockerfile docker build -t linoxide/nodejs:0.0.3 . --no-cache=true

Creating a docker container

➜  docker-dockerfile docker run -it --name nodejs-dockerfile linoxide/nodejs:0.0.3
Hello linoxide Yo World!!

Creating a second container by adding a command at the end of docker run, hence, the CMD will be omitted:

➜  docker-dockerfile docker run -it --name nodejs-dockerfile2 linoxide/nodejs:0.0.3 Horay
Hello linoxide Horay

11) VOLUME

The VOLUME instruction creates a mount point with the specified name and marks it as holding externally mounted volumes from native host or other containers.

# Example 10
# ----------
VOLUME /var/log

12) USER

USER <daemon or UID>

The USER instruction sets the user name or UID to use when running the image and for any RUN, CMD and ENTRYPOINT instructions.

# Example 11
# ----------
USER deep

13) WORKDIR

WORKDIR /path/to/folder

This instruction sets the working directory for any RUN, CMD, ENTRYPOINT, COPY and ADD instructions in the Dockerfile. If the WORKDIR does not exist, the the folder will be created as well as the parent-folders. We can understand WORKDIR as the linux ‘cd’ command, except that the folder will be created is does not exist.

Dockerfile examples

Please see the comments in each Dockerfile example.

Nodejs app Dockerfile example:

FROM node:argon
# Create app directory
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
# Install app dependencies
COPY package.json /usr/src/app/
RUN npm install
# Bundle app source
COPY . /usr/src/app
EXPOSE 8080
CMD [ "npm", "start" ]

Nginx Dockerfile example:

FROM debian:jessie
MAINTAINER NGINX Docker Maintainers "docker-maint@nginx.com"
ENV NGINX_VERSION 1.11.7-1~jessie
RUN apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys 573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62 \
     && echo "deb http://nginx.org/packages/mainline/debian/ jessie nginx" >> /etc/apt/sources.list \
     && apt-get update \
     && apt-get install --no-install-recommends --no-install-suggests -y \
                               ca-certificates \
                               nginx=${NGINX_VERSION} \
                               nginx-module-xslt \
                               nginx-module-geoip \
                               nginx-module-image-filter \
                               nginx-module-perl \
                               nginx-module-njs \
                               gettext-base \
     && rm -rf /var/lib/apt/lists/*
# forward request and error logs to docker log collector
RUN ln -sf /dev/stdout /var/log/nginx/access.log \
     && ln -sf /dev/stderr /var/log/nginx/error.log
EXPOSE 80 443
CMD [ "nginx", "-g", "daemon off;" ]

Conclusion

In this guide, we have seen the most important commands that we can use to automate building docker images from a Dockerfile. We have seen also the structure of Dockerfile and  two basic examples of docker images, one for to build nodejs web app image and one for nginx server.

Filed Under : CONTAINERS, TRENDING

Tagged With : , ,

Free Linux Ebook to Download

Leave a Reply

All comments are subject to moderation.