Skip to main content

Docker

Container platform. See also Podman (a drop-in alternative), Kubernetes, and Nginx.

Install

Ubuntu 24.04, 22.04 passed.

# Uninstall old versions
sudo apt-get remove docker docker-engine docker.io containerd runc

Images, containers, volumes, and networks stored in /var/lib/docker/ aren't automatically removed when you uninstall Docker.

sudo apt-get install ca-certificates curl gnupg lsb-release

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# OR
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt-get update

sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin

# Verify that the Docker Engine installation is successful by running the hello-world image
sudo docker run hello-world

Manage Docker as a non-root user

# Create the docker group.
sudo groupadd docker

# Add your user to the docker group.
# -a is a shortcut for --append: It means append the group to the list of groups the user belongs to!
# -G is a shortcut for --groups: It tells usermod that the next argument is a group. Note that you need to use a capital -G here because we don’t want to modify the user’s primary group but the list of supplemental groups the user belongs to.
sudo usermod -aG docker $USER

newgrp docker

# WARNING: Error loading config file: /home/user/.docker/config.json - stat /home/user/.docker/config.json: permission denied
sudo chown "$USER":"$USER" /home/"$USER"/.docker -R
sudo chmod g+rwx "$HOME/.docker" -R

Docker CLI

Docker commands need root permission — or add your user to the docker group (see Manage Docker as a non-root user).

container

exec

# Jump into running container
docker container exec --interactive --tty nginx bash

run

docker run [OPTIONS] IMAGE[:TAG|@DIGEST] [COMMAND] [ARG...]

[OPTIONS]

--detach

Start container into background.

--name

Set identifier by name.

--publish

port, outer network : inner network.

# Different OS
docker container run --platform

# Start container and jump into, container will close after exit
docker container run --interactive --tty nginx bash

# Service at http://localhost:81/
docker container run --detach --publish 81:80 nginx

docker container run --name nginx --publish 80:80 --detach nginx

docker container run --name postgres --detach --publish 5432:5432 --env POSTGRES_PASSWORD=passw0rd postgres

# Run with connect to a custom virtual network
docker container run --detach --publish 80:80 --network app --name nginx nginx

start

docker start [OPTIONS] CONTAINER [CONTAINER...]

[OPTIONS]

--attach , -a

Attach STDOUT/STDERR and forward signals.

docker container start -ai update-server

Operation

# Short
--publish, -p
--env, -e
--volume, -v
--detach, -d
# Removes the anonymous volumes associated with the container when the container is removed.
--rm
# connect the container's input to the host when attached
--interactive, -i
# connect the tty keyboard to the host when attached
--tty, -t
# Open virtual terminal, equal -i + -t
-it
# Bind current host path to container virtual path /source
-v $PWD:/source
# The default path when jump into container
-w /source
# Keep containers alive during daemon downtime
--live-restore
# Auto yes
--force, -f

# Show information
docker info

# Verify that Docker Compose is installed correctly by checking the version.
docker compose version

# List running containers
docker container ls

# List all containers
docker container list --all

# Show docker disk usage
docker system df

# Start
docker container start 6c438 # container ID



# Stop
docker container stop 6c438c6 # container ID

docker container inspect --format '{{ .Config.AttachStdout }}' 51b # container ID

# Delete all container
docker container rm --force $(sudo docker container ls -aq)

# Delete all stopped containers
docker container prune



# Stop by name
docker container stop nginx



# List process
docker container top nginx

# On linux it just process, other os is vm
ps aux



# Check port, left is container inner port
docker container port nginx

# Check container ip
docker container inspect --format '{{ .NetworkSettings.IPAddress }}' nginx

# check network
docker network ls

# inspect keyword is generic, get detail information
docker network inspect nginx
docker network inspect bridge

# Create virtual network
docker network create app



# Add connect to virtual network, no response is correct
docker network connect app pg

# Remove
docker network disconnect app pg

Image

docker image pull redis:7.0

docker image list

docker image list --filter=reference='golang-*'

docker image history nginx:latest

docker image inspect

docker image tag nginx:latest rojarsmith/nginx:latest

docker image push

# --tag, -t
# Build image, . will search Dockerfile in current path
docker image build --tag whoami .

docker image build --tag whoami:production --file Dockerfile.production .

# Show time
time sudo docker build . -t my-hello-world

sudo docker image rm <IMAGE ID:0ac85a4992a0>

# Remove all dangling images
docker image prune

# Remove all images without at least one container associated to them
docker image prune --all

Repository

# First time must create "Access Tokens"
docker login -u rojarsmith

docker image push rojarsmith/nginx:latest

# Create a repository
# REGISTRY_STORAGE_DELETE_ENABLED: support deleting the image
# registry:2, official image name: version
docker run -d -p 5000:5000 --name registry --env REGISTRY_STORAGE_DELETE_ENABLED=true registry:2

docker image tag rojarsmith/nginx:latest localhost:5000/rojarsmith/nginx:latest

# Push to local repository
docker image push localhost:5000/rojarsmith/nginx:latest

# Check image
curl http://localhost:5000/v2/rojarsmith/nginx/tags/list

# Fetch digest
curl --head http://localhost:5000/v2/rojarsmith/nginx/manifests/latest -H 'Accept: application/vnd.docker.distribution.manifest.v2+json'

# Delete image
curl -X DELETE http://localhost:5000/v2/rojarsmith/nginx/manifests/sha256:f26fbadb0acab4a21ecb4e337a326907e61fbec36c9a9b52e725669d99ed1261

Volume

docker volume create whatever

docker volume list

docker volume rm vol

docker volume inspect + `TAB`

docker volume prune

# Named volume, including the path in the container
docker container run --detach --name mysql --env MYSQL_ROOT_PASSWORD=whatever --volume mysql-data:/var/lib/mysql mysql

Compose

docker compose up

# Background
docker compose up -d

docker compose logs

Example:

# compose version
version: "3.8"

# 1 services block can contain multi containers
services:
# a service named "app"
app:
build: .

Mysql

.env and dockerfile put on the same path.

services:
mysql:
image: mysql:8
restart: unless-stopped
ports:
- "3306:3306"
env_file:
- .env
volumes:
- /root/mysql:/var/lib/mysql

Log

docker logs container

docker compose logs [service-name]

# Log
docker container logs nginx

# Show logging
docker container logs --follow nginx

Concept

  • On Windows and macOS, Docker runs inside a virtual machine.
  • Dockerize means packaging an app with Docker.
  • A container exits automatically once its default command finishes.
  • A container ID can be given as an abbreviation (a unique prefix).
  • Container ID and container name are interchangeable.
  • Docker's default bind address is 0.0.0.0.
  • A container name can serve as the DNS name on an internal virtual network.
  • Alpine is a small Linux distribution commonly used for images.
  • missing in docker image history is not an error — the layers share a hash.
  • The library prefix denotes an official image.
  • Different image tags can point to the same image hash.
  • docker.io is the default registry.
  • A full image name looks like docker.io/rojarsmith/nginx:latest.
  • A new CMD replaces the previous one.
  • Define ENTRYPOINT when using the container as an executable.
  • Reordering lines in a Dockerfile can improve build caching.
  • .dockerignore excludes files from the build context.
  • FROM scratch is a special base meaning "empty".
  • You cannot COPY or ADD files from outside the Dockerfile's directory.

Image naming

Full name: REPOSITORY(library/$IMAGE):TAG

Debian version LTS 10 buster, 11 bullseye.

-slim: not include buildpack-deps

node:lts-alpine tag will pull node LTS Alpine image. Smallest.

lts-slim is larger but can do more thing.

Dockerfile

WORKDIR must before copy.

All file must in the root of dockerfile's folder.

# Base image
FROM ruby:3.1.2-alpine
# echo $AUTHOR
ENV AUTHOR=rojarsmith

# Multi RUNs will make multi layers
RUN apk add --update --no-cache \
build-base \
curl

# Make dir and jump into
WORKDIR /app

# Copy all files from the Dockerfile's folder into the container's workdir /app
COPY . .

RUN gem install bundler:2.3.19 && \
bundle install -j4 --retry 3 && \
bundle clean --force && \
find /usr/local/bundle -type f -name '*.c' -delete && \
find /usr/local/bundle -type f -name '*.o' -delete && \
rm -rf /usr/local/bundle/cache/*.gem

# Container service port, can write after ENV
EXPOSE 3000

# Startup command
CMD ["bundle", "exec", "ruby", "whoami.rb", "-p", "3000", "-o", "0.0.0.0"]

Optimized (reorder so cache-friendly steps come first):

# Dockerfile
FROM ruby:3.1.2-alpine
ENV AUTHOR=rojarsmith <- May omit

EXPOSE 3000 <- Move up

CMD ["bundle", "exec", "ruby", "whoami.rb", "-p", "3000", "-o", "0.0.0.0"] <- Move up

WORKDIR /app <- Move up

RUN apk add --update --no-cache \
build-base \
curl

COPY . .

RUN gem install bundler:2.3.19 && \
bundle install -j4 --retry 3 && \
bundle clean --force && \
find /usr/local/bundle -type f -name '*.c' -delete && \
find /usr/local/bundle -type f -name '*.o' -delete && \
rm -rf /usr/local/bundle/cache/*.gem

Multi section can resize image.

# builder section
FROM alpine:3.16.2 AS builder
RUN echo 'Builder' > /example.txt

# tester section
FROM alpine:3.16.2 AS tester
COPY --from=builder /example.txt /example.txt
RUN echo 'Tester' >> /example.txt

# final section
FROM alpine:3.16.2
COPY --from=tester /example.txt /example.txt
CMD [ "cat", "/example.txt" ]

# docker container run example
# Builder
# Tester

Parameters

--build-arg

ARG

VOLUME

# Data will retain, auto build the volume and connect to /var/lib/mysql
# The virtual path is in the container
VOLUME /var/lib/mysql

The volume will not auto delete when delete container without special command.

# connect real path to virtual path
--volume ${PWD}/logs:/app/logs

Scenario

Backup & Restore Image

docker save -o myimage.tar myimage
docker load -i myimage.tar

Read files in the volume

docker run -it --rm -v /path/on/host:/vol busybox ls -l /vol

Optimize

# Clean no need cache
apk -U --no-cache add git

or

apk -U add git && \
rm -rf /var/cache/apk/*

-t, --virtual NAME Instead of adding all the packages to 'world', create a new virtual package with the listed dependencies and add that to 'world'; the actions of the command are easily reverted by deleting the virtual package

Tools

Fluentd

Collect logs.