Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Як потрапити всередину запущеного контейнера?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**`docker exec -it <container> <shell>`** запускає новий shell всередині запущеного container. ```bash docker exec -it web sh # більшість image (Alpine, distroless-debug тощо) docker exec -it web bash # якщо bash встановлено (Ubuntu, Debian) odker exec -it -u root web sh # форсувати root всередині docker exec -it -e FOO=bar -w /app web sh # кастомні env + working dir ``` **Головне:** exec запускає НОВИЙ процес; не приєднується до PID 1. Shell крутиться поруч із твоїм застосунком. У distroless і `FROM scratch` image shell немає, exec у них з бінарем, що тобі реально потрібен, або `--entrypoint sh` на `docker run` для дебагу.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Отримання 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.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.