Контейнеризация: Docker и OCI
Контейнеризация: Docker и OCI
1) Базовые понятия и стандарты OCI
OCI Image Spec — формат образов (манифест, конфиг, слои, index для multi-arch).
OCI Runtime Spec — как запускать контейнер (bundle, `config.json`); реализация: runc, а также gVisor, Kata Containers.
OCI Distribution Spec — взаимодействие с реестрами (push/pull, авторизация).
Docker = UX и экосистема вокруг OCI: Dockerfile/BuildKit/CLI/Compose/Hub. В Kubernetes Docker Engine заменен на containerd/CRI-O, но формат образов одинаков.
2) Образы: слои, теги, метаданные
Образ = слои (layered filesystem) + config (entrypoint/cmd/env/labels) + manifest.
Теги: не используйте `:latest` в проде; пинning `:1.21.3`, git-SHA или дата+SHA.
LABEL: владелец, контакт, vcs-url, org.opencontainers. (title, description, revision, source).
Multi-arch: манифест-индекс отдает правильный вариант для `amd64/arm64`.
3) Сборка: Dockerfile, BuildKit, multi-stage
3.1 Принципы
Минимизируйте слои, фиксируйте версии, чистите кеши пакетных менеджеров.
Сначала копируйте manifest/lock-файлы, затем `RUN install deps` — улучшает кэш.
.dockerignore обязателен (исключите `.git`, артефакты, секреты).
Предпочтительны образцы distroless/alpine/минимальные базы.
3.2 BuildKit фишки
Параллельные билды, секреты в сборке (`--secret`), кэш-маунты, buildx для multi-arch.
Пример кэш-маунта:dockerfile syntax=docker/dockerfile:1. 6
RUN --mount=type=cache,target=/root/.cache/pip pip install -r requirements. txt
3.3 Multi-stage примеры
Go (statически линкованный, distroless):dockerfile syntax=docker/dockerfile:1. 6
FROM golang:1. 23 AS build
WORKDIR /src
COPY go. mod go. sum./
RUN go mod download
COPY..
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o /app
FROM gcr. io/distroless/static:nonroot
USER 65532:65532
COPY --from=build /app /app
ENTRYPOINT ["/app"]
Node.js (prod-слой без dev-deps):
dockerfile syntax=docker/dockerfile:1. 6
FROM node:22-alpine AS deps
WORKDIR /app
COPY package. json./
RUN npm ci --omit=dev
FROM node:22-alpine AS build
WORKDIR /app
COPY --from=deps /app/node_modules./node_modules
COPY..
RUN npm run build
FROM node:22-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY --from=deps /app/node_modules./node_modules
COPY --from=build /app/dist./dist
USER node
CMD ["node","dist/server. js"]
Python (wheel-кеш, non-root):
dockerfile syntax=docker/dockerfile:1. 6
FROM python:3. 12-slim AS base
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1
WORKDIR /app
FROM base AS deps
RUN --mount=type=cache,target=/root/.cache/pip pip install --upgrade pip
COPY requirements. txt.
RUN --mount=type=cache,target=/root/.cache/pip pip wheel --wheel-dir=/wheels -r requirements. txt
FROM base
COPY --from=deps /wheels /wheels
RUN pip install --no-index --find-links=/wheels -r /app/requirements. txt && rm -rf /wheels
COPY..
USER 1000:1000
CMD ["python","-m","app"]
Java (JLink/Layered Spring):
dockerfile syntax=docker/dockerfile:1. 6
FROM maven:3. 9-eclipse-temurin-21 AS build
WORKDIR /src
COPY pom. xml./
RUN mvn -q -e -DskipTests dependency:go-offline
COPY..
RUN mvn -q -DskipTests package
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY --from=build /src/target/app. jar /app/app. jar
ENTRYPOINT ["java","-XX:+UseContainerSupport","-jar","/app/app. jar"]
4) Минимальные образы, PID 1 и сигналы
Distroless — меньше поверхность атаки, нет shell/менеджера пакетов.
PID 1 должен корректно проксировать сигналы, иначе «зомби-процессы». Используйте `ENTRYPOINT` в exec-форме и tini/встроенный init:dockerfile
ENTRYPOINT ["tini","--","/app"]
`HEALTHCHECK` задавайте разумно (частота/таймаут, без лишней нагрузки).
5) Секьюрность контейнеров
5.1 Политики и hardening
Non-root (USER), rootless Docker/containers.
Capabilities: убирайте лишнее (`--cap-drop=ALL --cap-add=NET_BIND_SERVICE` и т. п.).
seccomp/AppArmor/SELinux: включайте профили по умолчанию или строгие.
Read-only FS + `tmpfs` для `/tmp`, no-new-privileges.
Secrets: не в образах; монтируйте из менеджера секретов (K8s/vault/docker secrets).
5.2 Supply chain
SBOM (CycloneDX/SPDX) и сканирование (Trivy/Grype).
Подпись (cosign, sigstore) и политика при pull (verify).
Репетиции обновлений: базовые образы с CVE-патчами регулярно пересобираются.
6) Хранение и файловые драйверы
По умолчанию overlay2 (быстрый и стабильный). В rootless средах часто fuse-overlayfs.
volumes для данных и кэшей, bind-mount для разработки.
Не записывайте в `/` — используйте путь данных (`/data`), отделяйте state от образа.
7) Сеть и DNS
Docker сети: bridge (по умолчанию), host (минимум оверхеда, конфликты портов), none, macvlan/ipvlan (L2/L3 интеграция).
DNS-резолвер Docker берет из хоста/daemon.json; для prod настройте локальные кеш-резолверы.
В K8s сеть управляет CNI (Calico/Cilium/Flannel). Для sidecar/mesh — перехваты (iptables).
8) Ресурсы и QoS (cgroups v2)
Ограничения: `--cpus`, `--memory`, `--pids-limit`, `--cpuset-cpus`.
Устанавливайте requests/limits (в K8s) → влияет на планирование и QoS.
Мониторьте OOMKilled, throttling, latency spikes из-за GC/IO.
bash docker run --cpus=1. 5 --memory=512m --pids-limit=256 --read-only --tmpfs /tmp:rw,size=64m...
9) Логи и наблюдаемость
Драйверы логов: `json-file` (с ротацией), `journald`, `gelf`, `awslogs`, `syslog`.
Настройте ротацию:json
{ "log-driver":"json-file","log-opts":{"max-size":"10m","max-file":"5"} }
Метрики: Docker Engine API, cAdvisor, node-экспортеры; трассировка через агент в контейнере или sidecar.
10) Регистры и аутентификация
Частные реестры: ECR/GCR/ACR/Harbor/GitHub Container Registry.
Rate-limits Docker Hub; используйте зеркала/кеши (registry-cache).
Политика retention/immutable tags, репликация между регионами.
`docker login` не храните в скриптах; используйте CI-секреты и OIDC-федерацию.
11) docker-compose vs оркестраторы
Compose — локальная разработка/интеграционные стенды.
Прод: Kubernetes (Deployment/StatefulSet/DaemonSet, Ingress, Secrets, PVC) с containerd/CRI-O; политики безопасности и rollout-стратегии.
Swarm устарел для больших продов, годится для простых кластеров.
yaml version: "3. 9"
services:
api:
build:.
ports: ["8080:8080"]
environment: ["DB_URL=postgres://pg/DB"]
depends_on: ["pg"]
pg:
image: postgres:16-alpine volumes: ["pgdata:/var/lib/postgresql/data"]
volumes: { pgdata: {} }
12) Healthcheck, старт/стоп, graceful shutdown
Используйте `HEALTHCHECK` с таймаутами и ограничениями по `retries`.
Корректный graceful: ловите SIGTERM, завершайте входящие, закрывайте соединения, затем выход.
В K8s: `preStop` hook + `terminationGracePeriodSeconds`, readiness перед liveness.
13) Best practices по языкам/стекам (сводка)
Node: `npm ci`, `NODE_ENV=production`, выключить dev-deps в runtime, `--heapsnapshot` off, `uWS/GZip` за L7-прокси.
Python: wheels, `gunicorn --graceful-timeout`, `GTHREADS`/`UVICorn` по CPU, не хранить venv внутри общего слоя без нужды.
Go: CGO off (если можно), `-ldflags="-s -w"`, distroless/static, `GOMAXPROCS` по cgroups.
Java: слойный JAR, `-XX:MaxRAMPercentage`, CDS/Layered JAR для кэша.
14) Supply chain и политика образов
Генерите SBOM на CI, сохраняйте рядом с артефактом.
Сканируйте образы на каждом пуше; гейт на критические CVE.
Подписывайте образы (cosign), включите policy controller (в K8s — Kyverno/Conftest/Gatekeeper).
Разделяйте build и run учетки/сети; кэшируйте зависимости в приватном реестре.
15) Анти-паттерны
`:latest` в проде; отсутствие иммутабельных тегов.
Сборка «внутри прод-хоста» без изоляции; хранение секретов в Dockerfile.
Запуск под root, `--privileged`, широкие capabilities.
Толстые образы (>1–2 ГБ), отсутствие.dockerignore.
Логика init в ENTRYPOINT через shell-форму → проблемы с сигналами.
Писать постоянные данные в слой контейнера вместо volume.
Healthcheck, делающий дорогие запросы к прод-БД.
16) Чек-лист внедрения (0–45 дней)
0–10 дней
Стандартизировать Dockerfile (multi-stage,.dockerignore, LABEL, pinned base).
Включить BuildKit/buildx, кэш-маунты для менеджеров пакетов.
Перейти на non-root и `seccomp`/AppArmor/SELinux профили по умолчанию.
11–25 дней
Минимизировать runtime-образы (alpine/distroless), навести порядок с логами (ротация).
Настроить лимиты ресурсов, healthchecks, корректный PID 1/tini.
Поднять приватный реестр/кеш, подключить сканер CVE и генерацию SBOM.
26–45 дней
Ввести подпись образов и политику допуска в кластер.
Организовать multi-arch (amd64/arm64) для нужных сервисов.
Документировать runbook сборки/релиза, отчет по размерам/уязвимостям/времени сборки.
17) Метрики зрелости
Иммутабельные теги и воспроизводимые сборки для ≥ 95% сервисов.
Средний размер runtime-образа < 200–300 МБ (по стеку).
100% prod-контейнеров — non-root, с ограниченными capabilities и read-only FS.
SBOM и сканирование CVE на каждый push; критичные CVE → блокируются.
Подпись образов и policy-enforcement в окружениях.
Время холодного старта контейнера ≤ целевого SLO (напр., 2–5 сек), корректный graceful shutdown.
18) Заключение
Контейнеризация по-взрослому — это стандарты OCI + дисциплина сборки + безопасность по умолчанию + наблюдаемость и политика поставки. Используйте multi-stage и BuildKit, минимизируйте runtime-образы, запускайте non-root под строгими профилями, фиксируйте теги, сканируйте и подписывайте, держите логи/ресурсы/сеть под контролем. Так контейнеры станут предсказуемой и управляемой основой вашей платформы — от девелопмента до продакшена.