Docker Compose на VPS: несколько сервисов в одном файле
Как описать в одном docker-compose.yml сайт, приложение и базу данных, связать их внутренней сетью, дождаться готовности базы и не открывать лишние порты наружу.
Содержание 21
- Коротко
- Что такое Compose и зачем он нужен
- Структура проекта
- Шаг 1. Опишите сервисы
- Шаг 2. Проверьте файл
- Шаг 3. Запустите
- Шаг 4. Смотрите логи
- Шаг 5. Обновляйте аккуратно
- Остановка и удаление
- Как это связано с панелью и кабинетом EGN
- Симптом → причина → решение
- Пример: несколько проектов на одном VPS
- Пример: перезапуск после перезагрузки
- Чек-лист перед переездом на новый VPS
- Частые ошибки
- Итог
- Вопросы и ответы
- Нужен ли отдельный VPS для каждого контейнера?
- Чем Compose отличается от docker run?
- Как обновить приложение без простоя?
- Почему docker compose config так важен?
Один контейнер на VPS — это просто. Когда появляются сайт, приложение, база и бот, ручной запуск каждого превращается в ежедневную головную боль. Docker Compose описывает все сервисы в одном файле: какие образы нужны, как они связаны, какие порты открыты и где хранятся данные. Команда docker compose up -d поднимает весь набор, а docker compose down его останавливает.
Коротко
Опишите сервисы в одном файле docker-compose.yml: веб-сервер, приложение и базу. Наружу публикуйте только порты веб-сервера, а базе задайте healthcheck и depends_on с condition: service_healthy, чтобы приложение стартовало после готовности базы. Запуск — docker compose up -d, проверка — docker compose ps и docker compose logs -f.
Что такое Compose и зачем он нужен
Compose — это инструмент Docker для описания нескольких контейнеров как одного приложения. Вместо десяти команд docker run с длинными списками портов и переменных вы храните настройку в файле, который можно хранить в git и переносить на другой VPS.
Compose не заменяет оркестраторы вроде Kubernetes, и для одного VPS он подходит лучше всего. Если сервисов больше десятка или нужна работа на нескольких машинах, тогда смотрите в сторону других инструментов. Для связки «сайт, приложение, база» Compose даёт всё необходимое.
Установите Docker по официальной инструкции Docker для вашей версии Ubuntu, затем проверьте:
docker --version
docker compose versionКоманда docker compose с пробелом — это текущий плагин Compose. Старый docker-compose с дефисом больше не нужен.
Структура проекта
Создайте каталог проекта и поместите в него три файла: docker-compose.yml, .env с секретами и каталог приложения с Dockerfile:
/opt/shop/
├── docker-compose.yml
├── .env
└── app/
├── Dockerfile
└── main.pyФайл .env Compose читает автоматически, и значения из него подставляются в docker-compose.yml через ${ИМЯ}. Так пароли не попадают в сам файл конфигурации.
Шаг 1. Опишите сервисы
Создайте docker-compose.yml. Имя проекта задаётся верхним уровнем name, поле version больше не нужно:
name: shop
services:
web:
image: nginx:stable
ports:
- "80:80"
depends_on:
- app
app:
build: ./app
environment:
DATABASE_URL: postgresql://app:${DB_PASSWORD}@db:5432/app
depends_on:
db:
condition: service_healthy
restart: unless-stopped
db:
image: postgres:16
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: ${DB_PASSWORD:?задайте DB_PASSWORD в .env}
POSTGRES_DB: app
volumes:
- dbdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d app"]
interval: 5s
timeout: 3s
retries: 10
restart: unless-stopped
volumes:
dbdata:Обратите внимание на три вещи. У db нет блока ports: база не публикуется наружу и доступна только внутри сети Compose по имени db. У app depends_on ждёт не просто запуска базы, а её healthcheck. У dbdata есть именованный том, поэтому данные переживают docker compose down и пересоздание контейнера.
Конструкция ${DB_PASSWORD:?...} останавливает запуск с понятной ошибкой, если переменная не задана. Файл .env рядом с ним:
DB_PASSWORD=замените-на-длинный-парольПароль для строки подключения делайте из букв и цифр: символы вроде @, / или : в postgresql:// придётся экранировать.
chmod 600 /opt/shop/.envОбраз nginx:stable без своего конфига отдаёт стандартную страницу. Для реального сайта подключите свой конфиг через том или соберите образ из собственного Dockerfile. Пример показывает схему связей, а не готовый сайт.
Шаг 2. Проверьте файл
Перед запуском проверьте, что Compose правильно подставил переменные и связи:
cd /opt/shop
docker compose configКоманда выведет итоговую конфигурацию с подставленными значениями. Если переменная не задана, Compose предупредит об этом и подставит пустую строку (для переменных с :? остановится с ошибкой). Пароль появится в выводе, поэтому не копируйте его в чат и не сохраняйте в файлы логов.
Шаг 3. Запустите
docker compose up -d --build
docker compose psФлаг --build пересобирает образ приложения из Dockerfile. Колонка STATUS в выводе ps показывает, что база стала healthy, а приложение running.
Порядок старта при depends_on с проверкой здоровья выглядит так:
Шаг 4. Смотрите логи
Логи всех сервисов выводятся командой:
docker compose logs -fЧтобы посмотреть только приложение, добавьте имя сервиса: docker compose logs -f app. Для последних строк без режима реального времени используйте --tail 100.
Шаг 5. Обновляйте аккуратно
Обновление образа делается тремя командами: скачать новый образ, пересоздать контейнеры, почистить старые образы.
docker compose pull
docker compose up -d
docker image prune -fПеред обновлением базы сделайте дамп, как описано в статье База данных на VPS: MySQL или PostgreSQL без утечек в интернет. Смена мажорной версии PostgreSQL, например с 15 на 16, требует миграции данных, а не просто замены образа.
Остановка и удаление
Есть два похожих, но разных действия. docker compose down останавливает и удаляет контейнеры и сеть, но тома сохраняются. docker compose down -v удаляет и тома, то есть данные базы. Вторую команду используйте только если точно знаете, что данные не нужны.
docker compose downКак это связано с панелью и кабинетом EGN
В карточке VPS в кабинете EGN есть вкладка «Консоль», которая открывает терминал в браузере. Если SSH-клиент недоступен, через неё можно выполнить docker compose ps и docker compose logs. Вкладка «Мониторинг» показывает нагрузку сервера, чтобы понять, не упёрся ли он в процессор или память. Публикуя порт командой Docker, помните про вкладку «Фаервол»: облачный фаервол в режиме «Свои правила» режет входящие подключения до сервера и поэтому работает и для опубликованных портов Docker.
Переустановка VPS стирает и контейнеры, и тома. Поэтому перед переустановкой сделайте копии, как описано в статье Бэкапы VPS: снапшоты, S3 и restic.
Симптом → причина → решение
| Симптом | Частая причина | Что сделать |
|---|---|---|
app падает при старте с ошибкой подключения к базе |
depends_on без condition |
Добавьте condition: service_healthy и healthcheck у базы |
Контейнер db в статусе unhealthy |
Неверная команда в test или база ещё не инициализировалась |
Проверьте docker compose logs db, увеличьте retries |
port is already allocated |
Порт 80 занят другим процессом | Найдите его через sudo ss -tlnp | grep ':80' и освободите |
Данные пропали после down |
Использован down -v или не задан том |
Проверьте docker volume ls, восстановите из копии |
| Переменная пустая в контейнере | Нет .env или опечатка в имени |
Запустите docker compose config и сравните значения |
| Образ не обновляется | Используется тег без pull |
Выполните docker compose pull перед up |
Пример: несколько проектов на одном VPS
Каждый проект живёт в своём каталоге с собственным docker-compose.yml. Compose по умолчанию берёт имя проекта из имени каталога, а в примере имя задано явно через name: shop. Так два проекта не смешают контейнеры и тома. Порты для них должны быть разными: если оба публикуют 80, второй не запустится. Один из проектов можно поставить за nginx на другом порту, как в статье Сайт на VPS с nginx и Let’s Encrypt.
Пример: перезапуск после перезагрузки
Параметр restart: unless-stopped возвращает контейнер в работу после перезагрузки VPS, если вы сами его не останавливали. Проверьте это, перезагрузив сервер командой sudo reboot и выполнив docker compose ps после входа. Если сервис не поднялся, проверьте, что служба Docker включена: sudo systemctl is-enabled docker.
Чек-лист перед переездом на новый VPS
Если проект переезжает на другой сервер, Compose делает это почти механически, если соблюдены три условия:
- Файлы
docker-compose.ymlи каталог приложения лежат в git или в резервной копии. .envперенесён отдельно и не попадает в репозиторий. Его храните в защищённом месте.- Тома скопированы или восстановлены из дампа. Для PostgreSQL это
pg_dumpдо переезда иpsqlпосле. Копирование каталога данных при работающей базе ненадёжно.
После переезда выполните docker compose up -d, проверьте docker compose ps и один запрос к приложению. Только после этого переключайте DNS.
Частые ошибки
- Публикуют порт базы.
ports: - "5432:5432"открывает базу на все адреса, и ufw её не закроет. Не публикуйте её вовсе или указывайте127.0.0.1:. - Пароли в docker-compose.yml. Вынесите их в
.envс правами 600. - Нет healthcheck. Без него
depends_onждёт только запуска контейнера, а база ещё может инициализироваться. down -vпо привычке. Эта команда удаляет данные. Запомните разницу сdown.- Образы без тегов.
image: postgresбез версии обновит базу непредсказуемо. Указывайтеpostgres:16. - Логи растут без предела. По умолчанию Docker хранит логи контейнеров в json-файлах без ограничения. Задайте лимит для сервиса:
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"Этот блок добавляется внутрь описания сервиса на уровне image или restart.
Итог
Docker Compose на одном VPS даёт порядок: один файл описывает все сервисы, внутренняя сеть закрывает базу, healthcheck задаёт правильный порядок запуска, а .env держит секреты отдельно. Публикуйте наружу только веб-сервер, делайте копии томов и проверяйте восстановление. Если к сервисам добавятся игровой сервер и бот, посмотрите статью Discord-бот на VPS: запуск через systemd, она описывает, как держать бота вне контейнеров.
Вопросы и ответы
Нужен ли отдельный VPS для каждого контейнера?
Нет. На одном VPS можно держать несколько сервисов в Compose, главное — не публиковать лишние порты и следить за ресурсами.
Чем Compose отличается от docker run?
docker run запускает один контейнер с параметрами в командной строке. Compose описывает весь набор в файле, его удобно хранить в git и переносить.
Как обновить приложение без простоя?
Простой короткий, так как up -d пересоздаёт контейнер. Для настоящего нулевого простоя нужен второй экземпляр и балансировщик, это уже другой уровень.
Почему docker compose config так важен?
Он показывает итоговую конфигурацию после подстановки переменных. Ошибку в имени переменной или отступе проще найти там, чем в работающем контейнере.
Сервер под игры за пару минут — egnetwork.ru
Читать далее
Вся рубрика →Мониторинг ресурсов VPS: htop, iotop и vnstat
Как за несколько минут понять, что тормозит VPS: процессор, память, диск или сеть. Команды htop, iotop, vnstat, free и df с примерами вывода и таблицей симптомов.
Трафик VPS: как посчитать объём и не получить сюрприз в конце месяца
Что считается трафиком, как перевести скорость канала в терабайты за месяц, как читать счётчик входящего и исходящего трафика в кабинете EGN и что делать, если лимит закончился.
IPv6 на VPS: как пользоваться префиксом /64
Что значит /64, как проверить адрес на сервере, назначить дополнительные адреса, настроить ufw для IPv6 и не выпасть из сети. Примеры команд и типичные ошибки.