Легкий старт в облаке VMware До -50% на виртуальную инфраструктуру для новых клиентов и новых проектов Иконка

Когда речь заходит о запуске приложений в облаке, многие сразу думают о сложных системах оркестрации вроде Kubernetes. Но часто для старта или роста проекта нужны более простые инструменты. Docker Compose знаком каждому разработчику как способ поднять окружение на локальной машине. Однако его возможности выходят далеко за пределы ноутбука. При переносе в облачную инфраструктуру, Docker превращается в рабочий инструмент для развёртывания и масштабирования, который не требует выделенной команды администраторов.

Оказывается, чтобы получить отказоустойчивое приложение, не обязательно сразу переходить на тяжелую оркестрацию. Достаточно правильно спроектировать архитектуру, добавить облачные сервисы для хранения данных и настроить балансировщик нагрузки. В итоге вы получаете предсказуемое масштабирование и экономию ресурсов там, где это действительно нужно.

В этой статье мы посмотрим, как Docker Compose работает на виртуальных машинах.


Что такое Docker Compose

Docker Compose чаще всего воспринимают как инструмент для локальной разработки. Это логично: разработчик пишет файл docker-compose.yml, описывает в нём пару сервисов — базу данных и веб-сервер, и одной командой поднимает всё окружение. Это удобно и быстро.

Но сам по себе Docker Compose — это просто инструмент для описания многоконтейнерных приложений и управления ими. Он не знает, где запускается: на ноутбуке, в тестовой виртуалке или на мощном сервере в облаке. Единственное требование — наличие Docker Engine. Поэтому не стоит рассматривать Compose только как локальный инструмент. Он вполне способен управлять приложениями и на удалённых серверах.

Если добавить к этому режим swarm, встроенный в Docker, Compose превращается в простую систему оркестрации. Она не дотягивает до возможностей Kubernetes, но для многих задач этого и не требуется. Вот что становится доступно:

  • балансировка нагрузки внутри кластера между несколькими экземплярами сервиса;
  • распределение контейнеров по разным физическим или виртуальным серверам;
  • обновление приложения без остановки его работы (rolling updates);
  • декларативное описание желаемого состояния сервисов.

И всё это работает без установки дополнительного программного обеспечения.

Как работает Docker в облаке

Запуск Compose-файла в облаке и на локальной машине даёт разный результат. В облаке снимаются ограничения, которые на домашнем компьютере кажутся естественными.

Первое — это ресурсы. Оперативная память и процессорное время в облаке не привязаны к физическому железу, которое стоит под столом. Если приложению не хватает мощности, виртуальную машину можно остановить и перезапустить с большим количеством ядер и гигабайтами RAM. Для Compose это выглядит так, будто сервер просто стал мощнее — контейнеры начинают использовать новые ресурсы автоматически.

Второе — сетевая инфраструктура. Здесь появляются возможности, которых нет при локальном использовании Docker:

  • публичный IP-адрес, делающий приложение доступным из любой точки мира сразу после старта контейнеров;
  • облачные балансировщики нагрузки, которые распределяют трафик между контейнерами;
  • возможность привязать собственный домен и настроить SSL-сертификаты;
  • встроенная DDoS-защита на уровне сети провайдера.

Третье — это состояние или данные. Любой контейнер по умолчанию не хранит данные после перезапуска. На локальной машине это решают томами, которые лежат где-то в файловой системе ноутбука. В облаке появляются дополнительные возможности:

  • быстрые сетевые диски, которые подключаются к виртуалкам как обычные устройства;
  • объектные хранилища для статики и пользовательских файлов;
  • снапшоты дисков для быстрого резервного копирования;
  • интеграция с управляемыми базами данных, которые не нужно разворачивать внутри контейнеров.

С точки зрения Compose это просто внешние директории и сетевые адреса, но с характеристиками, которые сложно достичь локально.

Как строить архитектуру для масштабирования

Чтобы Compose-приложение можно было масштабировать, нужно разделять сервисы на две категории: те, которые не хранят состояние, и те, которые его хранят.

Сервисы, которые не хранят состояния (stateless):

  • веб-интерфейсы (frontend);
  • API-сервисы;
  • фоновые обработчики задач;
  • любые приложения, которые не сохраняют данные локально.

Это сервисы без состояния. Они получают запрос, обрабатывают его, возвращают ответ. Всё, что им нужно для работы, они берут из настроек окружения или из внешних источников. Такие сервисы можно запускать в нескольких экземплярах без риска что-то сломать.

Cервисы с состоянием (stateful):

  • реляционные базы данных (PostgreSQL, MySQL);
  • хранилища кэшей (Redis, Memcached);
  • поисковые движки (Elasticsearch);
  • очереди сообщений (RabbitMQ).

С ними сложнее. Если просто запустить три копии PostgreSQL, каждая будет работать со своими данными, и согласованности между ними не будет. Кластеризация баз данных — отдельная сложная тема, которую Compose не решает.

Поэтому практичный подход — вынести хранение данных за пределы Compose. Облачные провайдеры предлагают управляемые сервисы для баз данных и Redis. В этом случае в Compose-файле остаются только прикладные сервисы. Они подключаются к внешней базе через стандартные переменные окружения:

  • хост базы данных;
  • порт для подключения;
  • имя пользователя;
  • пароль;
  • имя базы данных.

Такая архитектура упрощает масштабирование. Можно спокойно увеличивать количество копий фронтенда или API, не думая о том, как синхронизировать между ними файлы сессий или пользовательские загрузки. База данных живёт своей жизнью под управлением облачного провайдера, разработчику не нужно настраивать репликацию вручную.

YAML: язык облачной инфраструктуры

Здесь мы часто упоминаем YAML, который используется для описания сервисов в docker-compose.yml. В другой статье подробно разбираем синтаксис YAML, частые ошибки при его написании и полезные инструменты.

Читать

Настройка Docker Compose

Любое приложение, которое планируется запускать через Docker, требует упаковки в образ. Для этого нужен Dockerfile — текстовый файл с инструкциями по сборке. В нём описывается, на основе какого базового образа собирать приложение, какие зависимости устанавливать и как его запускать.

Для веб-проектов обычно требуется несколько таких файлов: отдельно для веб-сервера, отдельно для бэкенда, а иногда и для очередей задач. Каждый сервис живёт в своём контейнере и собирается из своего Dockerfile.

Сборка образов

Веб-сервер на Nginx. Если в проекте используется Nginx для отдачи статических файлов или проксирования запросов к бэкенду, его образ собирается просто. Берётся официальный образ nginx из реестра Docker Hub, в него копируется заранее подготовленный конфигурационный файл. Этот файл содержит настройки того, как сервер должен обрабатывать запросы: какие порты слушать, куда перенаправлять трафик на динамические страницы и где искать статику.

Бэкенд на PHP. Для PHP-приложений Dockerfile выглядит иначе. В качестве основы обычно берётся образ с нужной версией PHP и уже установленными расширениями. Дальше в образ копируется код проекта, устанавливаются зависимости через Composer, настраиваются права доступа к файлам. Важный момент — в образах для разработки часто оставляют инструменты отладки, а для продакшна их исключают, чтобы образ занимал меньше места и работал быстрее.

Бэкенд на Python. Для Python-приложений логика похожая. В Dockerfile указывается базовый образ с Python, копируется файл с зависимостями (requirements.txt), выполняется их установка. Затем копируется остальной код проекта и прописывается команда запуска — обычно это вызов Gunicorn или другого WSGI-сервера.

Фоновые обработчики. Если в проекте есть задачи, которые выполняются асинхронно (отправка писем, обработка изображений), для них тоже создаётся отдельный образ. Он собирается на том же коде, что и основной бэкенд, но команда запуска в нём будет другой — не веб-сервер, а воркер, который слушает очередь задач.

Как всё соединяется

Когда образы готовы, наступает очередь Compose-файла. Это главный инструмент, который описывает, как контейнеры должны взаимодействовать друг с другом и с внешним миром.

Структура YAML-файла стандартная и состоит из нескольких ключевых разделов.

1. Раздел version. В самом начале указывается версия синтаксиса Compose. От неё зависит, какие функции будут доступны. Для современных проектов обычно используется версия 3.

2. Раздел services. Здесь перечисляются все сервисы, из которых состоит приложение. Для каждого указывается:

  • Образ — откуда брать контейнер. Это может быть либо локальный образ, собранный из Dockerfile (тогда указывается путь к папке с ним), либо готовый образ из реестра Docker Hub, например postgres или redis.
  • Порты — какие порты контейнера должны быть доступны снаружи, на виртуальной машине. Если веб-сервер внутри контейнера слушает порт 80, а снаружи мы хотим обращаться к нему по стандартному HTTP-порту, это указывается в виде «80:80».
  • Переменные окружения — параметры подключения к базам данных, ключи API, режимы работы приложения. Они передаются в контейнер, и код приложения читает их во время запуска.
  • Зависимости — Compose позволяет указать, что один сервис должен запускаться только после другого. Например, бэкенд должен стартовать после того, как поднялась база данных. Хотя это не гарантирует, что база будет готова принимать подключения, это помогает выстроить порядок запуска.
  • Тома — механизм, который связывает папки на хост-машине с папками внутри контейнера. Через тома пробрасывается код приложения, чтобы не пересобирать образ при каждом изменении, и сохраняются данные, которые должны жить дольше одного контейнера.

3. Раздел networks. По умолчанию Compose создаёт для проекта отдельную сеть, через которую контейнеры могут обращаться друг к другу по именам сервисов. То есть внутри контейнера с бэкендом можно подключиться к базе данных просто по имени сервиса «db», не зная IP-адреса. Если нужна более сложная конфигурация с изоляцией сервисов, сети настраиваются вручную.

4. Раздел volumes. Здесь объявляются именованные тома, которые используются для хранения данных. Они удобны тем, что Docker сам управляет их расположением на диске, а разработчику не нужно думать о путях к папкам.

Переменные окружения, запуск и проверка

Чтобы не хранить чувствительные данные прямо в Compose-файле, используется файл .env. В нём записываются переменные вроде паролей к базам данных или ключей доступа к API. Docker Compose автоматически подхватывает этот файл, если он лежит рядом с docker-compose.yml, и подставляет значения в нужные места.

Это удобно ещё и тем, что один и тот же Compose-файл можно использовать для разных окружений. Для разработки достаточно своего .env файла с локальными настройками, для продакшна — своего. Сам файл .env при этом не попадает в систему контроля версий, если его правильно добавить в .gitignore.

Когда все файлы подготовлены, приложение запускается командой docker-compose up -d. Флаг -d переводит его в фоновый режим, чтобы контейнеры не занимали окно терминала.

После запуска можно посмотреть логи через docker-compose logs, убедиться, что все сервисы стартовали без ошибок, и проверить, отвечает ли приложение на нужном порту. Если что-то пошло не так, Compose позволяет зайти внутрь контейнера с помощью docker-compose exec и посмотреть, что происходит.

Масштабирование приложений с Docker Compose

Когда приложение упаковано в контейнеры и описано в Compose-файле, встаёт вопрос о его росте. Нагрузка редко остаётся стабильной: утром посетителей мало, днём их становится больше, а во время рекламной кампании трафик может вырасти в десятки раз. Чтобы выдерживать такие колебания, инфраструктура должна уметь масштабироваться — добавлять ресурсы туда, где они нужны в данный момент. Docker Compose вместе с возможностями облака даёт несколько способов это сделать.

Вертикальное масштабирование

Самый простой способ дать приложению больше ресурсов — добавить их виртуальной машине, на которой всё работает. В облаке это происходит гораздо быстрее, чем при работе с локальной машиной.

Для Docker Compose этот процесс проходит незаметно. Контейнеры после перезапуска видят уже новые ресурсы. Если приложение умело утилизировать несколько ядер или использовать много оперативной памяти, оно начинает работать быстрее без изменения кода или конфигурации. Это называется вертикальным масштабированием.

У такого подхода есть ограничение. Рано или поздно упираешься в максимум доступной конфигурации. Кроме того, при сбое сервера приложение ляжет целиком, пока администратор не поднимет его заново. Поэтому вертикальное масштабирование хорошо как первая мера, но для серьёзных нагрузок нужно горизонтальное.

Горизонтальное масштабирование

Docker Compose позволяет легко увеличивать количество экземпляров отдельных сервисов. Для этого используется параметр --scale при запуске. Например, если в проекте есть API, которое стало получать слишком много запросов, команда docker-compose up --scale api=3 -d запустит три копии этого сервиса вместо одной.

Compose создаст три контейнера с одним и тем же образом, даст им разные имена и добавит в общую сеть проекта. Если посмотреть список запущенных контейнеров, они будут отображаться как api_1, api_2, api_3.

Как запросы от пользователей распределяются между этими копиями? В базовой конфигурации Compose этого не происходит. Каждый контейнер слушает свой порт, и клиенту нужно обращаться к конкретному экземпляру. Чтобы нагрузка распределялась равномерно, нужен балансировщик.

Балансировка трафика

В облаке эту задачу решает балансировщик нагрузки. Это отдельный сервис провайдера, который принимает входящие запросы на один публичный IP-адрес и распределяет их по нескольким серверам или контейнерам.

Схема работы выглядит так. На виртуальной машине запускается несколько копий API-сервиса. Каждый из них публикует свой порт наружу, например 8081, 8082, 8083. В панели управления облаком создаётся балансировщик, которому указывается:

  • прослушивать 80-й или 443-й порт;
  • передавать трафик на внутренний IP-адрес виртуальной машины;
  • распределять запросы по портам 8081, 8082, 8083.

Балансировщик проверяет, отвечают ли контейнеры, и если один из них перестаёт быть доступным, временно исключает его из ротации. Это добавляет отказоустойчивости: при падении одного контейнера приложение продолжает работать на остальных.

Некоторые проекты идут дальше и запускают несколько виртуальных машин с одинаковым набором контейнеров. Тогда балансировщик распределяет трафик уже между машинами, а внутри каждой машины Compose управляет своими копиями сервисов.

Ограничения масштабирования

При горизонтальном масштабировании важно помнить про разделение на stateless и stateful сервисы. Увеличивать количество копий можно только для тех приложений, которые не хранят данные локально.

Веб-интерфейсы, API, фоновые обработчики задач — с ними проблем нет. Каждый новый экземпляр делает одно и то же: принимает запрос, обращается к внешней базе, отдаёт ответ. Пользователю неважно, на какой именно экземпляр он попал.

С базами данных и кэшами такой номер не пройдёт. Если запустить три копии PostgreSQL через --scale, каждая из них будет работать со своим набором файлов. Данные, записанные в первый экземпляр, не появятся во втором.

Поэтому при проектировании архитектуры сразу закладывают вынос баз данных за пределы Compose. Тогда масштабирование прикладных сервисов становится безопасным и предсказуемым.

Обновление приложения без остановки

Когда приложение работает в нескольких экземплярах, появляется возможность обновлять его без прерывания обслуживания пользователей. Docker Compose в связке с режимом swarm или просто при аккуратном использовании команд позволяет реализовать постепенное обновление.

Идея в том, что контейнеры обновляются не все сразу, а по очереди. Сначала останавливается и заменяется новой версией один экземпляр. Балансировщик тем временем направляет трафик на оставшиеся работающие копии. Когда первый контейнер запустился и прошёл проверки здоровья, обновляется следующий. И так до полной замены всех экземпляров.

В простых случаях можно использовать команду docker-compose up -d --no-deps --build api. Флаг --no-deps говорит Compose не перезапускать зависимые сервисы, а --build пересобирает образ перед запуском. Compose по очереди останавливает старые контейнеры и запускает новые.

Для более тонкого контроля над процессом лучше перевести проект в режим Docker swarm и использовать стеки. Тогда обновлениями управляет встроенный оркестратор, и можно задавать параметры: сколько контейнеров может быть недоступно одновременно, с какой задержкой запускать следующие и как долго ждать проверки работоспособности.

Автоматическое восстановление после сбоев

Docker Compose сам по себе не перезапускает контейнеры, если они упали с ошибкой. Но эту функцию берёт на себя Docker Engine. В файле docker-compose.yml для каждого сервиса можно указать политику перезапуска restart.

Самый распространённый вариант — unless-stopped. Он означает, что контейнер будет автоматически запускаться заново при любом завершении, кроме случаев, когда его остановили явно командой docker stop. Это полезно, чтобы приложение само возвращалось в строй после временных сбоев.

В облаке к этому добавляется мониторинг виртуальной машины. Если упадёт сама машина, провайдер может настроить автоматический перезапуск инстанса. А если проект развёрнут на нескольких машинах, балансировщик просто перестанет слать трафик на упавший сервер, пока тот не восстановится.

Частное облако для ваших проектов

Для критичных проектов требуется изолированная инфраструктура. В частном облаке от ИТ-ГРАД вы получаете выделенные ресурсы, полный контроль над сетями и повышенную безопасность данных, а все возможности масштабирования через Docker Compose сохраняются.

Заказать

Docker Compose и Оркестраторы

Docker Compose и Kubernetes решают похожие задачи, но на разных уровнях сложности. Compose родился как инструмент для описания многоконтейнерных приложений на одной машине. Он простой, понятный и делает ровно то, что от него ждёшь: запускает контейнеры в определённом порядке, создаёт сеть, пробрасывает тома.

Kubernetes задумывался как промышленная система для управления контейнерами в дата-центрах. Он умеет распределять нагрузку между десятками серверов, перезапускать упавшие контейнеры на других машинах, масштабировать сервисы по метрикам, обновлять приложения без остановки и делать еще много всего.

Выбор между ними — это не вопрос «что лучше», а вопрос «что подходит под текущие задачи и размер команды».

Когда выбирать Docker Compose

Есть большой класс проектов, для которых Kubernetes избыточен. В них Compose справляется без лишней сложности.

Небольшие команды или соло-разработчики. Если над проектом работает два-три человека, тратить время на изучение Kubernetes и поддержку кластера накладно. Compose знаком большинству разработчиков, он просто пишется и читается. Поднять среду на новой машине можно за пару минут.

Проекты с монолитной архитектурой. Если приложение состоит из двух-трёх сервисов (фронтенд, бэкенд, база), Compose описывает эту структуру исчерпывающе. Kubernetes для такого проекта потребует написания десятков YAML-файлов: деплойменты, сервисы, ингрессы, конфигмапы.

Ограниченный бюджет. Виртуальная машина в облаке стоит денег, но кластер Kubernetes обычно требует минимум трёх машин для отказоустойчивости. Compose позволяет экономить, запуская всё на одной машине.

Разработка и тестирование. Локально или в тестовом окружении Compose незаменим. Он быстро стартует, легко перезапускается, не требует сложных настроек сети и прав доступа.

Предсказуемая нагрузка. Если проект не ожидает миллионных посещений в сутки и резких скачков трафика, возможностей вертикального масштабирования и нескольких копий сервисов на одной машине хватает с запасом.

Когда лучше использовать Kubernetes

Kubernetes стоит рассматривать, когда проект достигает определённой зрелости и сложности.

Большая команда. Если над инфраструктурой работают несколько человек, а микросервисов больше десяти, единообразие Kubernetes начинает окупаться. Все описывают приложения одинаковыми манифестами, используются общие подходы к конфигурации и мониторингу.

Микросервисная архитектура. Когда каждый сервис живёт своей жизнью, имеет отдельный репозиторий, свою базу данных и свой цикл релизов, управлять этим в Compose становится трудно. Kubernetes предоставляет инструменты для изоляции и независимого развертывания сервисов.

Потребность в автоскейлинге. Если нагрузка на приложение непредсказуема и меняется в течение дня, хочется, чтобы система сама добавляла ресурсы днём и убирала ночью. Kubernetes умеет это делать на основе метрик CPU, памяти или кастомных показателей.

Требования к отказоустойчивости. Для критичных сервисов, где простой стоит денег, важна способность кластера переживать падение отдельных серверов без потери данных и остановки обслуживания. В Kubernetes это заложено в архитектуру.

Экосистема и расширения. Вокруг Kubernetes сложилась огромная экосистема. Есть инструменты для управления секретами, для наблюдения, для безопасного доступа, для организации сервисных сеток. Если проекту нужны такие возможности, их проще получить в Kubernetes, чем собирать самостоятельно из отдельных компонентов.

Использование в связке

На практике многие команды используют оба инструмента параллельно. Compose остаётся для локальной разработки и тестовых окружений. На тестовых серверах тоже часто поднимают стенды через Compose, потому что это быстро и не требует настройки кластера.

Kubernetes забирает на себя продакшн-среду, где важны надёжность, масштабирование и автоматизация. При этом код приложения не меняется — контейнеры собираются те же самые, просто запускаются они не через docker-compose up, а через kubectl apply.

Такой подход позволяет не усложнять разработку на ранних этапах и плавно переходить к более мощным инструментам по мере роста проекта. Compose не становится ненужным, он просто занимает свою нишу быстрого и удобного инструмента для работы с контейнерами на одной машине.

Заключение

Docker Compose часто воспринимают как инструмент только для локальной разработки, но его возможности выходят далеко за пределы ноутбука. При грамотной архитектуре, с вынесением баз данных в управляемые сервисы и использованием облачных балансировщиков, Compose превращается в рабочий инструмент для запуска и масштабирования приложений. Он позволяет быстро увеличивать количество копий сервисов, обновлять их без остановки и восстанавливать после сбоев. А когда проект перерастает возможности одной машины, всегда можно перейти на Kubernetes, сохранив те же Docker-образы.

Чтобы использовать все преимущества контейнеризации и масштабирования, нужна надёжная виртуальная инфраструктура. Развернуть частное облако вам помогут специалисты ИТ-ГРАД. Мы поможем подобрать конфигурацию и запустим вашу инфраструктуру в короткие сроки.

Частые вопросы

1. Можно ли использовать один и тот же docker-compose.yml для локальной разработки и для продакшна в облаке?

Да, но с небольшими изменениями. Обычно создают один основной файл с описанием сервисов и несколько дополнительных, которые переопределяют настройки под конкретное окружение. Например, для продакшна можно убрать проброс лишних томов с кодом, добавить политики перезапуска и указать другие переменные окружения. Эти переменные удобно хранить в файлах .env для каждого окружения — локально один файл, на сервере другой.

2. Что делать с базами данных при масштабировании? Их тоже масштабировать через --scale?

Масштабировать базы данных через --scale нельзя. Если запустить несколько копий PostgreSQL или MySQL, каждая будет работать со своим набором данных, и согласованности между ними не будет. Базы данных либо оставляют в единственном экземпляре внутри Compose, либо, что правильнее для продакшна, выносят в отдельные сервисы облачного провайдера. Тогда прикладные сервисы можно масштабировать сколько угодно — они все будут подключаться к одной внешней базе.

3. Как быть с хранением файлов, которые загружают пользователи, если контейнеры не хранят данные?

Для пользовательских файлов нужны тома или внешнее хранилище. Если проект работает на одной машине, можно использовать именованные тома Docker — они будут храниться на диске виртуальной машины. Если серверов несколько или планируется масштабирование, лучше подключить объектное хранилище от облачного провайдера. Тогда все экземпляры приложения будут читать и записывать файлы в одно место, и не возникнет ситуации, когда файл загрузили через один сервер, а отдать пытаются через другой.

4. Когда Docker Compose перестаёт быть удобным и пора переходить на Kubernetes?

Обычно это происходит, когда проекту становится тесно на одной виртуальной машине. Если даже мощная конфигурация сервера не справляется с нагрузкой, приходится распределять сервисы на несколько машин. Compose в чистом виде не умеет управлять контейнерами на разных серверах. В этом случае можно сначала попробовать Docker Swarm — он работает с теми же Compose-файлами и добавляет распределенность. Если и его возможностей не хватает (например, нужен сложный автоскалинг по метрикам или тонкие сетевые политики), тогда приходит время Kubernetes.

Оцените эту статью

Средняя оценка:, всего оценок: