이전 포스팅에서 Sentinel 이 무엇을 하는지에 대해서 설명드렸었습니다. 이제 이것을 설정하고 설치해 보도록 하겠습니다.



Redis 의 Sentinel


Sentinel 은 redis 와 동일한 서버에 설치해도 되고, 독립적인 서버에 설치해도 상관 없습니다. port 만 따로 설정하시면 됩니다. 제가 이번에 구성할 Sentinel 의 서버 모양세는 아래와 같습니다.




그림이 조금 복잡한데, 기존의 Redis Server 들이 있고, 각각의 Sentinel 서버가 이 4대의 Redis 를 감시합니다.


Sentinel 이 3대인 이유는 1개인 경우 문제가 발생하였을때를 대비하기 위함입니다. 그리고 Sentinel 이 FailOver 를 하기 위해서 Redis 를 감시한다고 했는데, 네트워크 문제라든지 어떤 결함으로 인해 문제가 발생했을때 이것이 Sentinel 자체의 문제일 수 있어서 여러대를 두는 것입니다. 좀더 확실한 문제 파악을 위해서랄까요...? 더불어 2대가 아니라 3대인 이유는 나중에 설명 드리겠지만, SDown 과 ODown 이라는 개념이 있는데 이때를 위해서 짝수가 아닌 홀수개의 Sentinel 이 좋기 때문입니다.

다수결시에 5:5 로 투표되면 결론이 나지 않기 때문에 홀수로 둔다고 생각하시면 맞습니다. 어느쪽이든 결론이 나게 하기 위함이죠.





Sentinel 설정하기




처음 Sentinel 을 install 했던 디렉토리에 보면 sentinel.conf 라는 파일이 존재합니다. 이게 sentinel 을 위한 conf 파일입니다. 눈치 빠르신분들은 아셨겠지만, sentinel 은 결국 conf 파일만 다른 redis 입니다. 먼저 파일을 수정해 보도록 하지요. 수정에 앞서서 원본 파일은 유지하고 복사해서 쓰도록 하겠습니다. :) stn8000.conf 라고 파일을 복사하겠습니다.



[und3r@sungwook redis-3.0.1]$ sudo cp sentinel.conf /etc/redis/stn8000.conf



이제 파일을 열어서 수정합니다.

위에 계획한대로 포트는 8000 으로 변경합니다.


그리고 아래부분에 redis master 의 정보를 입력합니다.


설명에 나와있듯이 mymaster 는 임의의 alias 입니다. 크게 손댈필요 없어서 그냥 기본값으로 두었습니다. ip 와 port 는 제가 설정한 redis 정보로 넣어주었습니다. 마지막에 나오는 quorum 값으로 2가 되어 있습니다. 간략히 설명하자면, 앞서 failover 시에 master 가 죽었다라는 결정을 짓기 위해서 '다수결'을 사용한다고 말씀 드렸습니다. A 라는 sentinel 이 B 라는 master 에 연결을 실패했을때, master 가 죽었다고 판단할 수는 없기 때문이죠. redis master 는 멀쩡한데, 단지 A 와 B 사이의 네트워크 문제일 수도 있잖아요. 그렇기때문에 다수결을 사용합니다. A 와 B 가 단지 연결이 안된 상태를 'Subjective Down' 이라고 합니다. 즉 '주관적인 다운상태' 인 것이죠. 'Objective Down' 은 '객관적인 다운상태'를 말합니다. Sentinel 서버 몇대가 해당 master 를 SDown 으로 판단할때 ODown 으로 처리할 것인가?(failover 할 것인가?) 에 대한 숫자가 바로 이 'quorum' 값입니다.


저는 위에서 sentinel 서버를 3대 두기로 했습니다. 그렇기 때문에 2대의 sentinel 서버에서 master 로 연결을 실패하면 failover 하도록 할 것입니다. 그러므로 2 를 quorum 값으로 넣었습니다. 만약에 sentinel 서버를 1대만 둘 것이라면 이 값은 1 이 되어야 겠지요. 만약 저처럼 sentinel 서버가 3 대인데, quorum 값을 4나 그보다 더 큰 값으로 설정하면, 절대로 failover 가 발생하지 않습니다. 사람은 3명인데 다수결이 4명 이상 나와야 하는 말도 안되는 상황인 것이죠. 그러므로 적절히 자신의 시스템에 맞게 수정하셔야 합니다.



몇 초 동안 master 로의 연결이 실패했을 때, SDown 으로 간주할 것인가 입니다. 단위는 밀리세컨드 입니다. 기본값 30초는 너무 길기에 5 초로 설정하였습니다. 주의할 것은 데이터가 많아서 failover 가 일어난 뒤에 redis 에서 sync 하는 시간이 길어지면, 운이 좋지 않아서 설정한 값인 5초가 넘어가 버린다면, 다시한번 failover 가 일어납니다. 왜냐면 redis 는 single thread 이기 때문이죠. 그러므로 적절히 상황을 보고 설정하시는게 좋습니다.


failover 가 발생했을 때, slave 들은 master 로부터 데이터를 받아야(synch) 합니다. 이때 몇개씩 나누어서 synch 할 것인지에 대한 설정입니다. 만약 이 값이 3이라면 slave 3개가 동시에 master 로부터 synch 를 시도합니다. 그만큼 master 에는 부하가 걸리겠지요. 그렇기때문에 저는 1로 설정했습니다. 1개씩 순차적으로 master 와 synch 하라는 의미인 셈입니다.


failover 의 타임아웃을 몇초로 둘 것인지 입니다. 기본값은 3분이네요. 이 역시 데이터의 양에 따라서 적절히 넣어주시면 됩니다.



마지막으로 master 및 slave 의 password 를 입력해 줍니다.





이렇게 1개의 sentinel 설정파일이 세팅이 완료되었습니다. 이 파일을 각각 stn8001.conf, stn8002.conf 로 복사하고 포트번호만 8001, 8002 로 수정해서 나머지 파일도 만들어 줍니다.




그리고 중요한게 있습니다!!

redis 의 master,slave 들의 각 conf 파일을 조금 수정해 주어야 합니다. 그것은 바로! masterauth 와 requirepass 값입니다. sentinel 에 의해서 failover 시에 master 가 slave 가 되고, slave 가 master 가 되는 작업들이 뒤죽박죽 진행이 될텐데, 이때 모든 redis 들의 비밀번호가 같아야 되지 않겠습니까? 왜냐면 sentinel 은 master / slave 관계만 설정해 줄 뿐, 비밀번호를 알아내고, 설정해 주지는 않기 때문이죠. (애초에 비밀번호를 알아낼 수가 없죠) 그래서 모든 redis 의 비밀번호를 동일하게 설정해 줍니다. :)




마지막으로 완성된 sentinel 의 conf 파일은 다음과 같습니다.



# Example sentinel.conf

# port <sentinel-port>
# The port that this sentinel instance will run on
port 8000

# sentinel announce-ip <ip>
# sentinel announce-port <port>
#
# The above two configuration directives are useful in environments where,
# because of NAT, Sentinel is reachable from outside via a non-local address.
#
# When announce-ip is provided, the Sentinel will claim the specified IP address
# in HELLO messages used to gossip its presence, instead of auto-detecting the
# local address as it usually does.
#
# Similarly when announce-port is provided and is valid and non-zero, Sentinel
# will announce the specified TCP port.
#
# The two options don't need to be used together, if only announce-ip is
# provided, the Sentinel will announce the specified IP and the server port
# as specified by the "port" option. If only announce-port is provided, the
# Sentinel will announce the auto-detected local IP and the specified port.
#
# Example:
#
# sentinel announce-ip 1.2.3.4

# dir <working-directory>
# Every long running process should have a well-defined working directory.
# For Redis Sentinel to chdir to /tmp at startup is the simplest thing
# for the process to don't interfere with administrative tasks such as
# unmounting filesystems.
dir /tmp

# sentinel monitor <master-name> <ip> <redis-port> <quorum>
#
# Tells Sentinel to monitor this master, and to consider it in O_DOWN
# (Objectively Down) state only if at least <quorum> sentinels agree.
#
# Note that whatever is the ODOWN quorum, a Sentinel will require to
# be elected by the majority of the known Sentinels in order to
# start a failover, so no failover can be performed in minority.
#
# Slaves are auto-discovered, so you don't need to specify slaves in
# any way. Sentinel itself will rewrite this configuration file adding
# the slaves using additional configuration options.
# Also note that the configuration file is rewritten when a
# slave is promoted to master.
#
# Note: master name should not include special characters or spaces.
# The valid charset is A-z 0-9 and the three characters ".-_".
sentinel monitor mymaster 127.0.0.1 7000 2

# sentinel auth-pass <master-name> <password>
#
# Set the password to use to authenticate with the master and slaves.
# Useful if there is a password set in the Redis instances to monitor.
#
# Note that the master password is also used for slaves, so it is not
# possible to set a different password in masters and slaves instances
# if you want to be able to monitor these instances with Sentinel.
#
# However you can have Redis instances without the authentication enabled
# mixed with Redis instances requiring the authentication (as long as the
# password set is the same for all the instances requiring the password) as
# the AUTH command will have no effect in Redis instances with authentication
# switched off.
#
# Example:
#
sentinel auth-pass mymaster mypassword

# sentinel down-after-milliseconds <master-name> <milliseconds>
#
# Number of milliseconds the master (or any attached slave or sentinel) should
# be unreachable (as in, not acceptable reply to PING, continuously, for the
# specified period) in order to consider it in S_DOWN state (Subjectively
# Down).
#
# Default is 30 seconds.
sentinel down-after-milliseconds mymaster 5000

# sentinel parallel-syncs <master-name> <numslaves>
#
# How many slaves we can reconfigure to point to the new slave simultaneously
# during the failover. Use a low number if you use the slaves to serve query
# to avoid that all the slaves will be unreachable at about the same
# time while performing the synchronization with the master.
sentinel parallel-syncs mymaster 1

# sentinel failover-timeout <master-name> <milliseconds>
#
# Specifies the failover timeout in milliseconds. It is used in many ways:
#
# - The time needed to re-start a failover after a previous failover was
#   already tried against the same master by a given Sentinel, is two
#   times the failover timeout.
#
# - The time needed for a slave replicating to a wrong master according
#   to a Sentinel current configuration, to be forced to replicate
#   with the right master, is exactly the failover timeout (counting since



#   the moment a Sentinel detected the misconfiguration).
#
# - The time needed to cancel a failover that is already in progress but
#   did not produced any configuration change (SLAVEOF NO ONE yet not
#   acknowledged by the promoted slave).
#
# - The maximum time a failover in progress waits for all the slaves to be
#   reconfigured as slaves of the new master. However even after this time
#   the slaves will be reconfigured by the Sentinels anyway, but not with
#   the exact parallel-syncs progression as specified.
#
# Default is 3 minutes.
sentinel failover-timeout mymaster 180000

# SCRIPTS EXECUTION
#
# sentinel notification-script and sentinel reconfig-script are used in order
# to configure scripts that are called to notify the system administrator
# or to reconfigure clients after a failover. The scripts are executed
# with the following rules for error handling:
#
# If script exits with "1" the execution is retried later (up to a maximum
# number of times currently set to 10).
#
# If script exits with "2" (or an higher value) the script execution is
# not retried.
#
# If script terminates because it receives a signal the behavior is the same
# as exit code 1.
#
# A script has a maximum running time of 60 seconds. After this limit is
# reached the script is terminated with a SIGKILL and the execution retried.

# NOTIFICATION SCRIPT
#
# sentinel notification-script <master-name> <script-path>
#
# Call the specified notification script for any sentinel event that is
# generated in the WARNING level (for instance -sdown, -odown, and so forth).
# This script should notify the system administrator via email, SMS, or any
# other messaging system, that there is something wrong with the monitored
# Redis systems.
#
# The script is called with just two arguments: the first is the event type
# and the second the event description.
#
# The script must exist and be executable in order for sentinel to start if
# this option is provided.
#
# Example:
#
# sentinel notification-script mymaster /var/redis/notify.sh

# CLIENTS RECONFIGURATION SCRIPT
#
# sentinel client-reconfig-script <master-name> <script-path>
#
# When the master changed because of a failover a script can be called in
# order to perform application-specific tasks to notify the clients that the
# configuration has changed and the master is at a different address.
#
# The following arguments are passed to the script:
#
# <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>
#
# <state> is currently always "failover"
# <role> is either "leader" or "observer"
#
# The arguments from-ip, from-port, to-ip, to-port are used to communicate
# the old address of the master and the new address of the elected slave
# (now a master).
#
# This script should be resistant to multiple invocations.
#
# Example:
#
# sentinel client-reconfig-script mymaster /var/redis/reconfig.sh


daemonize yes
pidfile /var/run/sentinel_8000.pid
logfile /var/log/sentinel_8000.log


마지막에 있는 3줄은 sentinel 을 데몬으로 돌리기 위해서 넣은 것입니다. 추가하시길 권장합니다. :)






Sentinel 동작원리




이제 모든 설정이 완료되었습니다. 그런데 의문이 하나 드실것 같습니다. '기본적으로 failover 를 하기 위한 녀석이라면, master 정보 뿐 아니라 slave 들의 정보도 있어야 하지 않는가?' 라는 것이죠. 그래야 master 가 죽으면 slave 를 master 로 승격시켜 줄 수 있지 않을까요?

이게 가능한 이유는 redis 에 info 라는 명령어를 통해서 sentinel 이 slave 정보들을 읽을 수 있기 때문입니다. 현재 상태에서 master 에 접속후 info 명령을 내리면 다음과 같은 slave 들의 정보를 볼 수 있습니다.



# Replication

role:master

connected_slaves:3

slave0:ip=127.0.0.1,port=7001,state=online,offset=29,lag=0

slave1:ip=127.0.0.1,port=7002,state=online,offset=29,lag=0

slave2:ip=127.0.0.1,port=7003,state=online,offset=29,lag=0

master_repl_offset:29

repl_backlog_active:1

repl_backlog_size:1048576

repl_backlog_first_byte_offset:2

repl_backlog_histlen:28


이렇게 slave 들의 정보를 master 를 통해서 가져올 수 있기 때문에, sentinel 에 master 정보만 입력해도 되는 것입니다.



그리고 아까 quorum 이라는 갓을 통해서 '다수결' 로 failover 를 한다고 했었습니다. 그렇다면 sentienl 끼리는 어떻게 이런 의사결정을 할까요? sentienl 은 다른 sentinel 에 대한 정보를 어떻게 가져오고 대화를 할까요? 그것은 sentienl 이 내부적으로 redis 의 Pub/Sub 기능을 사용해서 정보를 주고 받기 때문입니다. __sentinel__:hello 라는 채널을 통해서 말이죠. 그러므로 sentinel 끼리는 conf 에서 따로 설정 없이도 잘 통신하게 됩니다. 서버 구성도에서 빨간 점선으로 그려놓은 부분입니다.






Sentinel 시작하기




실행에 앞서서, 이전 redis 의 경우 install_server.sh 라는 것을 이용하여 daemon 스크립트를 생성했었습니다. 아쉽게도 sentinel 은 제공되지 않는데요, 그래서 기존 redis 것을 복사 후, 수정해서 쓰도록 하겠습니다. :)



#!/bin/sh
#Configurations injected by install_server below....

EXEC=/usr/local/bin/redis-sentinel
CLIEXEC=/usr/local/bin/redis-cli
PIDFILE=/var/run/sentinel_8000.pid
CONF="/etc/redis/stn8000.conf"
REDISPORT="8000"


case "$1" in
    start)
        if [ -f $PIDFILE ]
        then
            echo "$PIDFILE exists, process is already running or crashed"
        else
            echo "Starting Redis Sentinel server..."
            $EXEC $CONF
        fi
        ;;
    stop)
        if [ ! -f $PIDFILE ]
        then
            echo "$PIDFILE does not exist, process is not running"
        else
            PID=$(cat $PIDFILE)
            echo "Stopping ..."
            $CLIEXEC -p $REDISPORT shutdown
            while [ -x /proc/${PID} ]
            do
                echo "Waiting for Redis Sentinel to shutdown ..."
                sleep 1
            done
            echo "Redis Sentinel stopped"
        fi
        ;;
    status)
        PID=$(cat $PIDFILE)
        if [ ! -x /proc/${PID} ]
        then
            echo 'Redis Sentinel is not running'
        else
            echo "Redis Sentinel is running ($PID)"
        fi
        ;;
    restart)
        $0 stop
        $0 start
        ;;
    *)
        echo "Please use start, stop, restart or status as first argument"
        ;;
esac


각각의 sentinel 포트에 맞게 수정하시면 됩니다. :)


이제 실행해 보겠습니다.


[und3r@sungwook redis-3.0.1]$ sudo /etc/init.d/sentinel_8000 start

Starting Redis Sentinel server...

[und3r@sungwook redis-3.0.1]$ sudo /etc/init.d/sentinel_8001 start

Starting Redis Sentinel server...

[und3r@sungwook redis-3.0.1]$ sudo /etc/init.d/sentinel_8002 start

Starting Redis Sentinel server...

[und3r@sungwook redis-3.0.1]$

[und3r@sungwook redis-3.0.1]$

[und3r@sungwook redis-3.0.1]$ ps aux | grep sentinel

root     16556  0.8  0.1 137436  2104 ?        Ssl  12:39   0:00 /usr/local/bin/redis-sentinel *:8000 [sentinel]

root     16562  0.7  0.1 137436  2096 ?        Ssl  12:39   0:00 /usr/local/bin/redis-sentinel *:8001 [sentinel]

root     16568  0.7  0.1 137436  2096 ?        Ssl  12:39   0:00 /usr/local/bin/redis-sentinel *:8002 [sentinel]

und3r    16572  0.0  0.0 103244   864 pts/0    S+   12:40   0:00 grep sentinel

[und3r@sungwook redis-3.0.1]$



로그를 찍어보면 정상적으로 실행 됬음을 알 수 있습니다.


[und3r@sungwook redis-3.0.1]$ cat /var/log/sentinel_8000.log

16562:X 29 May 12:39:53.838 * Increased maximum number of open files to 10032 (it was originally set to 1024).

                _._

           _.-``__ ''-._

      _.-``    `.  `_.  ''-._           Redis 3.0.1 (00000000/0) 64 bit

  .-`` .-```.  ```\/    _.,_ ''-._

 (    '      ,       .-`  | `,    )     Running in sentinel mode

 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 8000

 |    `-._   `._    /     _.-'    |     PID: 16562

  `-._    `-._  `-./  _.-'    _.-'

 |`-._`-._    `-.__.-'    _.-'_.-'|

 |    `-._`-._        _.-'_.-'    |           http://redis.io

  `-._    `-._`-.__.-'_.-'    _.-'

 |`-._`-._    `-.__.-'    _.-'_.-'|

 |    `-._`-._        _.-'_.-'    |

  `-._    `-._`-.__.-'_.-'    _.-'

      `-._    `-.__.-'    _.-'

          `-._        _.-'

              `-.__.-'


16712:X 29 May 14:26:05.453 # Sentinel runid is 9e8a60666d5ac0fbc809244d49a6d048ca779c06

16712:X 29 May 14:26:05.453 # +monitor master mymaster 127.0.0.1 7000 quorum 2

16712:X 29 May 14:26:05.454 * +slave slave 127.0.0.1:7001 127.0.0.1 7001 @ mymaster 127.0.0.1 7000

16712:X 29 May 14:26:05.456 * +slave slave 127.0.0.1:7002 127.0.0.1 7002 @ mymaster 127.0.0.1 7000

16712:X 29 May 14:26:05.457 * +slave slave 127.0.0.1:7003 127.0.0.1 7003 @ mymaster 127.0.0.1 7000

16712:X 29 May 14:26:11.315 * +sentinel sentinel 127.0.0.1:8001 127.0.0.1 8001 @ mymaster 127.0.0.1 7000

16712:X 29 May 14:26:13.043 * +sentinel sentinel 127.0.0.1:8002 127.0.0.1 8002 @ mymaster 127.0.0.1 7000







Sentinel 동작 확인(검증)




이제 모든 설정이 다 끝났습니다. 확인을 해 볼까요?


먼저 master 를 shutdown 합니다. 그리고나서 slave 로 들어가서 master 로 승격이 제대로 되었는지 확인해 볼 것입니다.


1. 상태 확인

[und3r@sungwook ~]$ ps aux | grep redis

root     16390  1.1  0.1 137436  2300 ?        Ssl  12:05   1:40 /usr/local/bin/redis-server *:7000

root     16396  0.1  0.1 137436  2156 ?        Ssl  12:05   0:13 /usr/local/bin/redis-server *:7001

root     16403  0.1  0.1 137436  2164 ?        Ssl  12:05   0:12 /usr/local/bin/redis-server *:7002

root     16410  0.1  0.1 137436  2160 ?        Ssl  12:06   0:12 /usr/local/bin/redis-server *:7003

root     16712  0.4  0.1 137436  2228 ?        Ssl  14:26   0:01 /usr/local/bin/redis-sentinel *:8000 [sentinel]

root     16718  0.4  0.1 137436  2236 ?        Ssl  14:26   0:01 /usr/local/bin/redis-sentinel *:8001 [sentinel]

root     16724  0.4  0.1 137436  2228 ?        Ssl  14:26   0:01 /usr/local/bin/redis-sentinel *:8002 [sentinel]

und3r    16734  0.0  0.0 103244   868 pts/1    S+   14:31   0:00 grep redis

보시는것처럼 redis 4대와 sentinel 3대가 돌고 있습니다.


2. master 확인

[und3r@sungwook ~]$ redis-cli -p 7000 -a mypassword

127.0.0.1:7000> info replication

# Replication

role:master

connected_slaves:3

slave0:ip=127.0.0.1,port=7001,state=online,offset=80358,lag=1

slave1:ip=127.0.0.1,port=7002,state=online,offset=80358,lag=1

slave2:ip=127.0.0.1,port=7003,state=online,offset=80358,lag=1

master_repl_offset:80358

repl_backlog_active:1

repl_backlog_size:1048576

repl_backlog_first_byte_offset:2

repl_backlog_histlen:80357

보시는것처럼 현재 7000 번 포트의 redis 가 master 로 동작하고 있습니다.


3. master shutdown

[und3r@sungwook ~]$ sudo /etc/init.d/redis_7000 stop

[sudo] password for und3r:

Stopping ...

Redis stopped


4. sentinel 의 로그 확인

[und3r@sungwook ~]$ tail -n 30 /var/log/sentinel_8000.log

16712:X 29 May 14:26:05.457 * +slave slave 127.0.0.1:7003 127.0.0.1 7003 @ mymaster 127.0.0.1 7000

16712:X 29 May 14:26:11.315 * +sentinel sentinel 127.0.0.1:8001 127.0.0.1 8001 @ mymaster 127.0.0.1 7000

16712:X 29 May 14:26:13.043 * +sentinel sentinel 127.0.0.1:8002 127.0.0.1 8002 @ mymaster 127.0.0.1 7000

16712:X 29 May 14:32:24.253 # +sdown master mymaster 127.0.0.1 7000

16712:X 29 May 14:32:24.313 # +odown master mymaster 127.0.0.1 7000 #quorum 2/2

16712:X 29 May 14:32:24.313 # +new-epoch 1

16712:X 29 May 14:32:24.314 # +try-failover master mymaster 127.0.0.1 7000

16712:X 29 May 14:32:24.317 # +vote-for-leader 9e8a60666d5ac0fbc809244d49a6d048ca779c06 1

16712:X 29 May 14:32:24.326 # 127.0.0.1:8002 voted for 9e8a60666d5ac0fbc809244d49a6d048ca779c06 1

16712:X 29 May 14:32:24.330 # 127.0.0.1:8001 voted for 9e8a60666d5ac0fbc809244d49a6d048ca779c06 1

16712:X 29 May 14:32:24.381 # +elected-leader master mymaster 127.0.0.1 7000

16712:X 29 May 14:32:24.381 # +failover-state-select-slave master mymaster 127.0.0.1 7000

16712:X 29 May 14:32:24.437 # +selected-slave slave 127.0.0.1:7001 127.0.0.1 7001 @ mymaster 127.0.0.1 7000

16712:X 29 May 14:32:24.437 * +failover-state-send-slaveof-noone slave 127.0.0.1:7001 127.0.0.1 7001 @ mymaster 127.0.0.1 7000

16712:X 29 May 14:32:24.529 * +failover-state-wait-promotion slave 127.0.0.1:7001 127.0.0.1 7001 @ mymaster 127.0.0.1 7000

16712:X 29 May 14:32:25.396 # +promoted-slave slave 127.0.0.1:7001 127.0.0.1 7001 @ mymaster 127.0.0.1 7000

16712:X 29 May 14:32:25.397 # +failover-state-reconf-slaves master mymaster 127.0.0.1 7000

16712:X 29 May 14:32:25.433 * +slave-reconf-sent slave 127.0.0.1:7003 127.0.0.1 7003 @ mymaster 127.0.0.1 7000

16712:X 29 May 14:32:26.398 * +slave-reconf-inprog slave 127.0.0.1:7003 127.0.0.1 7003 @ mymaster 127.0.0.1 7000

16712:X 29 May 14:32:26.533 # -odown master mymaster 127.0.0.1 7000

16712:X 29 May 14:32:27.432 * +slave-reconf-done slave 127.0.0.1:7003 127.0.0.1 7003 @ mymaster 127.0.0.1 7000

16712:X 29 May 14:32:27.502 * +slave-reconf-sent slave 127.0.0.1:7002 127.0.0.1 7002 @ mymaster 127.0.0.1 7000

16712:X 29 May 14:32:28.517 * +slave-reconf-inprog slave 127.0.0.1:7002 127.0.0.1 7002 @ mymaster 127.0.0.1 7000

16712:X 29 May 14:32:29.602 * +slave-reconf-done slave 127.0.0.1:7002 127.0.0.1 7002 @ mymaster 127.0.0.1 7000

16712:X 29 May 14:32:29.659 # +failover-end master mymaster 127.0.0.1 7000

16712:X 29 May 14:32:29.659 # +switch-master mymaster 127.0.0.1 7000 127.0.0.1 7001

16712:X 29 May 14:32:29.661 * +slave slave 127.0.0.1:7003 127.0.0.1 7003 @ mymaster 127.0.0.1 7001

16712:X 29 May 14:32:29.661 * +slave slave 127.0.0.1:7002 127.0.0.1 7002 @ mymaster 127.0.0.1 7001

16712:X 29 May 14:32:29.661 * +slave slave 127.0.0.1:7000 127.0.0.1 7000 @ mymaster 127.0.0.1 7001

16712:X 29 May 14:32:34.699 # +sdown slave 127.0.0.1:7000 127.0.0.1 7000 @ mymaster 127.0.0.1 7001

master 가 이상이 있음을 인지하고 sdown 상태에서 odown 상태가 됩니다. 이후 failover 가 동작되어 7001 번 slave 가 master 로 승격되었네요.


5. 7001번 slave 의 master 승격 확인

[und3r@sungwook ~]$ redis-cli -p 7001 -a mypassword

127.0.0.1:7001> info replication

# Replication

role:master

connected_slaves:2

slave0:ip=127.0.0.1,port=7003,state=online,offset=10809,lag=0

slave1:ip=127.0.0.1,port=7002,state=online,offset=10809,lag=0

master_repl_offset:10809

repl_backlog_active:1

repl_backlog_size:1048576

repl_backlog_first_byte_offset:2

repl_backlog_histlen:10808

7001 번이 정상적으로 master 로 승격되었습니다.



이때 본래 master 였던 7000 번을 start 하면 어떻게 될까요?

[und3r@sungwook ~]$ sudo /etc/init.d/redis_7000 start

Starting Redis server...




[und3r@sungwook ~]$ tail -n 3 /var/log/sentinel_8000.log

16712:X 29 May 14:32:29.661 * +slave slave 127.0.0.1:7000 127.0.0.1 7000 @ mymaster 127.0.0.1 7001

16712:X 29 May 14:32:34.699 # +sdown slave 127.0.0.1:7000 127.0.0.1 7000 @ mymaster 127.0.0.1 7001

16712:X 29 May 14:38:46.074 # -sdown slave 127.0.0.1:7000 127.0.0.1 7000 @ mymaster 127.0.0.1 7001

마지막줄을 보면 sentinel 에서 7000 번이 정상적으로 start 된 것을 확인하였네요.(-sdown)



[und3r@sungwook ~]$ redis-cli -p 7000 -a mypassword

127.0.0.1:7000> info replication

# Replication

role:slave

master_host:127.0.0.1

master_port:7001

master_link_status:down

master_last_io_seconds_ago:-1

master_sync_in_progress:0

slave_repl_offset:1

master_link_down_since_seconds:1432878032

slave_priority:100

slave_read_only:1

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

직접 7000 번 redis 로 들어가서 확인해보면, slave 로 동작을 잘 하고 있습니다. master 는 현재 master 로 승격된 7001 번 redis 를 가리키고 있고요.


어떻게 이게 가능할까요? sentinel 에서 아래처럼 7000 번 redis 의 conf 파일을 수정하기 때문입니다.

# Generated by CONFIG REWRITE

slaveof 127.0.0.1 7001






문제점




이렇게 모든 auto failover 시스템인 sentinel 에 대해서 알아 보았습니다. 그런데 여기서 또 의문이 듭니다. ^^;


client 에서는 write 를 위해서 master 인 7000 번을 사용하고, read 를 위해서는 slave 인 7001~3 번을 사용하고 있었습니다. 그런데 master 가 종료되었고, sentinel 에 의해서 slave 7001 이 master 가 되었습니다. 클라이언트는 여전히 7000 번이 master 라고 생각하고 있겠지요. 설령 7000 번이 죽었다는걸 알았다고 하더라도, 새로 master 로 승격된 slave 가 7001 번 이라는걸 알 수 없습니다.


물론 client 코드에서 이 문제를 해결할 수는 있긴 합니다. redis 사용하기 전에 7000~3 번까지의 모든 redis 에 대해서 연결을 시도하고, 성공하면 info 를 통해서 master 가 누구인지 알아오면 됩니다. 당연히 이 과정은 모든 connection 이전에 항상 체크해야 합니다. 어떤가요? 너무나 복잡하고 비효율 적입니다. 모든 클라이언트가 이 행동(짓거리;;)를 해야 하니까요.


이 문제는 haproxy 라는것을 사용하면 해결할 수 있습니다. 관련 내용은 다음 포스팅에서 다루도록 하겠습니다. :)





'Database > Redis' 카테고리의 다른 글

Redis 4부 - HAProxy  (6) 2015.05.29
Redis 2부 - Replication  (4) 2015.05.27
Redis 1부 - 설치  (5) 2015.05.27