Docker

Docker containerizes apps. Putting apps in containers makes deploying and scaling easier.

Key terms

Image

An image is an executable package that contains the instructions for building a container.

Container

An instance of an image. It allows all dependencies to travel with an app. Means app has the same runtime environment no matter where it runs. How is that different from package.json? Because it specifies runtime environment, not project dependencies, including OS and network config.

Setup

Docker runs on Linux. Running on Windows or Mac requires some virtualization. The two options are Docker Toolbox or the Mac app. Toolbox runs a VM to load docker, and the Mac app symlinks so executables for access on the command line. The mac app sounds lighterweight and easier, so I’ll use that.

It runs in the toolbar. Does it need to be running to use it? Yep.

Using

docker run ${APP} asks Docker daemon for APP. If it’s not found locally, daemon checks the cloud for the correct image. Then, the daemon runs the image to create a container. The container output is streamed to the client.

Dockerfile

The instructions to build the image. Commands like ‘Add’ and ‘Copy’ move local file system things into the image to be executed whenever you run it. The CMD is what starts the process that the app runs on. So, starting the server, for example.

Dockerfiles typically start with a FROM command. That builds your image on top of another, which will specify the operating system being used among other things. For example, the Node Alpine images are run Node on a Linux Alpine distribution.

Many Dockerfile commands have src and destination arguments, which highlights an important Docker concept. You are copying files from your local file system (Or network or some other source) to the image. Favor COPY over ADD

The build pipeline can be subdivided into multiple stages. Useful for things like transpiling code with dev dependencies then deploying a trimmed down production/transpiled build.

Unfortunately, Aptible does not like multistage deploys.

remote: ERROR -- : User Error: Error during build: Multi-stage Dockerfiles are not supported by Dockerfile Deploy. Use a single-stage Dockerfile, or switch to Direct Docker Image Deploy..

Multistage Example

Step 6/14 : RUN yarn build
 ---> Running in 30b9296467f6
yarn run v1.9.4
$ rm -rf build; babel src -d build --extensions '.js,.gql,' --keep-file-extension
Successfully compiled 17 files with Babel.
Done in 0.82s.
Removing intermediate container 30b9296467f6 # <- Previous container removed
 ---> c20bbd57adbd
Step 7/14 : FROM node:8.12-slim

FROM

FROM indicates on which container to build your container. Consider all the examples here. The various Node images specify a specific version of Node but also the OS and system packages included. This impacts container size, vulnerabilities and functionality. In the last Node project I was working on, even the slim versions of Node had system packages I didn’t need as well as dozens of reported vulnerabilities. The alpine releases had no vulnerabilities when I checked, but they also lacked all the system packages my app relied on (Like ones I took for granted, like bash). No matter, though. apk is included, so you can add the packages you need.

RUN

RUN lets you run command line instructions in your container. For example, when using an alpine release of Node, you’ll probably need to install system packages to install your project dependencies. Here’s an excerpt from my Dockerfile that installs system packages that are required to even run npm install. After they’re installed, the system packages are deleted to keep the image light and vulnerability free.

# These are the packages that will be removed after app dependencies are installed
RUN apk --no-cache add --virtual native-deps \
  g++ make python \
# This package will be installed and kept so we can SSH into the app and interact with it
  && apk add bash \

COPY . /src

# Install the runtime dependencies using the system packages installed above
RUN npm install --production \
# Delete the system packages that are no longer needed
    && apk del native-deps

COPY

COPY moves files from the local file system into the Docker container. To me, understanding the distinction between these spaces was necessary to understand Docker at a functional level. We build containers out of the files on our computers, but really those containers can be thought of as separate computers. First we need to copy files from our computer to the other computer before we can run them there.

Resources

https://gist.github.com/josh-works/06bf67376633b059457246dccb434456#mysql-and-docker https://nodejs.org/en/docs/guides/nodejs-docker-webapp/