VM vs Container

Three fundamental technologies of container

  1. Namespaces
  2. Control groups (Cgroups)
  3. Union Filesystem

Docker Desktop Architecture

|800

Building Container Images

General principles to build Docker image

Optimization Security Build Speed Clarity
Pin specific versions βœ… βœ… βœ…
Base images (either major +minor OR SHA256 hash) βœ… βœ…
System Dependencies βœ… βœ…
Application Dependencies βœ… βœ…
Use small + secure base images βœ… βœ…
Protect the layer cache βœ… βœ…
Order commands by frequency of change βœ…
COPY dependency requirements file β†’ install deps β†’ copy remaining source code βœ…
Use cache mounts βœ…
Use COPY --link βœ…
Combine steps that are always linked (use heredocs to improve tidiness) βœ… βœ…
Be explicit βœ… βœ…
Set working directory with WORKDIR βœ…
Indicate standard port with EXPOSE βœ…
Set default environment variables with ENV βœ… βœ…
Avoid unnecessary files βœ… βœ… βœ…
Use .dockerignore βœ… βœ… βœ…
COPY specific files βœ… βœ… βœ…
Use non-root USER βœ…
Install only production dependencies βœ… βœ… βœ…
Avoid leaking sensitive information βœ…
Leverage multi-stage builds βœ… βœ…

A sample best Docker image

# syntax=docker/dockerfile:1.5

FROM node:19.4-bullseye AS build

# Specify working directory other than /
WORKDIR /usr/src/app

# Copy only files required to install
# dependencies (better layer caching)
COPY package*.json ./

# Use cache mount to speed up install of existing dependencies
RUN --mount=type=cache,target=/usr/src/app/.npm \
  npm set cache /usr/src/app/.npm && \
  npm install

COPY . .

RUN npm run build

# Use separate stage for deployable image
FROM nginxinc/nginx-unprivileged:1.23-alpine-perl

# Use COPY --link to avoid breaking cache if we change the second stage base image
COPY --link nginx.conf /etc/nginx/conf.d/default.conf

COPY --link --from=build usr/src/app/dist/ /usr/share/nginx/html

EXPOSE 8080

Additional Features

# syntax=docker/dockerfile:1.5
# escape=\
# ^ OPTIONAL "directives" (must be at top if used)

# THIS IS A COMMENT

# ARG is the only instruction that can come before FROM 
ARG BASE_IMAGE_TAG=19.4
# ARGs can be overriden at build time 
# > docker build --build-arg BASE_VERSION=19.3 .

FROM node:${BASE_IMAGE_TAG}

LABEL org.opencontainers.image.authors="[email protected]"

RUN echo "Hey Team πŸ‘‹ (shell form)"
RUN ["echo", "Hey Team πŸ‘‹ (exec form)"]

# Heredocs allow for specifying multiple commands to 
# be run within a single step, across multiple lines
# without lots of && and \
RUN <<EOF
apt update
apt install iputils-ping -y
EOF

# --mount allows for mounting additional files
# into the build context
# RUN --mount=type=bind ...
# RUN --mount=type=cache ...
# RUN --mount=type=ssh ...
RUN --mount=type=secret,id=secret.txt,dst=/container-secret.txt \
  echo "Run the command that requires access to the secret here"

# Available only at build time
# (Still in image metadata though...)
ARG BUILD_ARG=foo

# Available at build and run time
ENV ENV_VAR=bar

# Set the default working directory 
# Use the convention of your language/framework 
WORKDIR path/to/the/working/directory

ENTRYPOINT [ "echo", "Hey Team πŸ‘‹ (entrypoint)" ]
CMD [ "+ (cmd)" ]

Container Registry

Running Containers

with Docker compose.

https://www.chainguard.dev/ provides secure docker images.

sneak and trivy to scan docker images.

Image Security
Β«What vulnerabilities exist in your image
that an attacker could exploit?"
β€’ Keep attack surface area as small as possible:
β€’ Use minimal base images (multi-stage builds are a key
enabler)
β€’ Don't install things you don't need (don't install dev deps)
β€’ Scan images!
β€’ Use users with minimal permissions
β€’ Keep sensitive info out of images
β€’ Sign and verify images
β€’ Use fixed image tags
β€’ Either pin major.minor (allows patch fixes to be integrated)
β€’ Pin specific image hash

Runtime Security
If an attacker successfully compromises a
container, what can they do? How difficult
will it be to move laterally?
β€’ Docker daemon (dockerd)
β€’ Start with --userns-remap option
(https://docs.docker.com/engine/security/userns-remap/
β€’ Individual containers:
β€’ Use read only filesystem if writes are not needed
β€’ --cap-drop=all, then --cap-add anything you need
β€’ Limit cpu and memory --cpus="0.5" --memory 1024m
β€’ Use β€” security-opt
β€’ seccomp profiles (https://docs.docker.com/engine/
security/seccomp/
β€’ apparmor profiles (https://docs.docker.com/engine/
security/ apparmor/)

Developer Experience

β€’ Easy/simple set up
β€’ Iterate without needing to rebuild container image
β€’ Bind mount code into container
β€’ Use hot reloading utilities
β€’ Include profiling and debugging tooling
β€’ Debug specific compose overlay file
β€’ Execute tests within container
β€’ Test specific compose overlay file
β€’ Continuous Integration
β€’ Ephemeral environments

Resources

Resume from 2:24:43
Complete Docker Course - From BEGINNER to PRO! (Learn Containers) - YouTube

Thoughts πŸ€” by Soumendra Kumar Sahoo is licensed under CC BY 4.0