Configuring Dockerfiles to build Docker Images
by John Vincent
Posted on June 2, 2021
Dockerfiles are used to build Docker images. A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image.
This is part of a series of discussions regarding Deploying TaskMuncher, a React and Node application, to a Multi-Container Docker Environment at AWS using Dockerhub and Travis CI
For more details, please see Overview of Create Multi-Container Docker TaskMuncher Application at AWS
Overview
Let's start by describing the systems that are to be created.
Dockerfiles
It may be seen from the following there are a number of Dockerfiles.
It should be noted that images are required for dev
, devprod
and aws
. There are a number of images that are used by all environments.
List of Dockerfile sections
Dockerfiles for all Environments
This section will cover the general usage images.
Dockerfile for MongoDB
./config/mongodb/Dockerfile
# Set the base image to Ubuntu
FROM ubuntu:20.04
# Update the repository sources list and install required packages
RUN apt-get update && apt-get install -y nginx curl gnupg2 wget
RUN curl -sL https://deb.nodesource.com/setup_12.x | bash -
RUN apt-get -y install nodejs
# get mongodb list
RUN wget -qO - https://www.mongodb.org/static/pgp/server-4.4.asc | apt-key add -
RUN echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/4.4 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-4.4.list
# Update the repository sources list
RUN apt-get update
# Install MongoDB package (.deb)
RUN apt-get install -y mongodb-org
# Create the default data directory
RUN mkdir -p /data/db
# Expose the default port
EXPOSE 27017
# Default port to execute the entrypoint (MongoDB)
CMD ["--port 27017"]
# Set default container command
ENTRYPOINT /usr/bin/mongod --bind_ip 0.0.0.0
This creates an image that has MongoDB
installed onto Ubuntu 20.04
.
Dockerfile for mongo-client
./config/mongo-client/Dockerfile
# Set the base image to Ubuntu
FROM ubuntu
# Update the repository sources list and install required packages
RUN apt-get update && apt-get install -y nginx curl gnupg2 wget
RUN curl -sL https://deb.nodesource.com/setup_12.x | bash -
RUN apt-get -y install nodejs
# get mongodb list
RUN wget -qO - https://www.mongodb.org/static/pgp/server-4.4.asc | apt-key add -
RUN echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/4.4 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-4.4.list
# Update the repository sources list
RUN apt-get update
# Install MongoDB package (.deb)
RUN apt-get install -y mongodb-org
# Create the default data directory
RUN mkdir -p /data/db
# Expose the default port
EXPOSE 27017
#
# Application specific commands follow
#
# use /app for our application
WORKDIR "/app"
# npm install only needs package.json
COPY server/package.json ./
COPY server/package-lock.json ./
# install npm modules
RUN npm install
# copy code
COPY server ./
# copy start file
COPY config/mongo-client/start-command ./start-command
RUN mkdir -p ./logs
CMD ["./start-command"]
This creates an image
- based on
Ubuntu
- with
Mongodb
installed so the mongo client can be used to access themongodb
container described above - with the application
server
software installed - which will run
./start-command
when the container starts.
The Dockerfile references ./config/mongo-client/start-command
#!/bin/sh
#
echo "Starting 30 seconds of sleep"
sleep 30
cd scripts
echo "Checking if data is already loaded"
abc=`mongo mongodb:27017/taskmuncher-2021 is-any-data.js`
TEXT=`echo $abc | awk -F'-----' '{print $2}'`
echo "TEXT $TEXT"
if [ "$TEXT" = "Data Added" ]; then
echo "Data has already been loaded"
else
echo "Data has NOT been loaded"
echo ""
echo "Loading Mongo Taskmuncher data"
mongo mongodb:27017/taskmuncher-2021 all-data.js
echo "Loaded Mongo Taskmuncher data"
fi
echo "Completed mongo-client:start-command"
which is just a way of seeding the MongoDB database.
For details
mongo mongodb:27017/taskmuncher-2021 all-data.js
means
- using
mongo
client - connect to service
mongodb
at port 27017 - and run script
all-data.js
It is very important to notice that service mongodb
actually refers to the host mongodb
which I have configured to be the MongoDB database for this application.
Dockerfile for nginx
./config/nginx/Dockerfile
FROM nginx
EXPOSE 8100
COPY ./config/nginx/default.conf /etc/nginx/conf.d/default.conf
FROM nginx
- get latest version of imagenginx
from DockerhubEXPOSE 8100
- open port 8100- copy nginx configuration file to nginx image
The nginx server configuration file ./config/nginx/default.conf
upstream client {
server client:8040;
}
upstream api {
server api:3110;
}
server {
listen 8100;
location / {
proxy_pass http://client;
}
location /sockjs-node {
proxy_pass http://client;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
location /api {
proxy_pass http://api;
}
}
listen 8100
- nginx is listening on port 8100
Requests to /api
will be proxied to
upstream api {
server api:3110;
}
which is hostname api
on port 3110, which corresponds with
{
"name": "server",
"image": "johnvincentio/taskmuncher:server",
"hostname": "api",
"essential": false,
"links": ["mongodb"],
"memory": 128
},
in dockerrun.aws.json
, see Create Dockerrun.aws.json file for AWS for details,
and
api:
image: taskmuncher-api-dev
depends_on:
- mongodb
build:
dockerfile: ./config/server/dev/Dockerfile
context: .
in docker-compose-*.yml
, see Configuring Makefiles to build and run Docker Images using Docker, Docker Compose and Dockerfile for details.
Requests to /
will be proxied to
upstream client {
server client:8040;
}
which is hostname client
on port 8040, which corresponds with
{
"name": "client",
"image": "johnvincentio/taskmuncher:client",
"hostname": "client",
"essential": false,
"memory": 128
},
in dockerrun.aws.json
, see Create Dockerrun.aws.json file for AWS for details,
and
client:
image: taskmuncher-client-dev
stdin_open: true
build:
dockerfile: ./config/client/dev/Dockerfile
context: .
in docker-compose-*.yml
, see Configuring Makefiles to build and run Docker Images using Docker, Docker Compose and Dockerfile for details.
Note that nginx/default.conf
can reference another container by using the service name in docker-compose-*.yml
.
Dockerfile for dockercfg
AWS needs a .dockercfg
file to allow AWS to authenticate with a Dockerhub private repository.
This image is created so that, when run in a Docker container, a .dockercfg
file may be generated.
For details, see Allow AWS to authenticate with a Dockerhub private repository
./config/dockercfg/Dockerfile
# Set the base image to Ubuntu
FROM ubuntu:20.04
# https://docs.docker.com/engine/install/ubuntu/
# Update the repository sources list
RUN apt-get update
RUN apt-get install -y curl apt-transport-https ca-certificates gnupg-agent software-properties-common
RUN curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
RUN add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
#
RUN apt-get update
RUN apt-get install -y docker-ce docker-ce-cli containerd.io
# use /app for our application
WORKDIR "/app"
# copy start file
COPY config/dockercfg/start-command ./start-command
# copy example cfg file
COPY config/dockercfg/examplecfg.txt ./examplecfg.txt
# copy docker password file
COPY config/dockercfg/my_password.txt ./my_password.txt
CMD ["./start-command"]
This creates an Ubuntu 20.04
image that will execute ./start-command
when the container is started.
./config/dockercfg/start-command
#!/bin/sh
#
cd /app
USERNAME="johnvincentio"
cat ./my_password.txt | docker login --username $USERNAME --password-stdin
cat /root/.docker/config.json
cat ./examplecfg.txt
echo "*** Build your own .dockercfg file from the above ***\n"
Development Dockerfiles
Development Dockerfiles are required for client
and server
Development Dockerfile for Client
./config/client/dev/Dockerfile
FROM node:15
# use /app for our application
WORKDIR "/app"
# npm install only needs package.json
COPY client/package.json ./
COPY client/package-lock.json ./
# install npm modules
RUN npm install
# copy code
COPY client ./
# copy env file
COPY config/client/dev/client.env ./.env
# copy start file
COPY config/client/dev/start-command ./start-command
# webpack is configured to use port 8040
EXPOSE 8040
# run this command when the container starts
CMD ["./start-command"]
FROM node:15
- get version 15 of imagenode
from DockerhubWORKDIR "/app"
- everything copied to the image will be copied to/app
COPY client/package.json ./
- copy named file to/app
RUN npm install
- execute this command on the image nowCOPY client ./
- copies all files from local file system./client
to/app
except for files listed in.dockerignore
EXPOSE 8040
- open port 8040CMD ["./start-command"]
- provides a script that will be run when the Docker container starts
partial ./config/client/dev/client.env
NODE_ENV=development
HOME_HOST=0.0.0.0
HOME_PORT=8040
HOME_URL=http://localhost:8100
SERVER_APIS_URL=http://localhost:8100
./config/client/dev/start-command
#!/bin/sh
#
export ENV_FILE=$(pwd)/.env
node_modules/.bin/webpack serve
which means
- set environment variable
ENV_FILE
to./env
- run webpack
Development Dockerfile for Server
./config/server/dev/Dockerfile
FROM node:15
# use /app for our application
WORKDIR "/app"
# npm install only needs package.json
COPY server/package.json ./
COPY server/package-lock.json ./
# install npm modules
RUN npm install
# copy code
COPY server ./
# copy env file
COPY config/server/dev/server.env ./.env
# copy start file
COPY config/server/start-command ./start-command
RUN mkdir -p ./logs
EXPOSE 3110
CMD ["./start-command"]
which references ./config/server/start-command
#!/bin/sh
#
ENV_FILE=./.env node server.js
which means
- set environment variable
ENV_FILE
to./.env
- execute
node server.js
Partial ./config/server/dev/server.env
HOME_URL=http://localhost:8100
PORT=3110
WHITE_LIST=http://localhost:8100
DATABASE_URL='mongodb://mongodb:27017/taskmuncher-2021'
HOME_URL
is the URL of the Client ApplicationPORT
- server listening on this portWHITE_LIST
- CORS URL white listmongodb:27017
- the hostname and port of the MongoDB service
Production in a Development Dockerfiles
Production in a Development Dockerfiles are required only for client
Production in a Development Dockerfile for Client
./config/client/devprod/Dockerfile
FROM node:15 as builder
# use /app for our application
WORKDIR "/app"
# npm install only needs package.json
COPY client/package.json ./
COPY client/package-lock.json ./
# install npm modules
RUN npm install
# copy code
COPY client ./
# copy env file
COPY config/client/dev/client.env ./.env
# build react app
RUN npm run build:actual:production
# handle nginx server
FROM nginx
EXPOSE 8040
COPY config/client/aws/nginx/default.conf /etc/nginx/conf.d/default.conf
COPY --from=builder /app/dist /usr/share/nginx/html
FROM node:15 as builder
- use version 15 of imagenode
from Dockerhub and assign a namebuilder
for later use.RUN npm run build:actual:production
- references a script inpackage.json
that does"build:actual:production": "rm -rf dist && webpack --mode production --progress"
, building the react production code.FROM nginx
- use latest version ofnginx
EXPOSE 8040
- open port 8040- Copies nginx configuration to nginx image
/app/dist
- contains the production react files that were constructed usingRUN npm run build:actual:production
COPY --from=builder
- copy from thenode
distribution to/usr/share/nginx/html
which is in the context ofFROM NGINX
The nginx server configuration file config/client/aws/nginx/default.conf
server {
listen 8040;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
}
is listening on port 8040 and distributing file from /usr/share/nginx/html
, which is where the production react files were just placed.
AWS Production Dockerfiles
Development Dockerfiles are required for client
and server
AWS Production Dockerfile for Client
./config/client/aws/Dockerfile
FROM node:15 as builder
# use /app for our application
WORKDIR "/app"
# npm install only needs package.json
COPY client/package.json ./
COPY client/package-lock.json ./
# install npm modules
RUN npm install
# copy code
COPY client ./
# copy env file
COPY config/client/aws/client.env ./.env
# build react app
RUN npm run build:actual:production
# handle nginx server
FROM nginx
EXPOSE 8040
COPY config/client/aws/nginx/default.conf /etc/nginx/conf.d/default.conf
COPY --from=builder /app/dist /usr/share/nginx/html
FROM node:15 as builder
- use version 15 of imagenode
from Dockerhub and assign a namebuilder
for later use.RUN npm run build:actual:production
- references a script inpackage.json
that does"build:actual:production": "rm -rf dist && webpack --mode production --progress"
, building the react production code.FROM nginx
- use latest version ofnginx
EXPOSE 8040
- open port 8040- Copies nginx configuration to nginx image
/app/dist
- contains the production react files that were constructed usingRUN npm run build:actual:production
COPY --from=builder
- copy from thenode
distribution to/usr/share/nginx/html
which is in the context ofFROM NGINX
The nginx server configuration file config/client/aws/nginx/default.conf
server {
listen 8040;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
}
is listening on port 8040 and distributing file from /usr/share/nginx/html
, which is where the production react files were just placed.
Partial ./config/client/aws/client.env
NODE_ENV=production
HOME_HOST=http://taskmuncherdocker-env.eba-mv2hwnxx.us-east-1.elasticbeanstalk.com
HOME_PORT=80
HOME_URL=http://taskmuncherdocker-env.eba-mv2hwnxx.us-east-1.elasticbeanstalk.com
SERVER_APIS_URL=http://taskmuncherdocker-env.eba-mv2hwnxx.us-east-1.elasticbeanstalk.com
The URL of the application is http://taskmuncherdocker-env.eba-mv2hwnxx.us-east-1.elasticbeanstalk.com
.
This URL was assigned when the application was created in AWS Elastic Beanstalk. For details, see Configure AWS for a Multi-Container Docker Application.
The URL is the same for the React client application and for the Node/Express application as Nginx is listening on port 80 and proxying requests to either the React Docker container or to the Node/Express Docker container based on the URL of the request.
AWS Production Dockerfile for Server
./config/server/aws/Dockerfile
FROM node:15
# use /app for our application
WORKDIR "/app"
# npm install only needs package.json
COPY server/package.json ./
COPY server/package-lock.json ./
# install npm modules
RUN npm install
# copy code
COPY server ./
# copy env file
COPY config/server/aws/server.env ./.env
# copy start file
COPY config/server/start-command ./start-command
RUN mkdir -p ./logs
EXPOSE 3110
CMD ["./start-command"]
./config/server/start-command
#!/bin/sh
#
ENV_FILE=./.env node server.js
which means
- set environment variable
ENV_FILE
to./.env
- execute
node server.js
Partial ./config/server/aws/server.env
HOME_URL=http://taskmuncherdocker-env.eba-mv2hwnxx.us-east-1.elasticbeanstalk.com
PORT=3110
WHITE_LIST=http://taskmuncherdocker-env.eba-mv2hwnxx.us-east-1.elasticbeanstalk.com
DATABASE_URL='mongodb://mongodb:27017/taskmuncher-2021'
HOME_URL
is the URL of the Client ApplicationPORT
- server listening on port 3110WHITE_LIST
- CORS URL white listmongodb:27017
- the hostname and port of the MongoDB service
The URL of the application is http://taskmuncherdocker-env.eba-mv2hwnxx.us-east-1.elasticbeanstalk.com
.
This URL was assigned when the application was created in AWS Elastic Beanstalk. For details, see Configure AWS for a Multi-Container Docker Application.
The URL is the same for the React client application and for the Node/Express application as Nginx is listening on port 80 and proxying requests to either the React Docker container or to the Node/Express Docker container based on the URL of the request.