Оригинал: CloudNativePG Recipe 6: Postgres Vertical Scaling with Storage — part 1 — 8 апреля 2024.
Боитесь, что Postgres «не масштабируется на запись» в одном узле? Думаете, что масштабировать его можно только горизонтально? Не торопитесь хоронить вертикальное масштабирование.
На KubeCon EU 2024 в Париже автор вместе с Gari Singh из Google Cloud делал доклад «Scaling Heights: Mastering Postgres Database Vertical Scalability with Kubernetes Storage Magic» (запись есть на YouTube-канале CNCF).
Кластер PostgreSQL — это primary + произвольное число read-only реплик. Главная ошибка — поспешно навешивать на Postgres ярлык «не масштабируется». Масштабирование не обязательно про разные ноды; на одной ноде тоже можно вытащить максимум из CPU, RAM и storage.
Начинайте с простого
Прежде чем уходить в горизонтальное масштабирование — измерьте. Активный/активный кластер по разным нодам — это ад с точки зрения DR/HA по сравнению с обычной парой primary + standby.
Алгоритм:
- Зафиксируйте бизнес-цели: RPO, RTO, нужный TPS.
- Прогоняйте бенчмарки: storage —
fio, БД —pgbench(либо встроенный OLTP, либо ваши кастомные запросы). - Если результаты не дотягивают — вот тогда смотрите в сторону active/active.
Тонкая настройка вертикали полезна и в распределённой схеме — узлы всё равно нужно выжимать.
Масштабирование томами в CloudNativePG
CloudNativePG управляет PersistentVolumeClaim напрямую, не используя StatefulSet (как делает большинство операторов). Тот же путь выбрала Strimzi для Kafka. Подробности — в разделе «Pod Controller» документации CloudNativePG.
Каждый инстанс обязательно имеет volume для PGDATA — секция storage. Дополнительно (опционально):
walStorage— отдельный volume для WAL;tablespaces— произвольное число PostgreSQL-tablespaces.
Каждому volume можно прописать свой storage class. Это удобно для:
- WAL → быстрый/дорогой класс;
- холодные tablespaces → медленный/дешёвый.
Volume можно добавлять и ресайзить на живом кластере (если CSI поддерживает). Плюсы такого масштабирования:
- изоляция и предсказуемость I/O;
- распределение I/O по томам;
- быстрее обслуживание (индексирование, REINDEX, VACUUM).
Интеграция с HA/DR не страдает — поддерживается volume snapshot backup/restore даже для больших БД. Знание сильных/слабых сторон ваших CSI-решений критично.
Отдельный volume для WAL
Минимальный пример:
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: separate-wal
spec:
instances: 3
storage:
size: 40Gi
walStorage:
size: 10GiКаждый инстанс получит два volume — для PGDATA и для WAL. CloudNativePG сам поддерживает symlink, чтобы pg_wal указывал в WAL-volume. Если добавляете WAL-volume на живом кластере — будет rolling update: реплики останавливаются по очереди, WAL переносится, symlink обновляется. Поведение primary настраивается через primaryUpdateMethod (рестарт или switchover).
По бенчмаркам автора, отдельный том под WAL даёт прирост 15–45% в зависимости от соотношения объёмов в памяти и на диске.
Важно правильно засайзить WAL-volume и согласовать с min_wal_size / max_wal_size. Если место кончится — Postgres встанет колом (в CloudNativePG идёт обсуждение, как это смягчить).
Можно прописать отдельный storage class:
walStorage:
storageClass: my-favourite-storage-class-for-wals
size: 10GiTemporary tablespaces
В PostgreSQL можно завести один или несколько временных tablespaces — для temp-таблиц, индексов и временных файлов сортировки. Управляются параметром temp_tablespaces. CloudNativePG абстрагирует это через .spec.tablespaces[*].temporary:
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: tmp-tablespace
spec:
instances: 3
storage:
size: 40Gi
walStorage:
size: 10Gi
tablespaces:
- name: tmptbs1
temporary: true
storage:
size: 40GiПроверка:
kubectl exec -ti tmp-tablespace-1 -c postgres \
-- psql -c 'SHOW temp_tablespaces' temp_tablespaces
------------------
tmptbs1
(1 row)Можно добавить второй:
tablespaces:
- name: tmptbs2
temporary: true
storage:
size: 40GiПроверка покажет:
temp_tablespaces
------------------
tmptbs1,tmptbs2
(1 row)Внутри транзакции PostgreSQL выбирает один tablespace из списка случайно и потом идёт по нему последовательно.
Разделение I/O для индексов и таблиц
Классика: один tablespace под таблицы (data), второй — под индексы (idx):
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: idx-tablespace
spec:
instances: 3
storage:
size: 40Gi
walStorage:
size: 10Gi
tablespaces:
- name: data
storage:
size: 40Gi
- name: idx
storage:
size: 40GiПри создании DDL:
- таблица —
... TABLESPACE data; - ограничения —
... USING INDEX TABLESPACE idx; - индексы —
... TABLESPACE idx.
Что дальше
Стек CNPG (Kubernetes + PostgreSQL + CloudNativePG) даёт огромную гибкость по storage. Главное — не утонуть в выборе. Начинайте просто, измеряйте, потом усложняйте. На on-prem активно работайте со storage-вендором — у автора есть бенчмаркинговый набор , который вендоры могут гонять у себя.
И главное — не теряйте данные. Производительность не должна стоить вам durability. Эксперименты «а что если резко выдернуть питание у storage» делайте на planning/test-окружениях, не на проде.
Продолжение — в Рецепте 7.