Як потрапити всередину запущеного контейнера?
Отримання 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.
Швидкий приклад
# Найпоширеніше: 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.
Поширені флаги, які варто знати
# Форсувати користувача (дефолт: 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
# НЕПРАВИЛЬНО: shell виходить одразу, бо немає TTY
$ docker exec web sh
$ Без TTY sh читає з закритого stdin і помирає. Завжди -it для інтерактиву.
Спробувати bash на Alpine
$ docker exec -it nginx-alpine bash
OCI runtime exec failed: ... "bash": executable file not found in $PATHAlpine постачається з busybox sh, не bash. Бери sh.
Спробувати exec у FROM scratch image
$ docker exec -it tiny-go-app sh
OCI runtime exec failed: ... "sh": executable file not foundImage без shell. Для debug або перебудуй з shell-шаром, або запусти docker run --entrypoint /bin/sh -it <image> (що теж впаде, якщо у image нема shell), або зроби окремий *-debug варіант. Стандартний патерн: distroless :debug теги включають busybox; бери їх лише для debug.
Плутати docker exec з docker attach
# attach: підключає до stdio PID 1. Вбивство shell може зупинити container.
$ docker attach web
# exec: запускає НОВИЙ процес. Вбивство його лишає container running.
$ docker exec -it web shdocker 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
$ docker exec -it db sh
/ # whoami
postgres
/ # psql
psql (16.4)
Type "help" for help.
postgres=# \l
# database list
postgres=# \q
/ # exitExec дає тобі shell; далі це просто звичайна shell-сесія.
Запуск one-off бекапу без інтерактивного shell
$ docker exec db pg_dump -U postgres app > backup.sql
# Backup-файл лягає на host. Container лишається running.-it не треба, бо немає взаємодії; pg_dump пише у stdout, host shell редиректить у файл.
Compose: exec у сервіс по імені
$ 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 redisExec у Compose це те, до чого будеш тягнутися щодня під час dev, він знає сервіси твого project без набирання container ID.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.
Коментарі
Ще немає коментарів