Docker: зачем он нужен и почему это не «магия контейнеров»
Docker: зачем он нужен и почему это не «магия контейнеров»
Docker нужен, чтобы перестать играть в игру «у меня работает». Контейнер - это упакованное окружение:
- твоя версия Node/Go
- зависимости
- настройки
- команда запуска
И всё это воспроизводимо на любом компьютере и сервере.
Проблема: «у меня работает»
Знакомая история: ты написал приложение, запустил локально - всё отлично. Коллега клонирует репу - не работает. На сервере - другая ошибка. Причины всегда одни и те же:
- Разные версии - у тебя Go 1.22, на сервере 1.19
- Разные зависимости ОС - libpq не установлена, openssl другой версии
- Разные переменные окружения -
.envзабыли, порт занят - Разные ОС - macOS vs Ubuntu vs Windows, пути к файлам, line endings
Docker решает это: образ содержит всё нужное, и запускается одинаково везде, где есть Docker.
Контейнер vs виртуальная машина
<ComparisonTable title="Контейнер vs VM" headers={["", "Виртуальная машина", "Контейнер"]} rows={[ ["Ядро ОС", "Своё (гостевая ОС)", "Общее с хостом"], ["Размер", "Гигабайты (Ubuntu ~2 ГБ)", "Мегабайты (Alpine ~5 МБ)"], ["Запуск", "Минуты", "Секунды"], ["Изоляция", "Полная (гипервизор)", "Процессная (namespaces, cgroups)"], ["Overhead", "Высокий (RAM под гостевую ОС)", "Минимальный"], ["Пример", "VirtualBox, VMware", "Docker, Podman"] ]} />
VM - это квартира с отдельным фундаментом. Контейнер - комната в общежитии: своя дверь, свои вещи, но стены и крыша общие. Контейнеры - это процессы Linux, изолированные через namespaces.
Архитектура Docker
Docker состоит из трёх частей:
- Docker CLI - команды, которые ты вводишь (
docker run,docker build) - Docker Daemon (
dockerd) - фоновый процесс, управляет контейнерами - Container Runtime (
containerd) - собственно запускает и изолирует процессы
Образ vs контейнер
Образ (image) - это рецепт и ингредиенты. Контейнер - это запущенное блюдо.
# Скачиваем образ (рецепт)
docker pull nginx:alpine
# Запускаем контейнер (блюдо)
docker run -d --name web nginx:alpine
# Из одного образа можно запустить сколько угодно контейнеров
docker run -d --name web2 nginx:alpine
docker run -d --name web3 nginx:alpine
Образ - read-only, его можно версионировать и хранить в registry (Docker Hub, GitLab Registry). Контейнер - это образ + тонкий writable-слой для данных процесса.
Docker Registry
Registry - хранилище образов. Как GitHub для кода, только для Docker-образов.
- Docker Hub - публичный по умолчанию (
nginx,postgres,golang) - GitLab Container Registry - приватный, встроен в GitLab
- GitHub Container Registry (ghcr.io) - приватный, привязан к GitHub
# Скачать публичный образ
docker pull postgres:16-alpine
# Залить свой образ в registry
docker tag myapp:latest registry.gitlab.com/user/myapp:v1.0
docker push registry.gitlab.com/user/myapp:v1.0
Где Docker поможет тебе прямо сейчас
| Задача | Без Docker | С Docker |
|---|---|---|
| Поднять PostgreSQL | Установка, pg_hba.conf, перезапуск | docker run -d postgres:16-alpine |
| Запустить Redis | Скачать, скомпилировать, настроить | docker run -d redis:7-alpine |
| Собрать Go backend | Установить Go, настроить GOPATH | docker build -t backend . |
| Показать проект коллеге | «Сначала установи Go 1.22, потом...» | docker compose up |
Мини-задание
- Установи Docker Desktop (macOS/Windows) или Docker Engine (Linux)
- Проверь:
docker --versionиdocker compose version - Запусти
docker run --rm hello-world- увидишь приветствие от Docker - Запусти
docker run --rm -it alpine sh- окажешься внутри контейнера Alpine - Внутри контейнера:
cat /etc/os-release,whoami,exit
Итог
- Docker упаковывает приложение + зависимости + ОС в один образ. «Работает у меня» становится «работает везде».
- Контейнер - не VM: он разделяет ядро хоста, запускается за секунды, весит мегабайты.
- Архитектура: CLI → Daemon → Container Runtime. Образы хранятся в Registry.
- OCI-стандарт: навыки переносятся между Docker, Podman, containerd.
Типичная ошибка
Начинающие часто путают образ и контейнер. Собрал образ - он есть в docker images. Запустил контейнер - он в docker ps. Удалил контейнер (docker rm) - образ всё ещё на месте. Удалил образ (docker rmi) - а контейнеры от него могут ещё работать (пока не остановишь).
Вторая ошибка - думать, что Docker заменяет CI/CD, мониторинг и тесты. Docker - это инструмент упаковки, а не волшебная кнопка «в прод».
Мини-практика (10-15 минут)
Запусти три разных контейнера и исследуй их изнутри:
# 1. Alpine - минимальная Linux
docker run --rm -it alpine sh
# Внутри: ls /, cat /etc/os-release, exit
# 2. Ubuntu - знакомый Linux
docker run --rm -it ubuntu bash
# Внутри: apt list --installed | head, exit
# 3. Nginx - рабочий веб-сервер
docker run --rm -d -p 8080:80 --name test-nginx nginx:alpine
# Открой http://localhost:8080 в браузере
docker stop test-nginx
Сравни размеры: docker images | grep -E "alpine|ubuntu|nginx". Заметь разницу - Alpine ~5 МБ, Ubuntu ~70 МБ.