13. Docker и Kubernetes (связка)
Цель темы: понимать, где заканчивается зона ответственности Docker и начинается Kubernetes: как образ Docker используется в поде, как ENTRYPOINT/CMD соотносятся с полем command контейнера, зачем нужен imagePullPolicy и securityContext, и чем отличаются container runtime (containerd, CRI-O) от «классического» Docker daemon.
Определения терминов
Образ в Kubernetes
Образ в манифесте пода — это тот же OCI/Docker-образ (из registry по имени и тегу или по digest). Kubernetes не собирает образы; kubelet на ноде пуллит образ через container runtime (containerd, CRI-O и т.д.) и запускает контейнер по спецификации пода. Формат образа не меняется: образ, собранный через docker build и запушенный в registry, подходит для Kubernetes без пересборки.
command и args в Pod
В спецификации контейнера в поде поля command и args переопределяют ENTRYPOINT и CMD образа. command соответствует ENTRYPOINT, args — аргументам (вместо CMD). Если задать только args, ENTRYPOINT образа остаётся, аргументы подставляются вместо CMD. Это важно для переопределения точки входа без пересборки образа (например, передать флаги приложению).
imagePullPolicy
imagePullPolicy — когда kubelet качает образ с registry: Always (каждый раз проверять по тегу), IfNotPresent (пуллить, если образа нет локально), Never (только локальный образ). Для тега latest по умолчанию используется Always; для конкретного тега (например, v1.0.0) — IfNotPresent. Для воспроизводимости в production используют теги по версии или по digest и при необходимости явно задают IfNotPresent или Always.
securityContext
securityContext — настройки безопасности контейнера (и пода): runAsUser, runAsNonRoot, readOnlyRootFilesystem, capabilities, seccompProfile и др. В Kubernetes они задаются в манифесте и передаются в runtime; аналог флагов Docker (--user, --cap-drop). Позволяет запускать контейнер от non-root, ограничивать capabilities и т.д. без изменения образа.
Container runtime (CRI)
Container runtime в Kubernetes — компонент на ноде, который по запросу kubelet пуллит образ и запускает контейнер (создаёт и управляет контейнерами по CRI — Container Runtime Interface). Типичные реализации: containerd (с runc), CRI-O. Раньше использовался Docker через dockershim (kubelet вызывал Docker daemon); dockershim удалён, сейчас используется containerd или CRI-O. Образы те же (OCI); отличия — в настройке и в том, что демона Docker на ноде может не быть.
Docker-образ в Kubernetes
Образ, собранный через Docker (или BuildKit) и запушенный в любой OCI-совместимый registry, указывается в Pod/Deployment так:
spec:
containers:
- name: app
image: myregistry.com/myapp:v1.0.0
# image: myregistry.com/myapp@sha256:abc123... # по digest
Kubelet передаёт запрос на pull и run в container runtime (containerd/CRI-O); runtime пуллит образ и создаёт контейнер. Никакой «конвертации» образа не требуется — формат OCI общий.
Практика
В production указывайте образ по тегу версии (v1.0.0) или по digest, чтобы деплой был воспроизводимым. imagePullPolicy при необходимости задавайте явно (например, IfNotPresent для ускорения при гарантированно обновлённом образе на ноде).
ENTRYPOINT/CMD и command/args
В образе:
- ENTRYPOINT — исполняемый файл/команда.
- CMD — аргументы по умолчанию для ENTRYPOINT.
В поде:
- command — переопределяет ENTRYPOINT образа целиком.
- args — переопределяет CMD (аргументы для ENTRYPOINT) или аргументы для command, если задан command.
| В поде | ENTRYPOINT образа | CMD образа |
|---|---|---|
| Не задано | Используется | Используется |
| Только args | Используется | Заменён на args |
| command | Заменён на command | Игнорируется (если не задан args, аргументов нет) |
| command + args | command как программа | args как аргументы |
Пример: образ с ENTRYPOINT ["/app/server"] и CMD ["--port", "8080"]. В поде можно передать другой порт без пересборки:
containers:
- name: app
image: myapp:v1.0
args:
- "--port"
- "9090"
ENTRYPOINT остаётся /app/server, аргументы будут --port 9090.
Полная замена команды (например, запуск shell для отладки):
containers:
- name: app
image: myapp:v1.0
command: ["/bin/sh", "-c"]
args:
- "sleep 3600"
imagePullPolicy
- Always — перед запуском контейнера всегда обращаться к registry и при изменении тега подтягивать новый образ. По умолчанию для тега
:latestили если тег не указан. - IfNotPresent — пуллить только если образа с таким именем и тегом ещё нет на ноде. По умолчанию для любого конкретного тега (например, v1.0.0).
- Never — использовать только локальный образ; если его нет, под не стартует. Для воздушных окружений или предзагруженных образов.
containers:
- name: app
image: myapp:v1.0.0
imagePullPolicy: IfNotPresent
При использовании digest (image: myapp@sha256:...) образ однозначно определён; политика чаще IfNotPresent, чтобы не дергать registry без необходимости.
securityContext
Настройки безопасности контейнера задаются на уровне пода (pod securityContext) и контейнера (container securityContext). Примеры:
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
containers:
- name: app
image: myapp:v1.0
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
- runAsUser / runAsNonRoot — от какого пользователя запускать процесс (безопаснее non-root).
- readOnlyRootFilesystem — корень ФС только для чтения; запись только в смонтированные тома.
- capabilities.drop: ALL — убрать все лишние capabilities.
В Docker аналоги задаются флагами --user, --read-only, --cap-drop. В Kubernetes это декларативно в манифесте и единообразно для оркестратора.
Отличия container runtime: containerd, CRI-O vs Docker daemon
На ноде Kubernetes контейнеры запускает не демон Docker (dockerd), а runtime по CRI:
- containerd — тот же движок, который использует Docker под капотом; образы в формате OCI, совместимы с Docker. На ноде нет команды
docker(если не установлен отдельно), но образы те же. - CRI-O — лёгкий runtime, совместимый с OCI и CRI; образы из тех же registry пуллируются так же.
Общие моменты:
- Образы собираются так же (docker build, push в registry) и в Kubernetes используются без изменений.
- Нет docker run/docker ps на ноде — управление контейнерами через kubectl и API сервера.
- Логи, метрики, сеть — зона ответственности Kubernetes (логи через kubelet, сеть через CNI). Отладка на ноде: crictl (для containerd/CRI-O) или установленный Docker CLI для совместимости (если образы те же, контейнеры видны через crictl).
Production
CI собирает образ через Docker/BuildKit, пушит в корпоративный registry. В манифестах Kubernetes указывается тот же образ (тег или digest). imagePullPolicy и securityContext задаются в шаблонах (Helm, Kustomize). На нодах — containerd или CRI-O; образы и контейнеры совместимы с тем, что вы тестировали локально в Docker.
Паттерны использования
| Паттерн | Описание |
|---|---|
| Один образ — Docker и Kubernetes | Собирать один раз, пушить в registry; использовать в Compose локально и в K8s в кластере. |
| Переопределение через args | Менять порт, конфиг или флаги приложения через args в поде, не пересобирая образ. |
| securityContext в манифесте | Запуск от non-root, drop capabilities, read-only root — задавать в Kubernetes, образ остаётся переносимым. |
| Тег или digest в production | Не использовать latest; фиксировать версию или digest для воспроизводимости. |
Антипаттерны
| Антипаттерн | Почему плохо | Что делать |
|---|---|---|
| Жёстко полагаться на docker на ноде | В K8s нода может быть без Docker; контейнеры управляются через CRI. | Использовать kubectl и crictl для отладки на ноде. |
| Игнорировать securityContext | Запуск от root и лишние capabilities увеличивают риски. | Задавать runAsNonRoot, drop capabilities в поде. |
| Путать command и args | command заменяет ENTRYPOINT; args заменяют CMD. | Читать справку по полям и проверять итоговую команду в поде. |