DockerSeniorExperience

Представьте, контейнер работает локально, но падает или не отвечает на сервере. Как вы будете разбираться?

Проверяем по порядку: тот ли образ (digest), логи и ExitCode на сервере, сравниваем env vars и HostConfig, воспроизводим с prod-конфигурацией интерактивно, проверяем ресурсы хоста (disk, memory, dmesg OOM). Самые частые причины: floating tag, отсутствующие env vars, memory limit, архитектура образа.

Почему «работает локально» — это не информация

Контейнер работает локально, но падает на сервере. Первый шаг — собрать конкретные данные, а не делать предположения. «Падает» может означать разное: crash при старте, OOM kill, timeout healthcheck, зависание без ответа.

Шаг 1: убедиться, что это тот же образ

# Локально:
docker inspect my-image --format '{{.Id}}'

# На сервере:
docker inspect my-image --format '{{.Id}}'

Digest должен совпадать. Если отличается — деплоится другой образ (floating tag, обновление в registry).

Шаг 2: проверить логи на сервере

docker logs my-container --tail 100 --timestamps
docker inspect my-container --format '{{json .State}}'
# Проверить OOMKilled, ExitCode, Error

Шаг 3: сравнить конфигурацию запуска

# На сервере:
docker inspect my-container --format '{{json .HostConfig}}'
docker inspect my-container --format '{{json .Config.Env}}'

Типичные расхождения между локальным и серверным запуском:

  • Переменные окружения: локально есть .env файл, на сервере — нет. DB_HOST, API_KEY, SECRET_KEY могут отсутствовать или быть неверными.
  • Сеть: локально контейнер обращается к host.docker.internal или к другому контейнеру по имени сервиса. На сервере сеть может быть другой.
  • Ресурсы: memory limit на сервере ниже. Проверьте docker stats и dmesg | grep oom.
  • Архитектура: образ собран под amd64, сервер arm64 (или наоборот). Проверьте docker inspect my-image --format '{{.Architecture}}'.

Шаг 4: воспроизвести на сервере с минимальными отличиями

# Запустить интерактивно с теми же переменными что в prod:
docker run --rm -it \
  --env-file /etc/my-app/prod.env \
  --memory 512m \
  my-image sh

# Внутри проверить:
env | grep -E 'DB|SECRET|API'
curl -v http://db:5432  # доступна ли зависимость?
df -h  # есть ли место на диске?

Шаг 5: проверить хост

df -h /var/lib/docker   # место для overlay filesystem
free -h                 # доступная память
systemctl status docker # состояние daemon
dmesg | tail -50        # kernel messages (OOM, segfault)

Подводные камни

  • Floating tag: :latest или :main на сервере может указывать на другой образ, чем локально. Всегда деплоить по digest.
  • Разные версии Docker Engine: локально Docker Desktop 4.x, на сервере Docker CE 23.x — поведение networking и cgroups может различаться.
  • seccomp и AppArmor: на сервере могут быть профили безопасности, которых нет локально. Если приложение использует системные вызовы, заблокированные профилем — это silent failure.
  • Объём логов: если logging driver настроен на json-file без ротации, диск заполняется, и контейнер падает из-за нехватки места. На сервере это критичнее, чем локально.
  • Timezone: сервер может быть в UTC, локальная машина — нет. Если приложение зависит от системного времени — учитывайте это.

What hurts your answer

  • Сразу обвинять Docker, не проверив соседние слои системы
  • Чинить симптом без минимального воспроизведения и evidence
  • Не учитывать версии, конфигурацию, окружение и recent changes

What they're listening for

  • Умеет локализовать проблему вокруг Docker
  • Двигается от симптома к гипотезам и проверкам
  • Отличает баг инструмента от ошибки использования или окружения

Related topics