Модуль 06 — Контейнеры (Docker) ⭐⭐
Контейнеры — стандарт упаковки приложений с 2014 года. База для Kubernetes. Без них шага в современный DevOps не сделать.
Что такое контейнер на самом деле
Контейнер — это не виртуальная машина. Это обычный Linux-процесс, изолированный двумя ядерными механизмами:
- Namespaces — изоляция ресурсов:
pid— процессы видят только себяnet— свой стек сетиmnt— своя файловая системаuts— своё имя хостаipc— своя межпроцессная связьuser— маппинг UID
- cgroups — лимиты ресурсов: CPU, RAM, диск, сеть
Образ — это слоёная файловая система (UnionFS / OverlayFS). Контейнер — это процесс, который видит этот образ как корневую ФС + свой writable слой сверху.
Стандарты OCI
- image-spec — формат образа (что в нём лежит)
- runtime-spec — как runtime запускает контейнер
- distribution-spec — API регистров
Docker — лишь одна из реализаций. Альтернативы: Podman (rootless), containerd, CRI-O.
Docker CLI — основные команды
docker run -d --name web -p 8080:80 nginx:1.27 # запустить
docker ps # активные контейнеры
docker ps -a # все, включая остановленные
docker logs -f web # логи (follow)
docker exec -it web sh # войти внутрь
docker stats # real-time метрики
docker stop web && docker rm web
docker images # список образов
docker pull alpine:3.20
docker rmi unused:tag
docker system df # использование диска
docker system prune -a --volumes # очистить всё
Dockerfile — главное мастерство
Простой пример
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY src/ ./src/
EXPOSE 8000
CMD ["python", "-m", "src.main"]
Production-grade Dockerfile (multi-stage, non-root)
# syntax=docker/dockerfile:1.7
# ============ Stage 1: build ============
FROM python:3.12-slim AS builder
WORKDIR /app
RUN pip install --no-cache-dir uv
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-cache --no-dev
# ============ Stage 2: runtime ============
FROM python:3.12-slim AS runtime
# non-root для безопасности
RUN useradd -r -u 1000 -m appuser
WORKDIR /app
# только нужное из builder'а
COPY --from=builder --chown=appuser:appuser /app/.venv /app/.venv
COPY --chown=appuser:appuser src/ ./src/
USER appuser
ENV PATH="/app/.venv/bin:$PATH" \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')" || exit 1
EXPOSE 8000
ENTRYPOINT ["python", "-m", "src.main"]
Ключевые принципы
- Multi-stage build — отделяй build-зависимости от runtime. Финальный образ компактный.
- Slim/distroless базы —
python:3.12-slim,gcr.io/distroless/static. Меньше = безопаснее (меньше CVE). - Non-root user — обязательно.
USER 1000илиUSER nonroot. - Кеш слоёв: ставь часто меняющиеся файлы (исходники) в конце Dockerfile.
- .dockerignore — не тащи
node_modules,.git,tests/,.env. - Pinning версий:
FROM python:3.12.5-slim, не:latest. Лучше: pin по digestsha256:.... - Очистка:
apt-get clean && rm -rf /var/lib/apt/lists/*. - Один процесс на контейнер. Не пихай postgres + nginx в один.
- BuildKit secrets —
RUN --mount=type=secret,id=npm,target=/.npmrc. Никаких секретов в layers.
Команды CMD vs ENTRYPOINT
| Конструкция | Что делает | Когда |
|---|---|---|
CMD ["a", "b"] | Default команда, переопределяется аргументами | Простые образы |
ENTRYPOINT ["bin"] + CMD ["arg"] | Bin фиксирован, args меняются | «Контейнер как команда» |
CMD bin arg (shell-form) | Запуск через sh -c | Почти никогда — теряется PID 1 |
Docker Compose
Локальный orchestrator — несколько контейнеров одной командой.
# compose.yaml
services:
api:
build: .
ports: ["8000:8000"]
environment:
DATABASE_URL: postgres://app:secret@db:5432/app
REDIS_URL: redis://cache:6379
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
volumes: [db_data:/var/lib/postgresql/data]
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app"]
interval: 5s
retries: 5
cache:
image: redis:7-alpine
volumes:
db_data:
docker compose up -d # запустить в фоне
docker compose logs -f api # логи сервиса
docker compose ps
docker compose down -v # остановить + удалить volumes
Volumes и сети
Volumes
- Bind mount — папка хоста → в контейнер. Для разработки.
- Named volume — Docker управляет. Для production.
- tmpfs — в RAM. Для секретов, временных файлов.
Сети
- bridge (по умолчанию) — каждый контейнер свой IP в подсети Docker.
- host — контейнер делит сетевой стек хоста. Перфоманс, минус изоляция.
- none — без сети.
- Custom bridge networks — DNS resolution между контейнерами по имени.
Container Registries
- Docker Hub — public, лимиты на pull для бесплатных
- GHCR (GitHub Container Registry) — отлично интегрирован с GitHub Actions
- GitLab Container Registry
- AWS ECR / GCP Artifact Registry / Azure ACR
- Harbor — self-hosted enterprise
Безопасность образов ⭐
Сканеры уязвимостей
- Trivy ⭐ — стандарт, простой, быстрый. CVE + secrets + misconfigs + license.
- Grype + Syft — Anchore, отлично работают вместе.
- Snyk Container — коммерческий лидер.
# Trivy — must-have в pipeline
trivy image myapp:1.0
trivy image --severity HIGH,CRITICAL --exit-code 1 myapp:1.0
trivy fs . # сканировать файловую систему
trivy config Dockerfile # misconfig в Dockerfile
SBOM — Software Bill of Materials
Список всех компонентов в образе. Стандарт supply chain security.
syft myapp:1.0 -o spdx-json > sbom.json
syft myapp:1.0 -o cyclonedx-json > sbom.cdx.json
grype sbom:./sbom.json # сканировать SBOM на CVE
Cosign — подпись образов
Подписываешь → пушишь подпись в registry → kubernetes admission controller проверяет подпись перед запуском.
# Keyless signing через GitHub OIDC (рекомендуется)
cosign sign ghcr.io/me/myapp:1.0
# Верификация
cosign verify \
--certificate-identity=https://github.com/me/myapp/.github/workflows/ci.yml@refs/heads/main \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com \
ghcr.io/me/myapp:1.0
Альтернативы Docker
| Инструмент | Особенность | Когда |
|---|---|---|
| Podman | Daemonless, rootless, Docker-CLI compat | Безопасность, RHEL экосистема |
| Buildah | Только сборка, без daemon | В CI |
| Kaniko (Google) | Сборка в контейнере без daemon | K8s pipelines |
| nerdctl | Клиент для containerd | Низкоуровневая работа |
| BuildKit | Docker build engine v2 | Уже у тебя по умолчанию |
Современные темы
- BuildKit — параллельная сборка, secrets, cache mounts
- docker buildx bake — оркестрация мульти-target сборок
- OCI artifacts — не только образы, но и Helm charts, SBOMs, configs
- WebAssembly containers — wasm runtime в Docker, ~ms cold start
Чек-лист
- ☐ Понимаю namespace и cgroup, могу объяснить
- ☐ Пишу Dockerfile с multi-stage и non-root
- ☐ Знаю разницу CMD и ENTRYPOINT
- ☐ Использую
.dockerignore - ☐ Образ моего Python-приложения < 100MB
- ☐ Trivy в CI обязательно
- ☐ Cosign keyless signing настроен
- ☐ SBOM генерируется через Syft
Источники
- Книга: «Docker Deep Dive» — Nigel Poulton (обновляется)
- Документация: docs.docker.com
- Best practices: Docker Dockerfile best practices
- Безопасность: OWASP Docker Top 10
- Container Security: «Container Security» — Liz Rice (бесплатно от O'Reilly)
Контейнеры освоены. Дальше — облако.