В прошлой главе мы разобрали, что такое контейнер и чем он отличается от виртуалки. Теперь руки на клавиатуру. И тут начинается первая боль: люди ставят Docker как попало - из старого пакета дистрибутива, через snap, через какой-то скрипт с форума - а потом удивляются, почему compose v2 не работает, BuildKit ругается, а образ не тянется. Грязная установка - это часы отладки на ровном месте.
Поэтому docker установка - это не "копипастни команду", а понимание, что именно ты ставишь и куда оно ложится. Docker Engine - это не один бинарник. Это демон dockerd, клиент docker (CLI), низкоуровневый рантайм containerd и runc под ним, плюс плагины buildx и compose. Когда ты пишешь docker run, CLI шлёт HTTP-запрос демону через Unix-сокет /var/run/docker.sock, демон через containerd создаёт контейнер, а runc уже дёргает namespaces и cgroups ядра. Запомни эту цепочку - она объяснит половину будущих ошибок.
Разберём docker install на трёх фронтах: Linux (основной, продакшен), Windows и Mac (Docker Desktop, разработка), и отдельно RU-реалии с зеркалами и RED OS. Потом запустим первый контейнер по шагам и разберём жизненный цикл руками.

Docker на Ubuntu: правильный путь через apt
Самая частая связка - docker ubuntu. И первое, что нужно сделать, - снести то, что могло прилететь из коробки. В репах Ubuntu есть пакеты docker.io, docker-doc, podman-docker и старый containerd - они конфликтуют с официальным Docker CE и обычно отстают на версию-другую.
Код: Выделить всё
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do
sudo apt-get remove -y $pkg
done
Код: Выделить всё
sudo apt-get update
sudo apt-get install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
Теперь сама установка. Ставим пять пакетов - это и есть полный Docker Engine:
Код: Выделить всё
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
- docker-ce - демон dockerd, мозг всей системы.
- docker-ce-cli - клиент docker, тот самый, в который ты печатаешь команды. Его можно ставить и без демона, чтобы рулить удалённым хостом.
- containerd.io - высокоуровневый рантайм. Это он реально держит контейнеры. Кстати, его же использует Kubernetes напрямую - и именно поэтому k8s выпилил dockershim (об этом ниже).
- docker-buildx-plugin - современная сборка на BuildKit, мультиплатформенные образы.
- docker-compose-plugin - тот самый compose v2, команда docker compose (через пробел, не дефис).
Официальный скрипт, post-install и rootless
Если лень руками - есть get.docker.com:
Код: Выделить всё
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
После установки демон запущен, но работать с ним пока можно только через sudo. Причина в том самом сокете /var/run/docker.sock - он принадлежит группе docker, а тебя в ней нет. Post-install шаг:
Код: Выделить всё
sudo usermod -aG docker $USER
newgrp docker # или просто перелогинься
docker run hello-world # теперь без sudo
Отсюда вытекает альтернатива - rootless Docker. Демон запускается от обычного пользователя, без root вообще, через user namespaces. Ставится отдельным пакетом:
Код: Выделить всё
sudo apt-get install -y docker-ce-rootless-extras
dockerd-rootless-setuptool.sh install
Docker Desktop на Windows и Mac: лицензия и WSL2
На сервере ты ставишь Engine. А вот docker windows и docker на Mac - это почти всегда Docker Desktop. Важно понимать: ядро Linux в Windows и macOS нет, поэтому Docker Desktop поднимает легковесную Linux-виртуалку, и контейнеры крутятся внутри неё. На Windows эта VM работает через WSL2 (Windows Subsystem for Linux 2 - полноценное Linux-ядро от Microsoft), на Mac - через свой гипервизор.
Установка docker desktop проста: качаешь инсталлятор с docker.com, на Windows он сам включит WSL2 и компоненты виртуализации (нужны включённые в BIOS VT-x/AMD-V и Hyper-V/WSL). После старта в трее появляется кит, а в терминале начинают работать docker и docker compose.
Про лицензию - это ловушка, на которой горят компании. Сам Docker Engine на Linux полностью бесплатен и open source (Apache 2.0). А вот Docker Desktop по состоянию на 2026 требует платной подписки (Pro/Team/Business) для коммерческого использования в организациях больше 250 сотрудников ИЛИ с годовой выручкой больше 10 млн долларов. Для личного использования, обучения, опенсорса и малого бизнеса - бесплатно. Юридический нюанс: платно именно приложение Desktop (GUI плюс та самая VM-обвязка), а не движок.
Что делать большой компании, не желающей платить за каждое место? Самый частый обход - поставить Docker Engine прямо внутрь WSL2-дистрибутива (например, в Ubuntu из Microsoft Store) ровно теми же apt-командами, что мы разбирали выше. Engine лицензией Desktop не покрывается, и ты получаешь честный бесплатный Docker на Windows. Минус - нет графики и удобных интеграций Desktop, всё руками. Для DevOps это обычно не проблема. Альтернативы Desktop, набравшие силу к 2026: Podman Desktop, Rancher Desktop, nerdctl поверх containerd - все бесплатные.
Проверка установки: docker version, info и демон
Первое, что делаешь после установки, - убеждаешься, что клиент и демон видят друг друга:
Код: Выделить всё
docker version
Код: Выделить всё
Client: Docker Engine - Community
Version: 29.4.0
API version: 1.51
Go version: go1.24.x
Server: Docker Engine - Community
Engine:
Version: 29.4.0
API version: 1.51 (minimum version 1.24)
containerd:
Version: 2.2.2
runc:
Version: 1.2.x
Глубже копнуть состояние демона:
Код: Выделить всё
docker info
Первый контейнер: docker run hello-world по шагам
Классика - docker run hello-world. Команда крошечная, а под капотом происходит вся магия:
Код: Выделить всё
docker run hello-world
Код: Выделить всё
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
c1ec31eb5944: Pull complete
Digest: sha256:...
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
- Unable to find image locally - демон поискал образ в локальном кеше (/var/lib/docker), не нашёл.
- Pulling from library/hello-world - полез в реестр. По умолчанию это Docker Hub, а library - неймспейс официальных образов. Тег latest подставился сам, потому что мы его не указали.
- Pull complete + Digest - слой скачан, digest sha256 - это криптографический отпечаток образа, гарантия, что ты получил ровно те байты. На этом digest потом строится supply chain: подпись cosign, проверка в Scout/Trivy.
- Hello from Docker! - демон создал контейнер из образа, запустил единственный процесс, тот напечатал текст и завершился. Контейнер тут же остановился - он живёт ровно столько, сколько живёт его главный процесс. Это фундаментальное правило: нет процесса - нет контейнера.
hello-world умер сразу. Возьмём что-то живущее - nginx - и пройдём весь цикл с реальными флагами.
Код: Выделить всё
docker run -d -p 8080:80 --name web nginx
- -d (detached) - запустить в фоне, вернуть управление терминалу. Без него консоль "залипнет" на логах контейнера.
- -p 8080:80 - проброс порта: порт 8080 хоста на порт 80 контейнера. Слева хост, справа контейнер - частая путаница. Теперь curl localhost:8080 достучится до nginx внутри.
- --name web - человекочитаемое имя. Без него Docker выдаст случайное вроде "nostalgic_turing", и обращаться к контейнеру придётся по ID.
Код: Выделить всё
docker ps
Код: Выделить всё
CONTAINER ID IMAGE COMMAND STATUS PORTS NAMES
a1b2c3d4e5f6 nginx "/docker-entrypoint.…" Up 12 seconds 0.0.0.0:8080->80/tcp web
Ещё пара флагов, которые понадобятся сразу:
- -it - это два флага: -i держит stdin открытым, -t выделяет псевдотерминал. Вместе дают интерактивную сессию. docker run -it ubuntu bash кинет тебя внутрь контейнера в шелл - почувствуй, что внутри своя файловая система, свои процессы, свой hostname.
- -v - проброс данных (том или bind-mount): docker run -v /home/user/site:/usr/share/nginx/html nginx подсунет nginx твою папку с сайтом. Без -v всё, что контейнер пишет, исчезнет при удалении - контейнеры эфемерны по природе. Тома - это отдельная большая тема следующих глав, тут просто запомни флаг.
Код: Выделить всё
docker stop web
docker rm web
Грабли и антипаттерны новичка
- docker compose vs docker-compose. Дефисная версия v1 на Python мертва и убрана. Если в твоих гайдах docker-compose через дефис - они устарели. Только docker compose через пробел.
- Запуск всего под sudo. Это симптом того, что ты пропустил post-install с группой. И помни: членство в группе docker = root, не раздавай налево.
- Путаница в портах и томах. И в -p, и в -v формат "хост:контейнер". Перепутал стороны - сервис не отвечает или монтируется не туда. 0.0.0.0 в выводе ps значит "доступно извне" - на проде иногда хочешь 127.0.0.1:8080:80, чтобы наружу не торчало.
- Накопление мусора. Остановленные контейнеры, висячие образы и тома жрут диск в /var/lib/docker, пока он не кончится в самый неподходящий момент. docker system df покажет аппетит, docker system prune подчистит. Не запускай prune вслепую на проде - снесёт нужное.
- RU-реалии с Docker Hub. Прямой pull с Docker Hub из России бывает нестабилен или режется лимитами. Решение - зеркала и российские реестры: Yandex Container Registry, VK Cloud, корпоративные Nexus/Harbor. Зеркало прописывается в /etc/docker/daemon.json в поле registry-mirrors, после чего sudo systemctl restart docker. Это не теория, а ежедневная боль локального DevOps.
- Поставь Docker по официальному репозиторию (или get.docker.com на тестовой машине). Сделай post-install с группой docker и проверь docker run hello-world без sudo.
- Выполни docker version и найди в выводе версии Server, containerd и runc. Объясни себе, почему блоков именно два.
- Запусти docker run -d -p 8080:80 --name web nginx, открой http://localhost:8080, убедись, что отвечает. Глянь docker ps и прочитай поле PORTS.
- Останови docker stop web, проверь docker ps (пусто) и docker ps -a (контейнер есть, но Exited). Удали docker rm web.
- Зайди внутрь чистого контейнера: docker run -it --rm ubuntu bash. Внутри выполни hostname, ps aux, ls / - сравни с хостом. Выйди через exit и убедись, что контейнер удалился сам (--rm).
- Из каких компонентов состоит Docker Engine и как docker (CLI) общается с dockerd?
- Чем docker stop отличается от docker rm и docker rm -f, и что происходит с сигналами процессу?
- Почему добавление в группу docker эквивалентно выдаче прав root и когда стоит выбрать rootless?
- В чём разница лицензий Docker Engine и Docker Desktop и как легально получить бесплатный Docker на Windows для большой компании?
Ты поставил Docker осознанно: знаешь, что demon, CLI, containerd и runc - это разные слои одной цепочки, понимаешь post-install и зачем нужен rootless, не попадёшь на лицензию Docker Desktop и не растеряешься на RU-реалиях с зеркалами. Ты прогнал первый контейнер от pull до exit и прошёл полный жизненный цикл run-ps-stop-rm с флагами -d -it -p -v --name. Это фундамент - дальше строим образы и разбираем слои.