Теория

docker — ПО для автоматизации развертывания и управления приложениями в средах с поддержкой контейнеризации.

Архитектура

docker — клиент-серверное приложение.

Термины

  • Docker enginedaemon, отвечающий за всё взаимодействие с контейнерами через свой REST API.
  • Container — развернутая из образа и работающая слабоизолированная среда. Являются stateless unit-ами.
  • Docker Registry — репозиторий с docker образами, расширениями и плагинами.
  • Dockerfile — файл с инструкцией для сборки образов.

Setup

Соус: docs.docker.com/engine/install/debian

Dependencies
sudo apt update && sudo apt install -y ca-certificates curl gnupg
Keys
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg \
  | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
Repository
echo "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
Installing docker
sudo apt update && sudo apt install -y \
  docker-ce \
  docker-ce-cli \
  containerd.io \
  docker-buildx-plugin \
  docker-compose-plugin
systemd
sudo systemctl restart docker
sudo systemctl enable docker.service
sudo systemctl enable containerd.service
Test
sudo docker run hello-world

Post-install

Разрешение использования docker от имени $USER:

sudo groupadd docker && sudo usermod -aG docker $USER
exit
Test
docker run hello-world

Remove

sudo systemctl disable {docker,containerd}.service
sudo apt purge -y {docker,containerd}\*
sudo rm -rf /var/lib/{docker,containerd}

Cleanup

docker stop and docker rm all containers:

docker stop "$(docker ps --quiet --all)" \
  && docker rm --force "$(docker ps --quiet --all)"

docker rmi all images:

docker rmi --force "$(docker images --quiet --all)"

docker rm all volumes:

docker volume rm --force "$(docker volume ls --quiet)"

docker rm all networks:

docker network rm --force "$(docker network ls --quiet)"

docker prune (Remove) unused data:

docker system prune --force --all

In one command:

docker stop "$(docker ps --quiet --all)" \
  && docker rm --force "$(docker ps --quiet --all)" \
  && docker rmi --force "$(docker images --quiet --all)" \
  && docker volume rm --force "$(docker volume ls --quiet)" \
  && docker network rm --force "$(docker network ls --quiet)" \
  && docker system prune --force --all

Check

Вывод следующих команд должен быть пустым:

docker ps --all
docker images --all 
docker volume ls

A эта должна вывести only default networks (bridge, host, none):

docker network ls

Networking

Network drivers

Сетевая подсистема docker является pluggable (подключаемой/модульной) и использует драйверы.

DriverDescription
bridgeDefault network driver. Используется, если контейнеру нужно общаться с другими контейнерами на одном хосте.
См. Bridge network driver
hostУбирает сетевую изоляцию между контейнером и хостом docker, используя сетевые возможности хоста напрямую.
См. Host network driver
noneFully изолирует контейнер от хоста, других контейнеров.
См. None network driver
overlayОверлейные сети соединяют несколько демонов docker вместе, что, кстати, избавляет от необходимости маршрутизации на уровне ОС. См. Overlay network driver
macvlanСети macvlan позволяют назначить MAC-адрес контейнеру, чтобы он выглядел как физическое устройство внутри сети. Docker daemon направляет трафик к контейнерам по их MAC-адресам. Используется для работы с устаревшими приложениями или для миграции с виртуальных машин.
См. Macvlan network driver
ipvlanСети IPvlan предоставляют юзерам total control над адресацией IPv4 и IPv6.
Короче, см. IPvlan network driver
Network pluginsВ docker можно ставить и юзать third-party network plugins.

curl and wget in docker

wget --no-check-certificate <URL>
curl --insecure <URL>

Images

Save and load images from tarballs

docker save examples:

docker save nginx:alpine > nginx.tar
docker save nginx:alpine --output nginx.tar
docker save 91ca84b4f577 --output nginx.tar
docker save \
  debian-lab:latest \
  nginx:alpine \
  ff30d51c7765 \
  --output images.tar

docker load examples:

docker load < tar.tar
docker load --input tar.tar

Untagged images

После docker load образы будут untagged (без REPOSITORY и TAG).
Нужно просто проставить им теги, зная IMAGE ID:

docker tag 91ca84b4f577 nginx:alpine

Dockerfile

Layers

About minimizing the number of layers

Слои создают только следующие инструкции:

  • RUN
  • COPY
  • ADD

Другие инструкции создают временные промежуточные образы, не увеличивая размер сборки.

Эффективнее следовать правилу Use multi-stage builds, что также позволит включать инструменты и debug-операции в промежуточные этапы сборки, не влияя на конечный образ.

Best practices

Create ephemeral containers

Нужно писать максимально эфемерные контейнеры. Подразумевается возможность остановки, уничтожения контейнера, его последующей перестройки и замены с минимальными настройками.

Don’t use docker commit on a running container

Не создавай образы из запущенных контейнеров — применяй docker commit только к остановленному контейнеру, иначе получившийся (некорректный) образ не будет воспроизводимым.

Симлинки не резолвятся в Dockerfile. Не пытайся в Dockerfile передать симлинки через инструкцию COPY — that’s not gon work.

Decouple applications

Я это правило объединил со схожим: “Don’t run more than one process in a container

Придерживайся (UNIX-way) идеи “Один контейнер — один процесс”. Пусть лучше будет несколько общающихся между собой контейнеров, что сохранит преимущества контейнеров (согласно философии контейнеров) и:

  • упростит поддерживаемость
  • более управляемая утилизация ресурсов
  • упростит горизонтальное масштабирование
  • возможность повторного использования контейнеров

Поэтому держи контейнеры чистыми и модульными.

Understand build context

При запуске docker build текущая директория называется build context. По умолчанию предполагается, что Dockerfile находится в текущей директории, но через -f/--file можно указать другое местоположение. Независимо от того, где находится указанный Dockerfile, всё рекурсивное содержимое текущей директории передастся docker daemon-у в качестве контекста сборки.

Build context example

mkcd myproject
<<< 'hello' > hello
Dockerfile
FROM busybox
COPY /hello /
RUN cat /hello
docker build -t hello-app:v1 .

Теперь раскинь Dockerfile и hello по разным директориям:

mkdir dockerfiles/ context/
mv Dockerfile dockerfiles/ && mv hello context/

Собери вторую версию образа (не полагаясь на кэш последней сборки):

docker build \
  --no-cache \
  -t hello-app:v2 \
  -f dockerfiles/Dockerfile \
  context/

В итоге включение ненужных файлов для создания (второй версии) образа приведет к увеличению:

  • контекста сборки
  • размера образа
  • времени сборки образа
  • времени его извлечения и перемещения
  • размера среды выполнения контейнера

Exclude with .dockerignore

Используй .dockerignore для исключения файлов, не относящихся к сборке — не нужно менять структуру репозитория с помощью .gitignore.
Шаблоны исключения .dockerignore аналогичны .gitignore.

Use multi-stage builds

Многоступенчатые сборки позволяют уменьшить размер конечного образа.

Подели Dockerfile на отдельные этапы так, что итоговый результат (конечный образ) содержит только необходимые для работы приложения файлы.

Example

## First stage
FROM node:14 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
 
## Second stage
FROM nginx:alpine
# Copying the built app from the first stage
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Don’t install unnecessary packages

Даже пакеты, которые “могут быть полезны” (свой любимый helix), не надо устанавливать.
Не надо обновлять пакеты системы.

Sort multi-line arguments

Пример: блок кода в главе “apt-get

Сортировка многострочных аргументов:

  • поможет избежать дублирования пакетов
  • упростит обновление списка
  • упрощает review

Leverage build cache

Использование build cache, очевидно, ускоряет сборку образа.

Для каждой инструкции docker проверяет возможность использования инструкции из кэша сборки.

docker аннулирует кэш для измененных слоев. Например, изменение файла, который тянется в образ с помощью ADD или COPY заставит docker аннулировать кэш для этого слоя и запустить ADD или COPY заново. И если слой изменился, это затронет и все следующие слои — все последующие слои также будут запущены заново (даже если последующие слои не будут собираться по-другому, их все равно нужно запустить заново).

Build images with BuildKit

Buildkitвстроенный улучшенный сборщик образов.
Он имеет автоматический сборщик мусора, механизмы параллелизма, кэширования и т.д.

Чтобы он собирал образы при запуске команды docker build , нужно включить его переменным окружения:

DOCKER_BUILDKIT=1 docker build .

или глобально в файле конфигурации docker daemon:

/etc/docker/daemon.json
{ "features": { "buildkit": true } }

и

sudo systemctl restart docker

Create reusable stages

Если несколько образов юзают общие компоненты — создай reusable stage и основывай уже на нем уникальные стадии, чтобы docker-у нужно было собрать общую стадию только один раз. Это сделает производные образы эффективнее в утилизации памяти и быстрее при загрузке — в общем, те же code reuse pros.

Rebuild your images often

Почаще пересобирай образы, дабы держать их в актуальном и безопасном состоянии, ибо само состояние образа иммутабельны (неизменяемы).

Чтобы быть уверенным в получении последних версий зависимостей при сборке, юзай --no-cache:

docker build --no-cache -t my-image:my-tag .

apt-get

Доп. соус

Всегда используй вместе apt-get update и apt-get install, чтобы избежать проблем с кэшированием:

RUN apt-get update && apt-get install -y --no-install-recommends \
    automake \
    ruby1.9.1 \
    ruby1.9.1-dev \
    s3cmd=1.1.* \
    && rm -rf /var/lib/apt/lists/*

Если разделить эти команды на разные RUN инструкции, то в будущем при добавлении, например, новых пакетов для установки docker build может не запустить apt-get update, а использовать кэшированный слой для этой инструкции, что приведет к тому, что пакеты не установятся или установятся их неактуальные версии.

ADD or COPY

Юзай COPY для базового копирования файлов в контейнер, из контекста сборки или из этапа многоэтапной сборки.

Юзай RUN --mount=type=bind для временного добавления файлов, которым не место в конечном образе:

RUN --mount=type=bind,source=requirements.txt,target=/tmp/requirements.txt \
    pip install --requirement /tmp/requirements.txt

Юзай ADD для загрузки удаленных артефактов как частей сборки.

Осторожнее с ADD

Существуют некоторые проблемы с безопасностью при использовании ADD для загрузки удаленных артефактов.

Alpine is not always the best choice

Alpine and Python Docker

Учти, что, например, с python могут возникнуть проблемы при использовании alpine.