Для обеспечения распределения нагрузки на кеш между несколькими серверами и отказоустойчивости необходимо использовать 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
Это необходимо сделать на каждом сервере кластера.
Рекомендуемые настройки для быстродействия
В файле конфигурации /etc/redis/redis.conf необходимо значение опции stop-writes-on-bgsave-error
заменить на no:
stop-writes-on-bgsave-error no
Также для повышения производительности рекомендуется отключить персистентность, но тогда при перезагрузках сервера Redis весь кеш будет сбрасываться, в результате чего будут потеряны и некоторые полезные для сервиса данные, например сохранённые позиции просмотров фильмов и передач. Для этого закомментируйте опции save
и добавьте save ""
:
save "" # save 900 1 # save 300 10 # save 60 10000
Добавление инстансов 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.RedisCluster", "REDIS_CLIENT_KWARGS": { "read_from_replicas": True }, "CONNECTION_POOL_CLASS": "core.cache.cluster_connection.ClusterConnectionPool", "CONNECTION_POOL_KWARGS": { "startup_nodes": [ {"host": "10.0.0.1", "port": "7000", "server_type": "master"}, {"host": "10.0.0.1", "port": "7001", "server_type": "slave"}, {"host": "10.0.0.1", "port": "7002", "server_type": "slave"}, ] } } } }
В качестве значения 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/
.
Заново запускаем инстансы и создаем кластер, как при начальной настройке.