Skip to main content

Як потрапити всередину запущеного контейнера?

Отримання shell всередині запущеного container це базовий debugging-хід. Команда docker exec, магічні флаги -it, а підступ у тому, що не у кожного image є shell.

Теорія

TL;DR

  • docker exec -it <name> <cmd> запускає <cmd> як НОВИЙ процес всередині container, не на додачу до PID 1.
  • -i тримає stdin відкритим; -t виділяє TTY. Обидва потрібні для інтерактивних shell.
  • Більшість Linux base-image мають /bin/sh. Деякі (Ubuntu, Debian) ще й /bin/bash. Distroless і FROM scratch не мають жодного.
  • Exec'нутий процес успадковує namespaces container (бачить ту саму filesystem, мережу, процеси), але батько у нього daemon, а не PID 1 застосунку.
  • docker exec для запуску у container, що вже running. Щоб отримати shell під час старту container, бери docker run -it <image> sh.

Швидкий приклад

bash
# Найпоширеніше: shell у running container $ docker exec -it web sh / # ls / bin dev etc home lib proc root run sbin sys tmp usr var / # exit # Одноразова команда і вихід $ docker exec web ls /etc/nginx # Повертає список без dropping у shell. # Як root, у конкретній директорії, з env-змінною $ docker exec -it -u root -w /etc/nginx -e DEBUG=1 web sh

Комбо -it настільки поширене, що запам'ятовується як один флаг. Без нього: тільки -i = stdin працює, але немає TTY-aware tools (як vi, less); тільки -t = TTY, але не можеш печатати; жодного = shell виходить одразу.

Як обрати shell

Базовий imageДоступний shell
alpine, все на Alpine/bin/sh (busybox ash)
debian, ubuntu, node, python/bin/sh і /bin/bash
nginx:1.27-alpine тощо/bin/sh
distroless (gcr.io/distroless/...)Жодного, exec неможливий
FROM scratchЖодного, тільки твій бінар
distroless:debug варіанти/busybox/sh

Якщо сумніваєшся, спробуй sh спочатку. Якщо не працює, спробуй bash. Якщо обидва не працюють, image мабуть без shell.

Поширені флаги, які варто знати

bash
# Форсувати користувача (дефолт: USER з image, часто root) docker exec -u 1000:1000 web id # Поставити working directory docker exec -w /app web ls # Передати env-змінну тільки на цей exec docker exec -e DEBUG=1 web env | grep DEBUG # Запустити detached (фон, не чекати exit) docker exec -d web touch /tmp/done # --privileged, щоб зняти обмеження capability (для debug) docker exec --privileged -it web sh

Типові помилки

Забути -i або -t

bash
# НЕПРАВИЛЬНО: shell виходить одразу, бо немає TTY $ docker exec web sh $

Без TTY sh читає з закритого stdin і помирає. Завжди -it для інтерактиву.

Спробувати bash на Alpine

bash
$ docker exec -it nginx-alpine bash OCI runtime exec failed: ... "bash": executable file not found in $PATH

Alpine постачається з busybox sh, не bash. Бери sh.

Спробувати exec у FROM scratch image

bash
$ docker exec -it tiny-go-app sh OCI runtime exec failed: ... "sh": executable file not found

Image без shell. Для debug або перебудуй з shell-шаром, або запусти docker run --entrypoint /bin/sh -it <image> (що теж впаде, якщо у image нема shell), або зроби окремий *-debug варіант. Стандартний патерн: distroless :debug теги включають busybox; бери їх лише для debug.

Плутати docker exec з docker attach

bash
# attach: підключає до stdio PID 1. Вбивство shell може зупинити container. $ docker attach web # exec: запускає НОВИЙ процес. Вбивство його лишає container running. $ docker exec -it web sh

docker exec зазвичай те, що тобі треба. attach для кейсів, коли треба бачити stdout/stderr PID 1 live (іноді для інтерактивних REPL як PID 1).

Реальне застосування

  • Debugging проблемного сервісу: docker exec -it web sh і подивитися всередині.
  • Запуск міграцій проти DB container: docker exec -it db psql -U postgres -f /tmp/migrate.sql (або docker compose exec).
  • Тригер one-off задачі: docker exec api npm run cache:flush.
  • Перевірка config всередині running container: docker exec web cat /etc/nginx/nginx.conf.

Питання для поглиблення

Q: Чому мій exec'нутий shell помирає, коли container рестартує?


A: Рестарт container вбиває усі процеси всередині, включно з твоїм. Exec-сесії не зберігаються через рестарти.

Q: Чи можу exec у зупинений container?


A: Ні. docker exec потребує running container. Для зупиненого можеш docker start -ai <name>, щоб оживити з прикріпленим stdio, або зробити commit у новий image і запустити його з shell як entrypoint.

Q: Яка різниця між docker exec і docker compose exec?


A: docker compose exec <service> це Compose-aware обгортка, знає ім'я сервісу, project-контекст і автоматично підключається до правильного container. Під капотом викликає docker exec. Ті самі флаги працюють.

Q: Як запустити shell як інший користувач?


A: docker exec -u root -it web sh (або будь-який UID/username). Корисно, коли дефолтний USER container non-root, а тобі треба встановити/inspect щось privileged.

Q: (Senior) Коли exec, а коли просто перебудувати image?


A: Exec для діагностики, перевірити файли, env, мережу у живому стані. Rebuild для фіксів, усе, що ти змінюєш через exec, втрачається при наступному рестарті. Спокуса exec-fix-і-вдавати-що-добре це справжній anti-pattern; завжди відображай фікс у Dockerfile або config.

Приклади

Прогулятися всередині Postgres container

bash
$ docker exec -it db sh / # whoami postgres / # psql psql (16.4) Type "help" for help. postgres=# \l # database list postgres=# \q / # exit

Exec дає тобі shell; далі це просто звичайна shell-сесія.

Запуск one-off бекапу без інтерактивного shell

bash
$ docker exec db pg_dump -U postgres app > backup.sql # Backup-файл лягає на host. Container лишається running.

-it не треба, бо немає взаємодії; pg_dump пише у stdout, host shell редиректить у файл.

Compose: exec у сервіс по імені

bash
$ docker compose exec api sh /app # cat /etc/hosts # /etc/hosts авто-згенеровано Compose з усіма сусідніми service-іменами 127.0.0.1 localhost 172.18.0.3 api 172.18.0.2 db 172.18.0.4 redis

Exec у Compose це те, до чого будеш тягнутися щодня під час dev, він знає сервіси твого project без набирання container ID.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Коментарі

Ще немає коментарів