Развёртывание через Kubernetes Operator¶
Kubernetes Operator — рекомендуемый способ управления кластером Picodata
в Kubernetes для production-окружений. Оператор реализует reconcile-loop
на основе собственного набора объектов (CRD, Custom Resource Definition)
PicoclusterDB и автоматически поддерживает желаемое состояние
кластера, создавая и обновляя контроллеры StatefulSet, сервисы
(Services), хранилища конфигураций (ConfigMaps), а также следит за
установкой и обновлением плагинов.
Требования¶
- Kubernetes 1.30+
- kubectl 1.28+
Установка оператора¶
Установка выполняется в два шага: сначала регистрируются CRD, затем разворачивается сам оператор.
Шаг 1. Установите CRD:
kubectl apply -f https://git.picodata.io/core/picodata-operator/-/raw/stable/picodata-operator-deploy/crd.yaml
Шаг 2. Установите оператор:
kubectl apply -f https://git.picodata.io/core/picodata-operator/-/raw/stable/picodata-operator-deploy/operator.yaml
Дождитесь готовности пода оператора:
kubectl rollout status deployment/picodata-operator-controller-manager \
-n picodata-operator-system
Установка через Helm¶
Helm-чарты позволяют управлять оператором и кластерами декларативно, без ручного применения манифестов.
Установка оператора¶
Установите оператор с помощью Helm:
helm install picodata-operator \
https://git.picodata.io/core/picodata-operator/-/archive/stable/chart.tar.gz \
-n picodata-operator-system --create-namespace
Дождитесь готовности:
kubectl rollout status deployment/picodata-operator-controller-manager \
-n picodata-operator-system
Обновление CRD
Helm добавляет CRD при первой установке, но не обновляет их при
helm upgrade. При обновлении версии оператора применяйте CRD отдельно:
kubectl apply -f https://git.picodata.io/core/picodata-operator/-/raw/vX.Y.Z/picodata-operator-deploy/crd.yaml
Развёртывание кластера через Helm¶
Создайте файл значений values.yaml с конфигурацией кластера:
image:
tag: "26.1.5"
adminPassword:
password: "<пароль>"
cluster:
defaultReplicationFactor: 2
defaultBucketCount: 3000
tiers:
- name: default
replicasets: 2
replicationFactor: 2
storage:
size: 10Gi
memtx:
memory: "512M"
pg:
enabled: true
resources:
requests:
cpu: "500m"
memory: "512Mi"
Установите кластер:
helm install my-cluster \
https://git.picodata.io/core/picodata-operator/-/archive/stable/chart-cluster.tar.gz \
-n picodata --create-namespace \
-f values.yaml
Следите за готовностью:
kubectl get picoclusterdb -n picodata -w
Обновление кластера¶
Для обновления версии Picodata или изменения конфигурации:
helm upgrade my-cluster ./chart-cluster -n picodata --set image.tag=26.2.0
Удаление¶
helm uninstall не удаляет ресурс PicoclusterDB — данные защищены от
случайного удаления. Для полного удаления кластера:
kubectl delete picoclusterdb -n picodata my-cluster-picodata-cluster
helm uninstall my-cluster -n picodata
PVC (данные) остаются после удаления CR. Для полного удаления данных:
kubectl delete pvc -n picodata -l app.kubernetes.io/instance=my-cluster
Создание namespace и секрета¶
Создайте namespace для кластера и секрет с паролем пользователя admin:
kubectl create namespace picodata
kubectl create secret generic picodata-admin-secret \
--namespace picodata \
--from-literal=password=<ваш-пароль>
Внимание!
Пароль должен соответствовать политике безопасности Picodata: не менее 8 символов, латинские буквы в верхнем и нижнем регистрах, цифры.
Описание ресурса PicoclusterDB¶
Кластер описывается ресурсом PicoclusterDB. Основные поля:
apiVersion: picodata.picodata.io/v1
kind: PicoclusterDB
metadata:
name: <имя-кластера>
namespace: <namespace>
spec:
image:
repository: docker.binary.picodata.io # реестр образов
tag: "picodata:26.1.5" # образ и версия Picodata
pullPolicy: IfNotPresent
clusterName: <имя-кластера> # имя кластера внутри Picodata
adminPassword:
secretName: picodata-admin-secret # Secret с паролем admin
key: password
cluster:
defaultReplicationFactor: 1 # фактор репликации по умолчанию для всех тиров
defaultBucketCount: 3000 # количество бакетов (шардов)
tiers: # список тиров кластера
- name: <имя-тира>
replicasets: 1 # количество репликасетов в тире
replicationFactor: 1 # количество инстансов в каждом репликасете
canVote: true # участвует ли тир в Raft-голосовании
storage:
size: 1Gi
memtx:
memory: "128M"
pg:
enabled: true # включить протокол PostgreSQL
resources:
requests:
cpu: "100m"
memory: "128Mi"
Общее количество подов тира равно replicasets × replicationFactor.
Имена подов строятся по шаблону: {тир}-{кластер}-{номер-репликасета}-{номер-инстанса}.
Развёртывание кластера¶
Минимальный кластер¶
Кластер из двух тиров: арбитр для Raft и основной тир для хранения данных. Пример минимальной конфигурации:
apiVersion: picodata.picodata.io/v1
kind: PicoclusterDB
metadata:
name: picodata
namespace: picodata
spec:
image:
repository: docker.binary.picodata.io
tag: "picodata:26.1.5"
pullPolicy: IfNotPresent
clusterName: picodata
adminPassword:
secretName: picodata-admin-secret
key: password
cluster:
defaultReplicationFactor: 1
defaultBucketCount: 3000
tiers:
- name: arbiter
replicasets: 1
replicationFactor: 1
canVote: true
storage:
size: 1Gi
memtx:
memory: "64M"
pg:
enabled: false
resources:
requests:
cpu: "100m"
memory: "128Mi"
- name: default
replicasets: 1
replicationFactor: 2
canVote: false
storage:
size: 10Gi
memtx:
memory: "512M"
pg:
enabled: true
resources:
requests:
cpu: "500m"
memory: "512Mi"
Примените манифест:
kubectl apply -f picodata-cluster.yaml
Наблюдение за запуском¶
Следите за готовностью подов:
kubectl get pods -n picodata -w
Ожидаемый результат (все поды 1/1 Running):
NAME READY STATUS RESTARTS AGE
arbiter-picodata-1-0 1/1 Running 0 3m
default-picodata-1-0 1/1 Running 0 3m
default-picodata-1-1 1/1 Running 0 2m
Проверьте состояние CR:
kubectl get picoclusterdb -n picodata
NAME READY AGE
picodata true 5m
Фазы кластера¶
Поле .status.phase отражает текущее состояние кластера:
| Фаза | Описание |
|---|---|
Pending |
пользовательский ресурс (CR) создан, оператор начинает первичную инициализацию |
Initializing |
Ресурсы (StatefulSet, Service) создаются, кластер формируется |
Ready |
Все поды запущены, кластер готов к работе |
Degraded |
Часть подов недоступна, кластер работает в деградированном режиме |
Unknown |
Состояние кластера не удалось определить |
Deleting |
CR помечен на удаление, идёт очистка ресурсов |
Наблюдайте за сменой фаз в реальном времени:
kubectl get picoclusterdb -n picodata -w
При удалении (kubectl delete picoclusterdb) оператор переводит кластер в фазу
Deleting и удерживает CR через финализатор до завершения очистки. После этого CR
исчезает из API.
Подключение к кластеру¶
Протокол PostgreSQL (psql)¶
Пробросьте pgproto-порт одного из подов тира default:
kubectl port-forward -n picodata pod/default-picodata-1-0 5432:5432
Подключитесь через psql:
psql "host=localhost port=5432 user=admin password=<пароль> dbname=picodata sslmode=disable"
Проверьте состояние инстансов через системную таблицу _pico_instance:
SELECT name, replicaset_name, current_state, tier
FROM _pico_instance;
Веб-интерфейс (Web UI)¶
Пробросьте HTTP-порт:
kubectl port-forward -n picodata svc/default-picodata 8081:8081
Откройте http://localhost:8081 для просмотра состояния кластера в веб-интерфейсе.
Внешний доступ¶
Ingress¶
Ingress обеспечивает доступ к веб-интерфейсу и метрикам (HTTP, порт 8081) через доменное имя. Требует установленного Ingress-контроллера (например, nginx).
Включите Ingress в манифесте тира:
tiers:
- name: default
ingress:
enabled: true
host: picodata.example.com
ingressClassName: nginx
annotations:
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
tls:
- secretName: picodata-tls
hosts:
- picodata.example.com
После применения манифеста веб-интерфейс будет доступен по адресу
https://picodata.example.com.
Примечание
Ingress даёт доступ только к HTTP-интерфейсу (порт 8081).
Для доступа по протоколу PostgreSQL (порт 5432) используйте externalService.
LoadBalancer / NodePort¶
Для доступа к PostgreSQL-интерфейсу снаружи кластера используйте
поле externalService:
tiers:
- name: default
externalService:
enabled: true
type: LoadBalancer # или NodePort
При type: LoadBalancer Kubernetes создаёт отдельный объект Service с внешним IP.
При type: NodePort PostgreSQL-интерфейс будет доступен на фиксированном порту каждого узла.
Получите внешний адрес:
kubectl get svc -n picodata -l picodata.io/service-type=external
Per-pod-сервисы для клиентов, работающих с шардированными данными¶
Драйверы picodata-jdbc и picopyn учитывают распределение данных по
шардам и умеют маршрутизировать запросы напрямую на под, хранящий нужный
шард, без излишней нагрузки на сеть. Чтобы это работало, каждый под
должен объявить свой собственный внешний адрес в
_pico_peer_address.
Включите perPod: true в секции externalService:
tiers:
- name: default
externalService:
enabled: true
type: LoadBalancer
perPod: true
Оператор создаст один LoadBalancer Service на каждый под тира. MetalLB
(или cloud LB) назначит каждому Service уникальный VIP. Init-контейнер дождётся
назначения VIP и передаст его Picodata в качестве advertise-адреса — Picodata
объявит адрес вида {vip}:{port} в таблице _pico_peer_address.
Требования
Необходим MetalLB (или облачный провайдер с поддержкой LoadBalancer) с пулом IP-адресов достаточного размера: минимум N + 1 адресов на тир (N подов + один tier-level Service).
Проверьте назначенные VIP и созданные Services:
kubectl get svc -n picodata -l picodata.io/external-pod-service=true
Проверьте адреса в таблице _pico_peer_address:
SELECT raft_id, address, connection_type
FROM _pico_peer_address
WHERE connection_type = 'pgproto';
raft_id | address | connection_type
---------+------------------------+-----------------
1 | 192.168.1.201:5432 | pgproto
2 | 192.168.1.202:5432 | pgproto
Настройка MetalLB для per-pod VIP¶
L2-режим (простой, без BGP)¶
L2-режим работает без дополнительного сетевого оборудования: MetalLB отвечает на ARP-запросы за выбранным IP-адресом. Подходит для разработки и развёртывания небольших кластеров. Ограничение: весь трафик на VIP проходит через один узел («спикер»).
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: picodata-pool
namespace: metallb-system
spec:
addresses:
- 192.168.1.200-192.168.1.229 # пул из ≥ N+1 адресов на тир
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: picodata-l2
namespace: metallb-system
spec:
ipAddressPools:
- picodata-pool
BGP-режим (рекомендуется для production)¶
В BGP-режиме каждый узел анонсирует маршруты к VIP напрямую в сеть через протокол BGP. Преимущества перед L2:
- трафик распределяется по всем узлам через ECMP;
- failover быстрее (секунды по BGP keepalive vs десятки секунд ARP);
- не нужен ARP-прокси, работает в любой IP-сети.
Предварительные требования:
- маршрутизатор (или ToR-коммутатор) с поддержкой BGP;
- договорённые ASN:
localASN(ASN кластера Kubernetes),peerASN(ASN роутера); - IP-адрес BGP-пира со стороны роутера.
Конфигурация MetalLB:
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: picodata-pool
namespace: metallb-system
spec:
addresses:
- 192.168.1.200-192.168.1.229 # пул из ≥ N+1 адресов на тир
---
apiVersion: metallb.io/v1beta2
kind: BGPPeer
metadata:
name: upstream-router
namespace: metallb-system
spec:
myASN: 64512 # ASN кластера Kubernetes (private range: 64512–65534)
peerASN: 64513 # ASN upstream-роутера
peerAddress: 192.168.0.1 # IP BGP-пира (роутер / ToR-коммутатор)
---
apiVersion: metallb.io/v1beta1
kind: BGPAdvertisement
metadata:
name: picodata-bgp
namespace: metallb-system
spec:
ipAddressPools:
- picodata-pool
peers:
- upstream-router
После применения конфигурации MetalLB установит BGP-сессию с роутером и начнёт
анонсировать маршруты к VIP из пула picodata-pool. Проверить сессию:
kubectl logs -n metallb-system -l component=speaker | grep -i bgp | tail -20
BGP-сессия должна перейти в состояние Established.
Несколько BGP-пиров
Если в кластере несколько ToR-коммутаторов, добавьте несколько ресурсов
BGPPeer — MetalLB будет анонсировать маршруты каждому из них.
BGP через Cilium (альтернатива MetalLB)¶
Если кластер использует Cilium в качестве CNI, MetalLB не нужен: Cilium включает собственный BGP Control Plane и умеет назначать LoadBalancer IP из внутреннего пула, анонсируя маршруты через BGP.
Предварительные требования:
- Cilium ≥ 1.13 с включённым
bgpControlPlane.enabled=true - маршрутизатор с поддержкой BGP
Пул IP-адресов (CiliumLoadBalancerIPPool):
apiVersion: "cilium.io/v2alpha1"
kind: CiliumLoadBalancerIPPool
metadata:
name: picodata-pool
spec:
cidrs:
- cidr: "192.168.1.200/27" # 30 адресов — достаточно для N подов + tier LB
BGP-политика (CiliumBGPPeeringPolicy):
apiVersion: "cilium.io/v2alpha1"
kind: CiliumBGPPeeringPolicy
metadata:
name: picodata-bgp
spec:
nodeSelector:
matchLabels:
kubernetes.io/os: linux # применяется ко всем узлам
virtualRouters:
- localASN: 64512
exportPodCIDR: false
neighbors:
- peerAddress: "192.168.0.1/32" # IP upstream-роутера
peerASN: 64513
serviceSelector:
matchExpressions:
- key: picodata.io/external-pod-service
operator: In
values: ["true"]
- key: picodata.io/cluster
operator: Exists
serviceSelector ограничивает анонс только per-pod сервисами Picodata.
Это предотвращает утечку внутренних ClusterIP в BGP-таблицу роутера.
Проверить что Cilium установил BGP-сессию:
cilium bgp peers
Node Local AS Peer AS Peer Address Session State Since Received Sent
k8s-node-01 64512 64513 192.168.0.1 Established 1h 12 15
k8s-node-02 64512 64513 192.168.0.1 Established 1h 12 15
Cilium 1.16+
В Cilium 1.16 появился новый BGP API (CiliumBGPClusterConfig, CiliumBGPPeerConfig,
CiliumBGPAdvertisement). Показанный выше CiliumBGPPeeringPolicy поддерживается
начиная с Cilium 1.13 и работает во всех версиях. Для новых установок
смотрите документацию Cilium.
Shard-aware без LoadBalancer: DNS + BGP pod routing¶
Если CNI кластера анонсирует pod CIDR в сеть через BGP (Cilium или Calico в BGP-режиме), LoadBalancer-сервисы для shard-aware доступа не нужны. Pod IP становятся маршрутизируемыми напрямую — без MetalLB и без per-pod VIP.
В этом случае оператор работает в режиме по умолчанию: advertise-адрес каждого
пода в _pico_peer_address — это стабильный FQDN:
hot-mycluster-1-0.hot-mycluster-interconnect.picodata.svc.cluster.local:5432
Shard-aware драйвер читает эти FQDN из _pico_peer_address, резолвит их в pod IP
и подключается напрямую.
Что нужно:
-
Pod IP маршрутизируемы снаружи — CNI в BGP-режиме анонсирует pod CIDR в сеть, клиент может достучаться до
10.244.x.xнапрямую. -
Kubernetes DNS доступен с клиента — один из вариантов:
- DNS forwarding: настроить на клиентской машине пересылку зоны
cluster.localна IP сервисаkube-dns(если он доступен из сети); - Делегирование зоны: сделать
cluster.localподдоменом вашего DNS, делегировав его на CoreDNS кластера.
- DNS forwarding: настроить на клиентской машине пересылку зоны
Что не нужно настраивать в PicoclusterDB:
tiers:
- name: hot
externalService:
enabled: true
type: LoadBalancer # tier-level LB для bootstrap (опционально)
# perPod: НЕ указывать — он не нужен
perPod: true и связанные per-pod Services не создаются. Оператор не выделяет
IP из пула MetalLB на каждый под.
Схема подключения:
Клиент (вне кластера)
│
├─ DNS: hot-mycluster-1-0.hot-mycluster-interconnect.picodata.svc.cluster.local
│ └─ forwarding *.cluster.local → CoreDNS кластера
│ └─ ответ: 10.244.1.5 ← pod IP
│
└─ TCP 10.244.1.5:5432
└─ маршрут через BGP: CNI анонсирует pod CIDR наружу
Ограничение
Подход работает только если клиент находится в той же L3-сети, куда CNI анонсирует pod CIDR. Для клиентов за NAT или через интернет по-прежнему нужны LoadBalancer-сервисы с публичными IP.
Управление плагинами¶
Сборка образа с плагином¶
Стандартный образ Picodata не включает плагины. Для запуска плагина необходимо
собрать кастомный Docker-образ, распаковав архив плагина в директорию
shareDir (по умолчанию /usr/share/picodata).
Пример Dockerfile для плагина Radix:
FROM docker.binary.picodata.io/picodata:26.1.5 as release
USER root
RUN mkdir -p /usr/share/picodata
COPY radix_1.0.1-centos_el8.tar.gz /usr/share/picodata/radix.tar.gz
RUN cd /usr/share/picodata && tar -xvzf radix.tar.gz && rm radix.tar.gz
RUN chown -R picodata:picodata /usr/share/picodata
USER picodata
После сборки и публикации образа укажите его в манифесте кластера:
spec:
image:
repository: registry.example.com/picodata-with-radix
tag: "26.1.2-radix-1.0.1"
Описание плагина в манифесте¶
Плагины объявляются на двух уровнях: кластера и тира.
На кластерном уровне задаётся версия плагина и параметры первичной миграции:
spec:
cluster:
shareDir: /usr/share/picodata # путь к директории с .so-файлами плагинов
plugins:
- name: radix
version: "1.0.0"
migrationContext:
tier_for_db_0: "default"
# ... остальные параметры
На уровне тира задаётся привязка сервисов плагина к тиру и listener-порт:
spec:
tiers:
- name: default
plugins:
- name: radix
services:
- name: radix
listenerPort: 8082
После применения манифеста оператор автоматически выполнит полный жизненный цикл
установки: CREATE PLUGIN → ADD SERVICE TO TIER → SET migration_context →
MIGRATE TO → ENABLE. Установка запускается только когда все поды тира готовы.
Статус установки плагина:
kubectl get picoclusterdb picodata -n picodata \
-o jsonpath='{.status.tiers[*].plugins}'
Подробнее: Управление плагинами
Обновление версии Picodata¶
Для обновления версии Picodata измените поле spec.image.tag в манифесте
и примените его:
spec:
image:
tag: "picodata:26.2.0"
kubectl apply -f picodata-cluster.yaml
Оператор выполнит безопасное обновление:
- Отключит (
DISABLE) все включённые плагины, несовместимые с новым образом - Дождётся фиксации изменения в Raft-кластере
- Обновит образ во всех StatefulSet-ах (rolling update)
- Установит и включит плагины для новой версии
Примечание
PVC (данные) при обновлении образа не затрагиваются.
Ребутстрап инстанса¶
Ребутстрап применяется когда инстанс не может вернуться в кластер после
потери данных или повреждения тома. Оператор исключает инстанс из кластера
(expel), стирает его данные и позволяет ему заново присоединиться как
новому члену репликасета.
Внимание!
Ребутстрап возможен только если репликасет содержит более одного инстанса (replicationFactor ≥ 2) и хотя бы один из оставшихся инстансов доступен.
Создайте ресурс PicoclusterInstanceRebootstrap:
apiVersion: picodata.picodata.io/v1
kind: PicoclusterInstanceRebootstrap
metadata:
name: rebootstrap-1
namespace: picodata
spec:
clusterRef:
name: picodata # имя PicoclusterDB
instanceName: default-picodata-1-0 # имя пода (инстанса)
kubectl apply -f rebootstrap.yaml
Следите за фазами выполнения:
kubectl get picoclusterinstancerebootstrap -n picodata -w
NAME INSTANCE PHASE AGE
rebootstrap-1 default-picodata-1-0 Expelling 10s
rebootstrap-1 default-picodata-1-0 Wiping 30s
rebootstrap-1 default-picodata-1-0 Rejoining 45s
rebootstrap-1 default-picodata-1-0 Succeeded 2m
После завершения ресурс PicoclusterInstanceRebootstrap можно удалить.
Резервное копирование¶
Экспериментальная функция
Резервное копирование и восстановление находятся в стадии эксперимента. Не используйте их в production без предварительного тестирования.
Настройка хранилища резервных копий¶
Добавьте в манифест кластера секцию backup с описанием тома для хранения
резервных копий:
spec:
backup:
mountPath: /pico/backup # путь внутри пода, куда монтируется том
volume:
# Для production используйте NFS или другой тип тома с ReadWriteMany.
# hostPath подходит только для одноузловых кластеров (minikube).
nfs:
server: nfs.example.com
path: /exports/picodata-backup
Ограничения резервного копирования¶
- Резервная копия запускается только если все поды кластера готовы (
1/1 Running). При недоступности хотя бы одного пода бэкап перейдёт в фазуFailed - Для одного кластера одновременно может выполняться только один
PicoclusterBackup. - Хранилище резервных копий должно быть доступно на запись со всех узлов кластера. Для multi-node-окружений используйте NFS или другой том с поддержкой ReadWriteMany. Оператор проверяет доступность тома перед запуском бэкапа (preflight-проверка)
- При перезапуске пода в ходе бэкапа консистентность резервной копии не гарантируется.
Факт перезапуска фиксируется в
status.podSnapshotsдля последующей диагностики - По истечении
spec.backupTimeoutоператор прерывает операцию черезpico.abort_ddl(). Прерванный бэкап не пригоден для восстановления
Создание резервной копии¶
Создайте ресурс PicoclusterBackup:
apiVersion: picodata.picodata.io/v1
kind: PicoclusterBackup
metadata:
name: backup-1
namespace: picodata
spec:
clusterRef:
name: picodata # имя PicoclusterDB
backupTimeout: 3600 # максимальное время ожидания (секунды)
kubectl apply -f backup.yaml
Следите за статусом:
kubectl get picoclusterbackup -n picodata -w
NAME CLUSTER STATUS AGE
backup-1 picodata Succeeded 5m
Путь к сохранённой резервной копии:
kubectl get picoclusterbackup backup-1 -n picodata \
-o jsonpath='{.status.backupDir}'
Ограничения восстановления¶
- Восстановление — деструктивная операция: оператор останавливает весь кластер (scale до 0), стирает данные каждого инстанса и заменяет их содержимым снапшота
- Для одного кластера одновременно может выполняться только одна операция восстановления
- При
backupRefссылаемыйPicoclusterBackupдолжен быть в фазеSucceeded. - Если восстановление завершилось с ошибкой, кластер остаётся остановленным (все StatefulSet масштабированы до 0) для ручного разбора причин. Автоматического отката нет.
- Хранилище резервных копий должно быть смонтировано в те же поды, что и при
создании бэкапа: оператор ожидает структуру
<mountPath>/<instanceName>/<backupDir>/
Восстановление из резервной копии¶
Создайте ресурс PicoclusterRestore:
apiVersion: picodata.picodata.io/v1
kind: PicoclusterRestore
metadata:
name: restore-1
namespace: picodata
spec:
clusterRef:
name: picodata # имя PicoclusterDB для восстановления
backupRef:
name: backup-1 # ссылка на ресурс PicoclusterBackup
Если ресурс PicoclusterBackup был удалён, укажите директорию напрямую:
spec:
clusterRef:
name: picodata
backupDir: "20260508T143720" # имя поддиректории в томе резервных копий
kubectl apply -f restore.yaml
Anti-affinity¶
По умолчанию оператор автоматически добавляет правило podAntiAffinity,
которое запрещает размещение двух подов одного репликасета на одном узле
Kubernetes. Это гарантирует отказоустойчивость при потере узла.
Требование к StorageClass¶
Anti-affinity работает корректно только если StorageClass использует
volumeBindingMode: WaitForFirstConsumer.
При режиме Immediate (значение по умолчанию во многих дистрибутивах)
PVC создаются и привязываются к узлам до планирования подов. В результате
оба PVC одного репликасета могут оказаться на одном узле, и поды не смогут
«разойтись» — один из них зависнет в состоянии Pending.
Проверьте режим вашего StorageClass:
kubectl get storageclass -o custom-columns=NAME:.metadata.name,BINDING:.volumeBindingMode
Если значение Immediate, создайте StorageClass с нужным режимом:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: picodata-sc
provisioner: <ваш-provisioner> # например, rancher.io/local-path
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Retain
И укажите его в тире:
tiers:
- name: default
storage:
size: 10Gi
storageClassName: picodata-sc
Примечание
В управляемых кластерах (GKE, EKS, AKS) стандартные StorageClass обычно
уже используют WaitForFirstConsumer. Проблема наиболее характерна для
self-hosted-кластеров с local-path, Longhorn или OpenEBS.
Отключение anti-affinity¶
Для развёртывания на одноузловом кластере (minikube, dev-окружение) отключите anti-affinity для нужного тира:
tiers:
- name: default
disableAutoAntiAffinity: true
Внимание!
Не отключайте anti-affinity в production: при потере узла могут упасть сразу несколько инстансов одного репликасета.
PodDisruptionBudget¶
Оператор автоматически создаёт PodDisruptionBudget для тиров с фактором
репликации ≥ 3. PDB ограничивает количество одновременно недоступных подов
при плановых операциях (drain-узлы, обновление кластера Kubernetes), сохраняя
кворум кластера.
При необходимости выполнить drain на кластере без свободных узлов — когда Kubernetes не может переместить поды — временно отключите PDB:
tiers:
- name: default
disablePDB: true
После завершения drain верните значение в false (или удалите поле).
Внимание!
Не оставляйте PDB отключённым в production. Без PDB плановое обслуживание узлов может нарушить кворум кластера.
Удаление кластера¶
kubectl delete picoclusterdb picodata -n picodata
Удаление пользовательского ресурса (CR) автоматически удаляет все связанные с ним ресурсы (StatefulSet, Service, ConfigMap) через механизм ownerReference.
Внимание!
PVC (постоянные тома с данными) при удалении CR не удаляются — это защита от случайной потери данных. Для полного удаления данных выполните:
kubectl delete pvc -n picodata --all
Мониторинг¶
Каждый инстанс Picodata отдаёт метрики в формате Prometheus на порту 8081,
путь /metrics.
ServiceMonitor через CR¶
Если в кластере установлен Prometheus Operator, включите автоматический сбор метрик в манифесте кластера:
spec:
serviceMonitor:
enabled: true
interval: "30s"
labels:
release: kube-prometheus-stack # должен совпадать с serviceMonitorSelector вашего Prometheus
Оператор создаёт по одному ServiceMonitor на каждый тир — Prometheus
начнёт получать данные от каждого инстанса через headless-сервис.
PodMonitor с job-лейблом по имени кластера¶
Для использования с Grafana-дашбордом рекомендуется настроить PodMonitor
так, чтобы label job в Prometheus соответствовал имени кластера. Это
позволяет выбирать кластер в выпадающем меню дашборда:
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
name: picodata
namespace: picodata
labels:
release: kube-prometheus-stack
spec:
jobLabel: app.kubernetes.io/instance # job = clusterName
selector:
matchExpressions:
- key: app.kubernetes.io/instance
operator: Exists
namespaceSelector:
matchNames:
- picodata
podMetricsEndpoints:
- port: http
path: /metrics
interval: 30s
Один ресурс охватывает все кластеры в пространстве имён (namespace) и не требует изменений при добавлении новых кластеров.
Grafana-дашборд¶
Официальный дашборд Picodata доступен в репозитории core/picodata:
monitoring/dashboard/Picodata.json
Импортировать через UI Grafana: Dashboards → Import → Upload JSON file или через URL файла.
После импорта выберите datasource Prometheus и нужный кластер в выпадающем меню job.
Читайте далее: