microsrv:~$
Консоль

← Все записи

Бессерверные виртуальные машины в microsrv: уходят в сон без трафика, просыпаются по запросу

Опубликовано•12 мин чтения

Бессерверные виртуальные машины в microsrv: уходят в сон без трафика, просыпаются по запросуБессерверные виртуальные машины в microsrv: уходят в сон без трафика, просыпаются по запросу

Обычная ВМ на microsrv работает постоянно — с выделенными vCPU и объёмом ОЗУ, пока вы не остановите её вручную. Serverless-режим добавляет другое поведение: при создании машины можно включить переход в спящий режим при отсутствии трафика и пробуждение по первому входящему пакету. Работает с любым типом трафика — HTTP, SSH, ICMP, внутри VPC или через WireGuard.

Коротко: первый пакет от любого клиента (даже из собственного VPC) пробуждает ВМ за 100–250 мс. Пакет не теряется — буферизуется в сети и доставляется после пробуждения, поэтому ответ приходит на этот же пакет. Процессы, память и открытые сокеты сохранены в контрольной точке, которую ВМ создала перед сном. Хранилище реплицировано на узлы. Сон не означает потерю данных.


1. Что значит «serverless» для виртуальной машины

Классический serverless — это функции и контейнеры, запускаемые по запросу, пока запрос обрабатывается через HTTP-шлюз. Для полноценной машины этого недостаточно: у неё есть SSH-доступ, собственные сервисы, трафик между машинами в VPC и исходящие запросы.

В microsrv serverless работает на уровне сетевого трафика, а не на уровне HTTP:

  • входящий трафик любого типа: HTTP, SSH, ICMP, UDP, TCP на любом порту. Первый пакет, адресованный частному IP машины, запускает пробуждение.
  • исходящий трафик тоже учитывается. Машина переходит в спящий режим, когда нет ни входящего, ни исходящего трафика дольше заданного тайм-аута.
  • клиентом может быть любой источник: внешний через WireGuard, другой сервис в том же VPC или собственный мониторинг.

Это машина с постоянной сетевой идентичностью: частный IP не меняется, домен .msrv.space остаётся прежним. Пока её никто не трогает, она не расходует вычислительные ресурсы и не занимает место (vCPU и RAM освобождены).


2. Типичные сценарии

База данных, которая включается по запросу

VM с PostgreSQL или MySQL спит, пока приложение не обращается к ней дольше тайм-аута. Подключение по обычному частному IP, или домену .msrv.space, само пробуждает машину. Данные не теряются. Состояние базы реплицируется на томе. Первый запрос после сна занимает несколько сотен миллисекунд, дальше скорость обычная.

Один нюанс. TCP не обеспечивает мгновенного первого ответа, если пакет ушёл в спящую машину. Клиент по стандарту сам повторит отправку. Поэтому таймаут подключения в пуле соединений должен быть больше 1–2 секунд.

Кэш: Redis / memcached

VM с кэшем засыпает, когда нет ни одной операции get/set, и просыпается по первому запросу клиента. Главное отличие от «пустого» холодного старта в контрольной точке. Она восстанавливает всё содержимое кэша. Машина просыпается не с пустым состоянием, а с тем же состоянием памяти, в котором её остановили. Подойдёт для кэшей с ограниченным рабочим набором (working set) и сервисов с неравномерной нагрузкой. Если данные должны доставляться мгновенно в любой момент, кэш держат активным.

Брокер сообщений: RabbitMQ / NATS / Kafka

VM с брокером поднимается по запросу от producer или consumer. Первый publish или subscribe просыпает машину, и сообщение доставляется через несколько сотен миллисекунд. Потерять сообщение нельзя. Очередь живёт на диске, а клиент по стандарту сам повторит отправку, пока брокер не поднимется. Если потребитель непрерывно опрашивает очередь, задайте max_awake_seconds, чтобы брокер не висел вечно.


3. Как устроено пробуждение: сеть, вычисления, хранилище

Сетевой слой перехватывает входящие пакеты, вычисления поднимают машину, а хранилище отдаёт ей память.

Сетевой слой: перехват и событие

Сетевой слой microsrv работает в ядре и видит каждый поступающий пакет на частный IP машины. Пока машина спит, каждый пакет проходит через один и тот же сценарий:

  1. Пакет перехватывается до того, как он попадёт в гостевую систему.
  2. Если машина спит, пакет не передаётся сразу. Он перехватывается в сети и помещается в stash (буфер): доставляется после пробуждения, RST клиенту не приходит.
  3. Одновременно в оркестратор отправляется событие «пробуждение». Lockless-очередь, без блокировок.
  4. Пока машина не проснулась, последующие пакеты клиента обрабатываются так же. Не доставляются, но и лишние пробуждения не инициируют. Один пакет не вызывает десять одинаковых событий.

Клиент при этом не получает RST и не завершается с ошибкой. Его TCP-стек сам делает повторную отправку, стандартный backoff (алгоритм задержек) по RFC 793, и второй пакет уже попадает на проснувшуюся машину.

Вычислительный слой: оркестратор и VM

Оркестратор, то есть shard-лидер, получает событие и делает три вещи. Сначала находится владелец пакета: метка в пакете сразу говорит, какой именно VM этот IP, перебирать ничего не надо. Затем стартует восстановление из контрольной точки: создаётся процесс гипервизора, гостевая ОС запускается вместе с процессами. После этого управление сетью возвращается. Пакеты идут на машину в обычном режиме, без прокси и без буферизации.

Восстановление идёт по требованию, lazy (ленивая загрузка). Сначала поднимается ядро и процессы, а оперативная память подкачивается по мере использования. Только те страницы, которые гость реально читает. Поэтому «проснулся» не означает «загружен полностью». По замерам весь процесс от события до того момента, когда машина принимает пакеты, занимает около 150 мс.

Хранилище: контрольная точка и репликация

  • Сон: когда трафика нет дольше idle_timeout_seconds, машина создаёт контрольную точку (checkpoint). Сохраняет оперативную память и состояние на реплицированный том, role=checkpoint, останавливает гипервизор и освобождает vCPU/RAM.
  • Пробуждение: оркестратор монтирует Primary-том и восстанавливает из него память и состояние.
  • Надёжность: том реплицирован на несколько узлов. Узел с Primary вышел из строя, выживший узел поднимает свою реплику и восстанавливает машину без потери данных.

4. Детальная диаграмма: от первого пакета до ответа

Путь одного входящего запроса, пока машина спит. Запрос проходит через три слоя: сеть, вычисления, хранилище.

 Клиент (wg peer, 10.42.0.2)
 │
 ▼  пакет → 10.42.0.3 (машина спит)
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐
│  СЕТЬ (ядро, VPC overlay)                                                                        │
│  t=0   состояние: 10.42.0.3 → DORMANT                                                            │
│  t=0   перехват: пакет → буферизация (stash), wake → lockless-очередь                            │
│  t=0   событие «wake» → lockless-очередь оркестратора                                            │
│  t=200+ дальнейшие пакеты клиента: обычный режим, RTT 10–18 мс, 0% потерь                        │
│  t≈130 ответ на первый пакет ≈ 136 мс (время пробуждения); машина проснулась                     │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘
        │ wake event (lockless)
        ▼
┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│  ВЫЧИСЛЕНИЯ (оркестратор + VM)                                                                                     │
│  t≈0   resolve owner (по метке, O(1)); single-flight: одно восстановление на цель                                  │
│  t≈100 коммит решения → restore из checkpoint (lazy RAM: подгружаются только нужные страницы)                      │
│  t≈150 гостевая ОС запущена; vCPU возобновлены; адрес анонсирован; состояние → ACTIVE                              │
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
        │ snapshot path (idle) / primary path (wake)
        ▼
┌──────────────────────────────────────────────────────────────────────────────────────────────────────┐
│  ХРАНИЛИЩЕ (реплицированный том)                                                                     │
│  wake:   Primary том → mount → map memory-ranges → lazy restore                                      │
│  idle:  idle_timeout → снимок → запись ОЗУ → освобождение ресурсов → DORMANT                         │
│  Репликация: Primary + replica; переживает отказ узла                                                │
└──────────────────────────────────────────────────────────────────────────────────────────────────────┘

Сеть не ждёт. Пакет перехватывается мгновенно, событие уходит в lockless-очередь. Вычисления не дублируют работу, потому что single-flight: 100 клиентов, пришедших одновременно, вызывают одно восстановление. Хранилище не блокирует. Primary том поднимается параллельно с восстановлением памяти. Клиент не падает. Пакет не сбрасывается с ошибкой: первый пакет буферизуется и повторно передаётся (replay) после пробуждения. Без буферизации клиент повторит отправку по RFC 793.


5. Замеры: сколько реально занимает пробуждение

Пример 1: ping с короткими интервалами

Ubuntu VM, serverless-режим включён, VPC 10.42.0.0/24, клиент wg peer 10.42.0.2, машина 10.42.0.3. Замеры я делал сам: первый пакет буферизуется в сети и достаётся после пробуждения, поэтому ответ приходит уже по первому пакету, без ожидания повторной отправки клиента.

$ ping -c 10 -i 0.2 10.42.0.3
PING 10.42.0.3 (10.42.0.3) 56(84) bytes of data.
64 bytes from 10.42.0.3: icmp_seq=1 ttl=64 time=136 ms
64 bytes from 10.42.0.3: icmp_seq=2 ttl=64 time=15.7 ms
64 bytes from 10.42.0.3: icmp_seq=3 ttl=64 time=13.8 ms
64 bytes from 10.42.0.3: icmp_seq=4 ttl=64 time=14.6 ms
64 bytes from 10.42.0.3: icmp_seq=5 ttl=64 time=13.8 ms
64 bytes from 10.42.0.3: icmp_seq=6 ttl=64 time=12.9 ms
64 bytes from 10.42.0.3: icmp_seq=7 ttl=64 time=10.6 ms
64 bytes from 10.42.0.3: icmp_seq=8 ttl=64 time=17.5 ms
64 bytes from 10.42.0.3: icmp_seq=9 ttl=64 time=14.5 ms
64 bytes from 10.42.0.3: icmp_seq=10 ttl=64 time=12.2 ms

--- 10.42.0.3 ping statistics ---
10 packets transmitted, 10 received, 0% packet loss, time 1807ms
rtt min/avg/max/mdev = 10.575/26.199/136.400/36.777 ms, pipe 3

Все 10 пакетов получили ответ. Потери нулевые. Первый пакет пришёл, пока машина спала. Ответ на него пришёл позже, через 136 мс. Это и есть время пробуждения. Второй пакет, через 200 мс, уже попал на живую машину. 15.7 мс. Дальше штатный режим, 10–18 мс.

То есть scale-from-zero занял около 130 мс. Ответ пришёл на первый же пакет, а не на повторную отправку клиента.

Пример 2: Nginx на той же машине

$ time curl -I 10.42.0.3
HTTP/1.1 200 OK
Server: nginx/1.24.0 (Ubuntu)
Date: Fri, 09 Oct 2026 11:44:51 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Wed, 07 Oct 2026 19:20:26 GMT
Connection: keep-alive
ETag: "6ac69b7a-267"
Accept-Ranges: bytes

Executed in  232.40 ms    fish           external
   usr time    3.61 ms  188.00 micros    3.42 ms
   sys time    0.06 ms   58.00 micros    0.00 ms

Полный цикл от первого пакета до ответа Nginx занял около 230 мс, и причина простая: гостевой сервис должен подняться, прежде чем запрос можно обработать. Nginx поднялся как сервис, открыл socket, взял запрос. Память подгружается по требованию, lazy restore поднимает только нужные страницы. А ping — это эхо ядра гостя, поэтому ответ прилетает почти сразу после того, как ядро проснулось: около 130 мс. Для реального HTTP-сервиса первая задержка около 230 мс. Для ping около 130 мс.

Для сравнения: без буферизации первого пакета ответ придёт только к повторной отправке клиента. Это около 380 мс для Nginx и около 200 мс для ping. Буферизация выигрывает около 100–150 мс.

Почему не «мгновенно»? Первый пакет пришёл, пока машина спала. Он не теряется, а буферизуется и доставляется после пробуждения. Задержка первого ответа складывается из времени восстановления и запуска сервиса. И ничего больше.

Пример 3: psql на базе данных

База данных спит. Захожу в неё через psql по частному IP:

$ time psql -h 10.42.0.2 \
  -U postgres -c 'EXPLAIN (ANALYZE) SELECT relname,
  pg_size_pretty(pg_total_relation_size(oid)) FROM pg_class
  ORDER BY pg_total_relation_size(oid) DESC LIMIT 10;'
 ... (дерево плана запроса)
 Planning Time: 5.556 ms
 Execution Time: 127.421 ms
(8 rows)

Executed in  562.82 ms

Клиент видит 562.82 мс. Сама команда отработала за 127.4 мс, планирование заняло 5.5 мс. Остальные ~435 мс — это пробуждение VM. То есть первый вызов к спящей базе складывается из пробуждения и нормального запроса, а число, которое видит клиент, честно отражает цену этого вызова.


6. Как засыпает машина (scale-to-zero)

Когда трафика нет дольше idle_timeout_seconds:

  1. Сетевой слой регистрирует: last_packet_ns не обновлялся дольше таймаута.
  2. Оркестратор переводит машину в состояние quiescing (приостановка).
  3. VM создаёт контрольную точку (checkpoint). Сохраняет память и состояние.
  4. Гипервизор останавливается. vCPU и RAM освобождаются (deallocated).
  5. Сеть переводит IP в состояние DORMANT.

Засыпание учитывает весь трафик, и входящий, и исходящий. Если машина сама отправляет пакеты, например телеметрия, таймер сбрасывается и она не засыпает. Есть жёсткий потолок, max_awake_seconds. Максимальное время непрерывной работы. Даже с активным исходящим трафиком машина принудительно засыпает, если проработала дольше лимита. Это закрывает слепую зону тихого исходящего трафика, который сам по себе не позволяет перейти в спящий режим.


7. Что это даёт на практике

  • Экономия на вычислениях. Машина не расходует vCPU/RAM, пока нет трафика. Для сервисов с редким доступом это снижение затрат на 90–99%.
  • Быстрый отклик (latency). Задержка около 100–250 мс, пользователь её не заметит. Для большинства сервисов это «мгновенно».
  • Нет потери состояния. Процессы, память, открытые сокеты сохраняются в контрольной точке. После пробуждения машина помнит, что делала.
  • Надёжность. Хранилище реплицировано. Узел может умереть, машина восстановится на выжившем узле.
  • Простота. Не нужно писать логику переподключения в приложении. Клиент просто отправляет пакет, машина просыпается.

8. Ограничения и подводные камни

  • Первый запрос медленнее. Задержка около 100–250 мс. Это цена сна. Если нужна нулевая задержка, держите машину активной. Отключите serverless.
  • Исходящий трафик. По умолчанию машина засыпает, когда нет трафика дольше таймаута. Здесь есть ловушка: тихий исходящий трафик, например телеметрия, не даёт заснуть. Задайте max_awake_seconds, жёсткий потолок непрерывной работы, после которого машина засыпает независимо от трафика.
  • Не для всех сервисов. Если сервис должен быть доступен 24/7 без задержек, serverless не подходит. Для таких случаев подойдёт обычная VM без serverless.
  • Контрольная точка занимает место. RAM плюс 128 МБ на том. Для больших машин это заметный объём.

9. Как включить

При создании виртуальной машины укажите:

{
  "serverless": {
    "enabled": true,
    "mode": "checkpoint",
    "idle_timeout_seconds": 60,
    "trigger_ports": [],
    "max_awake_seconds": 0
  }
}
  • mode: “checkpoint”. Восстановление из контрольной точки. Быстрее, чем холодный запуск.
  • idle_timeout_seconds: 60. Засыпать после 60 секунд без трафика.
  • trigger_ports: []. Любой порт. Пустой список значит любой трафик.
  • max_awake_seconds: 0. Без жёсткого потолка. Ноль значит выключено.

Источники и что почитать дальше

Как проверяли: Ubuntu VM, serverless-режим, VPC 10.42.0.0/24, клиент wg peer 10.42.0.2, машина 10.42.0.3. Замеры: ping -c 10 -i 0.2 (первый ответ на 1-й пакет, около 136 мс, 0% потерь), time curl -I (полный цикл около 230 мс) и time psql (первый запрос к спящей базе ~563 мс, сама команда ~127 мс). Проверено на 3-узловом кластере с реплицированным хранилищем.

FAQ

Почему не RST, а тихое падение (silent drop)? RST — это ошибка для клиента. Пока машина спит, пакеты перехватываются в сети: первый буферизуется и достаётся после пробуждения, а если клиент повторит отправку по RFC 793, второй пакет и так попадёт на проснувшуюся машину.

Может ли машина засыпать, если есть активные соединения? Да, если трафика нет дольше idle_timeout_seconds. Активные соединения завершатся, машина засыпает, и следующий запрос пробудит её за около 100–250 мс. Если нужен жёсткий лимит непрерывной работы, задайте max_awake_seconds.

Что происходит с открытыми TCP-соединениями при засыпании? Они завершаются. Пакеты к спящей машине не доставляются, клиент переподключается, и новый запрос уже пробудит машину.

Нужен ли домен для работы? Нет. Машина работает по частному IP в VPC. Домен .msrv.space — это для публикации наружу, HTTPS-шлюз.

Запускайте serverless виртуалки, контейнеры и базы данных

microsrv обеспечивает мгновенный старт виртуальных машин и контейнеров, быстрые СУБД на NVMe, а VPC и шлюзы доступа уже включены в стоимость.

Перейти в консольЗадать вопрос