A Dockerfile
is a simple set of instructions that describe how to run an application on a specific operating system (such as Ubuntu). The Dockerfile
allows you to package up the following items into a self-contained package called a container:
- A base image of the preferred operating system (using
FROM
) - Your application code
- Dependencies
- Other files
- Your preferred port to run the application on
- One command to run your application (using
CMD
)
Traditionally, developers have deployed their application code only onto a remote server (such as an AWS EC2 instance, a Heroku "dyno", or a Digital Ocean "droplet") using ssh
.
Since the developer only deployed their application code, and not a description of the underlying operating system/environment, they might not know all the specifics of the deployed environment (and its differences from their local one), leading to inconsistent or unexpected application behavior (for example, "it works on my local" but breaks when deployed).
Docker solves this problem by bundling the application code along with a description of the environment to run it in. A container is easy to run and test locally, and if it works on your local computer, you can be sure it will work in production, since the container will be run the exact same way (following the Dockerfile
instructions), no matter where it's running.
- Set up a very simple Node.js application and run it locally via
node index.js
- Write a
Dockerfile
describing how to run our application - Build a Docker image from our
Dockerfile
- Run our Docker image as a container, and observe that the app works the same as when run locally
# Use official Node.js
FROM node:8.6.0
# Install yarn
RUN curl -o- -L https://yarnpkg.com/install.sh | bash
# Copy files
COPY package.json .
COPY yarn.lock .
COPY index.js .
# Install dependencies
RUN yarn install
# Start app on port 8000
EXPOSE 8000
CMD node index.js
Make project directory and cd
into it, then initialize git and yarn
:
mkdir dockerfile-tutorial && cd $_
git init
yarn init -y
Create the main file index.js
:
touch index.js
Create the .gitignore
file, and add the node_modules
directory to it:
touch .gitignore
Add express
(a server, docs here: expressjs.com), and moment
(for date formatting, docs here: momentjs.com) as dependencies, using their latest exact versions:
yarn add express moment -E
In index.js
:
const express = require('express');
const moment = require('moment');
const app = express();
const PORT = 8000;
app.listen(PORT, () => {
const currentDateTime = Date.now();
const formattedDate = moment(currentDateTime).format('dddd, MMMM Do YYYY, h:mm:ss a');
console.log(`Example app listening on port ${PORT}, started\n${formattedDate}.`);
});
Run the app with node index.js
. The output should look something like this:
Example app listening on port 8000, started
Wednesday, October 11th 2017, 3:28:59 pm.
The app is running locally! Now, let's run it in a Docker container.
Now that our app is working locally, we want to describe how to run it in our Dockerfile
.
Here are the steps we'll need to follow to get the app working inside a container:
- Start from a base image containing Node.js
- Install Yarn package manager inside the container
- Copy our project's files into the container
- Use
yarn install
to install the dependencies inside the container - Tell the container to listen to a specific port (for example,
8000
) - Tell the container to run
node index.js
to run the project's main file with Node.js
First, create a Dockerfile
, making sure to use a capital "D":
touch Dockerfile
Choose a base image to start from. Check hub.docker.com for a base Node image. Search for "node", then find the "official" node
images, and select the tag matching your local version.
Note that many different base images are available on Docker Hub, for many different languages and frameworks.
Check local Node version:
node -v
Select the same tagged version of node
, and add it to the Dockerfile
using FROM
(docs here):
# Use official Node.js
FROM node:8.6.0
Install yarn
into the container using RUN
(docs here) to run a bash command (following instructions here):
# Install yarn
RUN curl -o- -L https://yarnpkg.com/install.sh | bash
Copy our application's files into the container using COPY
(docs here):
# Copy files
COPY package.json .
COPY yarn.lock .
COPY index.js .
Install the project's dependencies by running yarn install
:
# Install dependencies
RUN yarn install
Tell the container to listen to a port at runtime using EXPOSE
(docs here), and run the application using CMD
(docs here):
# Start app on port 8000
EXPOSE 8000
CMD node index.js
Our Dockerfile
is complete! Time to build it.
Use docker build
(docs here) to build an image called dockerfile-tutorial-image
from the instructions in our Dockerfile
:
docker build -t dockerfile-tutorial-image .
Note that if we run this command again, it will be extremely fast, since any unchanged layers in the image will use their cached versions.
Use docker run
(docs here) to run our image that we called dockerfile-tutorial-image
:
docker run -p 8000:8000 dockerfile-tutorial-image
To stop the container, from another Terminal, run:
docker ps
docker kill <unique_id>
This was a brief introduction to Docker and Dockerfile
s, and did not cover many of the more advanced features. Some other information to note:
- Consult Dockerfile best practices for more detailed information
- Try not to include build tools or unminified source code in the container. For example, if you build your project using
yarn build
fromsrc
tobuild
, consider deletingsrc
once the code has been built. - Use environment variables to pass sensitive data into the container, instead of including them inside
- Read about Amazon ECS / ECR cluster management
- Read about using IAM roles with containers
- Experiment with different base images on Docker Hub, or publish your own