How To Use Dockerfiles In Docker

This post will explain how to use Dockerfiles in Docker. You will see a brief introduction to each of the Dockerfile components. You will also see the complete process of installing and setting up WordPress with Dockerfile.

The Dockerfile is a simple text file that contains various instructions for building Docker images. It allows you to pull a basic Docker image and then effectively customize your Docker Image with all the bits that you need.

So what instructions can we use in the Dockerfile? The instructions can include the base image selection, installation of the required applications. The instructions can also include the addition of the configurations and the data files, and more.

Read how to set up Docker on Ubuntu if you need to install Docker. Also, this guide assumes that you are using Ubuntu Linux and that you have root access.

Basic Syntax of a Dockerfile

A Dockerfile is made up of several instructions, comments, and empty lines.

You add a comment to the Dockerfile using the hash symbol, as per the example below:

#Comment
INSTRUCTION arguments

You can also add instructions. Each instruction line has two parts; the first part is an instruction followed by one or more arguments.

It is a best practice convention to write instructions in UPPERCASE to differentiate them from the arguments.

Now, let us see an example that contains one comment and two instructions having one argument each:

#This is my Dockerfile
FROM ubuntu:20.04
CMD echo Hello World!!
Remember that the docker build system ignores any line that starts with a # symbol because it is a comment.

The line starting with FROM is an instruction, whereas ubuntu:20.04 is an argument.

Similarly, the line starting with CMD indicates an instruction and echo Hello World!!.

Components of Dockerfile with Build Instructions

Dockerfile contains several instructions that are used to build the docker image. A brief explanation of each instruction, syntax and some examples are shown below:

FROM

The FROM instruction is more important than all other instructions in the Dockerfile. It is used to set the base image to build a new image.

The Docker build system first checks whether the specified images are available in the Docker host or not. If the Docker Image is not found, the Docker build system will pull the Docker Image from the Docker Hub Registry.

The basic syntax of the FROM instruction is shown below:

FROM image_name:tag

Here the image_name is used to specify the base image, and the tag specifies the version.

For example:

FROM ubuntu:20.04

Here the image name is Ubuntu and is a base image, and 20.04 is the version of this Docker Image. You can also pull the latest version of the Docker image by using the word latest vs. 20.4.

You can also add multiple FROM instructions in a single Dockerfile to create and build images.

MAINTAINER

This instruction is used to define yourself as the maintainer of this Docker Image.

The syntax of the MAINTAINER directive is:

MAINTAINER author's_detail

For example, the MAINTAINER directive with the author name and the E-mail address:

MAINTAINER anto  <[email protected]>

However, this instruction has been deprecated. Now, the equivalent of this instruction is LABEL instruction. So now, you should use the LABEL directive, which is much more flexible than MAINTAINER.

LABEL maintainer="[email protected]"

As per Docker: You can add labels to your Docker Image to help organize images by project, record licensing information, aid in automation, or for other reasons. For each label, add a line beginning with LABEL and with one or more key-value pairs.

ADD

You can use this instruction to copy the files/directories from the Docker host system to the container.

The basic syntax of the ADD instruction is shown below:

ADD <source> <destination>

<source> specifies the path of the files/directories on the host system.

<destination> specifies the path on the container into which it will copy the source files/directories.

For example, You can copy the index.php file from the Docker host to /var/www/html/ on the container using the following syntax:

ADD index.php /var/www/html

ENV

Using this instruction, you can set an environment variable in the Docker image and use it for any application.

The basic syntax of ENV instruction is shown below:

ENV <key> <value>

<key> : Specify environment variable

<value> : Used to set the value for the environment variable

For example, you can set an environment variable for APACHE_ROOT to /var/www/html as below:

ENV APACHE_ROOT /var/www/html

We can also use it in the following way:

ENV varName=hello

USER

By default, the container is launched with the root user. However, you can use this instruction to change the default user name(UID) from the root user to the desired user.

The basic syntax of USER instruction is shown below:

USER UserName

Username: Name of the User.

You can, for example, set the username to anto as follows:

USER anto

Or

USER UID_of_user_anto

There is also an option of changing the user group (GID) using this instruction. The syntax for changing both the username and the user group is:

USER <username>[:<user group>]

You can run the id command to check your system’s username (UID) and user group (GID).

WORKDIR

You can use this instruction to change the current working directory from/to the path specified by this instruction. You can also use this instruction for other instructions such as RUN, CMD, ENTRYPOINT, COPY, and ADD.

The basic syntax of the WORKDIR is shown below:

WORKDIR <Directory Path>

<Directory Path>: Specifies the path for the working directory. You can use either an absolute or relative path.

For example:

WORKDIR /var/www/html

VOLUME

You use this instruction to create a directory in the Docker image filesystem. In addition, this instruction is used for mounting volumes from the Docker host or the other containers.

The basic syntax for the VOLUME directive is shown below:

VOLUME ["/directory_name"]

For example:

VOLUME ["/Database"]

Where: /Database, is the name of the mount point.

 EXPOSE

This instruction is used to open up a container network port for communication between the container and the outside world. We use this instruction to tell Docker about the network port the container listens on.

The basic syntax of EXPOSE directive is shown below:

EXPOSE <port> [<port>[/<protocol>...]

<port>: Specifies the port’s name that has to be exposed to the outside world.

<protocol>: You can use this optional field for a specific transport protocol, such as TCP and UDP. If you do not specify any protocol, then the protocol would be TCP by default.

For example:

EXPOSE 80/tcp

You can also add this instruction to specify multiple ports in a single line. For example, expose the port number 8080 as a UDP port and the port number 80 as a TCP port as follows:

EXPOSE 8080/udp 80/tcp

RUN

This instruction is used during the build process. It runs any commands on top of the new Docker Image and creates a new layer with the command execution results. As a result, the new Image will be used for Dockerfile’s next step.

The basic syntax for RUN instruction is as follows:

RUN [command]

[command]: Specifies the shell command executed during the image build time

RUN [<exec>, <arg1>,....<argn>]

<exec>: Specify the name and path of the executable to run during the build time.

<arg1>: Specify the variables number of the arguments for the executable.

For example, use the RUN instruction to execute the apt-get command, as shown below:

RUN apt-get update

Here the run command will update the system packages repository to the latest version. We usually use RUN for installing packages.

CMD

This command is very similar to the RUN command. The difference is that the RUN instruction is executed during the build time. Conversely, the CMD instruction is executed when the container is launched from the Docker Image.

Moreover, in Docker, a Dockerfile can only have one CMD instruction. So in case you specify more than one CMD instruction, then only the last CMD will work.

The primary use of this instruction is to set defaults for the executing container.

The basic syntax of the CMD instruction is shown below:

CMD [command]

[command]: Specifies the shell command has to be executed during the launch of the container.

CMD [<exec>, <arg1>,....<argn>]

<exec>: Specifies the name of the executable used to run during the container launch time.

<arg1>: Acts as an argument for the executable. We can also pass more than one arguments

For example:

CMD [ "echo", "Welcome to Dockerfile" ]

Or

CMD "Welcome to Dockerfile!!"

ENTRYPOINT

This instruction is used to set the primary command for the Docker Image. You can use it to set the default application every time a Container is created from the Docker Image. In addition, it helps us configure the executable container.

It is very similar to CMD instruction and allows you to identify which executable should be run when the container is started.

For Example:

CMD "Welcome to Dockerfile!!"
ENTRYPOINT echo "Welcome to Dockerfile!!"

Now, let’s see the exec form of the above commands:

CMD ["echo", "Welcome to Dockerfile!!"]
ENTRYPOINT ["Welcome to Dockerfile!!"]

Simple Use Case (Nginx)

You now have a basic understanding of Docker and each component in the Dockerfile. Now, it’s time to write your Dockerfile.

Let’s create a Dockerfile to create a docker image to run an Nginx container.

Create a text file named Dockerfile in your working directory and write the following instructions in it:

Run the following command in the terminal to create a Dockerfile:

nano Dockerfile

Now, add the following lines in this file:

# Dockerfile to build an Nginx image Based on Ubuntu's latest version

# Setting the base image to Ubuntu:20.04
FROM ubuntu:20.04

# Defining the maintainer
LABEL maintainer joe soap [email protected]

# Update the repository sources list
RUN apt-get update -y

# Installing necessary packages for Nginx
RUN apt-get install nginx -y

# Setting the default port for Nginx
EXPOSE 80

# Setting the default container command
CMD ["nginx", "-g", "daemon off;"]

Save and close the file.

Now, we are ready to create our first Nginx image with Docker using your Dockerfile.

To build this image, run the following command by going to the directory where your Dockerfile is located. That’s why we have specified . at the end, which stands for the current working directory:

sudo docker build -t nginx .

However, you can also build your Dockerfile by specifying the path where your Dockerfile is located like below:

sudo docker build -t nginx path/to/Dockerfile

Once the Nginx image has been built successfully, you should see the following output at the end:

…
…
Step 5/6 : EXPOSE 80
 ---> Running in 9a6bc97b3a29
Removing intermediate container 9a6bc97b3a29
 ---> 0cbc9bc3fbc8
Step 6/6 : CMD ["nginx", "-g", "daemon off;"]
 ---> Running in 553310fd250a
Removing intermediate container 553310fd250a
 ---> bb1a0c41b954
Successfully built bb1a0c41b954
Successfully tagged nginx:latest

Now, you can create an Nginx container using the Nginx image which you have created above.

Run the following command to create an Nginx container:

sudo docker run -p 80:80 -itd nginx

You should see the output like below:

d71c32781e0cc831fd23d5605c2048106cf78fa72c25e9110ea84be9c7a9f3c8

Now, you can check your running Nginx container with the following command:

sudo docker ps

You should see the following output:

CONTAINER ID       IMAGE        COMMAND        CREATED       STATUS      PORTS        NAMES
d71c32781e0c             nginx               "nginx -g 'daemon of…"   About a minute ago   Up About a minute   80/tcp              objective_zhukovsky

As you can see, the name of the container is objective_zhukovsky. The name is randomly picked unless you specify the container name beforehand.

Now, you can verify if the Nginx Web Server is running by typing the URL localhost:80 or 0.0.0.0:80 in your browser.

You can also run the following command to specify your name for the container.

sudo docker run –name nginx-server -p 80:80 -itd nginx

This time we have the name of the container, which is nginx-server.

Complex Use Case (WordPress, Nginx, MariaDB, PHP)

Now let us take a look at the complex use case. In this case, we are setting up a Dockerfile to install and set up WordPress on Ubuntu 20.04 using Docker.

This Dockerfile will do the following things:

  • Pull Ubuntu 20.04 image from the Docker Hub repository
  • Install Nginx, MariaDB, PHP and other PHP extensions
  • Download WordPress and extract it to the Ubuntu 20.04 image
  • Add start.sh and Nginx virtual host configuration file to the Ubuntu 20.04 image
  • Define the database in wp-config.php file
  • Expose the Nginx on port 80 and start the start.sh script at container startup

Let’s create a Dockerfile in your current working directory with the following command:

nano Dockerfile

Add the following contents:

# Set the base image to Ubuntu:20.04
FROM ubuntu:20.04
LABEL maintainer Joe <[email protected]>
USER root

# Setting Environment variable for timezone
ENV TZ=Asia/Kolkata
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

# Updating the repository sources list and install required packages.
RUN apt-get update -y
RUN apt-get install mariadb-server nginx php php-fpm php-common php-mysql php-gmp php-curl php-intl php-mbstring php-xmlrpc php-gd php-xml php-cli php-zip cu>

# Adding the start.sh script and nginx-site.conf file from the host system to container
ADD ./start.sh /start.sh
ADD ./nginx-site.conf /wordpress.conf
RUN mv /wordpress.conf /etc/nginx/sites-enabled/default

# Removing the default contents of Nginx
RUN rm -rf /usr/share/nginx/html/*

# Download WordPress source and copy it to the container
ADD http://wordpress.org/latest.tar.gz /wordpress.tar.gz

# Extract the WordPress source file and move the extracted directory to the Nginx default root.
RUN tar xvzf /wordpress.tar.gz
RUN mv /wordpress/* /usr/share/nginx/html/.

# Give proper permissions and ownership to the WordPress contents
RUN chown -R www-data:www-data /usr/share/nginx/html
RUN chmod -R 755 /usr/share/nginx/html

# Define the database settings in the wp-config.php file.
ENV WORDPRESS_DB="wordpress"
ENV WORDPRESS_PASSWORD="wordpress"
RUN sed -e "s/database_name_here/$WORDPRESS_DB/g" -e "s/username_here/$WORDPRESS_DB/g" -e "s/password_here/$WORDPRESS_PASSWORD/g" /usr/share/nginx/html/wp-co>

# Set proper permissions.
RUN chown www-data:www-data /usr/share/nginx/html/wp-config.php
RUN chmod 755 /start.sh

# Expose the Nginx on port 80 and start the start.sh script at container startup
EXPOSE 80
ENTRYPOINT ["/start.sh"]

Save and close the file when you are finished.

Next, create an nginx-site.conf file with the following command:

nano nginx-site.conf

Add the following contents in this file:

server {
    listen 80;
    listen [::]:80;
    root /usr/share/nginx/html;
    index  index.php index.html index.htm;
    server_name  localhost;
    client_max_body_size 100M;
    autoindex off;
    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
         include snippets/fastcgi-php.conf;
         fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
         fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
         include fastcgi_params;
    }
}

Save and close the file when you are finished.

Next, create a start.sh file with the following command:

nano start.sh

Add the following lines in this file:

#!/bin/bash
service mysql start 
service nginx start
/etc/init.d/php7.4-fpm start

if [ -f /usr/share/nginx/html/valid ]; then
  while true
  do
      echo "Press [CTRL+C] to stop.."
      sleep 1
  done
else
  mysqladmin -u root password password1
  mysql -u root -ppassword1 -e "CREATE DATABASE wordpress; GRANT ALL PRIVILEGES ON wordpress.* TO 'wordpress'@'localhost' IDENTIFIED BY 'wordpress'; FLUSH PR>
  touch /usr/share/nginx/html/valid

  while true
  do
      echo "Press [CTRL+C] to stop.."
      sleep 1
  done
fi

Save and close the file when you are finished.

Note: The above script will run whenever you start your container.

Now, run the following command to create a new image named WordPress:

sudo docker build --rm -t wordpress .

Once the Docker image has been built successfully, you can verify it with the following command:

sudo docker images

You should see the following output:

REPOSITORY               TAG                    IMAGE ID            CREATED            SIZE
wordpress                        latest                 9427df618837        3 minutes ago       836MB
ubuntu                             20.04                 d70eaf7277ea         2 days ago            72.9MB

Next, create a new container from the WordPress image using the following command:

sudo docker run -p 80:80 -itd wordpress

You can now verify your running container with the following command:

sudo docker ps

You should now see the following output if all went well:

CONTAINER ID      IMAGE       COMMAND        CREATED       STATUS       PORTS        NAMES
cf49d89f0f74             nginx         "nginx -g 'daemon of…"   6 hours ago         Up 6 hours          0.0.0.0:80->80/tcp   bold_ramanujan

If you have any issues, you can check the container logs with the following command:

sudo docker logs cf49d89f0f74

Now, you can access your WordPress container by using the URL localhost:80 or 0.0.0.0:80.

Wrapping up

You now know the basics of Docker. In addition, you learned about each component of a Dockerfile. In addition, you know how to write a Dockerfile to deploy a WordPress container.

You may also be interested in

About Anto Online

Anto, a seasoned technologist with over two decades of experience, has traversed the tech landscape from Desktop Support Engineer to enterprise application consultant, specializing in AWS serverless technologies. He guides clients in leveraging serverless solutions while passionately exploring cutting-edge cloud concepts beyond his daily work. Anto's dedication to continuous learning, experimentation, and collaboration makes him a true inspiration, igniting others' interest in the transformative power of cloud computing.

View all posts by Anto Online

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.