Журнал Tenku

Кластер Redis на VPS: установка и настройка с нуля

Один Redis — это одна точка отказа. Пока сервер жив, всё отлично: кэш, очереди, сессии. В момент, когда он падает или заканчивается память, вместе с ним останавливается приложение — и вы узнаёте об этом от пользователей, а не от мониторинга.

Кластер Redis решает две задачи сразу: распределяет данные по нескольким серверам (потому что один сервер имеет конечную память) и продолжает работать при отказе узла (реплика автоматически становится мастером). Ниже — полный путь от выбора тарифов до проверенного failover, команды можно выполнять по порядку. Инструкция рассчитана на обычные VPS с Linux, без облачных сервисов и без внешних оркестраторов.

Схема архитектуры кластера Redis на VPS: три мастера и три реплики

Схема размещения: три мастера и три реплики — шесть VPS в одной локации. Второй дата-центр на схеме — резервная площадка; она нужна, когда защищаться приходится не от отказа сервера, а от потери целого ДЦ.

Кому нужен кластер, а кому хватит одного сервера

Кластер — не «уровень повыше», а другая модель работы. Он стоит денег, серверов и внимания, поэтому сначала честно ответьте на два вопроса: сколько данных вы держите и что будет, если Redis выключится на час.

Хватит одного сервера, если:

  • объём данных — единицы гигабайт, помещается в RAM одного VPS с запасом;
  • простой в 10–30 минут приемлем (кэш, необязательные фоновые задачи);
  • вы готовы перезапустить сервис руками.

Нужен кластер, если:

  • данных больше, чем влезает в один разумный (по цене) VPS, или рост впереди;
  • простой означает потерю денег или клиентов (корзины, платежи, очереди заданий);
  • нужна запись, которая не останавливается при отказе одной машины.

Есть третий путь — managed-сервис. Облачный провайдер поднимает кластер за вас: в панели выбираете локацию, версию, конфигурацию ноды, количество реплик, политику вытеснения, пароль — и получаете готовый кластер с SLA. У Selectel, например, заявлена доступность записи 99,95% и чтения 99,99% для многонодного кластера. Платите за это существенно больше, чем за свой VPS, и теряете часть контроля: например, локацию и версию после создания не поменять. Ниже мы собираем свой кластер — это дешевле по железу и полностью под вашим управлением, но настраивать и обслуживать его придётся вам.

Три схемы отказоустойчивости: standalone, Sentinel, Cluster

Прежде чем идти в кластер, посмотрите на два более простых варианта — возможно, вам хватит их.

1. Standalone (обычный Redis). Один процесс, одна точка отказа. Резервная копия — только регулярные дампы. Когда данных больше, чем памяти, приходится идти в кластер.

2. Master + реплика + Sentinel. Один мастер на запись, одна или несколько реплик на чтение. Sentinels (минимум три процесса) следят за мастером и при его отказе назначают нового. Клиент спрашивает у Sentinel, где сейчас мастер. Это классическая схема «Redis для сервисов», и в реальных внедрениях её часто дополняют прокси — чтобы приложение обращалось к одному адресу и не знало про failover:

# sentinel.conf (на каждой из трёх машин)
sentinel monitor master01 10.0.0.11 6379 2
sentinel failover-timeout master01 6000
# haproxy: единая точка входа на порт 6379, трафик уходит текущему мастеру
listen redis
  bind *:6379
  option tcp-check
  tcp-check send PING\r\n
  tcp-check expect string +PONG
  tcp-check send info\ replication\r\n
  tcp-check expect string role:master
  server redis1 10.0.0.11:6379 check inter 3s
  server redis2 10.0.0.12:6379 check inter 3s

Схема отлично работает, но у неё есть предел: всё пишется на один узел, значит память ограничена одним сервером. И есть известная ловушка: если мастера перестают видеть друг друга (сеть), после истечения cluster-node-timeout реплика может стать мастером, а старый мастер — уйти в себя и «забыть» данные. Клиенты в это время могут писать в две разные копии. Sentinel снижает простой, но не увеличивает объём.

3. Redis Cluster. Данные разбиты на 16384 слота, которые распределяются между мастерами. Ключ всегда попадает к тому мастеру, который держит его слот. Минимум три мастера (иначе нет кворума для решения об отказе), у каждого — хотя бы одна реплика. Отказ мастера: реплика повышается автоматически, слоты переезжают к ней, кластер продолжает принимать записи.

Мастер A: слоты 0–5460      Реплика A'  (копия A)
Мастер B: слоты 5461–10922  Реплика B'  (копия B)
Мастер C: слоты 10923–16383 Реплика C'  (копия C)
Итого: 16384 слота, 3 мастера, 3 реплики

Клиент обязан быть cluster-aware: он спрашивает карту слотов и сам ходит к нужному узлу. Если направить на кластер обычный клиент, он получит ответ вида MOVED 866 10.0.0.13:6379 — это не ошибка, а подсказка «этот ключ живёт в другом месте». Дальше мы всё это проверим руками.

Сколько серверов нужно и как выбрать тарифы

Минимум для прода: 6 серверов — три мастера и три реплики, по одной машине на процесс. Меньше нельзя: с тремя узлами (1 мастер + 2 реплики) вы теряете шардинг, а с одним мастером — саму идею отказоустойчивости.

Почему мастеров обычно три, а не один: кластер принимает решения голосованием мастеров, и для выбора нового мастера нужно большинство доступных мастеров с зачётными слотами. При трёх мастерах кластер выдерживает отказ одного из них: два оставшихся — это большинство, они поднимают реплики и продолжают работу. При двух мастерах отказ одного уже блокирует запись: кворума нет. Три мастера — это минимальная рабочая схема, а не универсальный оптимум: добавляя мастеров, вы одновременно повышаете требования к доступности кворума, зато распределяете нагрузку и память шире. Для больших объёмов имеет смысл 5–6 мастеров с репликами — считайте по памяти и по нагрузке.

Как считать память. Redis держит данные в RAM, плюс накладные расходы (служебные структуры, фрагментация, буферы репликации). Ориентир: нужный объём данных × 2 и не меньше 25–30% свободной памяти сверху. Хотите хранить 1 ГБ полезных данных на каждом мастере — берите VPS с 2 ГБ RAM. Кэш на 512 МБ — хватит 1 ГБ.

Пример: данные 1 ГБ на узел → нужен VPS с 2 ГБ RAM
Итого 6 серверов × 2 ГБ = 12 ГБ RAM в кластере (~6 ГБ полезных данных с репликами)
Эконом-вариант на старт: 3 VPS × 2 инстанса (6 процессов) — дешевле, но без защиты от падения машины

Эконом-вариант (три VPS, на каждом по два инстанса Redis — мастер и реплика чужого мастера) встречается в интернете часто и стоит вдвое дешевле. Он годится для тестов и небольших проектов, но надо понимать цену: падение одной машины уносит сразу два процесса — мастер и реплику. Кластер обычно это переживает (при --cluster-replicas 1 реплика второго уровня не поднимается автоматически), но запас прочности падает. Если выбираете этот путь — берите серверы с запасом по ядрам и памяти, потому что на одной машине будут жить два Redis.

Реплика не должна жить на том же физическом хосте, что мастер

Два VPS в одной локации у одного провайдера — это ещё не два независимых сервера. Технически они могут оказаться виртуалками на одном гипервизоре, на одном дисковом массиве или за одним сетевым устройством: тогда падение хоста уносит и мастера, и его реплику. Redis сам этого не видит — он считает узлы независимыми всегда, когда они отвечают по сети, и никаких меток «кто на каком железе» в кластере нет. Отдельный случай — rack-zone awareness: в Redis Enterprise он есть как настройка, в open-source Redis его нет, там распределением реплик управляете вы.

Проверить независимость заранее можно так:

# 1) Не на одном ли гипервизоре (косвенный признак — DMI/BIOS и серийники хоста)
sudo dmidecode -s system-product-name; sudo dmidecode -s system-uuid
cat /sys/class/dmi/id/product_uuid          # у разных железных хостов значения разные

# 2) Не в одной ли подсети и не за одним ли шлюзом
ip -br addr; ip route get 8.8.8.8

# 3) Сравнить MAC-префиксы и трассу до мастеров
ip link | grep ether
traceroute -n 10.0.0.12

Соберите карту размещения и держите её рядом с кластером — при аварии она экономит часы:

Роль       Узел              Физический хост   Локация          Провайдер/гипервизор
Мастер A   10.0.0.11 (a1)   hv-msk-07         Москва, ДЦ-1     провайдер №1
Реплика A  10.0.0.14 (a2)   hv-msk-31         Москва, ДЦ-1     провайдер №1
Мастер B   10.0.0.12 (b1)   hv-msk-12         Москва, ДЦ-1     провайдер №1
Реплика B  10.0.0.15 (b2)   hv-nsk-04         Новосибирск      провайдер №2
Мастер C   10.0.0.13 (c1)   hv-nsk-02         Новосибирск      провайдер №2
Реплика C  10.0.0.16 (c2)   hv-nsk-11         Новосибирск      провайдер №2

Если виртуалки попали на один хост, есть два пути: попросить у провайдера развести машины по разным гипервизорам (обычно это делается в момент создания VPS) либо вручную назначить реплику на узел из другой локации — репликация между ДЦ добавляет 1–5 мс задержки на запись в синхронном виде, но для асинхронной репликации Redis это допустимо. Переназначить реплику можно явно, не доверяя автоматике:

# взять id мастера и перевести нужную реплику под него
redis-cli -a 'Пароль' cluster nodes | grep master
redis-cli -a 'Пароль' --cluster add-node 10.0.0.15:6379 10.0.0.11:6379   --cluster-slave --cluster-master-id <id-мастера-B>

Под кластер берите одинаковые по ресурсам VPS в одной локации: одинаковая память (иначе данные распределятся неравномерно), одинаковый диск, минимальный сетевой пинг между серверами. На Tenku конфигурации с 2, 4, 8 и 12 ГБ RAM и NVMe — от 299 ₽/мес за 2 ГБ; конкретные цены и наличие локаций смотрите на странице тарифов, там данные подтягиваются из биллинга. Если планируете держать кластер и приложение на разных машинах, посмотрите статью о том, сколько ресурсов нужно и как не переплачивать.

Подготовка серверов

Ниже считаем, что у вас шесть серверов с Ubuntu 24.04 (Debian 12 — то же самое) и приватной сетью между ними. Адреса названы условно: 10.0.0.11…10.0.0.16. Первые три будут мастерами, вторые три — репликами; фактическое распределение сделает redis-cli при создании.

1. Первичная настройка на каждом сервере. Как зайти по SSH и что сделать в первые минуты — в отдельной статье первый вход на VPS. Базово:

# обновление и утилиты
apt update && apt upgrade -y
apt install -y curl gnupg lsb-release ufw

# часовой пояс и имя узла
timedatectl set-timezone Europe/Moscow
hostnamectl set-hostname redis-a1

# swap 1 ГБ — страховка от жёсткого OOM, для Redis это «на крайний случай»
fallocate -l 1G /swapfile && chmod 600 /swapfile && mkswap /swapfile && swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab

2. Настройки ядра. Redis сам подсказывает их в логе при старте — сделаем заранее:

cat >> /etc/sysctl.conf <<'EOF'
vm.overcommit_memory = 1
net.core.somaxconn = 1024
EOF
sysctl -p

# отключаем Transparent Huge Pages (иначе растут задержки)
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' >> /etc/rc.local
chmod +x /etc/rc.local

3. Файрвол. У Redis два порта на каждый инстанс: обычный (6379) и служебный кластерный шина-порт, порт + 10000 (16379). Кластерный порт забывают чаще всего, и тогда кластер молча не собирается: CLUSTER MEET уходит, ответа нет.

# на каждом узле — доступ к Redis только из приватной сети
ufw default deny incoming
ufw allow from 10.0.0.0/24 to any port 6379 proto tcp
ufw allow from 10.0.0.0/24 to any port 16379 proto tcp
ufw allow 22/tcp
ufw enable

Если серверы в разных сетях или у вас нет приватной сети — используйте публичные IP, но обязательно ограничьте доступ по IP-адресам остальных узлов и поставьте пароль (см. ниже). Открытый в интернет Redis без пароля живёт до первых сканеров: обычно это часы. Про базовые правила — в статье про безопасность VPS.

4. Имена узлов. Чтобы конфиги читались людьми, добавьте в /etc/hosts на каждом сервере строки вида:

10.0.0.11 redis-a1
10.0.0.12 redis-b1
10.0.0.13 redis-c1
10.0.0.14 redis-a2
10.0.0.15 redis-b2
10.0.0.16 redis-c2

Установка Redis

В Ubuntu 24.04 штатный пакет — ветка 7.0.x. Этого достаточно для кластера: redis-cli --cluster умеет всё, что нужно. Если хотите более свежую 7.4.x — подключайте официальный репозиторий Redis.

Вариант через штатный репозиторий (проще):

apt install -y redis-server
redis-server -v
systemctl stop redis-server        # конфиг будем править до первого запуска

Вариант со свежей версией из официального репозитория:

curl -fsSL https://packages.redis.io/gpg | gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" > /etc/apt/sources.list.d/redis.list
apt update && apt install -y redis

Вариант со сборкой из исходников (если нужна конкретная версия или нестандартные модули). Так делали в старых руководствах, и он всё ещё рабочий:

apt install -y build-essential tcl pkg-config
cd /usr/local/src
curl -O https://download.redis.io/redis-stable.tar.gz
tar xzf redis-stable.tar.gz && cd redis-stable
make -j$(nproc) && make install

Конфигурация узла

Правим /etc/redis/redis.conf на каждом сервере. Ниже — минимальный рабочий набор для кластера. Комментарии в конфиге можно оставить как ориентир.

# слушаем приватный адрес узла, а не весь интернет
bind 10.0.0.11
protected-mode yes
port 6379

# кластер
cluster-enabled yes
cluster-config-file nodes-6379.conf
cluster-node-timeout 15000
cluster-replica-validity-factor 10
# FULL COVERAGE: если выставить no, кластер останется доступным даже при потере части слотов.
# Для продакшена с данными оставляйте yes — лучше остановиться, чем писать в неполный кластер.
cluster-require-full-coverage yes

# адрес, который узел сообщает другим узлам. Обязателен, если сервер за NAT/в облаке
# (иначе узлы увидят внутренний адрес контейнера и не смогут друг к другу подключиться)
# cluster-announce-ip 10.0.0.11

# пароль: защита и для клиентов, и для репликации (masterauth)
requirepass SlozhnyjParolIz30Simvolov
masterauth SlozhnyjParolIz30Simvolov

# данные: RDB + AOF (жёстче к диску, но переживает перезапуск без потерь)
appendonly yes
appendfsync everysec
save 900 1
save 300 10

# память: лимит и поведение при заполнении
maxmemory 1500mb
# для базы данных (нельзя терять ключи) — noeviction;
# для чистого кэша — allkeys-lru
maxmemory-policy noeviction

# логи
logfile /var/log/redis/redis-server.log

Три вещи, на которых спотыкаются чаще всего:

  • bind должен указывать на адрес, доступный другим узлам. bind 127.0.0.1 — кластер никогда не соберётся.
  • пароль нужен и в requirepass, и в masterauth: без второго реплика не сможет синхронизироваться с защищённым мастером.
  • maxmemory ставится ниже памяти сервера. Оставьте системе и буферам репликации 25–30%: maxmemory для VPS с 2 ГБ — около 1,5 ГБ.

Что именно гарантирует appendonly yes и appendfsync everysec. Эта связка защищает от потери данных при перезапуске процесса, но не даёт строгой гарантии сохранности последней записи: при everysec Redis сбрасывает AOF на диск раз в секунду, значит при аварии сервера теряется до ~1 секунды записей. Если запись подтверждена клиенту, но не легла ни в AOF, ни к реплике — она исчезнет. Вариант appendfsync always сбрасывает каждую запись, но заметно снижает пропускную способность и убивает задержки на медленных дисках. Практический вывод: AOF и репликация решают задачу доступности, а не сохранности — сохранность обеспечивают отдельные резервные копии, о которых речь ниже.

Запускаем и проверяем, что процесс поднялся в кластерном режиме:

systemctl enable redis-server && systemctl start redis-server
redis-cli -a 'SlozhnyjParolIz30Simvolov' info cluster | head -5
# cluster_enabled:1
# cluster_state:fail   ← это нормально: узел ещё не в кластере

Безопасность для продакшена: TLS, ACL, приватная сеть

Пароль и ограничение по IP — это минимум, а не финиш. В продакшене к ним добавляют шифрование и разделение прав.

Приватная сеть обязательна. И клиентский порт, и cluster bus (6379 и 16379) не должны быть доступны из интернета. Если серверы в разных сетях — поднимайте приватную сеть или VPN между узлами (WireGuard, IPsec), а публичные адреса закрывайте полностью. Открытый кластерный порт — это не только чтение ваших данных: через него можно перевести узлы в нерабочее состояние.

TLS для клиентских соединений и для межузлового трафика. Начиная с Redis 6 есть TLS для клиентов, с 7.2 — шифрование и самого кластерного канала (tls-cluster yes). Настройка парная: на всех узлах должны быть сертификаты, подписанные одним ЦС, а имена в сертификатах — совпадать с именами, которые узлы сообщают друг другу.

# фрагмент redis.conf: TLS для клиентов и для cluster bus (Redis 7.2+)
port 0                       # закрываем незашифрованный клиентский порт
tls-port 6379
tls-cert-file /etc/redis/tls/redis.crt
tls-key-file  /etc/redis/tls/redis.key
tls-ca-cert-file /etc/redis/tls/ca.crt
tls-cluster yes              # шифруем межузловой трафик (cluster bus — tls-port + 10000)
tls-replication yes

ACL вместо одного общего пароля. Redis 6+ умеет отдельных пользователей с ограничением команд и ключей — приложению незачем давать FLUSHALL и CONFIG:

redis-cli -a 'Пароль' ACL SETUSER app on '>парольПриложения' '~app:*' '+@read' '+@write' '+ping'
redis-cli -a 'Пароль' ACL SETUSER monitor on '>парольМонитора' '~*' '+@read' '+client|list' '+info'
redis-cli -a 'Пароль' ACL LIST

Секреты не передавайте через -a. Пароль в командной строке попадает в историю оболочки и в список процессов. Рабочие варианты: переменная REDISCLI_AUTH в защищённом окружении, файл ~/.rediscli с правами 600, менеджер секретов, а в конфигах — include отдельного файла с паролем, доступным только владельцу:

chmod 600 /etc/redis/redis.conf
printf 'export REDISCLI_AUTH=%s\n' "$(grep -oP '(?<=^requirepass ).*' /etc/redis/redis.conf)" > /root/.redis_auth
chmod 600 /root/.redis_auth && . /root/.redis_auth   # дальше: redis-cli без -a

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

Когда на всех шести серверах запущены узлы с cluster-enabled yes, собираем кластер одной командой. Порядок адресов важен: первые три станут мастерами, следующие три — их репликами.

redis-cli --cluster create \
  10.0.0.11:6379 10.0.0.12:6379 10.0.0.13:6379 \
  10.0.0.14:6379 10.0.0.15:6379 10.0.0.16:6379 \
  --cluster-replicas 1 -a 'SlozhnyjParolIz30Simvolov' --cluster-yes

Утилита предложит план распределения слотов и попросит подтверждение (флаг --cluster-yes отвечает согласием автоматически). Ожидаемый результат — строки про три мастера с диапазонами слотов и завершение вида All 16384 slots covered.

Проверяем состояние и карту:

redis-cli -a 'SlozhnyjParolIz30Simvolov' cluster info | grep -E 'cluster_state|cluster_slots_assigned|cluster_known_nodes'
# cluster_state:ok
# cluster_slots_assigned:16384
# cluster_known_nodes:6

redis-cli -a 'SlozhnyjParolIz30Simvolov' cluster nodes
# 8f2b… 10.0.0.11:6379@16379 myself,master - 0 0 1 connected 0-5460
# 3c91… 10.0.0.12:6379@16379 master - 0 0 2 connected 5461-10922
# a4d7… 10.0.0.13:6379@16379 master - 0 0 3 connected 10923-16383
# b1e0… 10.0.0.14:6379@16379 slave 8f2b… 0 0 4 connected
# …

# расширенная проверка (отвечает на вопрос «всё ли консистентно»)
redis-cli --cluster check 10.0.0.11:6379 -a 'SlozhnyjParolIz30Simvolov'

Если вместо cluster_state:ok вы видите fail и cluster_slots_assigned:0 — смотрите раздел «Типичные ошибки»: почти всегда это закрытый кластерный порт 16379 или bind не на тот адрес.

Проверка, что кластер жив

Клиентский доступ к кластеру всегда идёт с флагом -c: он включает переадресацию по слотам.

redis-cli -c -h 10.0.0.11 -p 6379 -a 'SlozhnyjParolIz30Simvolov'
127.0.0.1:6379> SET user:1001 "ok"
→ Redirected to 10.0.0.12:6379
127.0.0.1:6379> GET user:1001
"ok"

Полезные команды для отладки:

# в какой слот попадёт ключ и кто его держит
redis-cli -a 'Пароль' CLUSTER KEYSLOT user:1001

# современная карта слотов и шардов (Redis 7+), покажет владельца и реплики
redis-cli -a 'Пароль' CLUSTER SHARDS

# распределение ключей по узлам
redis-cli --cluster info 10.0.0.11:6379 -a 'Пароль'

Ответ MOVED 12345 10.0.0.13:6379 означает, что ключ принадлежит другому слоту — это нормальная работа кластера, а не ошибка. Ответ ASK встречается во время миграции слотов: клиент должен отправить команду целевому узлу. Все cluster-aware драйверы (redis-py, ioredis, predis, Lettuce, go-redis) умеют это сами — важно, чтобы клиент был кластерный, а не обычный:

# Python: кластерный клиент (не обычный Redis!)
from redis.cluster import RedisCluster
rc = RedisCluster(host='10.0.0.11', port=6379, password='Пароль', decode_responses=True)

Замеры: задержки, нагрузка и поведение при отказе

«Работает» и «быстро работает» — разные утверждения. Перед продом снимите три группы цифр: сетевую задержку до узлов, пропускную способность под нагрузкой и поведение при отказе.

# сетевая задержка до конкретного узла (измеряется RTT запросов, не один пинг)
redis-cli -h 10.0.0.12 -p 6379 -a 'Пароль' --latency
redis-cli -h 10.0.0.12 -p 6379 -a 'Пароль' --latency-history -i 5

# локальная задержка САМОГО redis на узле: это тест CPU/ядра, а не сети.
# Делайте его на узле, без нагрузки: сравнивайте узлы между собой
redis-cli --intrinsic-latency 100

Показательна разница между двумя последними командами: если --latency до узла в одном ДЦ десятки микросекунд, а --intrinsic-latency показывает миллисекунды — сервер тормозит сам (перегрузка CPU, медленный диск под AOF, своп), а не сеть.

Нагрузочный тест запускайте с отдельной машины, а не внутри кластера, и обязательно cluster-aware клиентом — redis-benchmark в режиме кластера умеет обходить узлы:

redis-benchmark -h 10.0.0.11 -p 6379 -a 'Пароль' --cluster   -t set,get -n 200000 -c 50 -d 100 --latency

Что сравнивать до и после изменений:

  • p50, p95, p99 задержки, а не среднее: среднее скрывает «хвосты», а приложение страдает именно от них;
  • пропускную способность (ops/sec) на мастеров отдельно — если один узел из трёх даёт половину общей нагрузки, слоты разложены неровно;
  • поведение при отказе: остановите мастера под нагрузкой и замерьте, сколько запросов упало и за сколько секунд клиент пошёл на новый узел. Это и есть ваш реальный RTO.

Клиент в кластере: MOVED, ASK, повторы и hash tags

Кластер меняет не только инфраструктуру, но и поведение приложения. Минимум, который нужно предусмотреть в коде.

MOVED и ASK — это не ошибки, а команды «сходи в другое место». Клиент должен уметь их обрабатывать и обновлять карту слотов. Всё, что нужно сделать приложению, — использовать кластерный драйвер (RedisCluster в redis-py, Cluster в ioredis, Predis\Cluster в PHP, Lettuce/go-redis в JVM и Go).

MOVED 5461 10.0.0.12:6379   ключ принадлежит другому слоту — обновить карту и повторить там
ASK   5461 10.0.0.12:6379   слот сейчас переезжает — выполнить один запрос на целевом узле
CLUSTERDOWN                 нет кворума или потеряны слоты — это уже авария, ретраи не помогут

Повторы записи должны быть идемпотентными. В момент failover клиент получает таймаут и не знает, применилась ли команда. Если это INCR счётчика баланса или списание — слепой повтор спишет дважды. Правильные подходы: идемпотентные операции, ключ-маркер (SETNX lock:op:<id>), версионирование, или перенос неидемпотентных действий в очередь с дедупликацией. Ограничьте число повторов (2–3) и добавьте паузу с ростом — иначе после восстановления узла вы получите шквал запросов.

Hash tags решают проблему мультиключевых операций. Ключи в кластере распределяются по слотам через CRC16, поэтому MGET user:1 user:2 может попасть в разные слоты и упасть с CROSSSLOT. Если ключи должны лежать рядом — задайте общий тег в фигурных скобках:

user:{42}:profile   и   user:{42}:settings   → один слот (тег «42»), работают транзакции и Lua
user:42:profile     и   user:43:profile      → разные слоты (для MSET/MGET вместе не годятся)

Правила, о которых лучше знать заранее: мультиключевые команды (MSET, MGET, SUNIONSTORE, PFCOUNT) работают только с ключами одного слота; SELECT в кластере не поддерживается — используйте отдельную нумерацию БД на уровне префиксов; Lua-скрипты обязаны обращаться только к ключам одного слота; KEYS на проде — плохая идея в любом Redis, а в кластере вы получите ещё и результат только одного узла.

Failover: проверяем отказ мастера

Это тест, который обязательно нужно провести до продакшена. Останавливаем Redis на одном мастере и смотрим, как кластер поднимет его реплику:

# на мастере A (10.0.0.11)
systemctl stop redis-server

# следим с любого другого узла
watch -n 2 "redis-cli -a 'Пароль' cluster nodes | grep master"

# ожидаемо: через cluster-node-timeout (15 с) плюс время выборов реплика A'
# (10.0.0.14) становится master, а старая строка получает флаг fail

Проверяем, что данные не потерялись и запись идёт:

redis-cli -c -h 10.0.0.12 -p 6379 -a 'Пароль' GET user:1001
redis-cli -c -h 10.0.0.12 -p 6379 -a 'Пароль' SET user:1002 "after-failover"

Возвращаем узел в строй — он вернётся уже репликой и синхронизируется автоматически:

systemctl start redis-server       # на 10.0.0.11
redis-cli -a 'Пароль' cluster nodes | grep 10.0.0.11
# 10.0.0.11:6379 … slave <id> … — узел снова в кластере, но уже как реплика

Замерьте фактическое время переключения у себя: в него входят cluster-node-timeout, время выборов и время, за которое приложение переподключится. Если приложение живёт без ретраев, для него 15–20 секунд недоступности записи — это авария, поэтому retry-логика в клиенте обязательна.

Отдельно про потери при переключении. Репликация в Redis асинхронная: мастер отвечает клиенту, не дожидаясь подтверждения от реплики. Если мастер упал, часть подтверждённых записей могла не дойти до реплики — они потеряются при повышении. Команда WAIT заставляет мастера дождаться подтверждения от реплик, но она не превращает репликацию в синхронную и не отменяет потерю при падении мастера после её завершения. Поэтому в схеме «Redis как единственное хранилище» проверяйте не только факт failover, но и объём потерянных записей в тесте: остановите мастера под нагрузкой и посчитайте, сколько подтверждённых операций не дожило до нового мастера.

Эксплуатация: рост, обновления, замена узлов

Добавить новый узел. Сначала запускаем Redis на новом сервере с тем же конфигом, потом добавляем в кластер:

redis-cli --cluster add-node 10.0.0.17:6379 10.0.0.11:6379 -a 'Пароль'                 # как мастер
redis-cli --cluster add-node 10.0.0.18:6379 10.0.0.11:6379 --cluster-slave -a 'Пароль' # как реплика

Новый мастер получит ноль слотов — пока в него не переедут ключи, он ничего не хранит. Перераспределяем:

redis-cli --cluster reshard 10.0.0.11:6379 -a 'Пароль'      # интерактивно: сколько слотов, куда
redis-cli --cluster rebalance 10.0.0.11:6379 -a 'Пароль'    # автоматически разложить слоты равномерно

Вывести узел. Сначала освобождаем слоты (если это мастер), затем удаляем:

redis-cli --cluster reshard 10.0.0.11:6379 --cluster-from <id-выводимого> --cluster-to <id-принимающего> --cluster-slots <N> -a 'Пароль' --cluster-yes
redis-cli --cluster del-node 10.0.0.11:6379 <id-узла> -a 'Пароль'

Обновить версию без простоя. Реплики обновляются по одной, затем мастера — по одному, с ручным переводом мастера на его реплику:

# на одном мастере: аккуратно понижаем узел в реплику и обновляем его парой
redis-cli --cluster check 10.0.0.11:6379 -a 'Пароль'   # перед каждым шагом
# после переключения мастера обновляйте освободившийся узел

Правило простое: никогда не обновляйте больше одного узла одновременно и не трогайте nodes-6379.conf руками — этот файл ведёт сам Redis, ручная правка ломает идентификаторы узлов.

Бэкапы: реплика — это не бэкап

Реплика защищает от отказа железа, но не от ошибки в приложении, которая удалила половину ключей: изменение мгновенно размножится на все копии. Поэтому бэкап — это снимок, вынесенный на другую машину, и проверенное восстановление.

# снимок RDB на каждом мастере
redis-cli -a 'Пароль' BGSAVE
redis-cli -a 'Пароль' INFO persistence | grep rdb_last_bgsave_status   # ok

# копируем дамп на отдельный сервер (не в тот же дата-центр, если есть возможность)
rsync -avz -e ssh /var/lib/redis/dump.rdb backup@backup-host:/srv/redis-$(date +%F)-a1.rdb

Что учесть:

  • AOF (appendonly yes) даёт более точное восстановление, но занимает больше диска — следите за размером и делайте BGREWRITEAOF;
  • храните дампы всех мастеров, иначе восстановите только часть слотов;
  • раз в месяц проверяйте восстановление на отдельной машине: поднять Redis, положить дамп, запустить, сравнить количество ключей (DBSIZE, INFO keyspace). Бэкап без проверки — это надежда, а не бэкап. Подробнее — в статье бэкапы VPS.

Восстановление после потери узла или целого ДЦ: RPO и RTO

Перед аварией стоит письменно ответить на два вопроса: RPO — сколько данных вы согласны потерять (в минутах или операциях) и RTO — за сколько времени обязаны поднять сервис. От этих цифр зависит вся схема: частота дампов, наличие второго ДЦ, наличие дежурного.

Сценарий 1: упал один узел. Ничего не делаем вручную: реплика станет мастером, кластер продолжит работу. Задача — вернуть узел и убедиться, что он подключился репликой:

redis-cli -a 'Пароль' cluster nodes          # узел должен быть slave и connected
redis-cli -a 'Пароль' --cluster check 10.0.0.11:6379

Сценарий 2: потеряны все узлы или всё ДЦ. Здесь нужен бэкап и план. Порядок действий:

# 1) убедиться, что бэкап целый и свежий (дата, размер, контрольная сумма)
ls -la /srv/redis-2026-10-16-*.rdb && sha256sum /srv/redis-2026-10-16-a*.rdb

# 2) поднять новый Redis на чистых серверах с тем же redis.conf (cluster-enabled yes)
# 3) раздать дампы: для каждого будущего мастера — его собственный dump.rdb в каталог данных
# 4) запустить узлы, затем собрать кластер заново с ТОЙ ЖЕ раскладкой слотов
redis-cli --cluster create 10.0.0.11:6379 10.0.0.12:6379 10.0.0.13:6379 \
  10.0.0.14:6379 10.0.0.15:6379 10.0.0.16:6379 --cluster-replicas 1 --cluster-yes -a 'Пароль'

# 5) проверить полноту данных
redis-cli -a 'Пароль' --cluster info 10.0.0.11:6379
redis-cli -a 'Пароль' INFO keyspace

Важные предупреждения по этому сценарию:

  • слоты нужно раздавать тем же мастерам, что и раньше, иначе часть данных окажется «не на своём» узле и кластер её не увидит;
  • RPO напрямую зависит от частоты дампов: ежедневный бэкап означает потерю до суток данных; для RPO в часы нужны копии каждые 1–6 часов;
  • реплика в том же ДЦ не спасает от потери ДЦ: нужен либо второй ДЦ, либо вынос дампов в другое место;
  • проверьте восстановление заранее на тестовом кластере: первое восстановление в момент аварии почти всегда идёт с ошибками.

Мониторинг: минимум, который спасает

Мониторить нужно не «сервер в целом», а состояние кластера. Соберите метрики:

redis-cli -a 'Пароль' info cluster   | grep -E 'cluster_state|cluster_slots_fail|cluster_known_nodes'
redis-cli -a 'Пароль' info memory   | grep -E 'used_memory_human|maxmemory_human|mem_fragmentation_ratio'
redis-cli -a 'Пароль' info stats    | grep -E 'evicted_keys|rejected_connections|keyspace_hits'
redis-cli -a 'Пароль' slowlog get 10

На что ставить алерты в первую очередь:

  • cluster_state != ok — авария, будите дежурного;
  • cluster_slots_fail > 0 — потеряны слоты, часть данных недоступна;
  • evicted_keys растёт (для кэша) — мало памяти, нужно расширять кластер;
  • rejected_connections > 0 — закончились файловые дескрипторы/память;
  • master_link_status:down на реплике — репликация сломалась;
  • размер AOF и свободное место на диске — заполненный диск останавливает запись.

Для графиков подойдёт любой exporter (redis_exporter + Prometheus + Grafana) или даже простой скрипт по cron, который раз в минуту снимает INFO и отправляет в ваш мониторинг. Про общую диагностику «тормозит сервер» — в статье почему VPS тормозит.

Полный набор метрик и алерты

Одного cluster_state:ok мало: кластер может быть «здоров», пока реплика молча отстаёт на час, а диск заполнен под AOF. Снимайте метрики по каждой группе и ставьте пороги:

redis-cli -a 'Пароль' INFO memory      | grep -E 'used_memory:|maxmemory:|mem_fragmentation_ratio|maxmemory_policy'
redis-cli -a 'Пароль' INFO stats       | grep -E 'evicted_keys|rejected_connections|keyspace_hits|keyspace_misses'
redis-cli -a 'Пароль' INFO persistence | grep -E 'aof_enabled|aof_last_write_status|aof_current_size|rdb_last_bgsave_status'
redis-cli -a 'Пароль' INFO replication | grep -E 'role|master_link_status|master_last_io_seconds_ago|slave_repl_offset'
redis-cli -a 'Пароль' INFO replication | grep -E 'lag|offset'      # на реплике: отставание в секундах

Минимум, который стоит повесить на алерты:

  • отставшая реплика: master_link_status:down или master_last_io_seconds_ago больше нормы (например, 10 с) — при аварии такая реплика не станет мастером без потери данных;
  • разъезд данных: сравните master_repl_offset мастера и реплики — растущая разница означает, что вы копите будущую потерю;
  • память: used_memory подходит к 80–85% maxmemory, mem_fragmentation_ratio устойчиво выше 1,5 — пора расширять кластер или чистить;
  • вытеснение и отказы: evicted_keys растёт (для БД это потеря данных), rejected_connections больше нуля;
  • диск и AOF: свободное место и aof_last_write_status:ok — на заполненном диске Redis перестаёт писать;
  • кворум: число живых мастеров упало ниже половины, cluster_slots_fail больше нуля;
  • деградация задержек: p99 из --latency или из метрик приложения выше привычного в 2 раза.

Дополнительно полезно логировать SLOWLOG (медленные команды) и следить за свопом: Redis, ушедший в swap, перестаёт держать обещанные микросекунды.

Типичные ошибки и как их не допустить

  • cluster_state:fail, слотов 0. Не открыт кластерный порт (6379 + 10000 = 16379) или bind смотрит на 127.0.0.1. Проверьте ss -tlnp | grep 6379 и правила файрвола.
  • CLUSTER MEET не срабатывает. Между узлами нет связи: разные подсети, NAT без cluster-announce-ip, блокировка по MTU. Проверьте redis-cli -h <другой узел> ping с каждого сервера.
  • Кластер не создаётся: «Not all 16384 slots covered». Один из мастеров не поднялся или вы указали адреса, до которых утилита не дотягивается.
  • Реплика не синхронизируется. На мастере задан requirepass, а на реплике нет masterauth (или пароли разные).
  • Клиент ловит MOVED. Подключились обычным клиентом вместо кластерного или забыли флаг -c в redis-cli.
  • Ошибки записи после роста данных. Кончилась память и maxmemory-policy noeviction запрещает вытеснение: расширяйте кластер или меняйте политику для кэша.
  • Данные стали недоступны после падения машины. Кластер собран «в один сервер по два инстанса» — падение унесло и мастера, и реплику.
  • cluster-node-timeout слишком маленький. При сетевых икотах кластер начинает «моргать»: реплики бесконечно перевыбирают мастеров. Для продакшена 15 000 мс — разумный минимум.
  • Файл nodes.conf правили руками. Узлы путают идентификаторы. Правильный путь — redis-cli --cluster fix.
  • Пароль в командной строке. Он попадает в историю оболочки. Или настройте переменную REDISCLI_AUTH в окружении, или используйте --askpass, или ACL-пользователей вместо общего пароля.

Чек-лист перед продом

  • Кластер собран из трёх мастеров и трёх реплик, cluster_state:ok, покрыты все 16384 слота.
  • Каждый инстанс слушает приватный адрес, кластерный порт 16379 закрыт для интернета.
  • Задан пароль: requirepass + masterauth, доступ закрыт по IP.
  • Проверен failover на живом кластере: запись восстановилась автоматически.
  • Клиент — кластерный, с ретраями и таймаутами, а не «обычный Redis на порт мастера».
  • maxmemory оставляет системе 25–30%, политика вытеснения осознанно выбрана.
  • Настроены бэкапы со всех мастеров на отдельную машину, восстановление проверено хотя бы раз.
  • Есть мониторинг состояния кластера и алерты на cluster_state и место на диске.
  • Обновления делаются по одному узлу, nodes.conf руками не правится.
  • Приложение протестировано с недоступной репликой: читает с мастера и не падает.

Managed Redis или свой кластер

Коротко и без рекламы: это разные компромиссы, а не «хорошо/плохо».

Свой кластер на VPS. Дешевле по железу, полный доступ к конфигу и данным, никакой платформы-прослойки. Ценой идёт ваша работа: настройка, обновления, бэкапы, дежурство. Шесть небольших VPS на Tenku обходятся заметно дешевле managed-кластера похожей ёмкости — актуальные тарифы смотрите на странице VPS.

Managed-сервис. Провайдер берёт на себя обвязку: разворачивание, патчи, бэкапы, мониторинг, отказоустойчивость — с SLA (например, 99,95% на запись и 99,99% на чтение). Вы платите за это в разы больше и принимаете ограничения: нельзя произвольно менять версию или локацию после создания, доступ к файлам и внутренним настройкам ограничен. Часто это правильный выбор, когда у команды нет дежурного админа, а простой стоит дороже подписки.

Практическое правило: свой кластер берите, если вы умеете читать INFO и готовы дежурить; managed — если Redis нужен как инструмент, а не как проект.

Частые вопросы

Сколько серверов нужно для кластера Redis?

Минимум три мастера (для кворума при отказе) и хотя бы одна реплика на каждого — итого шесть машин. Конфигурация из трёх серверов по два инстанса рабочий эконом-вариант для тестов, но при отказе машины вы теряете сразу мастера и реплику.

Можно ли поставить кластер Redis на один VPS?

Технически — да, запустив несколько процессов на разных портах. Практического смысла мало: падение машины уносит весь кластер, вы получаете только шардинг по памяти. Такая схема годится для разработки и тестов логики приложения.

Какой порт нужен для кластера Redis?

Клиентский порт (по умолчанию 6379) и кластерный шина-порт, который всегда на 10 000 больше: 16379. Если открыть только 6379, узлы не смогут обмениваться служебными сообщениями, и кластер не соберётся.

Чем Redis Cluster отличается от Sentinel?

Sentinel даёт отказоустойчивость: три сервиса следят за мастером и выбирают нового при отказе. Но данные всё равно живут на одном узле, то есть память ограничена одним сервером. Cluster добавляет шардинг: данные распределены по слотам между мастерами, поэтому растёт и память, и пропускная способность.

Что будет с данными при отказе мастера?

Если у мастера есть реплика, она становится мастером за время около cluster-node-timeout (по умолчанию 15 секунд) плюс время выборов. Запись в это время недоступна для части слотов. Данные, которые успели записаться на мастер, сохраняются в пределах настроек репликации.

Реплика заменяет бэкап?

Нет. Реплика мгновенно повторяет любое изменение, включая ошибочное удаление ключей, поэтому она защищает от отказа железа, но не от ошибки или атаки. Бэкап — отдельный снимок (RDB/AOF), вынесенный на другую машину, с проверенным восстановлением.

Как понять, что кластер здоров?

cluster_state:ok, cluster_slots_assigned:16384, у каждого мастера есть реплика в состоянии connected, а redis-cli --cluster check не находит проблем. Эти три проверки стоит повесить в мониторинг как базовые.

Итог

Кластер Redis — это не «поставить пакет и забыть», а небольшая инфраструктура: шесть узлов, пароль, закрытые порты, понимание слотов и проверенный failover. Зато после сборки вы получаете хранилище, которое переживает отказ машины и растёт по памяти — просто добавляя узлы.

Под кластер удобно брать несколько одинаковых VPS: одинаковые ресурсы, одна локация, минимальный пинг между узлами. Конфигурации с NVMe и нужным объёмом RAM — на странице тарифов; если сомневаетесь, хватит ли памяти на ваши данные, начните с трёх узлов и теста на реальной нагрузке. А если Redis нужен только как кэш на пару сотен мегабайт, начните со обычного VPS и схемы master-slave — и переходите в кластер, когда упрётесь в память или в требования по доступности.

Нужен сервер под задачу?

Выберите тариф и локацию — VPS появится в кабинете.

Смотреть тарифы