import { Appear, Notes, Head, } from 'mdx-deck' import { Invert } from 'mdx-deck/layouts' // import future from '@mdx-deck/themes/future' import highlight from '@mdx-deck/themes/syntax-highlighter-prism'
export const themes = [ // future, highlight ]
<title>Docker Field Guide Workshop</title>Provide context on the essentials of Docker to get started and be productive
- Docker installed
- Yarn installed
- Basic terminal skills
- Familiarity with NodeJS (exercises)
- Docker fundamentals
Coffee
- Dockerfile
Lunch
- Multi-stage builds
Coffee
- Dockerfile best practices
import { Split } from 'mdx-deck/layouts'
- Lightweight
- Portable
- Scalable
docker
vs.docker-engine
vs.docker-machine
vs.docker-compose
image
vs.container
vs.swarms
vs.stacks
vs.services
Dockerfile
vsdocker-compose.yml
docker-compose |
client CLI for managing multi-docker container applications |
docker-machine |
remote management of distributed "docker-daemon "s |
docker |
docker "client " CLI |
docker api |
interface between "docker-daemon " and "docker " client |
docker daemon |
the long runing "docker-engine " system process |
docker-engine |
brains which enables Docker's container runtime technology |
Getting started
docker --version
=> Docker version 18.09.2, build 6247962
docker run hello-world
Stack
Swarm
Service
Container
Image
Dockerfile
- (
Volumes
)
Stack |
inter-related "services " running in concert |
Swarm |
collection of multiple "Docker daemons " joined within a single "cluster " |
Service |
configuration of a container which can also be scaled across multiple "Docker daemons " |
Container |
running instance of an "image " |
Image |
read-only snapshot for being able to run a process |
Dockerfile |
recipe guide for creating an "image " |
Volumes |
persistent storage |
We'll focus on:
Stack
Swarm
Service
Container
Image
Dockerfile
- (
Volumes
)
- Pull images from public registry
- Run images as containers and execute their commands
- Understand how to clean up
Pull the required image from public registry
docker pull docker/whalesay
Check the image is available on your machine
docker images
Run the image to get a container
(and provide the args to the default CMD
)
docker run docker/whalesay 'docker rules!'
Is the container still running?
How do we check?
How do we re-execute the command?
docker container ls
View all containers (including those which are stopped)
docker container ls -a
Using docker run
will auto pull
images which it can't find locally
- Remove containers
- Remove images
Get container ID
docker container ls
Remove
docker container rm <container_id>
Delete all (stopped) containers
docker container prune
See: https://docs.docker.com/engine/reference/commandline/container_prune/
Get image ID
docker image ls
Remove
docker image rm <image_id>
Delete all unused images
docker image prune
See: https://docs.docker.com/engine/reference/commandline/image_prune/
- Delete all containers first before deleting images
- Containers are tied to an image so you get an error if you delete an image that has a linked container reference
- We've played around with some Docker resource concepts:
image
,container
- We've seem some of the Docker CLI commands:
pull
,run
,container
,image
- And we've done all this with some pre-built images from the docker hub
Recipe guide for creating Docker images
https://docs.docker.com/engine/reference/builder/
- Build contextConfigure the 'base image'
FROM <image>[:<tag>]
*required
FROM node:lts-jessie
FROM node:10-alpine
FROM node:12-jessie
FROM ruby:2.6-jessie
FROM ruby:2.5-stretch
FROM ruby:2.4-stretch
Execute command
and 'commit' result on top of current 'layer'
RUN <command>
RUN yarn install
RUN bundle install
# Note this is bad;
# we'll find out later why
RUN yum update
RUN yum install -y wget
Copy files/dirs from src
(on host
) to dest
(in image
).
COPY [--chown=<user>:<group>] <src>... <dest>
COPY ./deploy.sh /deploy.sh
# Some globs can be used as well
COPY package*.json /home/app/
COPY /src /home/app/
'Expose' container port to host
EXPOSE <port>[/<protocol>]
EXPOSE 80
EXPOSE 443
EXPOSE 5432
EXPOSE 1234/udp
TCP is default protocol
Creates a mount point with the specified name and declare it as holding external (host) data.
VOLUME ["<path>"]
VOLUME <path>
VOLUME /myvol
VOLUME ["/var/www/html"]
VOLUME ["/data"]
Configures user
for: RUN
, CMD
or ENTRYPOINT
directives
USER <user>[:<group>] or
USER <UID>[:<GID>]
USER root
USER node
USER www-data
# use user id and group id
USER 1001:1001
Set default process to execute on startup
# preferred
CMD ["executable","param1","param2"]
# pass params to ENTRYPOINT
CMD ["param1","param2"]
# Shell form (executed in /bin/sh -c)
CMD command param1 param2
CMD ["node", "app.js"]
CMD ./run.sh
Configure container that runs as an executable
# exec form
ENTRYPOINT ["executable", "param1", "param2"]
# shell form
ENTRYPOINT command param1 param2
ENTRYPOINT ["top", "-b"]
ENTRYPOINT ["nginx", "-g", "daemon off;"]
ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]
Combine CMD
with ENTRYPOINT
to provide configurable defaults
# Dockerfile -> imaeg name = 'custom-tail'
ENTRYPOINT ["tail"]
CMD ["-5"]
# tails 5 lines
docker run custom-tail
# tails 15 lines
docker run custom-tail -15
NodeJS App: Creating a Dockerfile
Folder: 01-nodejs-dockerfile
- Complete the todo list in the template file.
- Build image
- Run container and check output
(clone repo if you haven't already)
FROM node:lts-jessie
# Create app directory
WORKDIR /home/node/app
# Install app dependencies
COPY package.json .
# Add app
COPY . .
RUN yarn install
EXPOSE 8080
CMD ["node", "app.js" ]
cd exercies/01-nodejs-dockerfile
# build image
docker build -t my-node-app .
# run container
docker run -p 8080:8080 my-node-app
# check output
curl -i localhost:8080
-
We have an app all packaged in a Docker image
-
What if we want to do rapid iterations?
-
Do we need to keep rebuilding the image over and over?
Use a bind-mount to create an effective developer experience.
Folder: 02-nodejs-bind-mount
- Run
yarn install
in the exercise folder - Build the image
- Run the container using
docker run
- Customise the
run
command to bind mount the exercise folder into the container. - Change the contents of
app.js
response and verify it changes in the output
cd exercies/02-nodejs-bind-mount
# build image
docker build -t my-node-dev-app .
# run container
docker run \
-p 8080:8080 \
-v $PWD:/home/node/app \
my-node-dev-app
# check output
curl -i localhost:8080
# Change `app.js` response
curl -i localhost:8080
Docker image optimisation
We need to modify 2 of our directives:
FROM <image>[:<tag>] AS <stage_name>
COPY --from=<stage_ref | base>
Folder: 03-nodejs-multistage-build
- Optimise our Dockerfile using the multi-stage builds pattern.
FROM node:lts-jessie AS build
WORKDIR /home/node/app
COPY package*.json ./
RUN yarn install
COPY . .
# ...contd
# ...
RUN yarn build
FROM node:lts-jessie
WORKDIR /home/node/app
COPY --from=build /home/node/app/dist .
EXPOSE 8080
CMD ["node", "app.js" ]
Best practices
- Single process per container
- Optimise for small image sizes
- Combine shared components into a
base
image - Don't rely on
:latest
tag - Use
volumes
for storage
Best practices
- Order directives from least to most likely to change,
- e.g.
COPY
should come later
- e.g.
- Increase specificity in
COPY
to avoid un-intentional cache bust,- e.g. don't use
COPY . <dest>
- e.g. don't use
- Combine appropriate
RUN
directives - Use official images where possible
- reduces maintenance time, reduce size, configured for container use
- Use multi-stage builds where possible
- Enable 'Buildkit' for improved build times
export DOCKER_BUILDKIT=1
Simple Examples
FROM node:lts-jessie
WORKDIR /home/node/app
COPY package.json yarn.lock .
COPY . .
RUN yarn install
EXPOSE 8080
CMD ["node", "app.js" ]
FROM node:lts-jessie
WORKDIR /home/node/app
COPY package.json yarn.lock .
RUN yarn install
COPY ./*.js .
EXPOSE 8080
CMD ["node", "app.js" ]
FROM ubuntu:latest
RUN apt-get update
RUN apt-get install -y nginx
RUN groupadd -r nginx
RUN useradd --no-log-init \
-r -d /var/www/app -g nginx nginx
...
# Ignore this optimisation anti-pattern
FROM ubuntu:latest
RUN apt-get update \
&& apt-get install -y nginx --no-install-recommends \
&& rm -rf /var/lib/apt/lists/*
RUN groupadd -r nginx \
&& useradd --no-log-init \
-r -d /var/www/app -g nginx nginx
# Now we're good
FROM nginx:latest
...