Container Image — это упакованный и готовый к запуску набор файлов, библиотек и конфигураций, необходимых для работы приложения в контейнере. Проще говоря, это «снимок» приложения с его зависимостями, который можно запустить на любой машине, где есть контейнерный движок, например Docker или containerd.
В образ входят:
- базовый слой — операционная система или её минимальная часть (например, Alpine, Ubuntu, Debian);
- зависимости приложения — библиотеки, фреймворки, конфигурационные файлы;
- само приложение и инструкции для его запуска (entrypoint, команды).
💡 Почему это важно
Контейнерные образы обеспечивают единообразие среды: код, протестированный в одном окружении, будет работать так же в любом другом. Это исключает ситуации «у меня на локальной машине всё работало».
Как создаётся
Обычно разработчик пишет Dockerfile, где описывает шаги сборки: какую базу взять, что скопировать, какие команды выполнить. На выходе получается образ, который можно хранить в реестрах (Docker Hub, GitHub Container Registry, Harbor) и скачивать при необходимости.
Пример жизненного цикла Container Image
- Разработчик пишет код и Dockerfile.
- Система CI/CD собирает образ.
- Образ загружается в реестр.
- Kubernetes или другой оркестратор скачивает образ и разворачивает контейнеры.
Для клиента
Использование контейнерных образов упрощает развёртывание, повышает скорость выхода обновлений и улучшает безопасность — можно точно контролировать, из чего состоит рабочая среда.
Пример Dockerfile для простого Python-приложения:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "app.py"]
Структура и слои (layers)
Container Image собран из слоёв — неизменяемых наборов файлов. Каждый следующий слой «накладывается» поверх предыдущего, формируя итоговую файловую систему контейнера.
- Базовый слой: минимальная ОС или рантайм (alpine, debian-slim, distroless).
- Слои зависимостей: установленные пакеты, библиотеки, языковые рантаймы.
- Слои приложения: ваш код, статические ассеты, конфиги.
- Метаданные:
ENTRYPOINT,CMD,ENV,EXPOSE,LABEL— инструкции запуска и описательные поля.
Образы соответствуют формату OCI Image и хранятся как набор слоёв с контент‑адресацией: каждый слой и манифест имеют криптографический digest (sha256), что делает их идентифицируемыми и кэшируемыми.
Кэширование сборки
Docker/BuildKit кэширует слои: если инструкция в Dockerfile и её входные данные не менялись — слой берётся из кэша.
Практические приёмы:
- Сначала зависимости, потом код
COPY requirements.txt . RUN pip install -r requirements.txt COPY . .Обновление кода не ломает кэш слоя с зависимостями. - Игнорируйте лишнее: используйте
.dockerignore, чтобы не тащить временные файлы (node_modules, .git, логи). - Стабильные версии: фиксируйте версии пакетов, иначе кэш будет часто инвалидироваться.
- BuildKit: включайте
DOCKER_BUILDKIT=1— более умное кэширование и параллелизм.
Размер и производительность
- Используйте тонкие базовые образы (
alpine,-slim, distroless для продакшна). - Применяйте multi-stage builds: собирайте в одном этапе, копируйте артефакты в минимальный рантайм.
Пример (Go):
# stage 1: build
FROM golang:1.22 AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o app ./cmd/app
# stage 2: runtime
FROM gcr.io/distroless/static:nonroot
WORKDIR /app
COPY --from=build /src/app .
USER nonroot:nonroot
ENTRYPOINT ["/app/app"]
Результат — минимальный образ без шелла и package‑менеджера, меньше поверхность атаки, быстрее доставка.
Теги, digest’ы и неизменяемость
- Теги (
:latest,:v1.4.2) — удобны, но переопределяемы. - Digest (
@sha256:…) — неизменяемый идентификатор. Для воспроизводимости деплойте по digest’у:
image: registry.example.com/app@sha256:abc123...
Практика: храните и тег, и digest в манифестах (Kubernetes, Helm), чтобы сочетать читабельность и детерминизм.
Безопасность образов
- Минимизируйте поверхность атаки: distroless/ubi‑micro, убирайте компиляторы и шелл в рантайме.
- Не запускайте от root:
USER 10001:10001, задавайте непривилегированного пользователя. - Подписывайте образы: Cosign/Notary v2 — верифицируйте в CI/CD и на кластере (Policy/Admission).
- Сканируйте уязвимости: Trivy, Grype, Anchore; включите «break the build» на критических CVE.
- SBOM (Software Bill of Materials): генерируйте CycloneDX/SPDX (Syft, Trivy) и храните рядом с образом.
- Обновляйте базу: регулярно пересобирайте образы с новыми патчами базового слоя.
Регистры и политики
- Хранение: Docker Hub, GHCR, Harbor, ECR/GCR/ACR.
- Репликация между регионами — для снижения latency и отказоустойчивости.
- Политики очистки: автоматическое удаление «висячих» тегов и старых билдов.
- Контроль доступа: RBAC/OIDC, отдельные репозитории на окружение (
dev,staging,prod).
Диагностика и дебаг
- Добавляйте минимум инструментов в прод‑образ. Для диагностики — отдельный «debug»-вариант (с шеллом и curl).
- Логи и метрики — вне образа: стандартные потоки и sidecar‑агенты (не прошивайте агенты внутрь).
Шаблон «хорошего» Dockerfile (Node.js)
# build stage
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci --ignore-scripts
COPY . .
RUN npm run build && npm prune --production
# runtime stage
FROM gcr.io/distroless/nodejs20-debian12:nonroot
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules ./node_modules
ENV NODE_ENV=production
USER nonroot:nonroot
EXPOSE 3000
CMD ["dist/server.js"]
Особенности:
npm ci— воспроизводимая установка.prune --production— только runtime‑зависимости.- distroless — минимальный рантайм,
USER— без root.
Для чего это всё клиенту
Контейнерный образ — это стандартная единица поставки ПО. Корректная структура слоёв, воспроизводимая сборка, безопасность и управление тегами уменьшают риски, ускоряют релизы и упрощают аудит. Итог — предсказуемые деплои и контролируемая стоимость (меньше трафика и времени доставки).