
Многие слышали про Docker, но, запустив его в первый раз, не поняли, что происходит. Помню, как впервые прочитал слово «контейнер» и представил коробку 📦. На самом деле идея проще: Docker — это способ упаковать программу со всеми зависимостями так, чтобы она везде работала одинаково.
Разберёмся по шагам, без воды, сразу на практике.
Образ (image) и контейнер (container)
Представьте, что образ — это формочка для пирогов, а контейнер — готовый пирог.
Образ — это описание: какая система внутри, какие программы, как их настроить. Пишется в файле Dockerfile.
Контейнер — работающая программа, запущенная из образа. Из одного образа можно запустить много контейнеров, как из одной формочки испечь много пирогов.
Пример Dockerfile для простого Python-приложения:
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
CMD ["python", "app.py"]
Что здесь:
— FROM python:3.12-slim — берём готовый образ, в котором уже установлен Python 3.12
— WORKDIR /app — рабочая папка внутри контейнера
— COPY — копируем файлы с компьютера в образ
— RUN — выполняем команду при сборке (здесь — установка зависимостей)
— CMD — команда, которая запускается при старте контейнера
Зависимости копируются раньше кода не случайно: если вы поменяли только app.py, Docker возьмёт установленные пакеты из кэша, и пересборка займёт секунды.
Собираем образ:
docker build -t my-app:1.0 .
Здесь -t my-app:1.0 — имя образа и версия. Точка . означает, что Dockerfile лежит в текущей папке.
Запускаем контейнер из образа:
docker run my-app:1.0
Если приложение слушает порт (например, веб-сервер на порту 5000), запускаем так:
docker run -p 8080:5000 my-app:1.0
-p 8080:5000 значит: «порт 8080 на моём компьютере перенаправляй на порт 5000 в контейнере». Теперь приложение открывается по адресу http://localhost:8080.
Volume: хранение данных
Проблема: если удалить контейнер, пропадут все данные, которые он записал внутри себя.
Volume — это хранилище, которое живёт отдельно от контейнера. Контейнер можно удалить и создать заново, а данные останутся.
Пример: запускаем PostgreSQL с volume для данных:
docker run -d --name db \
-e POSTGRES_PASSWORD=сложный_пароль \
-v my_data:/var/lib/postgresql/data \
postgres:16
-v my_data:/var/lib/postgresql/data означает: «создай volume my_data и подключи его к папке /var/lib/postgresql/data в контейнере». Без POSTGRES_PASSWORD официальный образ PostgreSQL не запустится — это частая ошибка новичков.
Теперь, даже если контейнер удалить, данные в my_data сохранятся.
Посмотреть список volume:
docker volume ls
Удалить volume:
docker volume rm my_data
⚠️ Внимание: удаление volume стирает все данные в нём. Проверяйте дважды.
Docker Compose: несколько сервисов вместе
Обычно приложение состоит из нескольких частей: веб-сервер, база данных, кэш. Запускать каждый контейнер отдельной командой неудобно.
Docker Compose — это файл, в котором описаны все сервисы и связи между ними.
Файл compose.yaml (старое имя docker-compose.yml тоже работает):
services:
web:
image: my-app:1.0
ports:
- "8080:5000"
environment:
DATABASE_URL: postgresql://user:password@db:5432/mydb
depends_on:
- db
db:
image: postgres:16
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: mydb
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:
Что здесь:
— services — список сервисов (web и db)
— image — какой образ использовать
— ports — какие порты открыть наружу (у базы их нет, и это правильно: к ней ходит только web)
— environment — переменные окружения (логин, пароль, адреса). В реальном проекте пароли выносят в файл .env, который не попадает в Git
— depends_on — compose сначала запустит db, потом web. Но он не ждёт, пока база будет готова принимать подключения, поэтому приложение должно уметь переподключаться
— volumes — какие volume использовать
Сеть отдельно описывать не нужно: сервисы одного compose-файла сами видят друг друга по имени. Поэтому web обращается к базе по адресу db:5432.
Запускаем всё:
docker compose up -d
-d — запуск в фоне.
Посмотреть логи:
docker compose logs -f web
-f — показывать новые строки по мере появления.
Остановить всё:
docker compose down
Если нужно удалить ещё и volume (с данными!):
docker compose down -v
В старых инструкциях встречается команда docker-compose через дефис — это прежняя версия. Сейчас compose встроен в Docker и пишется через пробел.
Зачем это нужно
Docker пригодится в трёх случаях:
-
На своём сервере: упаковали приложение в образ — и оно работает одинаково на любой машине. Обновление простое: собрали новый образ, выполнили
docker compose up -d, и compose пересоздаст изменившиеся контейнеры. -
В облаке и Kubernetes: почти все современные платформы запускают приложения из Docker-образов. Без образа пришлось бы вручную устанавливать всё на сервер.
-
В разработке: у всей команды одинаковое окружение. Не будет ситуации «у меня работает, а у тебя нет» из-за разных версий Python или PostgreSQL.
Типичные ошибки
Ошибка 1: тяжёлый образ. Взяли полную систему, поставили компиляторы и инструменты сборки — образ весит гигабайт, качается и собирается долго.
Решение: берите облегчённые базовые образы (python:3.12-slim, node:22-alpine), а для компилируемых языков используйте многоэтапную сборку: собираете в одном образе, в итоговый кладёте только готовую программу.
Ошибка 2: забыть про volume — и данные потеряются при пересоздании контейнера.
Решение: для баз данных и всего, что пишет файлы, всегда указывайте volume.
Ошибка 3: открывать в интернет порты, которым там не место (база данных, Redis, панели управления).
Решение: публикуйте через ports только то, что должно быть доступно снаружи (веб, API). Если сервис нужен только на этом же сервере, привяжите его к локальному адресу: 127.0.0.1:5432:5432.
Ошибка 4: забыть, что контейнер изолирован. Если приложение ищет файл /etc/config.txt, а вы положили его в /home/user/config.txt на сервере, внутри контейнера он не найдётся.
Решение: копируйте файл в образ через COPY или подключайте его через volume, а при ошибках смотрите логи: docker compose logs.
Дальше
Docker — это начало. Когда сервис растёт, приходится:
- Масштабировать: запускать несколько копий приложения и распределять между ними нагрузку (балансировщик).
- Обновлять без простоя: rolling update — новые копии запускаются и начинают принимать запросы, а старые дорабатывают текущие и только потом останавливаются.
Для этого используют Kubernetes или облачные сервисы (AWS ECS, Google Cloud Run). Но это уже следующий шаг после Docker.
На чём бы ни был написан ваш сервис — Python, Node.js или что-то ещё, — Docker упакует его целиком. Начните с него, разберитесь, а потом усложняйте.
Есть задача — развернуть бота, мини-приложение или сервис? Напишите, помогу понять, нужен ли Docker в вашем случае и как всё настроить.
Нужен Telegram-бот для вашего дела?
Расскажите задачу — отвечу в течение дня, предложу решение и точную цену.
Написать в Telegram