TIL — Docker

Reference

Today in class I got an overview of Docker. Here are the slides from class (note: they are a few years old and some links are outdated.). Here is the Docker documentation.

The Basics of Docker-izing An App

  • Add a Dockerfile file to your project root
  • Add a .dockerignore file to ignore things like node_modules
  • docker build creates an image of the application
  • docker run creates a container from your app image and spins it up
  • You can stop a single container normally with Ctrl + C
  • Alternatively, docker-compose up create multiple containers if you have a docker-compose.yml file
  • docker-compose down will stop all running containers gracefully

Dockerfile

The Dockerfile is instructions for how to set up the container (see docs):

  • Start with an image that you’re building FROM (like Node, PHP), etc.
  • You can COPY or ADD application code into the container
  • WORKDIR is the working directory where the application code will run from and be copied to. /usr/src/app is a common convention apparently but it can be anything…best to avoid the root though.
  • RUN any commands that need to go before your app will run (for example npm install)
  • CMD must be the last command which tells the container how to start the app (i.e. npm start). Only the last CMD instruction will be carried out.

Here are some examples to run simple apps:

# Java Application
FROM openjdk:8 # base image
COPY . /usr/src/app # local location to container location
WORKDIR /usr/src/app
RUN apt-get update \
&& apt-get install -y --no-install-recommends maven
RUN mvn clean package
CMD ["java", "-jar", "./target/test-artifact-0.0.1-SNAPSHOT.jar"]

# Node Application
FROM node:12
WORKDIR /usr/src/app
COPY . .
RUN npm install
EXPOSE 3333
CMD ["npm", "start"]

docker build

The command order is:

docker build -t <app-name>:<version tag (optional, default: 'latest' )> <src dir path>

For example if I have navigation to my myApp application folder in the command line, I can run docker build -t myApp:0.0.1 .

  • -t will make the build logs show in your terminal
  • You can make up your app name and version tag
  • Don’t forget the . at the end which is in reference to the present directory, where your app and Dockerfile are sitting

If it has built correctly, you’ll see the myApp image on the list when you run docker images.

docker run

Example: docker run -it -p 3333:3333 node-mongo:0.0.1

  • -it gives you an interactive terminal for your app
  • -p 3333:3333 is port mapping, in case you need to avoid port conflicts on your local machine without changing the application code. It publishes to your localhost from the app: -p 127.0.0.1:4000:3333 where the app runs on port 3333.
  • In this example node-mongo:0.0.1 is the app name and version tag, as seen in the docker images list.

You can run a container in detached mode (i.e. in the background) with docker run -d and you won’t have access to its interactive terminal. See docs.

You can also run docker exec -ti <CONTAINER_ID> bash to inspect with the command line. exit to leave.

Docker Compose

A docker-compose.yml file gives instructions when you need to spin up more than one container to run your app, for example if you need your app server and database server running simultaneously. Here is the docker-compose v3 documentation.

Very important! If you docker-compose down a server that contains data (like a database server) then all the data will be trashed. The solution is to use VOLUMES to write the data to disk (see docs). Here’s an example of an app which requires a MongoDB server:

version: "3"

# Each service is a copy of the container
services:
app:
container_name: docker_mongo
restart: always
build: .
ports:
- "3333:3333"
links:
- mongo
mongo:
container_name: mongo
image: mongo
volumes:
# default Mongo location to (:) where the volume should be created on the volume
- "./data/db:/data/db"
ports:
- "27017:27017"

More Docker Commands

  • docker images shows you what images you have
  • docker image rm <image-name>:<version-tag> deletes the image from your Docker daemon
  • docker ps -a shows what containers are running

Best Practices

Be careful when writing the Dockerfile and be as specific and detailed as possible to ensure the app runs consistently over time.

Be specific when pulling an image to create a container. For example docker pull node:12.16 is better than docker pull node:lts because over time, the lts version will change, and your docker container will be updated on its own. Read: things might break! This article explains this in depth.

Didn’t learn this, but multi-stage builds are a way to get smaller containers. For example you can build from a slim version of a language or runtime and then pull in dependencies as they are needed. This article explains this in depth; see also Docker docs on the topic.

The Best Bit

Since I don’t really develop with others, I don’t have much use case yet for Docker-izing my apps. BUT using Docker will allow me to learn new things which might require different languages more easily, because I won’t need to go through a massive local installation every time. I can just create a container with the image of whatever language and off we go!