Docker Workshop

Docker logo

Introduction and expectations

Structure & Goals

  • What and why?
  • How?
    • Installation
    • User (Containers, Images, Networks, Volumes)
    • Developer
      • Dockerfile
      • docker-compose
    • Advanced Recipes
      • Moving images around (repos)
      • Inspecting / editing an image
  • Summary

What and Why?

  • What is docker?
  • What is a container?

Why?

  • Eliminate dependency hell.
    • Reduce requirements to stating required: CPU, RAM and storage.
  • Package, containerize, ship and run everywhere, anytime and at any scale.

Basic Concepts

  • Images - contains code to be executed. Similar to a statically-linked program.
  • Containers - a running image. Something like a process.
  • Registry - holds images
  • Dockerfile - recipe to cook a image.

How-to

Practical Part

Installation

Ubuntu Linux:

sudo apt install docker.io docker-compose                             
sudo usermod -a -G docker $USER             # re-login or restart

Mac:

brew install --cask docker

Windows: good luck! …

choco install docker-desktop

First steps: Docker for Users

Let’s run some containers:

docker run hello-world                  # execute image's default CMD
docker run debian cat /etc/os-release   # execute specific command
docker run -it debian                   # interactive TTY
  • Now install htop (apt update && apt install -y htop), run it and watch the processes.
  • Run the image again to see there will be no htop installed!

Finding Images

docker search mysql

Container mgmt

docker ps                               # list running containers
docker ps -a                            # list all containers
docker stop    CONTAINER_ID
docker restart CONTAINER_ID
docker kill    CONTAINER_ID
docker rm      CONTAINER_ID             # remove a container
  • TASK: restart the MySQL container
  • TASK: connect to the MySQL container

Solution

docker exec -it mysql bash          # execute `bash` in a running container
mysql -h 127.0.0.1 -p
  • TASK: now get rid the container
  • Q: was the data we entered through the console saved?

Images

docker pull ubuntu:20.04        # use a tag
docker images 
docker rmi ubuntu:20.04
  • BTW docker run does automatically a pull if it doesn’t find the image.
  • Default tag: latest

Maintenance

  • Q: how to find out the size of the docker images (can be a lot)?
  • Q: how to remove unused docker images?
docker system df
docker system prune
docker system info

Volumes

Let’s persist the data

docker run --name mysql -v ~/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=pw mysql
docker stop mysql
docker logs mysql
docker logs mysql --since 10m
docker rm mysql

We can mount in read-only mode too:

docker run -ti -v /etc/passwd:/tmp/passwd:ro busybox sh

Networks

  • Our mysql container is not reachable from outside and thus rather useless. Let’s fix this:
docker run -d -p 3306:3306 --name mysql -v ~/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=pw mysql
mysql -h 127.0.0.1 -p -u root
  • network drivers:
    • bridge (default). Each container gets its own network stack. Needs port forwarding.
    • host - container and host share the same network and ports.
curl localhost        # should fail
docker run --rm -d --network host --name my_nginx nginx
curl localhost        # should work 

Some switches

-ti             # allocate terminal; interactive
--name  NAME    # set a NAME for the container instead of a generated one
-d              # run on the background (daemon)
-e VAR=VALUE    # set environment `VAR` to `VALUE`. 
--env-file FILE # set the environment values from a `FILE`. 
--rm            # automatically remove container when it exists
  • More switches: man docker-run
  • Less switches (recommended): tldr docker-run

Q&A?

Docker for Developers

Dockerfile

  • Dockerfile - recipe to cook our own image.
  • Let’s clone the project and dockerize:
FROM node:20-alpine

WORKDIR /usr/src/app

USER node

COPY . .

EXPOSE 8080

CMD node app.js

Let’s use it:

docker build -t helloworld-demo-node .
docker run -d -p 8080:8080 helloworld-demo-node
curl localhost:8080

Dockerfile cont’d

docker tag SOURCE_IMAGE TARGET_IMAGE    # create a new tag (keeps old)
docker rmi OLD_TAG                      # remove a tag / image
docker build --platform linux/arm64     # cross build for Apple M1/M2
docker build -f DOCKERFILE .            # if we have multiple Dockerfiles
  • Q: how do we verify that we have tagged an image with a new tag?

Multistage Build

  • Let’s do the same for golang:
FROM golang
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY *.go ./
RUN CGO_ENABLED=0 GOOS=linux go build -o /main
EXPOSE 8080
ENTRYPOINT ["/main"]
  • Now let’s build and run it:
docker build -f Dockerfile -t helloworld-go-demo .
docker run -p 8080:8080 helloworld-go-demo
docker images | grep helloworld         # 913MB ...
  • There must be a better way …

Multistage Build cont’d

FROM golang AS build-stage
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY *.go ./
RUN CGO_ENABLED=0 GOOS=linux go build -o /main

# Run the tests in the container
FROM build-stage AS run-test-stage
RUN go test -v ./...

# Deploy the application binary into a lean image
FROM gcr.io/distroless/base-debian11 AS build-release-stage
WORKDIR /
COPY --from=build-stage /main /main
EXPOSE 8080
USER nonroot:nonroot
ENTRYPOINT ["/main"]

And check it out:

docker build -f Dockerfile.multistage -t helloworld-go-demo-light .
docker images | grep helloworld                 # 27 MB!
curl localhost:8080                             # make sure it still works

docker-compose

  • Rule: one process per container
  • How to accommodate multiple services?

Example: counter-app

version: "3.5"

services:
  web-fe:
    build: .
    command: python app.py
    ports:
      - target: 5000
        published: 5000
    networks:
      - counter-net
    volumes:
      - type: volume
        source: counter-vol
        target: /code
  redis:
    image: "redis:alpine"
    networks:
      counter-net:

networks:
  counter-net:

volumes:
  counter-vol:

docker-compose cont’d

  • Usage:
docker-compose up 
docker-compose up -d
docker-compose down
  • A more complicated example docker-compose.yaml
    • Check out shortcuts for volumes and ports.
  • Useful for orchestration of tests / experiments.
  • For production, if scaling is required, use kubernetes.

Q&A?

Advanced Recipes

Moving images around / Repositories

docker save IMAGE > image.tar
docker load < image.tar
  • Or push it to a temporary storage: https://ttl.sh/ (DANGER: not under your control)
  • Or use a docker hub / AWS ECR / … repository
  • TASK: save debian image; remove it from images; load it. Now verify it is there among the images.

Manually edit an image

  • update an image by editing a running container made from it:
docker exec -it container-ID bash 
# do your changes
docker commit container-ID image-name

TASK: add the htop package to your debian image

Inspecting Image

docker inspect IMAGE
  • TASK: what’s the command that will be executed in the mysql image? What mounts does it have? What ports does it expose?

Creating Small Images

  • Multistage-builds
  • RUN, COPY, ADD - create new layers. Join RUN commands (&&)

Just stop all stupid containers

  • set restart policy to no to all running dockers and stop them:
docker update `docker ps -q` --restart no
docker kill `docker ps -q`

ctop

docker run --rm -ti \
  --name=ctop \
  --volume /var/run/docker.sock:/var/run/docker.sock:ro \
  quay.io/vektorlab/ctop:latest

Resources

Final Q&A?

Feedback

  • What did you like?
  • What would you do differently?
  • What was missing?

email

Thank you for attention!