VM vs Docker: три продакшн-истории про то, как мы чуть не остались без домена, без базы и без нервов

На дворе 2026. Клиент, рекламное агентство на 35 человек, присылает сообщение: «Слушайте, а давайте всё на докер переведём, ВМ — это прошлый век, все так делают». Спрашиваю, кто «все». Отвечает — на митапе рассказывали.

Я 25 лет в этой индустрии и научился одному: если технология решает задачу — она хорошая. Если её притащили с митапа без задачи под неё — она станет проблемой через полгода. Так и вышло. Через полгода они переносили Active Directory обратно на виртуалку в три часа ночи, потому что контейнер с LDAP-сервером не пережил перезагрузку хоста.

Эта статья не про то, что лучше. Она про три реальных случая из практики, где VM и Docker работали вместе, по отдельности и один раз — друг против друга.

Контекст: зачем вообще сравнивать то, что не сравнивается

Сразу оговорка, которая многим не нравится: VM и контейнеры — не конкуренты. Это как сравнивать грузовик и контейнеровоз. Один везёт груз сам по себе, второй — это способ упаковки груза, который всё равно едет на чём-то. Docker в 95% случаев крутится либо на VM, либо на голом железе, которое по факту исполняет ту же роль.

Но на уровне принятия решений — «что мы поднимаем: виртуалку с сервисом или контейнер» — сравнение абсолютно рабочее, потому что от этого зависят деньги, время восстановления после сбоя и то, кто будет чинить это в два часа ночи.

Три ситуации ниже — из трёх разных инфраструктур, которые я вёл или консультировал за последние два года. Имена и детали изменены, цифры — реальные.

История первая: агентство, которое хотело «перевести всё на докер»

Было: пять физических серверов, на каждом — по одной-две роли: файловый сервер, 1С, почтовый релей, тестовый стенд для разработки, резервный DNS. Загрузка каждого — 8-15%. Электричество, охлаждение, лицензии на Windows Server на каждой машине — отдельная статья расходов, которую никто не считал до тех пор, пока я не попросил счета за год.

Задача: сократить количество железа и связанные с ним расходы, не потеряв в надёжности.

Что пробовали. Первая идея владельца — «докеризировать всё». Мы честно посчитали, что можно контейнеризировать: тестовый стенд разработки, внутренний Uptime Kuma для мониторинга, N8N для автоматизации отчётов, пару внутренних веб-сервисов. Это стейтлесс или условно-стейтлесс нагрузка, которая переживает перезапуск контейнера без последствий.

А вот 1С, файловый сервер с шарами для бухгалтерии и Active Directory — контейнеризировать не стали. И вот почему.

1С в контейнере — это боль с лицензированием и с тем, что часть модулей завязана на COM-объекты Windows, которые в Linux-контейнере просто не существуют. Можно было бы держать Windows-контейнер, но тогда теряется весь смысл — плотность и лёгкость обновлений, ради которых всё затевалось.

Файловый сервер с ACL для 35 человек и AD — это классическая stateful-нагрузка с правами доступа, которая должна пережить перезагрузку хоста без единой потерянной настройки. Один инцидент с испорченным разделом прав — и бухгалтерия неделю разбирается, кто что видит.

Что сделали в итоге. Пять физических серверов схлопнули в один хост на Proxmox с нормальным железом — 128 ГБ ОЗУ, RAID10 на NVMe. Внутри — три виртуалки под критичную инфраструктуру (AD/файлы, 1С, почтовый релей) и один LXC-контейнер с Docker внутри под всё стейтлесс — мониторинг, автоматизацию, тестовые окружения.

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

ЧтоБыло (5 серверов)Стало (1 хост Proxmox)Экономия/годЭлектричество и охлаждение~54 000 руб~14 000 руб~40 000 рубЛицензии Windows Server3 × 45 000 руб3 × 45 000 руб (VM остались)0 рубОбслуживание (выезды, диагностика)~90 000 руб~30 000 руб~60 000 рубВремя развёртывания тестового стенда2-3 часа4 минуты—Общая стоимость железа5 серверов, износ1 сервер + бэкап-хост~220 000 руб разово

Резервное копирование этого хоста — отдельная головная боль, которую мы закрыли Proxmox Backup Server: ставим и настраиваем PBS, не теряя VM.

Как много красивых архитектур, а тянет всё равно на «поставить как раньше, но дешевле» — и в этом нет ничего плохого.

История вторая: провал с контейнером, который стоил ночи без сна

Это тот самый эпизод с LDAP из начала статьи, только подробнее, потому что здесь и есть главный урок статьи.

Контекст: та же компания, три месяца спустя после первого этапа. Они вдохновились успехом контейнеризации мониторинга и решили сами, без меня, «доупаковать» ещё пару сервисов. Один из них — internal LDAP-прокси для авторизации во внутренних веб-панелях.

Что сделали. Подняли контейнер с OpenLDAP, накатили конфиг, подключили пару сервисов через него. Всё работало три недели. Данные пользователей, группы, пароли — всё внутри контейнера, в дефолтном volume, который Docker создаёт сам, если явно не указать путь на хосте.

Что случилось. Плановое обновление хоста Proxmox потребовало перезагрузки. После рестарта контейнер поднялся — но volume с базой LDAP оказался пустым. Не повреждён, не заблокирован — именно пустым, потому что при пересоздании контейнера (обновили образ заодно, чтобы два раза не перезагружаться) Docker создал новый анонимный volume вместо того, чтобы примонтировать старый.

Три часа ночи, разработчик агентства пишет мне в панике, потому что никто не может зайти ни в одну внутреннюю панель — все завязаны на LDAP, которого больше нет. Восстанавливали из бэкапа, которого — сюрприз — тоже не было, потому что «это же просто прокси для авторизации, что там бэкапить».

Вывод, который я вынес и с тех пор повторяю на каждом внедрении: для контейнера с состоянием (базы данных, каталоги пользователей, что угодно, что нельзя пересоздать заново одной командой) volume должен быть именованным, с явным путём на хосте, и обязан попадать в план бэкапа отдельной строкой. Я разбирал это подробно в статье про то, как хранить данные контейнеров и не терять их после docker system prune — рекомендую прочитать до того, как контейнеризировать что-то важнее галереи котиков.

После этого случая LDAP вернули на отдельную виртуалку. Не потому что Docker плохой — а потому что для критичной инфраструктурной службы, которая должна просто работать и не требовать, чтобы про неё помнили каждую минуту, изоляция и предсказуемость виртуалки того стоит. Пока толстый сохнет, худой сдохнет — вот и вся философия выбора между VM и контейнером для критичных вещей: лучше «толстое», но надёжное решение, чем «тонкое», но одноразовое.

История третья: гибридная схема, которая наконец заработала как надо

Третий случай — компания на 60 человек, разработка и поддержка нескольких веб-проектов. Здесь мы строили инфраструктуру с нуля, и именно поэтому получилось спроектировать разделение сразу правильно, а не через две аварии.

Принцип, который мы заложили с самого начала: инфраструктурный слой — на виртуалках, прикладной слой — в контейнерах.

На виртуалках у них: контроллер домена, VPN-шлюз, сервер резервного копирования, MikroTik CHR как маршрутизатор внутри Proxmox (я писал отдельно про установку MikroTik CHR на Proxmox — удобно, когда виртуальный роутер живёт рядом с остальной инфраструктурой и настраивается как обычный MikroTik).

В контейнерах — вся прикладная часть: пять production-проектов клиентов, тестовые окружения для каждого, Nginx Proxy Manager для маршрутизации доменов, CI-раннеры, N8N для внутренней автоматизации отчётов.

Почему это сработало. Прикладные проекты обновляются несколько раз в неделю, иногда несколько раз в день. Docker здесь даёт то, ради чего его придумали: пересобрал образ, перезапустил контейнер, откатился одной командой, если что-то пошло не так. Деплой одного проекта — 90 секунд вместо получаса на разворачивание отдельной VM под каждый новый стенд.

Инфраструктурный слой обновляется раз в квартал плановыми окнами и требует полной предсказуемости состояния — поэтому там VM, снапшоты перед каждым изменением и никакой самодеятельности.

ПараметрVM (инфраструктурный слой)Docker (прикладной слой)Время развёртывания нового окружения25-40 минут1,5-3 минутыОверхед по RAM на единицу1,5-2 ГБ (гостевая ОС)50-150 МБОткат к рабочему состояниюСнапшот, 2-5 минутПересоздание контейнера, 10-30 секундИзоляция при сбоеПолная, на уровне гипервизораЧастичная, зависит от volume и сетиЧастота измененийРаз в квартал-месяцНесколько раз в неделюЧто у нихAD, VPN, бэкап-сервер, роутер5 проектов, CI, NPM, автоматизация

За год работы этой схемы — ни одного инцидента, связанного с потерей данных контейнеров, потому что все volume для стейтлесс-сервисов вынесены на отдельный смонтированный диск с почасовым снапшотом на уровне ZFS, а всё, что имеет значение (базы клиентских проектов), либо в managed-СУБД на отдельной VM, либо бэкапится отдельно от контейнера.

Про то, как строить Docker Compose для такой схемы — структура, reverse proxy, секреты, автообновления — у меня есть отдельный разбор: Docker Compose для домашнего сервера, логика та же и для небольшой корпоративной инфраструктуры, просто масштаб другой.

Когда что брать: без религии

Земли — крестьянам, заводы — рабочим, а вот выбор между VM и контейнером — не идеология, а вопрос характера нагрузки. Правило, которое я применяю на входе в любой проект:

Если сервис хранит состояние, критичен для доступа остальных систем и обновляется редко — виртуальная машина. Контроллеры домена, VPN-шлюзы, СУБД без managed-обвязки, файловые серверы с ACL.

Если сервис можно пересобрать из образа за минуту, состояние вынесено наружу или его вообще нет, а обновления идут часто — контейнер. Веб-приложения, CI/CD, мониторинг, внутренние инструменты автоматизации, тестовые стенды.

Если сомневаетесь — спросите себя: «Если этот контейнер сейчас исчезнет вместе с диском, что я потеряю?» Если ответ «ничего, пересоберу за пять минут» — контейнер. Если ответ «пароли 60 сотрудников» — виртуалка, и без вариантов.

Что бы сделал иначе

Если бы я строил инфраструктуру агентства из первой истории заново — я бы сразу, на старте, а не после аварии с LDAP, ввёл простое правило: любой новый контейнер с volume регистрируется в списке бэкапа до того, как переходит в прод, а не «когда-нибудь потом». Это одна строчка в чек-листе, которая экономит одну бессонную ночь и разговор с клиентом, который начинается со слов «а почему у нас всё упало».

Главный вывод одной фразой: Docker — это не замена виртуализации, а способ не плодить виртуалки под каждую мелочь, и работает он тем лучше, чем честнее вы разделяете инфраструктуру на «то, что должно просто быть» и «то, что должно быстро меняться».

А у вас как разделена нагрузка между VM и контейнерами — по чёткому правилу или как исторически сложилось? Расскажите в комментариях, интересно свериться.

Больше про инфраструктуру без воды — в блоге IT-Аптека и в Telegram-канале.