Продолжение серии по Redis репликации.
Первая часть – Redis: репликация, часть 1 – обзор. Replication vs Sharding. Sentinel vs Cluster. Топология Redis.
Третья часть – Redis: репликация, часть 3 — redis-py и работа с Redis Sentinel из Python.
Чертвёртая часть – Redis: репликация, часть 4 — написание Ansible роли.
Собственно, вся история началась с того, что мы решили избавиться от memcached
.
На данный момент у нас на серверах запущены и memcahced
, и Redis.
И memcached
, и Redis работают как standalone приложения на разных сервера, т.е. их инстансы никак не связаны друг с другом, из-за чего возникает проблема:
- есть три хоста бекенда, которые расположены за AWS Applcation Load balancer
- на ALB включены Sticky Sessions, но они работают через cookies, которые игнорируются нашими мобильными приложениями (iOS/Android)
- соответственно, когда клиент обращается к бекенду – иногда он может получить закешированные данные, которые уже были удалены на другом инстансе
memcached
или Redis
Такая схема пререшла к нам после миграции приложения со старой инфрастуктуры, на которой был только один сервер, и до сих пор не доходили руки ни у меня, ни у команды бекенд-девелоперов привести её в порядок, хотя мысль об избавлении от memcached
и настройке репликации Redis обсуждалась давно.
Сейчас проблема решается с помощью костылей на бекенде, который перепроверяет данные, а что бы избавиться от этого – решили:
- избавиться от
memcached
вообще, т.к. Redis может выполнять те же функции, которые сейчас выполняетmemcached
- настроить репликацию данных Redis между всеми хостами
Настройка такой репликации и описывается в этом посте.
Сначала – базовая, и второй пример – с Sentinel.
Запускать будем на AWS EC2 инстансах с Debian 9.
Для работы с сервисами используем три домена – redis-0.setevoy.org.ua для мастера, redis-1.setevoy.org.ua и redis-2.setevoy.org.ua для двух слейвов.
Слейв в минимальном сетапе может быть и один, но т.к. второй сетап будет с Sentinel – то создадим сразу три инстанса.
Содержание
Базовая Master-Slave репликация
В этом варианте слейвы являются read-only репликами мастера, поддерживая у себя ту же информацию, которая добавляется на мастер.
Мастер шлёт на слейвы все изменения, которые выполняются в данных – записи клиентов, обновления ключей и т.д.
В случае, если связь между мастером и слейвом обрывается – слейв пытается подключиться к мастеру и выполнить частичную сихронизацию, обновив данные с того места, на котором произошёл (“Ни единого разрыва!“ (с)) обрыв связи.
В случае, если частичная синхронизация невозможна – слейв запрашивает у мастера полную синхронизацию. Тогда мастер создаёт полный снапшот всех имеющихся у него данных, отправляет их на слейв, а затем продолжает слать обновления в обычном режиме.
Несколько нюансов такой репликации:
- один мастер может иметь несколько слейвов
- слейвы могут принимать подключения от других слейвов, создавая таким образом “каскад” реплицированных нод – от мастера “вверху”, слейвов “в середине” и слейвов “внизу”
- категорически рекомендуется использовать persistence на мастере и слейвах во избежание потери данных, см. Safety of replication when master has persistence turned off
- слейвы по умолчанию работают в read-only режиме, см. Read-only slave
Настройка Redis Master
Устанавливаем Redis:
[simterm]
root@redis-0:/home/admin# apt -y install redis-server
[/simterm]
Редактируем /etc/redis/redis.conf
, в bind
задаём прослушивание всех интерфейсов:
... bind 0.0.0.0 ...
Можно указать IP через пробел, например:
... bind 127.0.0.1 18.194.229.23 ...
Другие интересные тут опции:
port 6379
– понятноslave-read-only yes
– слейвы принимают запросы только на read, на мастере роли не играетrequirepass foobared
– мастер требует авторизацию с паролем foobaredappendonly yes
иappendfilename "appendonly.aof"
– уменьшаем вероятность потери последних данных, см. Redis Persistence
Перезапускаем сервис:
[simterm]
root@redis-0:/home/admin# systemctl restart redis
[/simterm]
Проверяем, передавая с помощью опции -a
пароль:
[simterm]
root@redis-0:/home/admin# redis-cli -a foobared ping PONG
[/simterm]
Проверяем статус репликации:
[simterm]
root@redis-0:/home/admin# redis-cli -a foobared info replication # Replication role:master connected_slaves:0 master_repl_offset:0 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:0 repl_backlog_histlen:0
[/simterm]
Добавляем данные:
[simterm]
root@redis-0:/home/admin# redis-cli -a foobared set test 'test' OK
[/simterm]
Получаем их:
[simterm]
root@redis-0:/home/admin# redis-cli -a foobared get test "test"
[/simterm]
ОК – тут всё работает.
Настройка Redis Slave
На оставшихся двух хостах выполняем настройку слейвов.
Тут всё одинаково, просто повторяем на обоих.
Устанавливаем:
[simterm]
root@redis-1:/home/admin# apt -y install redis-server
[/simterm]
Редактируем /etc/redis/redis.conf
:
... slaveof redis-0.setevoy.org.ua 6379 ... masterauth foobared ... requirepass foobared ...
Тут:
slaveof
– указываем хост и порт мастераmasterauth
– пароль для авторизации на мастереrequirepass
– пароль для авторизации на слейве
Перезапускаем:
[simterm]
root@redis-1:/home/admin# systemctl restart redis
[/simterm]
Проверяем статус:
[simterm]
root@redis-1:/home/admin# redis-cli -a foobared info replication # Replication role:slave master_host:redis-0.setevoy.org.ua master_port:6379 master_link_status:up master_last_io_seconds_ago:5 master_sync_in_progress:0 ...
[/simterm]
Проверяем лог:
[simterm]
root@redis-1:/home/admin# tail -f /var/log/redis/redis-server.log 16961:S 29 Mar 10:54:36.263 * Connecting to MASTER redis-0.setevoy.org.ua:6379 16961:S 29 Mar 10:54:36.308 * MASTER <-> SLAVE sync started 16961:S 29 Mar 10:54:36.309 * Non blocking connect for SYNC fired the event. 16961:S 29 Mar 10:54:36.309 * Master replied to PING, replication can continue... 16961:S 29 Mar 10:54:36.310 * Partial resynchronization not possible (no cached master) 16961:S 29 Mar 10:54:36.311 * Full resync from master: 93585eeb7e32c0550c35f8d4935c9a18c4177ab9:1 16961:S 29 Mar 10:54:36.383 * MASTER <-> SLAVE sync: receiving 92 bytes from master 16961:S 29 Mar 10:54:36.383 * MASTER <-> SLAVE sync: Flushing old data 16961:S 29 Mar 10:54:36.383 * MASTER <-> SLAVE sync: Loading DB in memory 16961:S 29 Mar 10:54:36.383 * MASTER <-> SLAVE sync: Finished with success
[/simterm]
Коннект к мастеру есть, синхронизация прошла – окей, проверяем данные:
[simterm]
root@redis-1:/home/admin# redis-cli -a foobared get test "test"
[/simterm]
На слейве данные тоже есть.
Переключение Slave => Master роли
В случае, если мастер выйдет из строя – необходимо будет выполнить переключение роли слейва, что бы он стал мастером.
Если сейчас на слейве попробовать записать данные – Redis вернёт ошибку, т.к. по умолчанию слейвы находятся в read-only режиме:
... slave-read-only yes ...
Пробуем добавить данные на слейве:
[simterm]
root@redis-1:/home/admin# redis-cli -a foobared set test2 'test2' (error) READONLY You can't write against a read only slave.
[/simterm]
Подключаемся к слейву:
[simterm]
root@redis-1:/home/admin# redis-cli
[/simterm]
Авторизируемся:
[simterm]
127.0.0.1:6379> auth foobared OK
[/simterm]
Отключаем слейв-роль:
[simterm]
127.0.0.1:6379> slaveof no one OK
[/simterm]
Проверяем статус:
[simterm]
127.0.0.1:6379> info replication # Replication role:master connected_slaves:0 master_repl_offset:1989 repl_backlog_active:0 repl_backlog_size:1048576
[/simterm]
Добавляем новый ключ ещё раз:
[simterm]
127.0.0.1:6379> set test2 'test2' OK
[/simterm]
Проверяем:
[simterm]
127.0.0.1:6379> get test2 "test2"
[/simterm]
Учтите, что т.к. в файле настроек /etc/redis/redis.conf
всё ещё установлен параметр slaveof
– то при рестарте эта нода снова станет слейвом.
Redis Sentinel
Теперь дополним наш кластер, и добавим Redis в режиме Sentinel, который будет мониторить статус нод, и выполнять переключение ролей автоматически.
Общая схема работы будет выгядеть так:
Тут:
- M1 = Master
- R1 = Replica 1 / Slave 1
- R2 = Replica 2 / Slave 2
- S1 = Sentinel 1
- S2 = Sentinel 2
- S3 = Sentinel 3
M1 и S1 – будут на redis-0, R1 и S2 – на redis-1, R2 и S3 – на redis-2.
Запуск Sentinel
Для запуска Sentinel используем тот же Redis-сервер, но для него создадим отдельный конфиг /etc/redis/sentinel.conf
.
Добавляем его сначала на хосте с Redis Master:
sentinel monitor redis-test redis-0.setevoy.org.ua 6379 2 sentinel down-after-milliseconds redis-test 6001 sentinel failover-timeout redis-test 60000 sentinel parallel-syncs redis-test 1 bind 0.0.0.0 sentinel auth-pass redis-test foobared
Тут:
monitor
– адрес мастер-ноды, которую будем мониторить, 2 – кол-во инстансов Sentinel для принятия решенийdown-after-milliseconds
– время, после которого мастер будет считаться упавшимfailover-timeout
– время ожидания после смены ролей слейва на мастер в случае, если мастер вышел из строяparallel-syncs
– время одновременной сихронизации слейвов после переназначения роли мастера
Запускаем:
[simterm]
root@redis-0:/home/admin# redis-server /etc/redis/sentinel.conf --sentinel 10447:X 29 Mar 14:15:53.192 * Increased maximum number of open files to 10032 (it was originally set to 1024). _._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 3.2.6 (00000000/0) 64 bit .-`` .-```. ```\/ _.,_ ''-._ ( ' , .-` | `, ) Running in sentinel mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 26379 | `-._ `._ / _.-' | PID: 10447 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | http://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-' 10447:X 29 Mar 14:15:53.193 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128. 10447:X 29 Mar 14:15:53.195 # Sentinel ID is e9fb72c8edb8ec2028e6ce820b9e72e56e07cf1e 10447:X 29 Mar 14:15:53.195 # +monitor master redis-test 35.158.154.25 6379 quorum 2 10447:X 29 Mar 14:15:53.196 * +slave slave 3.121.223.95:6379 3.121.223.95 6379 @ redis-test 35.158.154.25 6379 10447:X 29 Mar 14:16:43.402 * +slave slave 18.194.45.17:6379 18.194.45.17 6379 @ redis-test 35.158.154.25 6379
[/simterm]
Проверяем статус Sentinel – подключаемся на порт 26379:
[simterm]
root@redis-0:/home/admin# redis-cli -p 26379 info sentinel # Sentinel sentinel_masters:1 sentinel_tilt:0 sentinel_running_scripts:0 sentinel_scripts_queue_length:0 sentinel_simulate_failure_flags:0 master0:name=redis-test,status=ok,address=35.158.154.25:6379,slaves=2,sentinels=1
[/simterm]
Тут:
master0:name=redis-test,status=ok
– мастер работаетslaves=2
– у него два слейваsentinels=1
– пока запущен только один Sentinel
Можно получить различные данные о статусе репликации.
Например – адрес текущего мастера:
[simterm]
root@redis-0:/home/admin# redis-cli -p 26379 sentinel get-master-addr-by-name redis-test 1) "35.158.154.25" 2) "6379"
[/simterm]
Повторяем запуск Sentinel на слейвах, используя такой же конфиг, как на мастере – в логе Sentinel на мастере должны появится сообщения о новых Sentinel инстансах:
[simterm]
... 10447:X 29 Mar 14:18:40.437 * +sentinel sentinel fdc750c7d6388a6142d9e27b68172f5846e75d8c 172.31.36.239 26379 @ redis-test 35.158.154.25 6379 10447:X 29 Mar 14:18:42.725 * +sentinel sentinel ecddb26cd27c9a17c4251078c977761faa7a3250 172.31.35.218 26379 @ redis-test 35.158.154.25 6379 ...
[/simterm]
Проверяем статус ещё раз:
[simterm]
root@redis-0:/home/admin# redis-cli -p 26379 info sentinel # Sentinel sentinel_masters:1 sentinel_tilt:0 sentinel_running_scripts:0 sentinel_scripts_queue_length:0 sentinel_simulate_failure_flags:0 master0:name=redis-test,status=ok,address=18.194.229.23:6379,slaves=2,sentinels=3
[/simterm]
sentinels=3
– окей.
Кроме всего прочего – Sentinel автоматически выполняет настройку:
[simterm]
root@redis-1:/home/admin# cat /etc/redis/sentinel.conf sentinel myid fdc750c7d6388a6142d9e27b68172f5846e75d8c sentinel monitor redis-test 35.158.154.25 6379 2 sentinel down-after-milliseconds redis-test 6001 bind 0.0.0.0 sentinel failover-timeout redis-test 60000 # Generated by CONFIG REWRITE port 26379 dir "/home/admin" sentinel auth-pass redis-test foobared sentinel config-epoch redis-test 0 sentinel leader-epoch redis-test 0 sentinel known-slave redis-test 18.194.45.17 6379 sentinel known-slave redis-test 3.121.223.95 6379 sentinel known-sentinel redis-test 172.31.35.218 26379 ecddb26cd27c9a17c4251078c977761faa7a3250 sentinel known-sentinel redis-test 172.31.47.184 26379 e9fb72c8edb8ec2028e6ce820b9e72e56e07cf1e sentinel current-epoch 0
[/simterm]
Тут добавился sentinel myid fdc750c7d6388a6142d9e27b68172f5846e75d8c, и целый блок после Generated by CONFIG REWRITE.
Redis Sentinel Automatic Failover
Теперь проверим что будет, если умрёт мастер.
Сделать это можно просто убив его вручную, напрмиер через kill -9
, либо с помощью redis-cli
– отправив ему команду DEBUG
и указав либо время, на которое его надо остановить, либо сигнал, от которого ему умереть 🙂
[simterm]
root@redis-0:/home/admin# redis-cli -a foobared DEBUG sleep 30
[/simterm]
В логе Sentinel на мастере:
[simterm]
... 10447:X 29 Mar 14:24:56.549 # +sdown master redis-test 35.158.154.25 6379 10447:X 29 Mar 14:24:56.614 # +new-epoch 1 10447:X 29 Mar 14:24:56.615 # +vote-for-leader ecddb26cd27c9a17c4251078c977761faa7a3250 1 10447:X 29 Mar 14:24:56.649 # +odown master redis-test 35.158.154.25 6379 #quorum 3/2 10447:X 29 Mar 14:24:56.649 # Next failover delay: I will not start a failover before Fri Mar 29 14:26:57 2019 10447:X 29 Mar 14:24:57.686 # +config-update-from sentinel ecddb26cd27c9a17c4251078c977761faa7a3250 172.31.35.218 26379 @ redis-test 35.158.154.25 6379 10447:X 29 Mar 14:24:57.686 # +switch-master redis-test 35.158.154.25 6379 3.121.223.95 6379 10447:X 29 Mar 14:24:57.686 * +slave slave 18.194.45.17:6379 18.194.45.17 6379 @ redis-test 3.121.223.95 6379 10447:X 29 Mar 14:24:57.686 * +slave slave 35.158.154.25:6379 35.158.154.25 6379 @ redis-test 3.121.223.95 6379 10447:X 29 Mar 14:25:03.724 # +sdown slave 35.158.154.25:6379 35.158.154.25 6379 @ redis-test 3.121.223.95 6379 ...
[/simterm]
Сейчас тут особо интересны вот эти две строки:
[simterm]
... 10384:X 29 Mar 14:24:57.686 # +config-update-from sentinel ecddb26cd27c9a17c4251078c977761faa7a3250 172.31.35.218 26379 @ redis-test 35.158.154.25 6379 10384:X 29 Mar 14:24:57.686 # +switch-master redis-test 35.158.154.25 6379 3.121.223.95 6379 ...
[/simterm]
Sentinel выполнил автоматическую перенастройку слейва в мастер.
35.158.154.25 – старый мастер, который умер, а 3.121.223.95 – новый мастер, который раньше был первым слейвом, с именем хоста redis-1.
Проверяем запись в него:
[simterm]
root@redis-1:/home/admin# redis-cli -a foobared set test3 'test3' OK
[/simterm]
Тогда как попытка записать данные на старом мастере, который теперь стал слейвом, приведёт к ошибке:
[simterm]
root@redis-0:/home/admin# redis-cli -a foobared set test4 'test4' (error) READONLY You can't write against a read only slave.
[/simterm]
Либо убиваем его вообще – и посмотрим, что сделает Sentinel:
[simterm]
root@redis-0:/home/admin# redis-cli -a foobared DEBUG SEGFAULT Error: Server closed the connection
[/simterm]
Лог:
[simterm]
... 10447:X 29 Mar 14:26:21.897 * +reboot slave 35.158.154.25:6379 35.158.154.25 6379 @ redis-test 3.121.223.95 6379
[/simterm]
Мастер просто перезапущен.
Команды Sentinel
Command | Description |
---|---|
sentinel masters |
список всех мастеров и их состояния |
sentinel master |
состояние конкретного мастера |
sentinel slaves |
список всех слейвов и их состояния |
sentinel sentinels |
список всех Sentinel инстансов и их состояния |
sentinel failover |
вручную запустить процесс восстановление и замены вышедшего из строя мастера |
sentinel flushconfig |
Force Sentinel to rewrite it’s configuration on disk |
sentinel monitor |
добавить новый мастер |
sentinel remove |
удалить мастер из-под наблюдения |
Ссылки по теме
- Redis Replication
- Пример файла настроек Sentinel
- Redis Sentinel — High Availability: Everything you need to know from DEV to PROD: Complete Guide
- Redis Sentinel: Make your dataset highly available
- How to run Redis Sentinel