Learning Platform
Глоссарий Troubleshooting
Урок 05.03 · 22 мин
Начальный
dockerdocker-execdocker-logsdebug

docker exec и docker logs

docker exec и docker logs — это две команды, которыми ты будешь дебажить упавший Airflow worker, искать причину почему DAG не запускается, и проверять, какие SQL-запросы прилетают в Postgres. Если ты понимаешь их как следует — ты понимаешь основу daily DE debugging workflow с контейнерами.

В этом уроке: как зайти в работающий контейнер через exec, как смотреть логи с фильтрами, что делать если в контейнере нет shell, и почему exec это не то же самое, что attach.


docker exec — запустить процесс в контейнере

docker exec запускает новый процесс внутри уже работающего контейнера. Самое типичное — открыть shell:

docker exec -it pg bash

Что произошло:

  1. Daemon нашёл контейнер с именем pg.
  2. Запустил новый процесс bash внутри namespaces этого контейнера.
  3. Прикрепил твой терминал к этому процессу через -it.

Теперь ты внутри. ps aux покажет процессы контейнера, ls / — его файловую систему.

Флаги:

  • -i — keep stdin open. Без этого нельзя печатать в bash.
  • -t — выделить tty. Без этого bash будет без prompt’а.
  • -it — почти всегда вместе для интерактивных задач.

Для разового запуска команды без интерактивной сессии флаги не нужны:

docker exec pg ls /var/lib/postgresql/data
docker exec pg psql -U postgres -c 'select count(*) from pg_database;'

Это полезно в скриптах: выполнить команду в контейнере и получить вывод обратно.


Что делать, если нет bash или sh

Не во всех образах есть bash. В минимальных alpine — есть только sh. В distroless — нет ничего (нет shell, нет coreutils).

Что доступно по образам
ubuntu, debianСтандартный Linux-stack. bash, ls, ps, vim, curl, всё на месте
alpineМинимальный образ, ~5 МБ. busybox-based. sh есть, bash нет. ls, ps, wget есть
distroless (gcr.io/distroless)Google distroless — только runtime + приложение. Никакого shell, никакого ls. exec -it не запустится
scratchПустой образ, только статический бинарь приложения. Например, Go-приложения часто так собираются

Стратегия для каждого случая

ubuntu/debian/python:slim: docker exec -it CONTAINER bash.

alpine: docker exec -it CONTAINER sh.

distroless / scratch: нет shell. Варианты:

  1. nsenter с хоста — зайти в namespaces контейнера хостовыми утилитами:
# Получить PID главного процесса контейнера
PID=$(docker inspect -f '{{.State.Pid}}' <container>)
sudo nsenter --target $PID --mount --uts --ipc --net --pid sh

Это работает только если на хосте есть sh и nsenter. На mac (где хост это macOS) — nsenter нет. Но в VM, в которой крутится контейнер, он есть.

  1. debug image — запустить параллельный контейнер с shell, который подсоединяется к namespace основного:
docker run --rm -it \
  --pid container:<main-container> \
  --network container:<main-container> \
  --volumes-from <main-container> \
  alpine sh

Это lifesaver, когда нужно дебажить distroless образ.

  1. kubectl debug (если в k8s) — то же самое, но с правильным CLI.

stdin, stdout, stderr: три file descriptors

docker logs — стандартный вывод контейнера

Контейнер пишет в stdout/stderr. Docker daemon собирает этот поток и сохраняет в JSON-файл на хосте (по умолчанию). Доступ через:

docker logs pg

Выводит весь лог с начала жизни контейнера. Если контейнер живёт пять дней — это может быть сотни мегабайт.

Полезные флаги

# Последние 100 строк
docker logs --tail 100 pg

# Только за последние 5 минут
docker logs --since 5m pg

# Только до конкретного времени
docker logs --until 2026-05-15T12:00:00 pg

# Follow — как tail -f
docker logs -f pg

# Timestamp каждой строки
docker logs -t pg

# Комбинировать
docker logs -f --tail 50 --since 10m pg

Очень частый паттерн: «что было в логах за последние 5 минут, и следить дальше»:

docker logs -f --since 5m pg

stderr vs stdout

docker logs показывает оба потока, смешано. Чтобы разделить, нужно перенаправить streams:

docker logs pg > stdout.log 2> stderr.log

Если приложение пишет ошибки в stderr (как должно), stderr.log будет содержать только их.

Это полезно, например, для разделения SQL-запросов (stdout) от ошибок (stderr).


Логи для остановленного контейнера

docker logs работает и для остановленных контейнеров — пока контейнер не удалён, лог доступен:

docker logs my-failed-job

Если контейнер удалён (docker rm) — лог тоже удалён вместе с ним.

Это причина, по которой при дебаге не стоит сразу удалять упавший контейнер. Сначала смотришь логи, потом rm.


Где физически лежит лог

На Linux logs лежат в файле:

/var/lib/docker/containers/<container-id>/<container-id>-json.log

Это JSON-файл, каждая строка — один лог-event:

{"log":"started\n","stream":"stdout","time":"2026-05-15T12:34:56.789Z"}

Docker может ротировать этот файл, но по умолчанию — нет. Если контейнер пишет много и долго, файл может вырасти до гигабайтов. В production стандарт — настраивать ротацию или использовать драйвер для centralized logging (syslog, fluentd, journald). Это тема модуля 14.


docker attach vs docker exec — важная разница

Часто junior’ы путают exec и attach. Они делают разное:

exec vs attach — что они делают
docker execЗапускает НОВЫЙ процесс в контейнере. Главный процесс (PID 1) не трогается. Выход из exec — главный процесс продолжает работать
docker attachПодключается к stdio ГЛАВНОГО процесса (PID 1). Видишь его stdout, можешь писать в его stdin. Ctrl+C убьёт главный процесс — контейнер остановится

Конкретный пример. Запускаем nginx:

docker run -d --name webby nginx
docker exec -it webby bash
# в bash. nginx продолжает работать. exit — webby продолжает работать
docker attach webby
# подключился к stdio nginx (PID 1). видишь access logs.
# Ctrl+C — убил nginx, контейнер остановился

attach редко нужен в DE-сценариях. Используется в основном для:

  • Прямой работы с REPL, запущенным в docker run -it (если случайно отключился).
  • Когда контейнер был запущен с явным интерактивным процессом и тебе нужно к нему обратно.

99% времени используется exec. Если ты хочешь «зайти в контейнер» — это exec -it CONTAINER bash.

WARNING

Если ты сделал docker attach, и хочешь выйти не убивая контейнер — нажми последовательно Ctrl+P, Ctrl+Q (detach sequence). Просто Ctrl+C — убьёт главный процесс и контейнер остановится.


Попробуй сам

# 1. Запусти Postgres
docker run -d --name pg -e POSTGRES_PASSWORD=s postgres:16.4
sleep 3

# 2. Зайди внутрь через exec
docker exec -it pg bash
# в bash:
ps aux                  # увидишь postgres + bash + ps
psql -U postgres -c 'select 1;'
exit                    # bash вышел, pg продолжает работать

# 3. Посмотри логи
docker logs pg | tail -20
docker logs -f --tail 5 pg &  # tail в фоне
sleep 2; kill %1                # убей tail

# 4. Запусти одноразовую команду через exec
docker exec pg date
docker exec pg env | grep POSTGRES

# 5. Прибраться
docker rm -f pg

Связь с дальнейшими модулями

exec и logs — это базовые инструменты для всех последующих debug-сценариев:

  • Модуль 12 (Postgres, MinIO, Redis): docker exec -it pg psql — стандартный способ зайти в локальную Postgres.
  • Модуль 13 (debug): тут раскроем глубокий debugging через exec, logs, events, stats.
  • Модуль 16 (Airflow стенд): docker logs airflow-worker --since 1m -f будешь набирать каждый раз, когда DAG не запускается.

Привыкай. После двух недель набора эти команды становятся рефлексом.


Проверка знанийKnowledge check
В чём принципиальная разница между docker exec и docker attach, и какую из них стоит использовать для «зайти в контейнер посмотреть что внутри»?
ОтветAnswer
docker exec и docker attach делают принципиально разное. docker exec запускает НОВЫЙ процесс внутри namespaces работающего контейнера. Например, docker exec -it pg bash — это новый процесс bash в контейнере pg, как дополнительный. Главный процесс контейнера (PID 1, у Postgres это postgres) при этом продолжает работать. Когда ты делаешь exit из bash, bash завершается, а контейнер живёт. docker attach подключается к stdio ГЛАВНОГО процесса контейнера (PID 1). Ты видишь его stdout (тот же поток, что в docker logs), можешь писать в его stdin. И, что критично, Ctrl+C посылает SIGINT главному процессу и убивает контейнер. Для обычного «зайти и посмотреть» — почти всегда docker exec -it pg bash (или sh для alpine). attach редко нужен и часто опасен — junior случайно нажмёт Ctrl+C и положит Postgres. Если уж пришлось attach — выходи через Ctrl+P Ctrl+Q (detach sequence), а не Ctrl+C.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 5. Что делает docker exec?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5