Що таке Docker container (контейнер)?
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, свій корінь файлової системи, свої мережеві інтерфейси.
Швидкий приклад
# Стартуємо 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 немає.
Ключові команди
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 як довгоживучий сервер
# НЕПРАВИЛЬНО: заходити і ставити/налаштовувати щось
$ docker exec -it web apt-get install vim
# Зникне, як тільки container рестартує.
# ПРАВИЛЬНО: запекти у image
FROM nginx:1.27-alpine
RUN apk add --no-cache vimContainer задумано як замінний. Конфігуруй через Dockerfile або environment-змінні, не редагуванням запущеного container.
Втратити дані, бо стан не у volume
# НЕПРАВИЛЬНО: дані помирають разом з container
$ docker run --name pg postgres:16
$ docker rm -f pg
# Усі твої таблиці зникли.
# ПРАВИЛЬНО: персистимо стан у volume
$ docker run -d --name pg \
-v pgdata:/var/lib/postgresql/data \
postgres:16Writable-шар це для тимчасового 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
$ 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 в одній сесії
$ 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 і коректно завершуватися.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.
Коментарі
Ще немає коментарів