Кластер Valkey в Kubernetes: полное руководство

Подробное руководство по запуску продакшн-кластера Valkey в Kubernetes: ConfigMap, StatefulSet с Parallel-подменеджментом, init-скрипты, headless-сервис, Prometheus-мониторинг, масштабирование и обработка сбоев.
Опубликовано:

Оригинал: 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:

Режимы развёртывания

РежимХарактеристикаКогда применять
StandaloneОдиночный инстанс, без HAРазработка, некритичные нагрузки
SentinelPrimary-Replica + мониторинг SentinelПростые HA-нужды, традиционные VM
ClusterШардирование + встроенный failoverПродакшн на Kubernetes

В Kubernetes Cluster-режим предпочтительнее Sentinel потому что:

Архитектура

Развёртывание состоит из следующих ресурсов Kubernetes:

  1. ConfigMap — конфиг Valkey и скрипты инициализации.
  2. StatefulSet — управление подами Valkey + sidecar redis-exporter.
  3. Headless Service — DNS-обнаружение подов и прямой pod-to-pod-обмен.
  4. ServiceMonitor (опционально) — интеграция с Prometheus.

ConfigMap

Содержит три компонента:

1. valkey.conf — основной конфиг

YAML
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
Нажмите, чтобы развернуть и увидеть больше

Ключевые параметры:

2. init-config.sh — инициализация пода

BASH
#!/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
Нажмите, чтобы развернуть и увидеть больше

3. init-cluster.sh — инициализация кластера

Скрипт обрабатывает три сценария:

Primary-0 (первый под):

BASH
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):

Replica (ordinal >= primaries):

Формула распределения:

PLAINTEXT
primaries = (replicas + 1) / 2
replicas_per_primary = (replicas - primaries) / primaries
Нажмите, чтобы развернуть и увидеть больше

StatefulSet

Ключевые решения:

YAML
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: 5Gi
Нажмите, чтобы развернуть и увидеть больше

Headless Service

Обеспечивает DNS-обнаружение подов и прямой pod-to-pod-обмен:

YAML
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:

YAML
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:

PLAINTEXT
primaries = (replicas + 1) / 2
Нажмите, чтобы развернуть и увидеть больше
ReplicasPrimariesРеплик на primary
320
631
950

Scale up

SH
# С 6 до 9 узлов
kubectl scale statefulset valkey --replicas=9
Нажмите, чтобы развернуть и увидеть больше

Что происходит:

Влияние: кратковременный рост сетевого трафика и потребления памяти при миграции слотов. Даунтайма нет.

Scale down

SH
# С 9 до 6 узлов
kubectl scale statefulset valkey --replicas=6
Нажмите, чтобы развернуть и увидеть больше

Что происходит:

Когда масштабировать

Scale upScale down
Память стабильно >70%Память стабильно низкая
Много клиентских соединенийСнизилось число клиентов
Нужна лучшая производительность чтенияОптимизация затрат
Требуется больше избыточностиЭвакуация зоны / обслуживание

Правила:

Сценарии отказов

Отказ primary

Отказ реплики

Сетевой раздел (split-brain)

Заключение

Проверенная в продакшне конфигурация Valkey-кластера в Kubernetes:

Та же конфигурация работает и для Redis в кластерном режиме — отличий на уровне Kubernetes нет.

Начать поиск

Введите ключевые слова для поиска статей

↑↓
ESC
⌘K Горячая клавиша