Besides images and containers, Docker has a third very important term called a Dockerfile. A Dockerfile is like a recipe on how to create an environment for a specific application, which means that it contains the blueprint and exact description on how to build a specific image file. For example, if we would like to containerize a webserver-based application, we would define all the dependencies for it, such as the base Linux system that provides the system dependencies such as Ubuntu, Debian, CentOS, and so on (this does not mean we virtualize the complete operating system but just use the system dependencies), as well as all applications, dynamic libraries, and services such as PHP, Apache, and MySQL in the Dockerfile and also all special configuration options or environment variables. There are two ways to build your own custom images. One, you could download an existing base image as we did in the previous Wordpress recipe and then attach to the container using BASH, install your additional software, make the changes to your configuration files, and then commit the container as a new image to the registry. Alternatively, here in this recipe, we will teach you how to build your own Docker image from a new Dockerfile for an Express.js web application server and upload it to your own Docker Hub account.
To complete this recipe, you will require a working installation of the CentOS 7 operating system with root privileges, a console-based text editor of your choice, and a connection to the Internet in order to communicate with the Docker Hub. It is expected that Docker is already installed and is running. Also, for uploading your new image to the Docker Hub, you need to create a new Docker Hub user account there. Just go to https://hub.docker.com/ and register there for free. In our example, we will use a fictitious new Docker Hub user ID called johndoe
.
johndoe
directory name appropriately with your own ID), and open an empty Dockerfile where you put in your image's building blueprint:mkdir -p ~/johndoe/centos7-expressjs cd $_; vi Dockerfile
FROM centos:centos7 RUN yum install -y epel-release;yum install -y npm; RUN npm install express --save COPY . ./src EXPOSE 8080 CMD ["node", "/src/index.js"]
vi index.js
var express = require('express'), app = express(); app.get('/', function (req, res) {res.send('Hello CentOS 7 cookbook! ');}); app.listen(8080);
johndoe
with your own Docker Hub ID):docker build -t johndoe/centos7-expressjs .
docker run -p 8081:8080 -d johndoe/centos7-expressjs
curl -i localhost:8081
Hello CentOS 7 cookbook!
johndoe
, we will start to login to the site using the following command—stay in the directory where you put your Dockerfile from the last step–for example ~/johndoe/centos7-expressjs
(provide the username, the password, and the registration e-mail when asked):docker login
johndoe
with your own user ID), use:docker push johndoe/centos7-expressjs
docker search expressjs
Here in this short recipe, we showed you how to create your first Dockerfile which will create a CentOS 7 container to serve Express.js applications, which is a modern alternative to LAMP stacks where you program JavaScript on the client-and server-side.
So what did we learn from this experience?
As you can see, a Dockerfile is an elegant way to describe all the instructions on how to create an image. The commands are straight-forward to understand and you use special keywords to instruct Docker what to do in order to produce an image out of it. The FROM
command tells Docker which base image we should use. Fortunately, someone has already created a base image from the CentOS 7 system dependencies (this will be downloaded from Docker Hub). Next, we used the RUN
command, which just executes commands as on a BASH command-line. We use this command to install dependencies on our system in order to run Express.js applications (it's based on the Node.js rpm package which we access by installing the EPEL repository first). The COPY
command copies files from our host machine to a specific location on the container. We need this to copy our index.js
file which will create all our Express.js web server code in a later step on to the container. EXPOSE
, as the name implies, exposes an internal container port to the outside host system. Since by default Express.js is listening on 8080, we need to do this here. While all these commands shown up to this point will only be executed once when creating the image, the next command CMD
will be run every time we start the container. The command node /src/index.js
will be executed and instructs the system to start the Express.js web server with the index.js
file (which we already provided in this directory by copying it from the host machine). We don't want to go into any details about the JavaScript part of the program—it just handles HTTP GET requests and returns the Hello World
string. In the second part of this recipe, we showed you how to push our new created image to the Docker Hub. In order to do so, login with your Docker user account. Then we can push our image to the repository.
As this is a very simple Dockerfile, there is much more to learn about this subject. To see a list of all the commands available in the Dockerfile, use man Dockerfile
. Also, you should visit the Docker Hub and browse the Dockerfiles (under the section Source Repository hosted on GitHub) of some interesting projects to learn how to create some highly sophisticated image files with just a handful of commands on your own.