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.
missingindocker image historyis not an error — the layers share a hash.- The
libraryprefix denotes an official image. - Different image tags can point to the same image hash.
docker.iois the default registry.- A full image name looks like
docker.io/rojarsmith/nginx:latest. - A new
CMDreplaces the previous one. - Define
ENTRYPOINTwhen using the container as an executable. - Reordering lines in a Dockerfile can improve build caching.
.dockerignoreexcludes files from the build context.FROM scratchis a special base meaning "empty".- You cannot
COPYorADDfiles 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.