Контейнеризація: 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 під суворими профілями, фіксуйте теги, скануйте і підписуйте, тримайте логи/ресурси/мережу під контролем. Так контейнери стануть передбачуваною і керованою основою вашої платформи - від девелопменту до продакшену.