LEARN X · ЗА 15 МИН

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.
Поддержать проект