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
Mongodbinstalled so the mongo client can be used to access themongodbcontainer described above - with the application
serversoftware installed - which will run
./start-commandwhen 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
mongoclient - connect to service
mongodbat 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 imagenginxfrom 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 imagenodefrom DockerhubWORKDIR "/app"- everything copied to the image will be copied to/appCOPY client/package.json ./- copy named file to/appRUN npm install- execute this command on the image nowCOPY client ./- copies all files from local file system./clientto/appexcept for files listed in.dockerignoreEXPOSE 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_FILEto./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_FILEto./.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_URLis 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 imagenodefrom Dockerhub and assign a namebuilderfor later use.RUN npm run build:actual:production- references a script inpackage.jsonthat does"build:actual:production": "rm -rf dist && webpack --mode production --progress", building the react production code.FROM nginx- use latest version ofnginxEXPOSE 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:productionCOPY --from=builder- copy from thenodedistribution to/usr/share/nginx/htmlwhich 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 imagenodefrom Dockerhub and assign a namebuilderfor later use.RUN npm run build:actual:production- references a script inpackage.jsonthat does"build:actual:production": "rm -rf dist && webpack --mode production --progress", building the react production code.FROM nginx- use latest version ofnginxEXPOSE 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:productionCOPY --from=builder- copy from thenodedistribution to/usr/share/nginx/htmlwhich 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_FILEto./.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_URLis 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.