Хостинг
VDS
Аренда серверов
Домены
Почта
SSL
Хранилище
Сайты
Обзоры
Регламенты
AI Studio
Kubernetes
Архив
Развёртывание PostgreSQL 18 с репликацией и автоматическим failover в Kubernetes

Развёртывание PostgreSQL 18 с репликацией и автоматическим failover в Kubernetes


Введение

В данной инструкции описан процесс развёртывания высокодоступного кластера PostgreSQL 18 в Kubernetes с использованием оператора CloudNativePG.

Кластер состоит из трёх нод: одной главной (primary) и двух реплик (replica). При отказе главной ноды одна из реплик автоматически становится главной, обеспечивая непрерывность работы.


Требования

  1. Kubernetes-кластер с минимум 3 worker-нодами

  2. Дополнительный диск на каждой worker-ноде (минимум 10 ГБ)

  3. Установленные инструменты на управляющем хосте:

  4. kubectl
  5. helm

Настройка Local Storage с LSP

Для работы с локальными дисками используется Local Static Provisioner (LSP) — официальный провайсер Kubernetes (kubernetes-sigs). LSP отслеживает блочные устройства на узлах и автоматически создаёт PersistentVolume для каждого найденного диска с привязкой к конкретной ноде (node affinity). Ручная подготовка дисков на worker-нодах не требуется: провизионер находит устройство, а форматирование в ext4 (согласно fsType) выполняется автоматически при первом монтировании тома в под — диск монтируется только внутри подов.

Примечание: Детальное описание работы с PV/PVC и Local Storage вынесено в отдельные статьи. Здесь приведён минимально необходимый набор для PostgreSQL.

1. Создайте values-файл для провизионера

Файл values-block.yaml:

# Mount the host's `/dev/` by default so that block device symlinks can be
# resolved by the containers
mountDevVolume: false

# Configuration for classes of static volumes.
classes:
  - name: local-storage # Defines name of storage classes.
    hostDir: /dev
    namePattern: "vdb"
    volumeMode: Filesystem
    accessMode: ReadWriteOnce
    fsType: ext4
    blockCleanerCommand:
      - "/scripts/shred.sh"
      - "2"
    storageClass:
      name: local-storage
      reclaimPolicy: Retain # Available reclaim policies: Delete/Retain, defaults: Delete.
      isDefaultClass: true # set as default class
      provisioner: kubernetes.io/no-provisioner

Важно: namePattern должен совпадать с именем дополнительного диска на ваших нодах (обычно vdb, имя устройства можно посмотреть на ноде: lsblk). Подробнее работа с дисками описана в отдельной статье

2. Установите Local Static Provisioner

Сгенерируйте манифест через Helm и примените его:

helm repo add sig-storage-local-static-provisioner \
  https://kubernetes-sigs.github.io/sig-storage-local-static-provisioner
helm repo update

helm template sig-storage-local-static-provisioner/local-static-provisioner \
  --version 2.9.0 \
  --namespace sig-storage \
  -f values-block.yaml > lsp-block.yaml

kubectl create ns sig-storage
kubectl apply -f lsp-block.yaml

Провизионер разворачивается как DaemonSet. Каждая нода, где найден диск, подходящий под namePattern, получает автоматически созданный PV с node affinity — вручную перечислять ноды не нужно.

3. Проверьте созданные PV

Через короткое время провизионер отследит наличие /dev/vdb на узлах и создаст PV. Проверьте статус:

kubectl get pv

Ожидаемый результат — по одному PV на ноде:

NAME                CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS
local-pv-xxxxxx     35Gi       RWO            Retain           Available
local-pv-yyyyyy     35Gi       RWO            Retain           Available
local-pv-zzzzzz     35Gi       RWO            Retain           Available

Имена PV генерируются автоматически (хеш от имени устройства, ноды и класса).

4. Проверьте StorageClass

Провизионер автоматически создаёт StorageClass local-storage (указан в values-файле):

kubectl get sc local-storage

Далее в этой статье используется именно этот StorageClass (local-storage).


Установка CloudNativePG оператора

CloudNativePG — это Kubernetes-оператор, который управляет кластером PostgreSQL: создаёт инстансы, настраивает репликацию, автоматически выполняет failover.

1. Добавьте репозиторий Helm

helm repo add cnpg https://cloudnative-pg.github.io/charts
helm repo update cnpg

2. Установите оператор

helm install cnpg cnpg/cloudnative-pg --namespace cnpg-system --create-namespace

3. Проверьте установку

kubectl get pods -n cnpg-system

Ожидаемый результат:

NAME                                 READY   STATUS
cnpg-cloudnative-pg-xxxxxxxxx-xxxxx  1/1     Running

Создание кластера PostgreSQL

1. Создайте namespace

kubectl create namespace postgresql

2. Создайте секрет с паролем

Придумайте надёжный пароль и замените ВАШ_ПАРОЛЬ на него:

kubectl create secret generic postgres-credentials \
  --namespace=postgresql \
  --from-literal=username=app \
  --from-literal=password='ВАШ_ПАРОЛЬ'

Важно: В секрете ключ username должен быть имя пользователя, который будет владельцем базы данных. В даннмом примере указан пользователь app

3. Создайте файл pg-cluster.yaml

---
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: cluster-postgres
  namespace: postgresql
spec:
  instances: 3
  imageName: ghcr.io/cloudnative-pg/postgresql:18.1
  postgresql:
    parameters:
      max_connections: "200"
  storage:
    size: 35Gi
    storageClass: local-storage
  resources:
    requests:
      cpu: "1"
      memory: 1Gi
    limits:
      cpu: "2"
      memory: 1800Mi
  primaryUpdateStrategy: unsupervised
  bootstrap:
    initdb:
      database: appdb
      owner: app
      secret:
        name: postgres-credentials
      postInitApplicationSQL:
        - "CREATE EXTENSION IF NOT EXISTS pg_stat_statements;"
  affinity:
    enablePodAntiAffinity: true
---
apiVersion: v1
kind: Service
metadata:
  name: postgres-external
  namespace: postgresql
spec:
  type: LoadBalancer
  selector:
    cnpg.io/cluster: cluster-postgres
    cnpg.io/instanceRole: primary
  ports:
  - name: postgresql
    port: 5432
    targetPort: 5432
    protocol: TCP

Настройка ресурсов: значения resources должны соответствовать доступным ресурсам ваших нод. Проверьте доступные ресурсы командой kubectl describe nodes. Если на вашей ноде меньше ресурсов, уменьшите значения requests и limits.

StorageClass: local-storage — StorageClass, автоматически создаётся chart’ом LSP (см. раздел «Настройка Local Storage с LSP»).

4. Примените конфигурацию

kubectl apply -f pg-cluster.yaml

5. Дождитесь создания кластера

kubectl get cluster -n postgresql -w

Наблюдайте за состоянием кластера. Процесс может занять 2–5 минут. Ожидаемый результат:

NAME               AGE   INSTANCES   READY   STATUS                     PRIMARY
cluster-postgres   5m    3           3       Cluster in healthy state   cluster-postgres-1

Кластер готов, когда STATUS показывает Cluster in healthy state и READY равен 3.

6. Проверьте поды

kubectl get pods -n postgresql -l cnpg.io/podRole=instance -o wide

Каждый под должен находиться на отдельной ноде благодаря enablePodAntiAffinity:

NAME                 READY   STATUS    NODE
cluster-postgres-1   1/1     Running   worker-1
cluster-postgres-2   1/1     Running   worker-2
cluster-postgres-3   1/1     Running   worker-3

Настройка паролей

После создания кластера настройте пароли для пользователей PostgreSQL.

Важно: Пароль суперпользователя postgres генерируется CNPG случайно и неизвестен. Поэтому первичную настройку паролей выполняем через локальный сокет внутри пода (peer-аутентификация, пароль не нужен).

1. Определите имя primary пода

PRIMARY_POD=$(kubectl get pods -n postgresql -l cnpg.io/instanceRole=primary -o jsonpath='{.items[0].metadata.name}')
echo "$PRIMARY_POD"

2. Подключитесь к primary через локальный сокет

Соединение через Unix-сокет внутри пода — без пароля (peer-аутентификация):

kubectl exec -it "$PRIMARY_POD" -n postgresql -c postgres -- psql -U postgres -d appdb

3. Установите пароль для суперпользователя

ALTER USER postgres WITH PASSWORD 'ВАШ_ПАРОЛЬ_СУПЕРП';

4. Установите пароль для пользователя приложения

ALTER USER app WITH PASSWORD 'ВАШ_ПАРОЛЬ_APP';

5. Выйдите из консоли

\q

6. Обновите секрет

kubectl delete secret postgres-credentials -n postgresql
kubectl create secret generic postgres-credentials \
  --namespace=postgresql \
  --from-literal=username=app \
  --from-literal=password='ВАШ_ПАРОЛЬ_APP'

Внешний доступ

Сервис postgres-external (LoadBalancer) предоставляет доступ к primary инстансу PostgreSQL. После создания сервиса внешний IP будет назначен балансировщиком:

1. Получите внешний IP сервиса

kubectl get svc postgres-external -n postgresql

Ожидаемый результат:

NAME                TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)
postgres-external   LoadBalancer   10.96.x.x      <EXTERNAL-IP>  5432:xxxxx/TCP

2. Подключение снаружи

psql -h <EXTERNAL-IP> -p 5432 -U app -d appdb

Внутренние адреса

Если вы подключаетесь изнутри кластера (из другого пода), используйте встроенные сервисы CloudNativePG:

Сервис Назначение Адрес
cluster-postgres-rw Запись/чтение (только primary) cluster-postgres-rw.postgresql.svc.cluster.local:5432
cluster-postgres-r Все ноды (round-robin) cluster-postgres-r.postgresql.svc.cluster.local:5432
cluster-postgres-ro Только чтение (только replica) cluster-postgres-ro.postgresql.svc.cluster.local:5432

Важно: Сервисы типа NodePort не используются — на worker-нодах отсутствуют белые IP-адреса. Для внешнего доступа применяется LoadBalancer, а для внутреннего — ClusterIP-сервисы CloudNativePG.

Полезные команды

Статус кластера

kubectl get cluster -n postgresql

Текущий primary под

kubectl get pods -n postgresql \
  -l cnpg.io/instanceRole=primary

Статус подов PostgreSQL

kubectl get pods -n postgresql -l cnpg.io/podRole=instance

Логи пода

kubectl logs <имя-pod> -n postgresql -f

Подключение к консоли PostgreSQL

Через локальный сокет (без пароля, только с primary):

PRIMARY_POD=$(kubectl get pods -n postgresql -l cnpg.io/instanceRole=primary -o jsonpath='{.items[0].metadata.name}')
kubectl exec -it "$PRIMARY_POD" -n postgresql -c postgres -- psql -U postgres -d appdb

Подключение внутри кластера:

PGPASSWORD='ВАШ_ПАРОЛЬ_СУПЕРП' psql -h cluster-postgres-rw.postgresql.svc.cluster.local -U postgres -d appdb

Перезапуск всех нод кластера

kubectl patch cluster cluster-postgres -n postgresql --type merge --patch '{"spec":{"instances":3}}'

Нам доверяют тысячи компаний и разработчиков

22 года
Предоставляем услуги профессионального хостинга
35 000+
Клиентов доверяют нам размещение своих сайтов
99.99%
Подтвержденный uptime наших серверов хостинга
Callibri
Крылья
Tele-Club
Linline
Премьер зал
УГМК-Здоровье
Магнум
Алатырь
Туристер
Callibri
Крылья
Tele-Club
Linline
Премьер зал
УГМК-Здоровье
Магнум
Алатырь
Туристер
ВК49865