Skip to main content

Що таке Docker container (контейнер)?

Docker container це запущений екземпляр image. Механічно це процес на Linux kernel хоста, що отримує свою ізольовану в'юху системи через namespaces і ліміти ресурсів через cgroups.

Теорія

TL;DR

  • Container = процес хоста + примітиви ізоляції (Linux namespaces + cgroups). Не маленька VM, не окрема OS.
  • Один image може породити багато container. Кожен стартує з файловою системою image і додає тонкий writable-шар поверх.
  • Життєвий цикл: createdrunningpaused / exitedremoved. Перехід між станами через 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Що ізолює
pidProcess ID, container має свій PID 1
mntMount points, container має свою в'юху файлової системи
netМережеві інтерфейси, routing-таблиці, сокети
ipcShared memory, семафори, message queues
utsHostname і domain name
userUser і 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 і коректно завершуватися.

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

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

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

Коментарі

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