Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Що таке Docker container (контейнер)?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Docker container** це запущений екземпляр image. Це процес на kernel хоста, ізольований від інших процесів через Linux namespaces і cgroups, зі своєю в'юхою файлової системи, мережі і лімітами ресурсів. ```bash $ docker run -d --name web -p 80:80 nginx:1.27 # image (шаблон) → container (запущений процес) $ docker ps CONTAINER ID IMAGE STATUS PORTS NAMES a3f9d2b8c1e4 nginx:1.27 Up 2 seconds 0.0.0.0:80->80/tcp web ``` **Головне:** image це креслення, container це запущена штука. Container замінний, стан у файловій системі image втрачається при рестарті, якщо не змонтовано як volume.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Docker container** це запущений екземпляр image. Механічно це процес на Linux kernel хоста, що отримує свою ізольовану в'юху системи через namespaces і ліміти ресурсів через cgroups. ## Теорія ### TL;DR - Container = процес хоста + примітиви ізоляції (Linux namespaces + cgroups). Не маленька VM, не окрема OS. - Один image може породити багато container. Кожен стартує з файловою системою image і додає тонкий writable-шар поверх. - **Життєвий цикл:** `created` → `running` → `paused / exited` → `removed`. Перехід між станами через `docker run`, `docker stop`, `docker start`, `docker rm`. - Container **ефемерні**. Все, що ти пишеш у writable-шар, зникає при `docker rm`. Використовуй volume для стану, що має пережити. - Дефолтна ізоляція: PID, mount, network, IPC, UTS, user і cgroup namespaces. Container думає, що має свій PID 1, свій корінь файлової системи, свої мережеві інтерфейси. ### Швидкий приклад ```bash # Стартуємо container у фоні $ docker run -d --name web -p 8080:80 nginx:1.27-alpine a3f9d2b8c1e4f3a2... # Він запущений $ docker ps CONTAINER ID IMAGE STATUS PORTS NAMES a3f9d2b8c1e4 nginx:1.27-alpine Up 3 seconds 0.0.0.0:8080->80/tcp web # Зсередини він бачить свій світ $ docker exec web ps aux USER PID COMMAND root 1 nginx: master process nginx -g daemon off; nginx 29 nginx: worker process # PID 1 це nginx, container має своє дерево процесів. ``` Цей container це процес на твоєму хості (можеш знайти через `ps aux | grep nginx` ззовні), але всередині свого PID namespace він думає, що він процес 1. ### Стани життєвого циклу Container проходить через невеликий набір станів: ``` docker create docker start | | v v +--------+ docker run +---------+ docker stop +---------+ | created| -------------> | running | --------------> | exited | +--------+ +---------+ docker kill +---------+ ^ | | | | docker pause | docker rm | v v +--------+ +---------+ | paused | | removed | +--------+ +---------+ ``` - `docker run` це shorthand для `docker create` + `docker start`. - `docker stop` шле SIGTERM, чекає 10 секунд, тоді SIGKILL. `docker kill` йде одразу до SIGKILL. - Exited container ще існує, його можна рестартувати. Зникає тільки після `docker rm`. ### Як насправді працює ізоляція Container це просто процес. Linux kernel робить його схожим на власну машину через шість namespaces: | Namespace | Що ізолює | |---|---| | `pid` | Process ID, container має свій PID 1 | | `mnt` | Mount points, container має свою в'юху файлової системи | | `net` | Мережеві інтерфейси, routing-таблиці, сокети | | `ipc` | Shared memory, семафори, message queues | | `uts` | Hostname і domain name | | `user` | User і group ID (UID/GID remapping) | | `cgroup` | В'юха cgroup root | Поверх namespaces **cgroups** обліковують і лімітують CPU, пам'ять, block I/O і пропускну здатність мережі. Коли ти робиш `docker run --memory=512m`, daemon ставить memory cgroup, і kernel його enforce'ить. Перевищиш ліміт, kernel OOM-кільне твій процес всередині container. Все це enforce'ить один kernel: твій. Жодного guest kernel всередині container немає. ### Ключові команди ```bash docker run # create + start в одному кроці docker ps # список запущених container docker ps -a # список всіх (включно з exited) docker stop <id> # graceful stop (SIGTERM, потім SIGKILL через 10с) docker kill <id> # негайна зупинка (SIGKILL) docker rm <id> # видалити container (має бути stopped, або -f) docker exec <id> <cmd> # запустити команду всередині запущеного docker logs <id> # подивитися stdout/stderr container docker inspect <id> # повний JSON-dump стану container ``` ### Типові помилки **Сприймати container як довгоживучий сервер** ```bash # НЕПРАВИЛЬНО: заходити і ставити/налаштовувати щось $ docker exec -it web apt-get install vim # Зникне, як тільки container рестартує. # ПРАВИЛЬНО: запекти у image FROM nginx:1.27-alpine RUN apk add --no-cache vim ``` Container задумано як замінний. Конфігуруй через Dockerfile або environment-змінні, не редагуванням запущеного container. **Втратити дані, бо стан не у volume** ```bash # НЕПРАВИЛЬНО: дані помирають разом з container $ docker run --name pg postgres:16 $ docker rm -f pg # Усі твої таблиці зникли. # ПРАВИЛЬНО: персистимо стан у volume $ docker run -d --name pg \ -v pgdata:/var/lib/postgresql/data \ postgres:16 ``` Writable-шар це для тимчасового scratch-стану, не для реальних даних. Volume переживає видалення container. **Запускати кілька процесів у одному container** Конвенція: **один основний процес на container**. Запхай базу, веб-сервер і job-runner у один container, і ти втратиш можливість масштабувати їх окремо, логувати окремо, перезапускати ізольовано. Використовуй `docker compose` для оркестрації кількох single-process container. **Забути, що PID 1 має особливу семантику** У container PID 1 це процес, що ти вказав як `CMD` / `ENTRYPOINT`. PID 1 за замовчуванням ігнорує більшість сигналів і відповідальний за reaping zombie-дітей. Якщо твій застосунок форкає підпроцеси і не reap'ить їх, накопичуються zombie. Інструменти типу `tini` або `dumb-init` обробляють це за тебе (`docker run --init` додає `tini` автоматично). ### Реальне застосування - **CI/CD джоби:** GitHub Actions runners, GitLab CI, Jenkins agents, кожен крок пайплайну часто крутиться у власному короткоживучому container. Container стартує, виконує крок, виходить і видаляється. - **Локальні dev-стеки:** Postgres, Redis, MinIO, RabbitMQ, дев піднімає півдюжини container через `docker compose up` і знімає всі через `docker compose down`. - **Прод microservices:** кожен сервіс крутиться як один або кілька container за load balancer. Kubernetes планує їх по хостах; container це найменша одиниця деплою. - **Functions-as-a-service:** AWS Lambda container (на Firecracker microVM під капотом), Google Cloud Run, Azure Container Apps, всі дають девам container-інтерфейс. ### Питання для поглиблення **Q:** Що відбувається при `docker stop`? **A:** Daemon шле SIGTERM на PID 1 всередині container. PID 1 має його обробити і коректно завершитися. Після 10-секундного grace-періоду (налаштовується через `--time`) daemon шле SIGKILL. Застосунки, що ігнорують SIGTERM, отримають жорсткий kill. Для stateless-сервісів це нормально, для баз катастрофа. **Q:** Чи можуть два container з того самого image заважати один одному? **A:** За замовчуванням ні. Кожен отримує свої namespaces, свій writable-шар, свою мережу. Вони поділяють read-only шари image на диску і у пам'яті, але не бачать процесів чи файлів один одного. Якщо хочеш явно щось поділити, використай named volume або Docker network. **Q:** Яка різниця між `docker stop` і `docker rm`? **A:** Stop переводить running container у exited; container ще існує і його можна рестартувати через `docker start`. Remove видаляє container повністю: writable-шар, конфіг, ім'я. Типове CI-чищення це `docker rm -f $(docker ps -aq)`, щоб знести все. **Q:** Чому файли мають UID 1000 на хості, навіть якщо container крутився під іншим користувачем? **A:** Без user namespace remapping UID всередині container мапляться напряму на UID на хості. Якщо `node`-користувач у container має UID 1000, файли, що він пише у bind mount, виглядають належними host UID 1000, що може або не може бути реальним користувачем. Використовуй `--user $(id -u):$(id -g)` для вирівнювання, або налаштуй user namespaces для повного remapping. **Q:** (Senior) Чим Docker container відрізняється від Kubernetes pod? **A:** Pod це обгортка навколо одного або кількох container, що поділяють network і storage namespaces. У pod один IP; container всередині поділяють `localhost`. Використовуй pod, коли потрібен щільно зв'язаний sidecar-патерн (застосунок + log shipper). У 99 відсотках кейсів один container на pod це норма. У Docker немає вбудованого pod-концепту; є `docker compose` для multi-container застосунків, але без shared-network групування як у pod. ## Приклади ### Інспекція запущеного container ```bash $ docker run -d --name api node:22-alpine sleep 3600 $ docker inspect api --format '{{.State.Status}} (PID {{.State.Pid}})' running (PID 84231) # Цей PID існує на твоєму хості $ ps -p 84231 -o pid,user,comm PID USER COMMAND 84231 root sleep # Всередині container той самий процес це PID 1 $ docker exec api ps aux USER PID COMMAND root 1 sleep 3600 ``` Один процес, дві в'юхи. Хост бачить PID 84231; всередині свого PID namespace container бачить PID 1. Цей namespace-remapping це серце container-ізоляції. ### Життєвий цикл container в одній сесії ```bash $ docker run -d --name worker alpine sleep 1000 $ docker ps --filter name=worker STATUS NAMES Up 5 seconds worker $ docker stop worker $ docker ps -a --filter name=worker STATUS NAMES Exited (137) 2 seconds ago worker # 137 = 128 + SIGKILL(9). Container ігнорив SIGTERM до timeout. $ docker start worker # знов running $ docker rm -f worker # зник ``` Зверни увагу на exit code 137. `sleep` не обробив SIGTERM, тому після grace-періоду daemon його kill'нув. Реальні застосунки мають перехоплювати SIGTERM і коректно завершуватися.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.