Docker
Docker за 15 минут: образы, контейнеры, Dockerfile, сборка, тома, сети и Docker Compose — всё в одном комментированном шпаргалочном туре.
Docker упаковывает приложение вместе с его окружением в переносимый контейнер: «у меня работает» становится «работает везде». Ниже — весь инструмент на одной странице: концепции, команды, Dockerfile, Compose и рабочий процесс. Минимум прозы — всё объяснено прямо в комментариях кода.
1. Что такое Docker
Три ключевых понятия: образ (шаблон), контейнер (запущенный экземпляр) и виртуалка (для сравнения).
# ОБРАЗ (image) — неизменяемый «снимок»: файловая система + метаданные.
# Это как класс в ООП или установочный ISO. Сам по себе не выполняется.
# Состоит из слоёв (layers), которые переиспользуются между образами.
# КОНТЕЙНЕР (container) — запущенный экземпляр образа.
# Это как объект класса. Из одного образа можно поднять сто контейнеров.
# Изолированный процесс ОС со своей ФС, сетью и набором процессов.
# ВИРТУАЛКА (VM) vs КОНТЕЙНЕР:
# VM = своя гостевая ОС + гипервизор -> гигабайты, секунды-минуты на старт.
# Контейнер = общее ядро хоста, изоляция через namespaces/cgroups
# -> мегабайты, миллисекунды на старт.
# Итог: контейнеры легче и быстрее, но делят ядро с хостом.
docker --version # версия клиента
docker info # состояние демона: образы, контейнеры, драйверы
docker run hello-world # классический тест: тянет образ и печатает привет
2. Основные команды
docker pull nginx # скачать образ из реестра (по умолчанию Docker Hub)
docker pull nginx:1.27 # конкретный тег (версия); без тега берётся :latest
docker images # список локальных образов
docker run nginx # создать контейнер из образа и запустить
docker ps # список ЗАПУЩЕННЫХ контейнеров
docker ps -a # ВСЕ контейнеры, включая остановленные
docker stop <id|name> # мягко остановить (SIGTERM, затем SIGKILL)
docker start <id|name> # запустить ранее остановленный контейнер
docker restart <id|name> # перезапустить
docker rm <id|name> # удалить ОСТАНОВЛЕННЫЙ контейнер
docker rm -f <id|name> # удалить принудительно (сначала остановит)
docker rmi nginx # удалить образ
# Можно указывать только начало id, если оно уникально: docker stop a1b2
docker system prune # снести всё неиспользуемое (контейнеры, сети, кэш)
3. Флаги запуска
Главные флаги docker run, которые нужны почти всегда.
docker run -d nginx # -d (detached): в фоне, вернёт id контейнера
docker run -p 8080:80 nginx # -p ХОСТ:КОНТЕЙНЕР: проброс порта
# браузер -> localhost:8080 -> порт 80 внутри
docker run -v /data:/app/data nginx # -v ХОСТ:КОНТЕЙНЕР: монтирование каталога
docker run -e ENV=prod nginx # -e: переменная окружения внутри контейнера
docker run -e DB_HOST=db -e DB_PORT=5432 nginx # можно несколько -e
docker run --name web nginx # --name: человекочитаемое имя вместо id
docker run --rm alpine echo hi # --rm: удалить контейнер сразу после выхода
# удобно для одноразовых задач
docker run -it ubuntu bash # -i интерактивный + -t псевдо-TTY:
# получить рабочую shell внутри контейнера
# Боевой пример: фоновый веб-сервер с именем и пробросом порта
docker run -d --name web -p 8080:80 nginx
4. Работа с контейнером
docker exec -it web bash # зайти в shell УЖЕ запущенного контейнера
docker exec web ls /etc/nginx # выполнить одну команду без входа внутрь
docker logs web # вывести логи (stdout/stderr процесса)
docker logs -f web # -f: следить за логами в реальном времени (tail -f)
docker logs --tail 100 web # последние 100 строк
docker inspect web # полные метаданные в JSON: сеть, тома, env, статус
docker inspect -f '{{.State.Status}}' web # вытащить одно поле через шаблон
docker stats # live-метрики CPU/RAM/сети по контейнерам
docker top web # процессы внутри контейнера
docker cp web:/etc/nginx/nginx.conf ./ # скопировать файл из контейнера на хост
5. Dockerfile
Dockerfile — рецепт сборки образа. Каждая инструкция создаёт слой.
# FROM — базовый образ, с которого начинаем. Всегда первая инструкция.
# Тег slim/alpine = меньший размет итогового образа.
FROM python:3.12-slim
# WORKDIR — рабочий каталог внутри образа. Создаётся, если нет.
# Все последующие RUN/COPY/CMD выполняются относительно него.
WORKDIR /app
# ENV — переменная окружения, доступна при сборке и в рантайме.
ENV PYTHONUNBUFFERED=1
# COPY — копирует файлы с хоста (контекст сборки) в образ.
# Сначала только зависимости -> кэш не сбрасывается при правке кода.
COPY requirements.txt .
# RUN — выполняет команду ВО ВРЕМЯ СБОРКИ и фиксирует результат в слой.
# --no-cache-dir уменьшает размер образа.
RUN pip install --no-cache-dir -r requirements.txt
# Теперь копируем остальной код приложения.
COPY . .
# EXPOSE — документирует порт, который слушает приложение (не публикует его!).
# Публикация всё равно через -p при docker run.
EXPOSE 8000
# CMD — команда по умолчанию при запуске контейнера. Легко переопределить:
# docker run myimage <другая команда>. Должна быть одна (последняя побеждает).
CMD ["python", "app.py"]
# ENTRYPOINT vs CMD:
# ENTRYPOINT задаёт неизменяемую «голову» команды, CMD — аргументы по умолчанию.
# ENTRYPOINT ["python"] + CMD ["app.py"] -> запустится python app.py,
# а docker run myimage other.py -> python other.py.
6. Сборка образов
Сборка читает Dockerfile и контекст, кэширует слои между запусками.
docker build -t myapp . # -t имя : собрать из ./Dockerfile, точка = контекст
docker build -t myapp:1.0 . # с тегом версии
docker build -t myapp:1.0 -t myapp:latest . # сразу несколько тегов
docker build -f Dockerfile.prod -t myapp . # -f : нестандартное имя Dockerfile
# СЛОИ И КЭШ:
# Каждая инструкция = слой. При повторной сборке Docker переиспользует слои,
# пока инструкция и её входные файлы не изменились.
# Поэтому COPY requirements.txt + install идут ДО COPY всего кода:
# правка кода не сбрасывает кэш установки зависимостей -> сборка быстрее.
docker build --no-cache -t myapp . # принудительно пересобрать без кэша
docker tag myapp:1.0 user/myapp:1.0 # переименовать/подготовить к публикации
docker push user/myapp:1.0 # отправить образ в реестр (нужен docker login)
docker history myapp # посмотреть слои образа и их размер
7. Тома
Данные внутри контейнера исчезают при rm. Тома сохраняют их снаружи.
# NAMED VOLUME — управляется Docker, лучший выбор для БД и постоянных данных.
docker volume create pgdata
docker run -d --name db -v pgdata:/var/lib/postgresql/data postgres
docker volume ls # список томов
docker volume inspect pgdata # где физически лежит, кто использует
docker volume rm pgdata # удалить том (контейнеры должны быть удалены)
# BIND MOUNT — пробрасывает конкретный каталог хоста (удобно для разработки).
# Правишь код на хосте -> сразу виден в контейнере.
docker run -v $(pwd):/app -w /app node npm run dev
# tmpfs — данные только в памяти, исчезают со стопом (для секретов/кэша).
docker run --tmpfs /tmp alpine
# Разница: named volume переносим и управляем Docker; bind mount привязан
# к пути конкретной машины.
8. Сети
Сети дают контейнерам общаться друг с другом по имени, а не по IP.
docker network ls # список сетей (bridge, host, none — встроенные)
docker network create appnet # своя bridge-сеть
# Контейнеры в одной пользовательской сети видят друг друга по ИМЕНИ контейнера.
docker run -d --name db --network appnet postgres
docker run -d --name api --network appnet -e DB_HOST=db myapi
# внутри api хост "db" автоматически резолвится в IP контейнера db.
docker network inspect appnet # кто подключён, подсети, шлюз
docker network connect appnet web # подключить существующий контейнер к сети
docker network rm appnet # удалить сеть
# host-сеть: контейнер делит сетевой стек с хостом (без изоляции портов).
docker run --network host nginx
9. Docker Compose
Compose описывает несколько сервисов одним YAML-файлом и поднимает их вместе.
# docker-compose.yml — декларативное описание всего стека приложения.
services:
web: # имя сервиса = имя хоста в общей сети
build: . # собрать из локального Dockerfile
ports:
- "8000:8000" # проброс порта ХОСТ:КОНТЕЙНЕР
environment:
- DB_HOST=db # db ниже резолвится по имени сервиса
depends_on:
- db # стартовать после db (только порядок, не готовность)
volumes:
- .:/app # bind mount кода для разработки
db:
image: postgres:16 # готовый образ из реестра, без сборки
environment:
- POSTGRES_PASSWORD=secret
volumes:
- pgdata:/var/lib/postgresql/data # named volume для данных БД
volumes:
pgdata: # объявление named volume для сервисов выше
docker compose up # поднять весь стек (логи в консоль)
docker compose up -d # в фоне
docker compose up --build # пересобрать образы перед запуском
docker compose ps # статус сервисов
docker compose logs -f web # логи конкретного сервиса в реальном времени
docker compose exec web bash # shell внутри сервиса
docker compose down # остановить и удалить контейнеры и сеть
docker compose down -v # ... и удалить тома (осторожно: сотрёт данные БД)
10. .dockerignore и best practices
Файл .dockerignore исключает мусор из контекста сборки — образ меньше, сборка быстрее.
# .dockerignore (синтаксис как у .gitignore):
.git
node_modules
__pycache__
*.log
.env
Dockerfile
.dockerignore
# BEST PRACTICES:
# 1) Лёгкий базовый образ: alpine/slim вместо полного дистрибутива.
# 2) Кэш дружелюбно: сначала COPY зависимостей + install, потом COPY кода.
# 3) Один процесс на контейнер (веб отдельно, БД отдельно).
# 4) Не клади секреты в образ — пробрасывай через -e или Compose.
# 5) Фиксируй версии тегов (postgres:16, а не latest) для воспроизводимости.
# 6) Multi-stage сборка: собирать в одном образе, копировать артефакт в финальный.
FROM golang AS build
# ... сборка бинарника ...
FROM alpine
# COPY --from=build /app/bin /app -> итоговый образ без компилятора, в разы меньше.
11. Типичный рабочий процесс
От пустого каталога до запущенного контейнера — за шесть шагов.
# 1. Написать Dockerfile рядом с кодом приложения.
# 2. Добавить .dockerignore, чтобы не тащить лишнее в контекст.
# 3. Собрать образ с тегом.
docker build -t myapp:1.0 .
# 4. Запустить контейнер: в фоне, с именем, пробросом порта и переменными.
docker run -d --name myapp -p 8080:8000 -e ENV=prod myapp:1.0
# 5. Проверить, что живой.
docker ps # контейнер в списке запущенных?
docker logs -f myapp # стартовал без ошибок?
# открыть в браузере http://localhost:8080
# 6. Обновить версию: правка кода -> пересборка -> пересоздание контейнера.
docker build -t myapp:1.1 .
docker rm -f myapp
docker run -d --name myapp -p 8080:8000 -e ENV=prod myapp:1.1
# Когда сервисов несколько (web + db + cache) — то же самое описывается
# одним docker-compose.yml и поднимается командой: docker compose up -d.