Сколько RAM нужно для Temporal
Temporal — не одно приложение, а связка из нескольких сервисов плюс база данных плюс (обычно) Elasticsearch, и ваши собственные воркеры добавляются сверху. Поэтому вопрос «сколько RAM нужно для Temporal» на самом деле распадается на четыре разных вопроса: сколько ест сам сервер, сколько ест хранилище, сколько ест визибилити и сколько съедят воркеры под вашу конкретную нагрузку. Ниже — ориентиры по каждому слою и рабочие конфиги, с которых можно стартовать, не гадая на кофейной гуще.
Содержание
- Из чего состоит Temporal и почему один ответ тут не работает
- Минимальный стенд для разработки и тестов
- Прод-стек: сколько закладывать по компонентам
- Кэш History-сервиса — главный рычаг для памяти сервера
- Сколько памяти съедают воркеры — и это не Temporal Server
- Как ограничить память в docker-compose и systemd, чтобы не поймать OOM-killer
- Мониторинг: откуда брать реальные цифры, а не ориентиры из статьи
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Из чего состоит Temporal и почему один ответ тут не работает
Temporal Server — это набор из четырёх логических сервисов: Frontend (принимает gRPC-запросы), History (хранит и продвигает состояние workflow), Matching (раздаёт задачи воркерам) и внутренний Worker-сервис (системные workflow вроде архивации). В деве и в небольших инсталляциях все четыре обычно запускают одним процессом temporal-server — тогда память считается одним куском. В нагруженном проде их разносят по отдельным подам/контейнерам с независимым масштабированием, и тогда каждый сервис нужно сайзить отдельно, в первую очередь History — он держит кэш workflow mutable state и обычно самый прожорливый.
Дальше — persistence store: PostgreSQL, MySQL или Cassandra хранят историю событий workflow, задачи, таймеры. Дальше — visibility store: либо тот же SQL (базовый функционал поиска), либо отдельный Elasticsearch (расширенный поиск по атрибутам, рекомендуется для прода). И наконец воркеры — это уже не Temporal, а ваш код на Go/Java/TypeScript/Python/.NET SDK, который выполняет activities и продвигает workflow; их память зависит от вашей логики и настроек кэша, а не от самого Temporal.
Отсюда и вывод: нельзя ответить «X гигабайт» без уточнения, что именно вы поднимаете — тестовый стенд на одной VM или прод-кластер с реальным потоком workflow.
Минимальный стенд для разработки и тестов
Для локальной разработки и CI Temporal предлагает temporal server start-dev — встроенный SQLite и упрощённый UI, без внешних зависимостей. Это самый лёгкий вариант, и он комфортно работает в пределах 1-2 ГБ RAM, включая память самого процесса разработчика (IDE, тесты). Для CI-контейнера этого достаточно с большим запасом.
Если нужен более «настоящий» стенд — официальный docker-compose из репозитория temporalio/docker-compose поднимает связку из PostgreSQL, Temporal Server (all-in-one), Temporal UI и Elasticsearch. Ориентировочно на такой стек стоит закладывать 3-4 ГБ RAM для комфортной работы без свопа: сам сервер и Postgres в простое держатся в пределах нескольких сотен мегабайт каждый, но Elasticsearch по умолчанию резервирует память под heap заранее (обычно половину доступной ОЗУ или заданный ES_JAVA_OPTS), и именно он чаще всего оказывается узким местом на маленькой VPS.
# фрагмент docker-compose.yml — сознательно урезанная память ES для теста
elasticsearch:
image: elasticsearch:7.17.9
environment:
- discovery.type=single-node
- ES_JAVA_OPTS=-Xms256m -Xmx256m
mem_limit: 700m
postgresql:
image: postgres:15
environment:
- POSTGRES_PASSWORD=temporal
mem_limit: 400m
temporal:
image: temporalio/auto-setup:1.24.2
mem_limit: 700m
С такими лимитами весь стек укладывается примерно в 2 ГБ и стабильно живёт на VPS с 4 ГБ RAM, оставляя запас под ОС и ваши воркеры. Если Elasticsearch вам не критичен на этапе теста — можно временно отключить advanced visibility и обойтись базовым SQL-визибилити, это ещё срежет память.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверПрод-стек: сколько закладывать по компонентам
Для боевой инсталляции цифры зависят от throughput (workflow/сек, размер истории событий, retention), но есть практичные отправные точки, от которых можно отталкиваться и дальше корректировать по метрикам:
| Компонент | Старт для небольшой нагрузки | Что растит потребление |
|---|---|---|
| Frontend service | 512 МБ – 1 ГБ | число одновременных gRPC-соединений |
| History service | 1-2 ГБ на инстанс | размер кэша mutable state, число открытых workflow, глубина истории событий |
| Matching service | 512 МБ – 1 ГБ | число task queue и глубина backlog задач |
| Internal Worker | 256-512 МБ | архивация, батчевые операции |
| PostgreSQL/MySQL | от 2 ГБ | размер БД, shared_buffers, число соединений |
| Elasticsearch (visibility) | от 2 ГБ heap + столько же под ОС | retention видимости, число search-атрибутов |
| Temporal UI | 128-256 МБ | практически не масштабозависим |
Это стартовые ориентиры, а не гарантированные цифры — у Temporal нет единого «на 1000 workflow/сек нужно N гигабайт», потому что решает не количество workflow, а их «тяжесть»: сколько активностей на workflow, насколько длинная история событий, как часто идут сигналы и таймеры. Небольшой прод (десятки-сотни workflow в минуту, разумные истории) обычно укладывается в 8-12 ГБ на весь стек с запасом; если у вас long-running saga-процессы с тысячами событий в истории — сайзинг History и Postgres придётся пересматривать индивидуально, ориентируясь на реальные метрики, а не на статьи в интернете.
Отдельно про PostgreSQL как хранилище — под Temporal стоит явно выставить shared_buffers и max_connections, потому что History-сервис держит собственный connection pool на каждый инстанс, и при горизонтальном масштабировании History число соединений к базе растёт линейно.
Кэш History-сервиса — главный рычаг для памяти сервера
Самый управляемый параметр памяти со стороны сервера — размер кэша shard'ов и mutable state в History-сервисе. По умолчанию Temporal кэширует состояние активных workflow в памяти, чтобы не перечитывать историю событий из базы на каждый шаг. Чем больше кэш — тем меньше нагрузка на Postgres/Cassandra и быстрее обработка, но тем больше RAM резервируется заранее.
Ключевые переменные (задаются в dynamicconfig или через переменные окружения History-сервиса):
# dynamicconfig/production.yaml — фрагмент
history.cacheMaxSize:
- value: 512
constraints: {}
history.shardOwnershipTransferDelay:
- value: 5s
history.cacheMaxSize — это число закэшированных mutable state, не мегабайты напрямую, но именно этим параметром вы регулируете компромисс «память сервера против нагрузки на БД». На старте лучше не задирать значение выше дефолтного, пока не увидите по метрикам cache_hit_ratio, что кэш реально мажет мимо и стоит расширять.
Сколько памяти съедают воркеры — и это не Temporal Server
Воркеры — это ваш собственный процесс на SDK, и по умолчанию Temporal Server о них ничего не знает: они просто long-poll'ят Matching-сервис за задачами. Память воркера определяется вашими настройками конкурентности и sticky-кэшем workflow execution — это тоже кэш, но уже на стороне клиента, и он тоже резервируется заранее.
В Go SDK и Java SDK основные ручки:
// Go SDK — настройки worker.Options
workerOptions := worker.Options{
MaxConcurrentActivityExecutionSize: 100,
MaxConcurrentWorkflowTaskExecutionSize: 50,
// размер sticky-кэша workflow — держит состояние в памяти между тасками
// чтобы не гонять полную историю по сети на каждый шаг
}
worker.SetStickyWorkflowCacheSize(10000)
Каждая запись в sticky-кэше — это состояние одного workflow execution, и её вес зависит от того, сколько локальных переменных и side-эффектов держит ваш workflow-код в памяти. На практике для несложных workflow (пара десятков полей состояния) 10 000 записей в кэше — это, ориентировочно, от нескольких сотен мегабайт до пары гигабайт RAM у воркера, но точную цифру даст только замер на вашем реальном коде: слишком по-разному ведут себя workflow с большими payload'ами и workflow с примитивным состоянием. Если воркер регулярно упирается в память — первое, что стоит уменьшить, это именно StickyCacheSize и MaxConcurrentWorkflowTaskExecutionSize, а не память сервера.
Как ограничить память в docker-compose и systemd, чтобы не поймать OOM-killer
Temporal Server написан на Go, и без явных лимитов Go-рантайм не всегда агрессивно отдаёт память обратно ОС — сборщик мусора ориентируется на кучу, а не на общий RAM хоста. На небольшой VPS это может выглядеть как «Temporal ест всю память», хотя реально это накопленный, но не освобождённый heap. С Go 1.19+ (в том числе в актуальных сборках Temporal) помогает GOMEMLIMIT:
services:
temporal:
image: temporalio/auto-setup:1.24.2
environment:
- GOMEMLIMIT=700MiB
mem_limit: 900m
deploy:
resources:
limits:
memory: 900M
GOMEMLIMIT задаёт мягкий потолок для кучи Go-рантайма — сборщик мусора начинает работать агрессивнее, приближаясь к лимиту, вместо того чтобы ждать системного OOM. Ставьте его заметно ниже mem_limit контейнера (в примере — 700 МБ кучи против 900 МБ лимита), чтобы оставить запас под стек и служебные аллокации рантайма.
Для Elasticsearch правило простое и хорошо задокументированное: Xms и Xmx должны быть равны (чтобы JVM не перевыделяла heap на лету) и не превышать половину доступной контейнеру памяти — оставшаяся половина уходит под файловый кэш ОС, на котором Elasticsearch реально экономит на чтении индексов. Про общие принципы контроля памяти в контейнерах — в статье про лимиты CPU и памяти в Docker.
Мониторинг: откуда брать реальные цифры, а не ориентиры из статьи
Все ориентиры выше — стартовая точка, а не окончательный сайзинг. Правильный путь — поднять стек с щедрым, но ограниченным лимитом, снять метрики за неделю-две реальной нагрузки и подрезать по факту. Temporal из коробки отдаёт Prometheus-метрики с каждого сервиса (temporal_server_...), а также SDK-метрики с воркеров (temporal_sticky_cache_size, temporal_worker_task_slots_available и т.п.) — их удобно завести в готовую связку Prometheus и Grafana и смотреть не только на CPU/RAM хоста, но и на внутренние метрики самого Temporal: cache hit ratio у History, размер очередей у Matching, задержки task dispatch у Matching-сервиса.
Общее практическое правило по резерву памяти на сервере — не только для Temporal, но полезно держать в голове: сколько RAM закладывать с запасом в принципе, чтобы не жить впритык и не ловить деградацию при пиках. Для Temporal это особенно актуально, потому что резкий рост числа одновременно открытых workflow (например, при ретраях после сбоя) кратковременно раздувает и History-кэш, и sticky-кэш воркеров одновременно.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Можно ли запустить Temporal на VPS с 2 ГБ RAM?
Для temporal server start-dev с SQLite — да, комфортно. Для полноценного docker-compose стека с Postgres и урезанным Elasticsearch — впритык, реалистичнее закладывать от 4 ГБ, иначе высок риск свопа или OOM при первом же пике.
Обязателен ли Elasticsearch для продакшна?
Формально нет — базовая визибилити на SQL работает и в проде, но с ограничениями по фильтрам поиска и производительности на больших объёмах workflow. Elasticsearch добавляет минимум 2 ГБ heap и столько же под файловый кэш ОС, так что решение — это компромисс между удобством поиска и расходом памяти.
Что съедает память быстрее — сервер Temporal или воркеры?
Зависит от профиля нагрузки. Много коротких простых workflow — узкое место обычно в Matching и в connection pool к базе. Мало, но «тяжёлых» long-running workflow с большим состоянием — узкое место чаще в sticky-кэше воркеров и в History-кэше сервера.
Растёт ли потребление RAM с ростом retention истории workflow?
Напрямую влияет на размер базы данных (Postgres/Cassandra), а не на RAM сервера в состоянии простоя — но при переиндексации, бэкапах и при повторном чтении длинных историй нагрузка на память СУБД растёт заметно, так что длинный retention стоит закладывать сразу в сайзинг персистентности, а не только приложения.
Нужен ли отдельный сервер под Temporal, или можно на одной VPS с другими сервисами?
Для теста и небольшой нагрузки — можно совмещать, если явно выставлены mem_limit на каждый контейнер. Для прода с реальным SLA лучше разносить хотя бы БД на отдельный инстанс — Temporal и так генерирует ощутимую нагрузку на I/O к персистентности, и соседние сервисы будут с ней конкурировать за память страничного кэша.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →