Есть 2 сервера приложений(app01 и app02), на которых установлен Redis и Sentinel для мониторинга активного мастера и перевода одного из существующих слейв серверов в режим мастера в случае выхода со строя текущего мастера, а также перевода старого мастера в режим slave к вновь избранному мастеру.
Также есть третий сервер(advisor), на котором запущен только Sentinel. Этот сервер нужен для участия в голосовании и поддержки кворума при выборе нового мастера. Базы Redis он на себе содержать не будет.
Всего в наличие 3 сервера(не включая HAproxy)
Перед Redis-кластером будет настроен Haproxy, который будет мониторить состояние обоих Redis-серверов и направлять запросы клиентов ТОЛЬКО на текущий мастер
В коде приложения для Redis-запросов происходит подключение к HAproxy-серверу
Т.е. схема прохождения запроса будет выглядить так
1 |
Client->Haproxy->Redis-master |
1 2 3 |
app01-192.168.100.1 app02-192.168.100.2 advisor-192.168.100.3 |
Порядок настройки:
1.Установка Redis на серверах приложений (app01 и app02)
2.Настройка Master/Slave репликации из двух Redis-серверов(app01 и app02)
3.Настройка Sentinel на всех трех серверах(app01,app02,advisor)
4.Тестирование переключения мастера
5.Настройка HAproxy
1.Установка Redis на серверах приложений (app01 и app02)
1 |
# apt-get update && apt-get dist-upgrade |
Установка будет выполнена из исходного кода
1 |
# apt-get install build-essential tcl |
1 |
# cd /tmp |
1 |
# curl -O http://download.redis.io/redis-stable.tar.gz |
1 |
# tar xzvf redis-stable.tar.gz |
1 |
# cd redis-stable |
1 |
# make |
1 |
# make test |
1 |
# make install |
1 |
# mkdir /etc/redis |
1 |
# cp /tmp/redis-stable/redis.conf /etc/redis |
1 |
# adduser --system --group --no-create-home redis |
1 |
# mkdir /var/lib/redis /var/run/redis /var/log/redis |
1 |
# chown redis:redis /var/lib/redis /var/run/redis /var/log/redis |
1 |
# chmod 770 /var/lib/redis |
2.Настройка Master/Slave репликации из двух Redis-серверов(app01 и app02)
Настройка конфигурационных файлов Redis
Master (app01)
1 |
# nano /etc/redis/redis.conf |
1 2 3 4 5 |
bind 192.168.100.1 127.0.0.1 requirepass mypassword masterauth mypassword protected-mode yes port 6379 |
Slave(app02)
1 |
# nano /etc/redis/redis.conf |
1 2 3 4 5 6 |
bind 192.168.100.2 127.0.0.1 requirepass mypassword masterauth mypassword protected-mode yes port 6379 slaveof 192.168.100.1 6379 |
protected-mode по умолчанию yes, который не разрешит подключение по сети, если
а) не указан точно интерфейс в опции bind
б) не установлен мастер-пароль в секции requirepass, который требует авторизоваться прежде чем получить доступ к редис(соответственно необходимо на slave добавить опцию masterauth mypassword)
В данном случае Redis был установлен с исходников, поэтому создадим Unit-файл
Создание Unit-файла для Redis для системы инициализации systemd
1 |
# nano /etc/systemd/system/redis.service |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
Unit] Description=Advanced key-value store After=network.target Documentation=http://redis.io/documentation, man:redis-server(1) [Service] Type=forking ExecStart=/usr/local/bin/redis-server /etc/redis/redis.conf PIDFile=/var/run/redis/redis.pid TimeoutStopSec=0 Restart=always User=redis Group=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 PrivateTmp=yes 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 Alias=redis.service |
Для того,чтобы Redis, который запускается под пользователем redis, смог перезписать свой конфигурационный файл, изменим его владельца/группу на redis на обоих Redis-серверах, а также ограничим доcтуп к файлу /etc/redis/redis.conf только для redis пользователя/группы в целях безопаности(доступ к дамп-файлу /var/lib/redis/dump.rdb был ограничен на этапе установки Redis)
1 |
# chown redis:redis /etc/redis/redis.conf |
1 |
# chmod 660 /etc/redis/redis.conf |
С точки зрения безопасности не обходимо ограничить доступ к Redis на уровне файрволла т.е. разрешить подключение к Redis,Sentinel-серверам только с app01,app02,advisor,HAProxy-серверов
В дополнение к ограничению на сетевом доступе можно использовать простую аутентифкиацию в Redis по паролю
(параметр requirepass в конфигурационном файле Redis)
Запускаем redis на мастере и слейве
1 |
# systemct start redis |
Лог Redis на Slave-сервере
1 |
# tail -f /var/log/redis/redis.log |
1 2 3 4 5 6 7 8 9 10 11 12 |
19 May 16:38:35.754 * Ready to accept connections 19 May 16:38:35.754 * Connecting to MASTER 192.168.100.1:6379 19 May 16:38:35.754 * MASTER <-> SLAVE sync started 19 May 16:38:35.754 * Non blocking connect for SYNC fired the event. 19 May 16:38:35.755 * Master replied to PING, replication can continue... 19 May 16:38:35.756 * Trying a partial resynchronization (request dc6870279c00ebc02029bbf19e95a5262db33566:1). 19 May 16:38:35.757 * Full resync from master: cf5c9ae2f1700e734fe04a916f5d96b534c28d2d:0 19 May 16:38:35.757 * Discarding previously cached master state. 19 May 16:38:35.889 * MASTER <-> SLAVE sync: receiving 175 bytes from master 19 May 16:38:35.889 * MASTER <-> SLAVE sync: Flushing old data 19 May 16:38:35.889 * MASTER <-> SLAVE sync: Loading DB in memory 19 May 16:38:35.889 * MASTER <-> SLAVE sync: Finished with success |
В этот момент на мастере Redis в логах
1 |
# tail -f /var/log/redis/redis.log |
1 2 3 4 5 6 7 8 9 |
19 May 16:38:35.752 * Slave 192.168.100.2:6379 asks for synchronization 19 May 16:38:35.752 * Unable to partial resync with slave 192.168.100.2:6379 for lack of backlog (Slave request was: 1). 19 May 16:38:35.752 # Warning: slave 192.168.100.2:6379 tried to PSYNC with an offset that is greater than the master replication offset. 19 May 16:38:35.753 * Starting BGSAVE for SYNC with target: disk 19 May 16:38:35.753 * Background saving started by pid 14554 19 May 16:38:35.883 * DB saved on disk 19 May 16:38:35.884 * RDB: 0 MB of memory used by copy-on-write 19 May 16:38:35.885 * Background saving terminated with success 19 May 16:38:35.885 * Synchronization with slave 192.168.100.2:6379 succeeded |
Проверка состояния репликации
На мастере выполняем
1 |
# redis-cli -p 6379 -a mypassword info replication |
1 2 3 4 5 6 7 8 9 10 11 12 |
# Replication role:master connected_slaves:1 slave0:ip=192.168.100.2,port=6379,state=online,offset=686,lag=0 master_replid:cf5c9ae2f1700e734fe04a916f5d96b534c28d2d master_replid2:0000000000000000000000000000000000000000 master_repl_offset:686 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:686 |
На Slave выполняем
1 |
# redis-cli -p 6379 -a mypassword info replication |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# Replication role:slave master_host:192.168.100.1 master_port:6379 master_link_status:up master_last_io_seconds_ago:0 master_sync_in_progress:0 slave_repl_offset:812 slave_priority:100 slave_read_only:1 connected_slaves:0 master_replid:cf5c9ae2f1700e734fe04a916f5d96b534c28d2d master_replid2:0000000000000000000000000000000000000000 master_repl_offset:812 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:812 |
Если необходимо вручную перевести роль мастера на новый сервер
Для того,чтобы из slave сделать мастер достаточно выполнить
На slave
1 |
# redis-cli -p 6379 -a mypassword slaveof no one |
И проверяем
1 |
# redis-cli -p 6379 -a mypassword info replication |
Для автоматического перевода роли master на один из существующих slave-серверов используем Sentinel. Также Sentinel автоматически перенастраивает все остальные slave-сервера на новый мастер
3. Настройка Sentinel на всех трех серверах(app01,app02,advisor)
Настройка Sentinel на app01
1 |
# cat /etc/redis/sentinel.conf |
1 2 3 4 5 6 7 8 9 10 11 |
port 26379 daemonize yes pidfile "/var/run/redis/sentinel.pid" logfile "/var/log/redis/sentinel.log" dir /var/lib/redis/sentinel sentinel monitor master01 192.168.100.1 6379 2 sentinel auth-pass master01 mypassword sentinel down-after-milliseconds master01 3000 sentinel failover-timeout master01 6000 sentinel parallel-syncs master01 1 protected-mode no |
Имя монитора master01(может быть произвольным). Это имя должно быть одинаковое на всех экземплярах Sentinel
Текущий Redis-мастер app01-сервер с адресом 192.168.100.1, кворум для голосования составляет 2 ноды Sentinel
1 |
sentinel monitor master01 192.168.100.1 6379 2 |
Аутентифицируемся на Redis Master/Slave нодах по паролю
1 |
sentinel auth-pass master01 mypassword |
Кол-во миллисекунд, через которое мастер считается недоступным и начинается процедура голосования для выбора нового мастера
1 |
sentinel down-after-milliseconds master01 3000 |
Создание Unit-файла для Sentinel на всех трех серверах
1 |
# nano /etc/systemd/system/sentinel.service |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
[Unit] Description=Sentinel for Redis Wants=redis.service After=network.target redis.service Documentation=https://redis.io/topics/sentinel [Service] Type=forking PIDFile=/var/run/redis/sentinel.pid ExecStart=/usr/local/bin/redis-server /etc/redis/sentinel.conf --sentinel ExecReload=/bin/kill -HUP $MAINPID ExecStop=/bin/kill -s TERM $MAINPID TimeoutStopSec=0 Restart=on-failure User=redis Group=redis [Install] WantedBy=multi-user.target Alias=sentinel.service |
Создание необходимых каталогов/файлов, выставление на них корректных прав/владельца/группу на всех трех серверах
1 |
# mkdir /var/lib/redis/sentinel && chown -R redis:redis /var/lib/redis/sentinel |
1 |
# chown -R redis:redis /etc/redis/ |
1 |
# touch /var/log/redis/sentinel.log && chown redis:redis /var/log/redis/sentinel.log |
1 |
# chmod 660 /etc/redis/sentinel.conf |
Запускаем Sentinel на app01
1 |
# systemctl start sentinel |
Логи Sentinel на app01
1 |
# cat /var/log/redis/sentinel.log |
1 2 3 4 |
19 May 17:14:18.208 * Running mode=sentinel, port=26379. 19 May 17:14:18.338 # Sentinel ID is 1105f58322cd7943b67f31b8f9b45db5e7876e33 19 May 17:14:18.338 # +monitor master master01 192.168.100.1 6379 quorum 2 19 May 17:14:18.339 * +slave slave 192.168.100.2:6379 192.168.100.2 6379 @ master01 192.168.100.1 6379 |
Проверяем состояние мастера через Sentinel(значение ключа role-reported)
1 |
# redis-cli -p 26379 sentinel master master01 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
1) "name" 2) "master01" 3) "ip" 4) "192.168.100.1" 5) "port" 6) "6379" 7) "runid" 8) "5c69276467b3de1a9dec539665f21fb3264113f6" 9) "flags" 10) "master" 11) "link-pending-commands" 12) "0" 13) "link-refcount" 14) "1" 15) "last-ping-sent" 16) "0" 17) "last-ok-ping-reply" 18) "108" 19) "last-ping-reply" 20) "108" 21) "down-after-milliseconds" 22) "3000" 23) "info-refresh" 24) "5928" 25) "role-reported" 26) "master" 27) "role-reported-time" 28) "76344" 29) "config-epoch" 30) "0" 31) "num-slaves" 32) "1" 33) "num-other-sentinels" 34) "0" 35) "quorum" 36) "2" 37) "failover-timeout" 38) "6000" 39) "parallel-syncs" 40) "1" |
Настройка Sentinel на app02
1 |
# cat /etc/redis/sentinel.conf |
1 2 3 4 5 6 7 8 9 10 11 |
port 26379 daemonize yes pidfile "/var/run/redis/sentinel.pid" logfile "/var/log/redis/sentinel.log" dir /var/lib/redis/sentinel sentinel monitor master01 192.168.100.1 6379 2 sentinel auth-pass master01 mypassword sentinel down-after-milliseconds master01 3000 sentinel failover-timeout master01 6000 sentinel parallel-syncs master01 1 protected-mode no |
1 |
# mkdir /var/lib/redis/sentinel && chown -R redis:redis /var/lib/redis/sentinel |
1 |
# chown -R redis:redis /etc/redis/ |
1 |
# touch /var/log/redis/sentinel.log && chown redis:redis /var/log/redis/sentinel.log |
1 |
# chmod 660 /etc/redis/sentinel.conf |
Запускаем Sentinel на app02
1 |
# systemctl start sentinel |
1 |
# cat /var/log/redis/sentinel.log |
1 2 3 4 |
19 May 17:23:47.467 # Sentinel ID is a375bc1f86f214068794ec45711a0b666da55ce0 19 May 17:23:47.467 # +monitor master master01 192.168.100.1 6379 quorum 2 19 May 17:23:47.468 * +slave slave 192.168.100.2:6379 192.168.100.2 6379 @ master01 192.168.100.1 6379 19 May 17:23:49.318 * +sentinel sentinel 1105f58322cd7943b67f31b8f9b45db5e7876e33 192.168.100.1 26379 @ master01 192.168.100.1 6379 |
После чего как на app01 так и на app02 проверяем состояние мастера и убеждаемся в наличие уже двух sentinel-серверов(один из них, на котором выполняем саму команду)
1 |
# redis-cli -p 26379 sentinel master master01 |
1 2 3 4 |
… 33) "num-other-sentinels" 34) "1" … |
Проверка/просмотр текущего мастера Redis
1 |
# redis-cli -p 26379 sentinel get-master-addr-by-name master01 |
1 2 |
1) "192.168.100.1" 2) "6379" |
Настройка Sentinel на advisor
1 |
# cat /etc/redis/sentinel.conf |
1 2 3 4 5 6 7 8 9 10 11 |
port 26379 daemonize yes pidfile "/var/run/redis/sentinel.pid" logfile "/var/log/redis/sentinel.log" dir /var/lib/redis/sentinel sentinel monitor master01 192.168.100.1 6379 2 sentinel auth-pass master01 mypassword sentinel down-after-milliseconds master01 3000 sentinel failover-timeout master01 6000 sentinel parallel-syncs master01 1 protected-mode no |
1 |
# mkdir /var/lib/redis/sentinel && chown -R redis:redis /var/lib/redis/sentinel |
1 |
# chown -R redis:redis /etc/redis/ |
1 |
# touch /var/log/redis/sentinel.log && chown redis:redis /var/log/redis/sentinel.log |
1 |
# chmod 660 /etc/redis/sentinel.conf |
Запускаем Sentinel на advisor
1 |
# systemctl start sentinel |
Проверка состояния кворума кластера master01
1 |
# redis-cli -p 26379 sentinel ckquorum master01 |
1 |
OK 3 usable Sentinels. Quorum and failover authorization can be reached |
Просмотр списка slave-серверов кластера master01
1 |
# redis-cli -p 26379 sentinel slaves master01 |
1 2 3 4 5 6 7 |
1) "name" 2) "192.168.100.2:6379" 3) "ip" 4) "192.168.100.2" 5) "port" 6) "6379" … |
Просмотр списка master-серверов кластера master01
1 |
# redis-cli -p 26379 sentinel master master01 |
1 2 3 4 5 6 7 |
1) "name" 2) "master01" 3) "ip" 4) "192.168.100.1" 5) "port" 6) "6379" … |
4. Тестирование переключения мастера
Выключаем redis на первом сервере(app01) и проверяем, что мастером стал второй сервер(app02)
Логи Redis на app02
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
19 Jun 09:45:28.426 # Connection with master lost. 19 Jun 09:45:28.426 * Caching the disconnected master state. 19 Jun 09:45:28.605 * Connecting to MASTER 192.168.100.1:6379 19 Jun 09:45:28.605 * MASTER <-> SLAVE sync started 19 Jun 09:45:28.605 # Error condition on socket for SYNC: Connection refused 19 Jun 09:45:29.607 * Connecting to MASTER 192.168.100.1:6379 19 Jun 09:45:29.607 * MASTER <-> SLAVE sync started 19 Jun 09:45:29.607 # Error condition on socket for SYNC: Connection refused 19 Jun 09:45:30.607 * Connecting to MASTER 192.168.100.1:6379 19 Jun 09:45:30.607 * MASTER <-> SLAVE sync started 19 Jun 09:45:30.608 # Error condition on socket for SYNC: Connection refused 19 Jun 09:45:31.607 * Connecting to MASTER 192.168.100.1:6379 19 Jun 09:45:31.607 * MASTER <-> SLAVE sync started 19 Jun 09:45:31.607 # Error condition on socket for SYNC: Connection refused 19 Jun 09:45:31.799 # Setting secondary replication ID to e8e135efb9677e5a0fced11be7151d4a9cbd940b, valid up to offset: 1760909720. New replication ID is 5c1d6e4bf72b637682aeb620d19f0c5bb559d7ca 19 Jun 09:45:31.799 * Discarding previously cached master state. 19 Jun 09:45:31.799 * MASTER MODE enabled (user request from 'id=14 addr=192.168.100.2:59966 fd=10 name=sentinel-a375bc1f-cmd age=187 idle=0 flags=x db=0 sub=0 psub=0 multi=3 qbuf=0 qbuf-free=32768 obl=36 oll=0 omem=0 events=r cmd=exec') 19 Jun 09:45:31.800 # CONFIG REWRITE executed with success. |
Логи Sentinel на app02 сервере, который становится в этом момент текущим мастером
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
19 Jun 09:45:31.476 # +sdown master master01 192.168.100.1 6379 19 Jun 09:45:31.534 # +odown master master01 192.168.100.1 6379 #quorum 2/2 19 Jun 09:45:31.534 # +new-epoch 2460 19 Jun 09:45:31.534 # +try-failover master master01 192.168.100.1 6379 19 Jun 09:45:31.537 # +vote-for-leader a375bc1f86f214068794ec45711a0b666da55ce0 2460 19 Jun 09:45:31.602 # 3d46f7c09b45edbb1fc7d17181150e802dc17bce voted for a375bc1f86f214068794ec45711a0b666da55ce0 2460 19 Jun 09:45:31.657 # +elected-leader master master01 192.168.100.1 6379 19 Jun 09:45:31.657 # +failover-state-select-slave master master01 192.168.100.1 6379 19 Jun 09:45:31.706 # 1105f58322cd7943b67f31b8f9b45db5e7876e33 voted for a375bc1f86f214068794ec45711a0b666da55ce0 2460 19 Jun 09:45:31.728 # +selected-slave slave 192.168.100.2:6379 192.168.100.2 6379 @ master01 192.168.100.1 6379 19 Jun 09:45:31.728 * +failover-state-send-slaveof-noone slave 192.168.100.2:6379 192.168.100.2 6379 @ master01 192.168.100.1 6379 19 Jun 09:45:31.799 * +failover-state-wait-promotion slave 192.168.100.2:6379 192.168.100.2 6379 @ master01 192.168.100.1 6379 19 Jun 09:45:32.551 # +promoted-slave slave 192.168.100.2:6379 192.168.100.2 6379 @ master01 192.168.100.1 6379 19 Jun 09:45:32.551 # +failover-state-reconf-slaves master master01 192.168.100.1 6379 19 Jun 09:45:32.616 # +failover-end master master01 192.168.100.1 6379 19 Jun 09:45:32.616 # +switch-master master01 192.168.100.1 6379 192.168.100.2 6379 19 Jun 09:45:32.616 * +slave slave 192.168.100.1:6379 192.168.100.1 6379 @ master01 192.168.100.2 6379 19 Jun 09:45:35.689 # +sdown slave 192.168.100.1:6379 192.168.100.1 6379 @ master01 192.168.100.2 6379 |
Из логов видно, что текущий мастер стал недоступен
1 |
19 Jun 09:45:31.476 # +sdown master master01 192.168.100.1 6379 |
Проверка наличия кол-ва нод,которые должны принять участие в выборе нового мастера – т.е кворум должен быть удовлетворен – минимум 2 ноды(из имеющихся 3-х нод с Sentinel)
1 |
19 Jun 09:45:31.534 # +odown master master01 192.168.100.1 6379 #quorum 2/2 |
Выбор нового мастера
1 |
app02 Sentinel(a375bc1f86f214068794ec45711a0b666da55ce0) и advisor(3d46f7c09b45edbb1fc7d17181150e802dc17bce) голосуют за нового мастера app02(a375bc1f86f214068794ec45711a0b666da55ce0) |
1 2 3 |
19 Jun 09:45:31.534 # +try-failover master master01 192.168.100.1 6379 19 Jun 09:45:31.537 # +vote-for-leader a375bc1f86f214068794ec45711a0b666da55ce0 2460 19 Jun 09:45:31.602 # 3d46f7c09b45edbb1fc7d17181150e802dc17bce voted for a375bc1f86f214068794ec45711a0b666da55ce0 2460 |
В качестве нового мастера выбирается app02(192.168.100.2)(на нем выполняется команда slaveof-noone)
А сервер app01 переводится в режим slave(failover-state-reconf-slaves)
1 2 3 4 5 6 7 8 9 |
19 Jun 09:45:31.657 # +elected-leader master master01 192.168.100.1 6379 19 Jun 09:45:31.657 # +failover-state-select-slave master master01 192.168.100.1 6379 19 Jun 09:45:31.706 # 1105f58322cd7943b67f31b8f9b45db5e7876e33 voted for a375bc1f86f214068794ec45711a0b666da55ce0 2460 19 Jun 09:45:31.728 # +selected-slave slave 192.168.100.2:6379 192.168.100.2 6379 @ master01 192.168.100.1 6379 19 Jun 09:45:31.728 * +failover-state-send-slaveof-noone slave 192.168.100.2:6379 192.168.100.2 6379 @ master01 192.168.100.1 6379 19 Jun 09:45:31.799 * +failover-state-wait-promotion slave 192.168.100.2:6379 192.168.100.2 6379 @ master01 192.168.100.1 6379 19 Jun 09:45:32.551 # +promoted-slave slave 192.168.100.2:6379 192.168.100.2 6379 @ master01 192.168.100.1 6379 19 Jun 09:45:32.551 # +failover-state-reconf-slaves master master01 192.168.100.1 6379 19 Jun 09:45:32.616 # +failover-end master master01 192.168.100.1 6379 |
Переключение активного мастера с app01(192.168.100.1) на app02(192.168.100.2)
1 |
19 Jun 09:45:32.616 # +switch-master master01 192.168.100.1 6379 192.168.100.2 6379 |
App01(192.168.100.1) сервер переводится/настраивается как slave и помечается как недоступный в данный момент
1 2 |
19 Jun 09:45:32.616 * +slave slave 192.168.100.1:6379 192.168.100.1 6379 @ master01 192.168.100.2 6379 19 Jun 09:45:35.689 # +sdown slave 192.168.100.1:6379 192.168.100.1 6379 @ master01 192.168.100.2 6379 |
Sentinel на каждом sentinel-сервере обновляет свой конфигурационный файл sentinel.conf, указывая в нем нового мастера(192.168.100.2)
1 |
19 Jun 09:45:32.620 # +config-update-from sentinel a375bc1f86f214068794ec45711a0b666da55ce0 192.168.100.2 26379 @ master01 192.168.100.1 6379 |
Т.е вместо строки
1 |
sentinel monitor master01 192.168.100.1 6379 2 |
будет строка
1 |
sentinel monitor master01 192.168.100.2 6379 2 |
А также перестраиваит все имеющиейся слейвы(в данном случае только бывший мастер 192.168.100.1,который автоматически становится слейвом) на работу c новым мастером
1 |
19 Jun 09:45:32.616 * +slave slave 192.168.100.1:6379 192.168.100.1 6379 @ master01 192.168.100.2 6379 |
Путем динамического изменения конфигурационного файла sentinel.conf
Заменяя строку
1 |
sentinel known-slave master01 192.168.100.2 6379 |
на строку
1 |
sentinel known-slave master01 192.168.100.1 6379 |
А также помечает, что данный слейв пока выключен/недоступен
1 |
19 Jun 09:45:35.689 # +sdown slave 192.168.100.1:6379 192.168.100.1 6379 @ master01 192.168.100.2 6379 |
Как происходит автоматический перевод активного мастера в слейв, а слейв в мастер?
При переводе слейв->мастер на слейве выполняется команда
1 |
# redis-cli slaveof no one |
При переводе мастер->слейв на бывшем мастере выполняется команда
1 |
# redis-cli slaveof <IP-address-new-master-server> 6379 |
Т.е.
1 |
# redis-cli slaveof 192.168.100.2 6379 |
Лог Sentinel на app01
1 |
# tail -f /var/log/redis/sentinel.log |
1 2 3 4 5 6 7 8 9 |
19 Jun 09:45:31.371 # +sdown master master01 192.168.100.1 6379 19 Jun 09:45:31.657 # +new-epoch 2460 19 Jun 09:45:31.707 # +vote-for-leader a375bc1f86f214068794ec45711a0b666da55ce0 2460 19 Jun 09:45:32.488 # +odown master master01 192.168.100.1 6379 #quorum 3/2 19 Jun 09:45:32.488 # Next failover delay: I will not start a failover before Tue Jun 19 09:45:44 2018 19 Jun 09:45:32.620 # +config-update-from sentinel a375bc1f86f214068794ec45711a0b666da55ce0 192.168.100.2 26379 @ master01 192.168.100.1 6379 19 Jun 09:45:32.620 # +switch-master master01 192.168.100.1 6379 192.168.100.2 6379 19 Jun 09:45:32.620 * +slave slave 192.168.100.1:6379 192.168.100.1 6379 @ master01 192.168.100.2 6379 19 Jun 09:45:35.672 # +sdown slave 192.168.100.1:6379 192.168.100.1 6379 @ master01 192.168.100.2 6379 |
Лог sentinel на advisor
1 |
# tail -f /var/log/redis/sentinel.log |
1 2 3 4 5 6 7 8 9 |
19 Jun 09:45:31.499 # +sdown master master01 192.168.100.1 6379 19 Jun 09:45:31.578 # +new-epoch 2460 19 Jun 09:45:31.605 # +vote-for-leader a375bc1f86f214068794ec45711a0b666da55ce0 2460 19 Jun 09:45:31.605 # +odown master master01 192.168.100.1 6379 #quorum 3/2 19 Jun 09:45:31.605 # Next failover delay: I will not start a failover before Tue Jun 19 09:45:44 2018 19 Jun 09:45:32.622 # +config-update-from sentinel a375bc1f86f214068794ec45711a0b666da55ce0 192.168.100.2 26379 @ master01 192.168.100.1 6379 19 Jun 09:45:32.622 # +switch-master master01 192.168.100.1 6379 192.168.100.2 6379 19 Jun 09:45:32.622 * +slave slave 192.168.100.1:6379 192.168.100.1 6379 @ master01 192.168.100.2 6379 19 Jun 09:45:35.704 # +sdown slave 192.168.100.1:6379 192.168.100.1 6379 @ master01 192.168.100.2 6379 |
Проверка текущего мастера
1 |
# redis-cli -p 26379 sentinel get-master-addr-by-name master01 |
1 2 |
1) "192.168.100.2" 2) "6379" |
App02 стал Redis-master, а app01 – Redis-slave и помечен как выключен
После включения redis на app01 он помечается как активный slave и реплицирует данные с Redis-master(app02)
Логи Redis на app01
1 |
root@app01:~# tail -f /var/log/redis/redis.log |
1 2 3 4 5 6 7 8 9 10 11 |
19 Jun 10:14:57.998 * Before turning into a slave, using my master parameters to synthesize a cached master: I may be able to synchronize with the new master with just a partial transfer. 19 Jun 10:14:57.999 * SLAVE OF 192.168.100.2:6379 enabled (user request from 'id=2 addr=192.168.100.1:54896 fd=8 name=sentinel-1105f583-cmd age=10 idle=0 flags=x db=0 sub=0 psub=0 multi=3 qbuf=0 qbuf-free=32768 obl=36 oll=0 omem=0 events=r cmd=exec') 19 Jun 10:14:57.999 # CONFIG REWRITE executed with success. 19 Jun 10:14:58.690 * Connecting to MASTER 192.168.100.2:6379 19 Jun 10:14:58.691 * MASTER <-> SLAVE sync started 19 Jun 10:14:58.691 * Non blocking connect for SYNC fired the event. 19 Jun 10:14:58.691 * Master replied to PING, replication can continue... 19 Jun 10:14:58.692 * Trying a partial resynchronization (request e8e135efb9677e5a0fced11be7151d4a9cbd940b:1760909720). 19 Jun 10:14:58.692 * Successful partial resynchronization with master. 19 Jun 10:14:58.692 # Master replication ID changed to 5c1d6e4bf72b637682aeb620d19f0c5bb559d7ca 19 Jun 10:14:58.692 * MASTER <-> SLAVE sync: Master accepted a Partial Resynchronization. |
Логи Redis на app02
1 |
root@app02:~# tail -f /var/log/redis/redis.log |
1 2 |
19 Jun 10:14:58.689 * Slave 192.168.100.1:6379 asks for synchronization 19 Jun 10:14:58.690 * Partial resynchronization request from 192.168.100.1:6379 accepted. Sending 427078 bytes of backlog starting from offset 1760909720 |
Логи Sentinel на app01
1 |
root@app01:~# tail -f /var/log/redis/sentinel.log |
1 2 |
19 Jun 10:14:48.023 # -sdown slave 192.168.100.1:6379 192.168.100.1 6379 @ master01 192.168.100.2 6379 19 Jun 10:14:57.998 * +convert-to-slave slave 192.168.100.1:6379 192.168.100.1 6379 @ master01 192.168.100.2 6379 |
Логи Sentinel на app02
1 |
root@app02:~# tail -f /var/log/redis/sentinel.log |
1 |
19 Jun 10:14:48.566 # -sdown slave 192.168.100.1:6379 192.168.100.1 6379 @ master01 192.168.100.2 6379 |
Логи Sentinel на advisor
1 |
root@advisor:~# tail -f /var/log/redis/sentinel.log |
1 |
19 Jun 10:14:48.638 # -sdown slave 192.168.100.1:6379 192.168.100.1 6379 @ master01 192.168.100.2 6379 |
Настройка haproxy на работу с кластером
1 |
# nano /etc/haproxy/haproxy.cfg |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
… frontend redis_frontend mode tcp bind <IP-address-HAproxy-server>:6379 default_backend redis-backend backend redis-backend mode tcp option tcplog option tcp-check tcp-check send AUTH\ mypassword\r\n tcp-check expect string +OK tcp-check send PING\r\n tcp-check expect string +PONG tcp-check send info\ replication\r\n tcp-check expect string role:master tcp-check send QUIT\r\n tcp-check expect string +OK server redis_node1_app01 192.168.100.1:6379 maxconn 4096 check inter 3s server redis_node2_app02 192.168.100.2:6379 maxconn 4096 check inter 3s … |
1 |
# haproxy -c -f /etc/haproxy/haproxy.cfg && systemctl reload haproxy |
Haproxy будет проверять оба Redis-инстанса и определять текущего мастера (по наличию в ответе от Redis-сервера роли мастер expect string role:master)
И передавать запросы клиентов только текущему Redis-мастеру.
A slave-сервер буде отмечен в Haproxy как недоступный для передачи на него запросов клиентов(выброшен с кластера)
При переключении роли мастера( за этим следит и выполняет Sentinel) HAproxy начнет отправлять запросы на новый мастер, а старый мастер, который автоматически будет назначен как Slave сентинелом, будет выброшен с HAProxy-кластера автоматически.
Несколько полезных Redis-параметров связанных с репликацией
1 |
stop-writes-on-bgsave-error yes |
По умолчанию Redis будет запрещать запись,если последняя операция сохранения на диск закончилась неуспешно. После восстановления успешного сохранения на диск, автоматически будет разрешена запись в Redis
Если slave теряет доступ к мастеру или во время, когда репликация еще в процессе, slave может работать/действовать в одном из двух режимов
А) По умолчанию, slave все еще отвечает на запросы(команды) клиентов, возможно, отдавая уже устаревшие данные
1 |
slave-serve-stale-data yes |
Б)Slave отвечает на все запросы клиентов(кроме запросов типа INFO и SLAVEOF) ошибкой типа
«SYNC with master in progress»
1 |
slave-serve-stale-data no |
1 |
slave-read-only yes |
Должен ли slave принимать запросы на запись
Начиная с версии 2.6 по умолчанию запрещено Slave-серверу принимать запросы на запись т.е.используется режим read-only
Репликация в Redis
1.Redis-репликация асинхронная. При этом можно настроить мастер, чтобы он не принимал запросы на запись, если к нему не подключено меньше, чем определенно количество slave-серверов или отставание больше или равное определенного кол-ва секунд, которое считается от последнего ping полученного от slave-сервера.
По умолчанию эта возможность отключена(min-slaves-to-write установлена в ноль, а min-slaves-max-lag в 10)
Данные опции закомментированы по умолчанию(т.е не применяются)
1 2 |
# min-slaves-to-write 3 # min-slaves-max-lag 10 |
Также для отключения этой возможности можно установить один из параметров в значение 0
2.Redis-slave могут выполнять частичную синхронизацию/пересинхронизацию(вместо полной) с мастером, если репликация потеряна/остановлена/разломана в течение относительно небольшого периода времени. Параметры repl-backlog-size определяет размер буфера, который содержит в себе данные для slave-серверов во время того, как slave-сервера отключены от мастера.
При восстановлении связи между мастером и слейвом(когда слейв хочет переподключиться снова) может не требоваться полная ресинхронизация, а использоваться частичная ресинхронизация
1 |
repl-backlog-size 1mb |
Backlog выделяется один раз, как только появляется подключенный slave
Время, по истечению которого, backlog-буфер очищается определяется параметром
repl-backlog-ttl. Если необходимо никогда не очищать этот буфер, то устанавливаем значение этого параметра в ноль
1 |
repl-backlog-ttl 3600 |
3.Репликация автоматический процесс и не требует пользовательского вмешательства.
Slave будет автоматически пытаться подключиться к мастери синхронизироваться с ним.
1 |
slaveof <masterip> <masterport> |
Если мастер защищен паролем(на мастере установлен параметр requirepass), то для успешной синхронизации Slave должен аутентифицироваться у мастера, иначе мастер откажет слейву в его запросах.
1 |
masterauth <master-password> |
Стратегии репликации в Redis
1.Disk-backed
Redis-мастер создает новый процесс, который записывает redis базу данных (RDB) на диск в виде файла . Затем этот файл передается на slave-сервера инкрементально.
2.Diskless
Является экспериментальной в данный момент
Redis-мастер создает новый процесс, который напрямую записывает RDB-файл в Slave-сокет без использования диска вообще
При Disk-backed репликации, пока RDB-файл создается/генерируется больше slave-серверов могут быть поставлены в очередь и обслужены сразу после того, как завершится дочерний процесс Redis-мастера, который создает RDB-файл для slave-серверов
При Diskless репликации, наоборот, если запущен один процесс передачи данных(синхронизации), то все вновь прибывшие slave-сервера становятся в очередь и их запросы будут обработаны после того, как закончится выполнения передачи данных текущего(запущенного) slave-сервера
При этом мастер будет ожидать определенное в параметре repl-diskless-sync-delay кол-во времени перед началом передачи данных в надежде на то, что несколько slave-серверов подключатся к нему для репликации, и RDB-передача может быть параллельной на несколько slave-серверов.
Если необходимо отключить такую задержку, то выставляем значение параметра repl-diskless-sync-delay в ноль
На медленных дисках и сетях с высокой пропусной способностью Diskless репликация работает быстрее
1 2 |
repl-diskless-sync no repl-diskless-sync-delay 5 |
Слейвы посылают PING-запросы на мастер с интервалом, определенным в параметре repl-ping-slave-period
1 |
repl-ping-slave-period 10 |
Установка таймаута репликации — используется сразу для трех состояний
Мастер недоступен с точки зрения Slave-сервера(нет передачи данных, ответа на ping-запрос)
Слейв недоступен с точки зрения мастера(REPLCONF, ACK пинги)
Массовая передача ввода/вывода в течение SYNC с точки зрения slave-сервера
1 |
repl-timeout 60 |
Приоритет slave-сервера
1 |
slave-priority 100 |
Целочисленное значение,используемое Redis Sentinel для выбора нового мастера из существующих slave-серверов, когда текущий мастер не работает корректно.
Чем ниже значение приоритета, тем более предпочтительнее этот slave-сервер станет мастером(Например, из slave-серверов с приоритетом 10,25,100 Sentinel в качестве мастера выбирает сервер с приоритетом 10)
Если не обходимо, чтобы Slave-сервер никогда не стал мастером,то значение параметра
slave-priority необходимо установить в ноль
Redis CLI плезные команды
справка
1 |
127.0.0.1:6379[1]> help |
Проверка доступности Redis-сервера
1 |
# redis-cli ping |
Очистка терминала
1 |
127.0.0.1:6379[1]> clear |
Просмотр команд и данных которые мастер посылает на slave в потоке репликации
1 |
# redis-cli --slave |
Очистка всех ключей со всех баз данных
1 |
# redis-cli flushall |
Просмотр Redis-статистики
1 |
# redis-cli --stat |
По умолчанию частота просмотра каждую секунду
Изменить на частоту просмотра каждые 3 секунды
1 |
# redis-cli --stat -i 3 |
Сканирование больших ключем
1 |
# redis-cli --bigkeys |
Получение списка ключей
1 |
# redis-cli --scan |
Получение списка ключей определенного шаблона
1 |
# redis-cli --scan --pattern 'health*' |
Монитоинг команд выполняемых в Redis
1 |
# redis-cli monitor |
Мониторинг латенции Redis(время укаазно в миллисекундах)
1 |
# redis-cli --latency |
Проверка максимальной и среднего значеия латенции
1 |
# redis-cli --latency-history |
Просмотр латенции сервера(планироващика ядра, гипервизора, в случае использования виртуализации),на котором запускается Redis
Команду нужно выполнять на сервере, на котором запущен Redis
Например, запускать тест в течение 10 секунд
1 |
# redis-cli --intrinsic-latency 10 |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Max latency so far: 1 microseconds. Max latency so far: 8 microseconds. Max latency so far: 9 microseconds. Max latency so far: 11 microseconds. Max latency so far: 13 microseconds. Max latency so far: 14 microseconds. Max latency so far: 297 microseconds. Max latency so far: 393 microseconds. Max latency so far: 508 microseconds. Max latency so far: 548 microseconds. 274502290 total runs (avg latency: 0.0364 microseconds / 36.43 nanoseconds per run). Worst run took 15043x longer than the average latency. |
Проверка целостности Redis-базы данных
1 |
# redis-check-rdb /var/lib/redis/dump.rdb |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[offset 0] Checking RDB file /var/lib/redis/dump.rdb [offset 26] AUX FIELD redis-ver = '4.0.1' [offset 40] AUX FIELD redis-bits = '64' [offset 52] AUX FIELD ctime = '1526863031' [offset 67] AUX FIELD used-mem = '622480' [offset 83] AUX FIELD aof-preamble = '0' [offset 133] AUX FIELD repl-id = 'ed53fc0f971000724760d403ed62a7e768ef3716' [offset 151] AUX FIELD repl-offset = '19015252' [offset 160] Checksum OK [offset 160] \o/ RDB looks OK! \o/ [info] 0 keys read [info] 0 expires [info] 0 already expired |
Создание бекапа/дампа базы Redis
1 |
# redis-cli --rdb /root/dump.rdb |
1 2 |
SYNC sent to master, writing 63992 bytes to '/root/dump.rdb' Transfer finished with success. |
Для того,чтобы обеспечить цолостность/консистентность данных при создании дампа базы Redis необходимо перед создание дампа выполнит команду SAVE, чтобы сбросит все данные,содержащиеся в памяти на диск
т.е.
1 |
# redis-cli save && redis-cli --rdb /root/dump.rdb |
Команда по выключению Redis-сервера SHUTDOWN, автоматически сохраняет данные с памяти на диск
1 |
# redis-cli shutdown |
Просмотр команд и данных которые мастер посылает на slave в потоке репликации
1 |
# redis-cli --slave |
Выполнение симуляции LRU
Например, в конфиге Redis установлен лимит в 128Mb оперативной памяти для кеша Redis и политика очистки ключей – все ключи наименее используемые
1 2 |
maxmemory 128mb maxmemory-policy allkeys-lru |
Специальный тест выполняет GET и SET запросы с политикой 20% ключей будет запрошено 80% всех запросов. Кол-во ключей 10млн
1 |
# redis-cli --lru-test 10000000 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
220000 Gets/sec | Hits: 8922 (4.06%) | Misses: 211078 (95.94%) 209000 Gets/sec | Hits: 24068 (11.52%) | Misses: 184932 (88.48%) 197750 Gets/sec | Hits: 35514 (17.96%) | Misses: 162236 (82.04%) 204250 Gets/sec | Hits: 48182 (23.59%) | Misses: 156068 (76.41%) 213250 Gets/sec | Hits: 62041 (29.09%) | Misses: 151209 (70.91%) 206750 Gets/sec | Hits: 70624 (34.16%) | Misses: 136126 (65.84%) 175750 Gets/sec | Hits: 67086 (38.17%) | Misses: 108664 (61.83%) 209000 Gets/sec | Hits: 87765 (41.99%) | Misses: 121235 (58.01%) 215500 Gets/sec | Hits: 98401 (45.66%) | Misses: 117099 (54.34%) 209000 Gets/sec | Hits: 103113 (49.34%) | Misses: 105887 (50.66%) 215250 Gets/sec | Hits: 113052 (52.52%) | Misses: 102198 (47.48%) 195500 Gets/sec | Hits: 107462 (54.97%) | Misses: 88038 (45.03%) 183000 Gets/sec | Hits: 101788 (55.62%) | Misses: 81212 (44.38%) 183500 Gets/sec | Hits: 102517 (55.87%) | Misses: 80983 (44.13%) 187000 Gets/sec | Hits: 104745 (56.01%) | Misses: 82255 (43.99%) 180750 Gets/sec | Hits: 101693 (56.26%) | Misses: 79057 (43.74%) 188250 Gets/sec | Hits: 106084 (56.35%) | Misses: 82166 (43.65%) |
Коэффициент пропущенных ключей состаляет около 44%, что достаточно много.Поэтому есть смысл увеличить размер оперативной памяти выделяемой под кеш Redis, например, до 512 Mb
1 |
# redis-cli --lru-test 10000000 |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
182000 Gets/sec | Hits: 105357 (57.89%) | Misses: 76643 (42.11%) 207000 Gets/sec | Hits: 124233 (60.02%) | Misses: 82767 (39.98%) 212750 Gets/sec | Hits: 132143 (62.11%) | Misses: 80607 (37.89%) 211000 Gets/sec | Hits: 135588 (64.26%) | Misses: 75412 (35.74%) 205750 Gets/sec | Hits: 135979 (66.09%) | Misses: 69771 (33.91%) 205250 Gets/sec | Hits: 138532 (67.49%) | Misses: 66718 (32.51%) 183500 Gets/sec | Hits: 126871 (69.14%) | Misses: 56629 (30.86%) 153250 Gets/sec | Hits: 107792 (70.34%) | Misses: 45458 (29.66%) 195000 Gets/sec | Hits: 139182 (71.38%) | Misses: 55818 (28.62%) 211250 Gets/sec | Hits: 153523 (72.67%) | Misses: 57727 (27.33%) ……… 199000 Gets/sec | Hits: 190707 (95.83%) | Misses: 8293 (4.17%) 203250 Gets/sec | Hits: 194891 (95.89%) | Misses: 8359 (4.11%) |
После увеличения памяти с 128 до 512 Mb, коэффициент пропущенных ключей снизился до 4%
Источник:
https://redis.io/topics/sentinel
https://seanmcgary.com/posts/how-to-build-a-fault-tolerant-redis-cluster-with-sentinel
https://discuss.pivotal.io/hc/en-us/articles/205309388-How-to-setup-HAProxy-and-Redis-Sentinel-for-automatic-failover-between-Redis-Master-and-Slave-servers
https://discuss.pivotal.io/hc/en-us/articles/205309278-How-to-setup-Redis-Master-and-Salve-replication
https://redis.io/topics/rediscli
http://www.linux-admins.net/2014/09/deploying-haproxy-15-from-source.html