Вертикальное масштабирование: выжать максимум из одной машины
Для кого: инженеры и техлиды, которым уже мало «просто добавить сервер». Если вы не можете объяснить, почему CPU 30% при P99 = 2 секунды, или не знаете, что делать с 500k QPS на одной БД — этот урок выбьет почву из-под ног.
Провокация №1: вы профилировали продакшен?
- Симптом: «У нас тормозит, но CPU свободен». Перевод: никто не снимал профили, никто не знает, где время тратится.
- Проверьте себя: если последняя запись в
profile.txtстарше месяца, вы не контролируете систему. - План минимум: перед каждым релизом запускайте профилирование хотя бы на staging под реальной нагрузкой. Раз в неделю — на production replica (read-only) или с помощью
py-spy/perf.
1. Когда вертикальное масштабирование оправдано
| Признак | Что делаем |
|---|---|
| CPU < 60%, P99 растёт | Профилируем код, ищем блокировки, I/O-узкие места |
| CPU > 80% и линейный рост RPS | Scale-up + оптимизация — ещё есть смысл |
| Память > 85%, GC > 200 мс | Убираем утечки, уменьшаем heap, включаем G1/ZGC, переходим на streaming |
| Диски/сеть упёрлись | Добавляем NVMe, 25–100G NIC, оптимизируем batching |
| Один сервер дороже кластера | Пора scale-out. Вертикаль исчерпана |
Ключевой критерий: если удвоение CPU/RAM даёт <50% прироста throughput или стоит дороже, чем добавление второго сервера + LB, значит, пора горизонтально.
Провокация №2: вы знаете top-3 функции по CPU?
- Node.js/Go/Python — неважно. Если вы не можете назвать топ-3 «пожирателя CPU» за последние сутки, значит, у вас нет наблюдаемости.
- Чек:
node --prof,go tool pprof,py-spy top. Если команда их никогда не запускала — начинайте с этого.
2. Профилирование: инструменты и практика
2.1 Node.js
- CPU профили:
node --prof app.js node --prof-process isolate-*.log > profile.txt head -50 profile.txt - Flamegraph:
clinic flame -- node app.js. - Event loop lag:
clinic bubbleprof. - Chrome DevTools:
node --inspect, открываемchrome://inspect, записываем CPU/Heap Snapshot.
Action: заведите регламент «1 flamegraph в неделю по топ-эндпоинту». Без доказательств (где CPU сгорает) оптимизация = пальцем в небо.
2.2 Python / Go / JVM
- Python:
cProfile,line_profiler,py-spy record -o profile.svg --pid. Используйтеasyncioдля I/O-bound задач. - Go:
go test -cpuprofile=cpu.out,go tool pprof cpu.out. Добавьте/debug/pprofна сервисы. - JVM:
async-profiler,jcmd,Java Flight Recorder. Следите за GC-паузами (gc_pause_seconds), heap usage.
2.3 Наблюдаемость
- Prometheus/Grafana:
process_cpu_seconds_total,process_resident_memory_bytes,gc_pause_duration_seconds,thread_count. - Tracing: Jaeger/Zipkin, чтобы видеть cross-service задержки.
- Логи: включите structured logging с request ID и latency.
Провокация №3: «Нам не нужна оптимизация, у нас монолит»
Монолит = не повод игнорировать performance. Часто проще оптимизировать 2 функции, чем запускать 10 микросервисов.
3. Оптимизация CPU/памяти/I/O
3.1 Антипаттерны
| Проблема | Диагностика | Решение |
|---|---|---|
| N+1 запросы | pprof/tracing показывают каскады | JOIN, batch, DataLoader |
| Большие JSON в памяти | Heap > 80%, GC паузы | Streaming (pipeline, chunked), gzip на лету |
| Синхронные блокировки | Flamegraph показывает sync.Mutex | Async queue, sharded locks, actor model |
| Локальный кэш без лимита | RSS растёт линейно, после деплоя падает | LRU/TTL, Redis |
| Логирование всего подряд | CPU/Mem spikes на io.Writer | Async logging, sampling |
3.2 Node.js рецепты
- Event loop: выносите долгие операции в worker threads/queues.
- Cluster mode:
pm2 start app.js -i max, следите заpm2 monit. - Memory: используйте
clinic heapprofiler, ограничивайтеmax_old_space_size, отключайте «бесконечные» кэши. - Streaming: вместо
res.json()огромного массива →Readable.from(cursor)+res.write.
3.3 Python рецепты
asyncio,aiohttp,httpx→ параллельные I/O.uvloopускоряет event loop.- GIL: вынесите CPU-bound в C extensions/Cython или процессы (
multiprocessing, Celery worker). pydanticv2/dataclassesвместоdictдля структурированного кода.
3.4 JVM/Go рецепты
- JVM: G1/ZGC,
-XX:+UseStringDeduplication,-Xms=-Xmx(фиксированный heap), избегайтеsynchronizedбольших секций. - Go: профилируйте
goroutine blocking, используйтеcontext, не выделяйте огромные слайсы в циклах.
Провокация №4: ваша БД — узкое место?
- Симптомы: CPU БД > 80%,
pg_locksпоказывает contention, реплики отстают. - Чек-лист:
- Добавьте индексы (95% успеха).
- Включите connection pooling (
pgbouncer,HAProxy,pgxpool). - Уберите long transactions, делайте batch insert/update.
- Используйте
EXPLAIN ANALYZEдля топ-5 запросов. - Включите read replica + app-side routing, если 80% трафика — чтение.
Decision: если одна БД дороже, чем шардированный кластер (Citus/Vitess), думайте про горизонталь. Но сначала выжмите максимум: индексы, partitioning, caching.
4. Memory & GC дисциплина
- Node: следите за
heapUsed,heapTotal,external. GC паузы > 100 мс = тревога. - JVM:
GC pause > 200 мс→ сокращайте heap, меняйте GC, уменьшайте allocation rate. - Python: используйте
objgraph,tracemallocдля утечек, избегайте циклических ссылок. - Go:
GOGC=100по умолчанию. Повышайте для экономии памяти, снижайте для латентности.
Практика: подключите автоматический alert, если rss > 80% или gc_pause > 200ms. Утечки ищите через heap dump + сравнение до/после нагрузки.
Провокация №5: I/O — немой убийца
- Симптом: CPU свободен, latency зашкаливает. Значит, упёрлись в диски/сеть.
- Решение:
- NVMe вместо HDD, RAID10 вместо RAID5.
- 25G/40G сетевые карты, если у вас Kafka/Redis и большие payload.
- Batching: отправляйте пачки сообщений, а не по одному (Kafka producer batches, HTTP pipelining, gRPC streaming).
- Compression: protobuf + gzip = меньше сети.
5. Экономика вертикального масштабирования: когда scale-up дороже scale-out
Критический вопрос: Купить один сервер за $1000/мес или 5 серверов по $150/мес? Неправильный выбор = переплата в 2-3 раза.
5.1 Ценовая нелинейность: закон убывающей отдачи
Проблема: CPU/RAM/disk не масштабируются линейно по цене.
Пример (AWS EC2, us-east-1, on-demand):
| Instance | vCPU | RAM | Цена/час | Цена/месяц | $/vCPU | $/GB RAM |
|---|---|---|---|---|---|---|
| t3.medium | 2 | 4GB | $0.0416 | $30 | $15 | $7.5 |
| t3.large | 2 | 8GB | $0.0832 | $60 | $30 | $7.5 |
| t3.xlarge | 4 | 16GB | $0.1664 | $120 | $30 | $7.5 |
| t3.2xlarge | 8 | 32GB | $0.3328 | $240 | $30 | $7.5 |
| c6i.4xlarge | 16 | 32GB | $0.68 | $490 | $30.6 | $15.3 |
| c6i.8xlarge | 32 | 64GB | $1.36 | $980 | $30.6 | $15.3 |
| c6i.12xlarge | 48 | 96GB | $2.04 | $1,470 | $30.6 | $15.3 |
Наблюдение:
- T3 family: линейная цена до 8 vCPU
- После 8 vCPU: цена за vCPU удваивается (compute-optimized)
- Большие инстансы (>16 vCPU): premium цена
Вывод: После определенного порога вертикальное масштабирование становится экономически невыгодным.
5.2 Break-even analysis: когда переходить на scale-out
Сценарий: API требует 16 vCPU и 32GB RAM.
Вариант A: Один большой сервер
- Instance:
c6i.4xlarge(16 vCPU, 32GB) - Цена: $490/месяц
- Availability: 99% (single instance)
- Deployment: downtime required
Вариант B: Кластер из 4 серверов
- Instance: 4×
t3.xlarge(4 vCPU, 16GB каждый) - Цена серверы: 4 × $120 = $480/месяц
- ALB: $25/месяц
- Total: $505/месяц
- Availability: 99.95% (multi-instance)
- Deployment: zero-downtime rolling
Сравнение:
| Метрика | Scale-up (1 сервер) | Scale-out (4 сервера) |
|---|---|---|
| Цена | $490/мес | $505/мес (+3%) |
| Availability | 99% | 99.95% |
| Deployment | Downtime | Zero-downtime |
| Failover | Manual (5-10 мин) | Auto (0 секунд) |
| Headroom | Нужен апгрейд | Добавить +1 сервер |
Вывод: При паритете цен scale-out выигрывает по надежности и гибкости.
5.3 Когда вертикальное масштабирование выгоднее
Кейс 1: Маленькая нагрузка (<500 RPS)
- 1× t3.medium ($30/мес) vs 2× t3.small + ALB ($50/мес)
- Экономия: 40% в пользу вертикали
Кейс 2: Stateful приложения (БД, кэш)
- PostgreSQL требует мощный CPU для сложных запросов
- Sharding БД = сложность × 10, costs × 3
- Вертикаль выгоднее до момента, пока 1 инстанс справляется
Кейс 3: Лицензирование per-core
- Oracle, SQL Server, некоторые коммерческие БД
- Цена = cores × license cost
- 1× 32-core ($X) дешевле, чем 4× 8-core ($4X)
Кейс 4: Low latency требования
- Intra-server communication быстрее inter-server
- Network latency = 0 vs 0.5-2ms
- Для highload trading, gaming — критично
5.4 Когда горизонтальное масштабирование выгоднее
Кейс 1: Большая нагрузка (>1000 RPS)
- Break-even point обычно 500-1000 RPS
- После этого scale-out дешевле и надежнее
Кейс 2: Stateless приложения
- API, web servers, workers
- Нет стоимости координации
Кейс 3: Бюрстовая нагрузка
- Auto-scaling легко добавляет/убирает серверы
- Вертикаль требует manual intervention
Кейс 4: Multi-region
- Легко распределить серверы по регионам
- Один большой сервер = single region
5.5 Реальный кейс: оптимизация монолита
Компания: SaaS стартап, монолит на Ruby on Rails
Было:
- 1× c5.9xlarge (36 vCPU, 72GB): $1,530/месяц
- CPU utilization: 40% avg, 90% peak
- Memory: 50GB используется
- Problem: дорого, но scale-out требует рефакторинг
Анализ затрат на рефакторинг:
| Вариант | Setup cost | Monthly cost | Break-even |
|---|---|---|---|
| Оставить как есть | $0 | $1,530 | — |
| Rightsizing (меньший инстанс) | $0 | $765 (c5.4xlarge) | Сразу |
| Scale-out (рефакторинг) | $20,000 (2 месяца работы) | $400 (4× t3.2xlarge + ALB) | 18 месяцев |
Что сделали:
Фаза 1 (немедленно): Right-sizing
- c5.9xlarge → c5.4xlarge (18 vCPU, 36GB)
- Стоимость: $1,530 → $765/месяц
- Экономия: $765/месяц, окупаемость немедленная
Фаза 2 (6 месяцев): Постепенный рефакторинг
- Вынесли фоновые задачи в отдельные воркеры (2× t3.large)
- Добавили Redis для сессий
- Итого: c5.2xlarge (монолит) + 2× t3.large (workers) + Redis
- Стоимость: $380 + $120 + $50 = $550/месяц
- Экономия: $1,530 → $550 = -$980/месяц (-64%)
ROI: Работа заняла 3 месяца ($15k зарплат). Окупилось за 15 месяцев.
5.6 Bare metal vs Cloud: когда самохост выгоднее
Сценарий: Нужен мощный сервер (32 vCPU, 128GB RAM).
Вариант A: AWS c6i.8xlarge
- Цена: $980/месяц (on-demand)
- Reserved 3yr: $550/месяц (-44%)
Вариант B: Hetzner Dedicated (bare metal)
- Server: AX102 (32 cores AMD EPYC, 128GB ECC)
- Цена: €119/месяц (~$130/месяц)
- Setup: €0
Но учтите скрытые costs:
| Статья | Cloud (AWS) | Bare metal (Hetzner) |
|---|---|---|
| Сервер | $550/мес (RI 3yr) | $130/мес |
| Data transfer | $45/мес (500GB) | $0 (20TB included) |
| Backups | Included (snapshots) | $20/мес (external) |
| Monitoring | CloudWatch $10 | Self-hosted $0 / Paid $20 |
| DevOps time | 5% = $400/мес | 15% = $1,200/мес |
| Total | $1,005/мес | $1,370/мес |
Вывод: Bare metal дешевле по железу, но дороже по DevOps времени. Выгоден только при масштабе (10+ серверов, dedica ted DevOps team).
5.7 Reserved Instances для вертикального масштабирования
Стратегия: Если нагрузка стабильна, экономьте 40-72% через Reserved Instances или Savings Plans.
Пример (c5.4xlarge):
| Pricing model | Price/hour | Price/month | Savings vs On-Demand |
|---|---|---|---|
| On-Demand | $0.68 | $490 | 0% |
| Reserved 1yr (no upfront) | $0.438 | $316 | -36% |
| Reserved 1yr (all upfront) | $0.395 | $285 | -42% |
| Reserved 3yr (partial upfront) | $0.289 | $208 | -58% |
| Savings Plans 3yr | $0.320 | $230 | -53% |
Рекомендация:
- Стабильная нагрузка (БД, cache) → Reserved 3yr: экономия 58%
- Растущая нагрузка → Savings Plans: гибкость + 53% экономии
- Burst нагрузка → On-Demand + Auto-scaling
5.8 Формула выбора: scale-up vs scale-out
def should_scale_out(current_cost, num_servers_needed, app_type):
"""
Решает, стоит ли переходить на горизонтальное масштабирование
"""
# Стоимость scale-up (один большой сервер)
scale_up_cost = get_instance_cost(target_specs)
# Стоимость scale-out (кластер малых серверов)
server_cost = get_instance_cost(target_specs / num_servers_needed)
lb_cost = 25 # ALB
scale_out_cost = (server_cost * num_servers_needed) + lb_cost
# Refactoring cost (если stateful)
refactoring_cost = 0
if app_type == "stateful":
refactoring_cost = 20000 # 2 месяца работы
# Break-even period
monthly_savings = scale_up_cost - scale_out_cost
break_even_months = refactoring_cost / monthly_savings if monthly_savings > 0 else float('inf')
# Решение
if break_even_months < 12: # Окупится за год
return True, f"Scale-out окупится за {break_even_months:.0f} месяцев"
else:
return False, "Scale-up выгоднее"
# Пример
should_scale_out(
current_cost=1530, # $1530/мес (c5.9xlarge)
num_servers_needed=4,
app_type="stateful"
)
# Output: (True, "Scale-out окупится за 18 месяцев")5.9 Cost optimization checklist для вертикального масштабирования
- Профилирование перед апгрейдом: Убедитесь, что bottleneck — это CPU/RAM, а не код
- Right-sizing: Проверьте utilization (CloudWatch, Datadog) — может быть, текущий инстанс избыточен
- Reserved Instances: Если нагрузка стабильна минимум год, экономьте 40%+
- Spot для dev/staging: Экономия 70% для некритичных окружений
- Сравните с scale-out: Посчитайте break-even point
- Учтите DevOps costs: Время команды = деньги
- Амортизация refactoring: Окупается ли переход на scale-out за 12-24 месяца?
6. Практическая дисциплина
- Профилирование:
- Раз в неделю делайте flamegraph/top для топ-эндпоинта.
- Сравнивайте профили до/после релизов.
- Load test scale-up:
- На staging увеличивайте лимиты CPU/памяти и смотрите, где упираетесь.
- Цель: найти точку, когда вертикальный рост перестаёт помогать.
- БД-интенсив:
- Запустите
EXPLAIN ANALYZEдля 5 самых медленных запросов. - Внедрите connection pool, измерьте latency.
- Запустите
- Memory audit:
- Снимите heap dump при нагрузке и после неё.
- Найдите классы/объекты, которые не освобождаются.
- I/O benchmark:
- Используйте
fio,iperf,wrkдля проверки дисков/сети. - Заведите SLO: «max disk latency < 5ms».
- Используйте
- FinOps анализ (НОВОЕ):
- Рассчитайте break-even point: scale-up vs scale-out для вашей нагрузки
- Сравните стоимость: on-demand, reserved instances (1yr, 3yr), savings plans
- Если используете монолит: оцените ROI рефакторинга в scale-out архитектуру
- Посчитайте TCO (Total Cost of Ownership) на 3 года: cloud vs self-hosted
Безопасность: профилирование и load test выполняйте на staging или
изолированных копиях. Для production-профайлеров (py-spy, pprof) следите
за overhead и снимайте не больше 30–60 секунд.
Что дальше
Вы squeezed всё из одной машины. Теперь либо переходите к горизонтальному масштабированию (уже умеете после урока 02), либо проектируйте гибридную схему: scale-up + scale-out. В следующих уроках — кэширование, очереди, CQRS. Каждый шаг будет таким же жестоким к самообману, как этот.