Оригинал: Valkey clusters in Kubernetes: a comprehensive guide — Alastair Appleby, ENAPI, 3 декабря 2024.
В ENAPI кластер Valkey обслуживает критичный кешинг-слой: статусы зарядных станций (EVSE), тарифы, сессии зарядки. Данные характеризуются высокой нагрузкой на запись и требуют постоянной доступности. Изначально использовался KeyDB из-за multi-master «из коробки», но развитие проекта замедлилось. Так команда пришла к Valkey.
Это руководство — результат продакшн-опыта: проверенная на практике конфигурация, покрывающая автоматический failover без Sentinel, корректную инициализацию кластера, распределение primary/replica и автомасштабирование.
Введение в Valkey
Valkey — форк Redis, созданный бывшими core-разработчиками и мейнтейнерами Redis в ответ на смену лицензии (RSAL → RSALv2 → SSPL). Те же люди, та же экспертиза, но лицензия BSD 3-Clause — настоящий open-source без ограничений.
Преимущества Valkey в кластерном режиме на Kubernetes:
- Автоматический failover без Sentinel.
- Нативный шардинг данных между узлами.
- Горизонтальное масштабирование.
- Интеграция с Prometheus и Grafana через sidecar redis-exporter.
Режимы развёртывания
| Режим | Характеристика | Когда применять |
|---|---|---|
| Standalone | Одиночный инстанс, без HA | Разработка, некритичные нагрузки |
| Sentinel | Primary-Replica + мониторинг Sentinel | Простые HA-нужды, традиционные VM |
| Cluster | Шардирование + встроенный failover | Продакшн на Kubernetes |
В Kubernetes Cluster-режим предпочтительнее Sentinel потому что:
- Шардирование из коробки.
- Архитектура естественно ложится на распределённую природу K8s.
- Не требует дополнительных Sentinel-процессов.
- Масштабирование проще.
Архитектура
Развёртывание состоит из следующих ресурсов Kubernetes:
- ConfigMap — конфиг Valkey и скрипты инициализации.
- StatefulSet — управление подами Valkey + sidecar redis-exporter.
- Headless Service — DNS-обнаружение подов и прямой pod-to-pod-обмен.
- ServiceMonitor (опционально) — интеграция с Prometheus.
ConfigMap
Содержит три компонента:
1. valkey.conf — основной конфиг
apiVersion: v1
kind: ConfigMap
metadata:
name: valkey
data:
valkey.conf: |
protected-mode no
notify-keyspace-events "KEA$"
cluster-enabled ${replicas > 1 ? "yes" : "no"}
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-allow-reads-when-down yes
cluster-announce-port 6379
cluster-announce-bus-port 16379
port 6379
cluster-port 16379
appendonly yes
replica-read-only noКлючевые параметры:
cluster-enabled— включается только приreplicas > 1.cluster-announce-port/cluster-announce-bus-port— порты для клиентов (6379) и шины кластера (16379).appendonly yes— персистентность через AOF.cluster-allow-reads-when-down yes— чтение при частичной недоступности кластера.
2. init-config.sh — инициализация пода
#!/bin/sh
cp /etc/valkey/valkey.conf /tmp/valkey.conf
if [ "${REPLICAS}" -gt "1" ]; then
echo "replica-announce-ip ${POD_IP}" >> /tmp/valkey.conf
echo "cluster-announce-ip ${POD_IP}" >> /tmp/valkey.conf
fi
cp /tmp/valkey.conf /config/valkey.conf
mkdir -p /data
chown -R 1000:1000 /data- Копирует базовый конфиг.
- Добавляет pod-специфичные IP для анонса в кластерном режиме.
- Настраивает права на
/data.
3. init-cluster.sh — инициализация кластера
Скрипт обрабатывает три сценария:
Primary-0 (первый под):
- Ждёт готовности локального Valkey.
- Проверяет, инициализирован ли уже кластер.
- Собирает список всех узлов и создаёт кластер:
ORDINAL=$(echo ${HOSTNAME} | rev | cut -d'-' -f1 | rev)
PRIMARIES=$(($((${REPLICAS} + 1)) / 2))
if [ "${ORDINAL}" = "0" ]; then
# Собрать все узлы: valkey-0..valkey-N
echo "yes" | valkey-cli --cluster create ${NODES} \
--cluster-replicas ${REPLICA_COUNT}Дополнительные primary (ordinal < primaries):
- Присоединяются к кластеру через primary-0.
Replica (ordinal >= primaries):
- Присоединяются к своему primary как реплики.
Формула распределения:
primaries = (replicas + 1) / 2
replicas_per_primary = (replicas - primaries) / primariesStatefulSet
Ключевые решения:
podManagementPolicy: Parallel— поды создаются параллельно, а не последовательно. Ускоряет масштабирование и восстановление. Безопасно, потому чтоinit-cluster.shсам разруливает race conditions.- Init-контейнер — настраивает конфигурацию и права до запуска Valkey.
- Основной контейнер — запускает
init-cluster.shв фоне иvalkey-server. - Sidecar
redis-exporter— экспортирует Prometheus-метрики.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: valkey
namespace: datastores
spec:
podManagementPolicy: Parallel
serviceName: valkey
replicas: 6 # 3 primary + 1 реплика на каждый
selector:
matchLabels:
app: valkey
template:
metadata:
labels:
app: valkey
spec:
initContainers:
- name: init-config
image: busybox
command: ["sh", "/scripts/init-config.sh"]
volumeMounts:
- name: config
mountPath: /etc/valkey
readOnly: true
- name: scripts
mountPath: /scripts
- name: workdir
mountPath: /config
- name: data
mountPath: /data
env:
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
containers:
- name: valkey
image: valkey/valkey:8.0.1
ports:
- name: client
containerPort: 6379
- name: cluster
containerPort: 16379
volumeMounts:
- name: data
mountPath: /data
- name: workdir
mountPath: /etc/valkey
- name: scripts
mountPath: /scripts
command: ["sh", "-c"]
args:
- |
sh /scripts/init-cluster.sh &
valkey-server /etc/valkey/valkey.conf
env:
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: HOSTNAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: REPLICAS
value: "6"
securityContext:
runAsUser: 1000
runAsGroup: 1000
- name: redis-exporter
image: oliver006/redis_exporter:latest
securityContext:
runAsUser: 59000
runAsGroup: 59000
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
resources:
requests:
cpu: 100m
memory: 100Mi
ports:
- containerPort: 9121
name: metrics
env:
- name: REDIS_ADDR
value: redis://localhost:6379
- name: REDIS_EXPORTER_IS_CLUSTER
value: "true"
volumes:
- name: config
configMap:
name: valkey
- name: scripts
configMap:
name: valkey
items:
- key: init-cluster.sh
path: init-cluster.sh
- key: init-config.sh
path: init-config.sh
defaultMode: 0755
- name: workdir
emptyDir: {}
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 5GiHeadless Service
Обеспечивает DNS-обнаружение подов и прямой pod-to-pod-обмен:
apiVersion: v1
kind: Service
metadata:
name: valkey
namespace: datastores
spec:
selector:
app: valkey
ports:
- name: client
port: 6379
targetPort: 6379
- name: cluster
port: 16379
targetPort: 16379
- name: metrics
port: 9121
targetPort: 9121
clusterIP: None # headlessМониторинг (опционально)
ServiceMonitor для Prometheus Operator:
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: valkey
namespace: datastores
labels:
release: prometheus-stack
spec:
selector:
matchLabels:
app: valkey
endpoints:
- port: metrics
interval: 30s
relabelings:
- sourceLabels: [__meta_kubernetes_endpoint_hostname]
targetLabel: instanceВместо IP-адреса пода в метриках будет hostname — удобнее для дашбордов Grafana.
Масштабирование и высокая доступность
Распределение primary/replica
Формула автоматически даёт нечётное число primary для предотвращения split-brain:
primaries = (replicas + 1) / 2| Replicas | Primaries | Реплик на primary |
|---|---|---|
| 3 | 2 | 0 |
| 6 | 3 | 1 |
| 9 | 5 | 0 |
Scale up
# С 6 до 9 узлов
kubectl scale statefulset valkey --replicas=9Что происходит:
- Новые поды (
valkey-6,valkey-7,valkey-8) создаются параллельно (Parallel). - Скрипт определяет роли: если ordinal < primaries — новый primary, иначе — реплика.
- Автоматический ребаланс слотов между новыми primary.
Влияние: кратковременный рост сетевого трафика и потребления памяти при миграции слотов. Даунтайма нет.
Scale down
# С 9 до 6 узлов
kubectl scale statefulset valkey --replicas=6Что происходит:
- Удаление подов в обратном порядке (
valkey-8,valkey-7,valkey-6). - Слоты с удаляемых primary мигрируют на оставшиеся.
- Клиенты перенаправляются на новые узлы.
Когда масштабировать
| Scale up | Scale down |
|---|---|
| Память стабильно >70% | Память стабильно низкая |
| Много клиентских соединений | Снизилось число клиентов |
| Нужна лучшая производительность чтения | Оптимизация затрат |
| Требуется больше избыточности | Эвакуация зоны / обслуживание |
Правила:
- Всегда нечётное число primary (формула гарантирует).
- Масштабируйте малыми шагами (6→9→12, не 6→12).
- Мониторьте память, CPU, сеть, replication lag и число соединений.
Сценарии отказов
Отказ primary
- Реплика автоматически повышается до primary.
- Кластер переконфигурируется без ручного вмешательства.
- При scale up создаётся новая реплика.
Отказ реплики
- Primary продолжает работу.
- При восстановлении реплика пересинхронизируется.
- На запись не влияет.
Сетевой раздел (split-brain)
- Кластер работает, если доступно большинство primary.
- Меньшинство переходит в read-only.
- Автоматическое восстановление при разрешении раздела.
Заключение
Проверенная в продакшне конфигурация Valkey-кластера в Kubernetes:
- StatefulSet + Parallel — быстрое масштабирование и восстановление.
- Init-скрипты — надёжная инициализация и переприсоединение узлов.
- Headless Service — DNS-обнаружение.
- redis-exporter sidecar — Prometheus-метрики из коробки.
- Автоматический failover без внешнего Sentinel.
Та же конфигурация работает и для Redis в кластерном режиме — отличий на уровне Kubernetes нет.