1. Домой
  2. Документы
  3. Smarty
  4. Масштабирование и отказоустойчивость
  5. Настройка Redis Cluster

Настройка Redis Cluster

Для обеспечения распределения нагрузки на кеш между несколькими серверами и отказоустойчивости необходимо использовать Redis в кластерном режиме, также как и Smarty.

Оптимальной схемой является настройка Master-ноды Redis на каждом сервере Smarty в режиме Multi-Master. Для обеспечения отказоустойчивости Redis минимальное количество серверов кластера — 3.

Инструкция по настройке Redis Cluster в режиме Multi-Master

Допустим, необходимо настроить 3 ноды на серверах с IP-адресами:
10.0.0.1
10.0.0.2
10.0.0.3

Схематично это выглядит так:

Такая конфигурация позволит пережить потерю двух из трех серверов кластера.

На каждом сервере должен быть установлен redis-server версии не ниже 3.0. Рекомендуется использовать последнюю версию Redis 5.x.

На ОС Debian сервер Redis можно установить с помощью команды:

apt install redis-server

Настройка прослушиваемых интерфейсов

В файле /etc/redis/redis.conf необходимо изменить опцию protected-mode в значение no, а также bind в 0.0.0.0:

protected-mode no
bind 0.0.0.0

Это позволит Redis прослушивать подключения на всех интерфейсах.

Настройка доступа к портам

Чтобы обезопасить систему, необходимо запретить по умолчанию доступ к портам 7000, 7001, 7002, 17001, 17002, 17003 на каждом сервере, и открыть доступ к этим портам только для других нод Redis-кластера. Например, при использовании утилиты ufw для iptables на первом сервере необходимо выполнить команды:

ufw allow in from 10.0.0.2 to any port 7000 proto tcp
ufw allow in from 10.0.0.2 to any port 7001 proto tcp
ufw allow in from 10.0.0.2 to any port 7002 proto tcp
ufw allow in from 10.0.0.2 to any port 17000 proto tcp
ufw allow in from 10.0.0.2 to any port 17001 proto tcp
ufw allow in from 10.0.0.2 to any port 17002 proto tcp
ufw allow in from 10.0.0.3 to any port 7000 proto tcp
ufw allow in from 10.0.0.3 to any port 7001 proto tcp
ufw allow in from 10.0.0.3 to any port 7002 proto tcp
ufw allow in from 10.0.0.3 to any port 17000 proto tcp
ufw allow in from 10.0.0.3 to any port 17001 proto tcp
ufw allow in from 10.0.0.3 to any port 17002 proto tcp
ufw allow out to any

На втором сервере:

ufw allow in from 10.0.0.1 to any port 7000 proto tcp
ufw allow in from 10.0.0.1 to any port 7001 proto tcp
ufw allow in from 10.0.0.1 to any port 7002 proto tcp
ufw allow in from 10.0.0.1 to any port 17000 proto tcp
ufw allow in from 10.0.0.1 to any port 17001 proto tcp
ufw allow in from 10.0.0.1 to any port 17002 proto tcp
ufw allow in from 10.0.0.3 to any port 7000 proto tcp
ufw allow in from 10.0.0.3 to any port 7001 proto tcp
ufw allow in from 10.0.0.3 to any port 7002 proto tcp
ufw allow in from 10.0.0.3 to any port 17000 proto tcp
ufw allow in from 10.0.0.3 to any port 17001 proto tcp
ufw allow in from 10.0.0.3 to any port 17002 proto tcp
ufw allow out to any

На третьем сервере:

ufw allow in from 10.0.0.1 to any port 7000 proto tcp
ufw allow in from 10.0.0.1 to any port 7001 proto tcp
ufw allow in from 10.0.0.1 to any port 7002 proto tcp
ufw allow in from 10.0.0.1 to any port 17000 proto tcp
ufw allow in from 10.0.0.1 to any port 17001 proto tcp
ufw allow in from 10.0.0.1 to any port 17002 proto tcp
ufw allow in from 10.0.0.2 to any port 7000 proto tcp
ufw allow in from 10.0.0.2 to any port 7001 proto tcp
ufw allow in from 10.0.0.2 to any port 7002 proto tcp
ufw allow in from 10.0.0.2 to any port 17000 proto tcp
ufw allow in from 10.0.0.2 to any port 17001 proto tcp
ufw allow in from 10.0.0.2 to any port 17002 proto tcp
ufw allow out to any

Порты 7000, 7001, 7002 мы будем использовать для подключения к Redis, а порты 17000, 17001, 17002 Redis использует для коммуникации между нодами кластера.

Установка утилиты redis-trib

На первом сервере необходимо выполнить команду:

wget https://bootstrap.pypa.io/get-pip.py && python get-pip.py && rm get-pip.py 
pip install redis-trib

Утилита redis-trib используется для настройки кластера и управления нодами.

Установка скрипта запуска Redis

Для удобного добавления конфигураций Redis и запуска нод необходимо добавить сервис в systemd:

touch /etc/systemd/system/redis-cluster-node@.service

Затем записать в этот файл следующий скрипт:

[Unit]
Description=redis cluster instance at port %i
After=network.target
Documentation=http://redis.io/documentation, man:redis-server(1)

[Service]
Type=forking
ExecStart=/usr/bin/redis-server /etc/redis/redis.conf \
--cluster-enabled yes \
--dbfilename dump_%i.rdb \
--port %i --cluster-config-file nodes_%i.conf \
--pidfile /var/run/redis/redis-server_%i.pid \
--logfile /var/log/redis/redis-server_%i.log

PIDFile=/var/run/redis/redis-server_%i.pid
TimeoutStopSec=0
Restart=always
User=redis
Group=redis
RunTimeDirectory=redis

ExecStartPre=-/bin/run-parts --verbose /etc/redis/redis-server.pre-up.d
ExecStartPost=-/bin/run-parts --verbose /etc/redis/redis-server.post-up.d
ExecStop=-/bin/run-parts --verbose /etc/redis/redis-server.pre-down.d
ExecStop=/bin/kill -s TERM $MAINPID
ExecStopPost=-/bin/run-parts --verbose /etc/redis/redis-server.post-down.d

UMask=007
PrivateTmp=yes
LimitNOFILE=65535
PrivateDevices=yes
ProtectHome=yes
ReadOnlyDirectories=/
ReadWriteDirectories=-/var/lib/redis
ReadWriteDirectories=-/var/log/redis
ReadWriteDirectories=-/var/run/redis
CapabilityBoundingSet=~CAP_SYS_PTRACE

# redis-server writes its own config file when in cluster mode so we allow
# writing there (NB. ProtectSystem=true over ProtectSystem=full)
ProtectSystem=true
ReadWriteDirectories=-/etc/redis

[Install]
WantedBy=multi-user.target

Затем выполнить команду:

systemctl daemon-reload

Это необходимо сделать на каждом сервере кластера.

Добавление инстансов Redis

На каждом сервере необходимо выполнить:

systemctl enable redis-cluster-node@7000
systemctl enable redis-cluster-node@7001
systemctl enable redis-cluster-node@7002

Затем запустить добавленные инстансы:

systemctl start redis-cluster-node@7000.service
systemctl start redis-cluster-node@7001.service
systemctl start redis-cluster-node@7002.service

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

На первом сервере необходимо выполнить команду создания Master-нод кластера:

redis-trib.py create 10.0.0.1:7000 10.0.0.2:7000 10.0.0.3:7000

Затем равномерно добавить реплики кластера (эти команды нужно выполнить только на первом сервере, где установлена утилита redis-trib):

redis-trib.py replicate --slave-addr 10.0.0.2:7001 --master-addr 10.0.0.1:7000
redis-trib.py replicate --slave-addr 10.0.0.3:7002 --master-addr 10.0.0.1:7000
redis-trib.py replicate --slave-addr 10.0.0.3:7001 --master-addr 10.0.0.2:7000
redis-trib.py replicate --slave-addr 10.0.0.1:7002 --master-addr 10.0.0.2:7000
redis-trib.py replicate --slave-addr 10.0.0.1:7001 --master-addr 10.0.0.3:7000
redis-trib.py replicate --slave-addr 10.0.0.2:7002 --master-addr 10.0.0.3:7000

Затем можно проверить список добавленных нод кластера:

redis-trib.py list --addr 10.0.0.1:7000

Внимание! При добавлении реплик иногда они могут подключиться не к тем мастерам, к которым было нужно согласно схеме кластера. Если это произошло, то нужно удалить неправильную ноду из кластера и снова добавить, например:

redis-trib.py del_node --addr 10.0.0.3:7002
redis-trib.py replicate --slave-addr 10.0.0.3:7002 --master-addr 10.0.0.1:7000

Проверка кластера

Для проверки запишем значение bar с ключом foo на первой ноде, и прочитаем его на других нодах:

redis-cli -h 10.0.0.1 -p 7000 -c set foo bar
redis-cli -h 10.0.0.2 -p 7000 -c get foo
redis-cli -h 10.0.0.3 -p 7002 -c get foo

Настройка подключения к кластеру Redis в Smarty

В файле конфигурации Smarty /etc/microimpuls/smarty/smarty.py на каждом сервере Smarty необходимо прописать блок CACHES следующим образом:

CACHES = {
    "default": {
        "BACKEND": "core.cache.backends.RedisCache",
        "LOCATION": "redis://10.0.0.1:7000/0",
        "OPTIONS": {
            "REDIS_CLIENT_CLASS": "rediscluster.client.StrictRedisCluster",
            "CONNECTION_POOL_CLASS": "core.cache.cluster_connection.ClusterConnectionPool",
            "CONNECTION_POOL_KWARGS": {
                "startup_nodes": [
                    # masters
                    {"host": "10.0.0.1", "port": "7000"},
                    {"host": "10.0.0.2", "port": "7000"},
                    {"host": "10.0.0.3", "port": "7000"},
                ]
            }
        }
    }
}

В качестве значения LOCATION на каждом сервере необходимо указать адрес локальной ноды Redis кластера, а в блоке startup_nodes указать все мастер-ноды кластера.

Восстановление поврежденного кластера Redis

Частичное восстановление кластера

Если произошла потеря нескольких нод кластера (например, двух), то для восстановления кластера на оставшемся сервере (допустим это сервер 10.0.0.1) необходимо выполнить следующие действия.

Поднимаем две новые ноды:

systemctl start redis-cluster-node@8000
systemctl start redis-cluster-node@8001

Восстанавливаем данные из оставшейся работоспособной ноды:

redis-trib.py rescue --existing-addr 10.0.0.1:7000 --new-addr 10.0.0.1:8000
redis-trib.py rescue --existing-addr 10.0.0.1:7000 --new-addr 10.0.0.1:8001

Полное восстановление кластера

На всех серверах останавливаем все процессы кластерного редиса

systemctl stop redis-cluster-node@7000
systemctl stop redis-cluster-node@7001
systemctl stop redis-cluster-node@7002

Затем удаляем все файлы из /var/lib/redis/.

Заново запускаем инстансы и создаем кластер, как при начальной настройке.