고가용성 - 센티널

원문: https://redis.io/topics/sentinel

레디스 센티널은 레디스의 가용성을 보장해준다. 센티널을 사용하면 특정 장애에는 사람의 개입 없이도 버티는 레디스의 서버구성을 만들 수 있다. 센티널은 모니터링, 통지(notification), 서버구성의 안내자 역할과 같은 기능을 제공한다.

다음은 센티널의 기능이다.

  • 모니터링: 센티널은 마스터 및 슬레이브 인스턴스가 예상대로 작동하는지 지속적으로 확인한다.
  • 통지(notification): 센티널은 API를 통해 시스템 관리자, 다른 컴퓨터 프로그램에 모니터링되는 레디스 인스턴스 중 하나에 문제가 있음을 알릴 수 있다.
  • 자동 페일오버: 마스터가 예상대로 작동하지 않는 경우, 센티널은 슬레이브를 마스터로 승격하는 페일오버 프로세스를 시작할 수 있으며, 다른 추가 슬레이브는 새 마스터를 사용하도록 재구성되어 레디스 서버를 사용하는 애플리케이션은 연결 시 사용할 새 주소에 대해 안내받는다.
  • 구성 제공자(Configuration provider): 클라이언트는 주어진 서비스를 담당하는 현재 레디스 마스터의 주소를 요청하기 위해 센티널에 연결한다. 장애 조치가 발생하면 센티널은 새 주소를 알려준다.

센티널의 분산 개념

레디스 센티널은 분산 시스템이다.

센티널 자체는 여러 센티널 프로세스가 함께 협력하여 실행되도록 설계되었다. 이런 구성의 이점은 다음과 같다.

  1. 장애 감지(Failure detection)는 마스터 상태가 비정상적이라는 것에 대해 여러 센티널이 동의하면 수행된다. 이는 잘못된 감지의 가능성을 낮춘다.
  2. 센티널은 모든 센티널 프로세스가 작동하지 않더라도 작동하여, 시스템이 장애에 대해 견고해 질 수 있게 만든다. 그 자체가 SPOF(Single Point Of Failure) 인 시스템은 필요가 없다.

센티널, 레디스 인스턴스(마스터와 슬레이브), 연결된 클라이언트는 어떤 관점에서 봤을 때 또한 더 큰 분산 시스템이다. 이 문서에서는 센티널의 기본 속성을 이해하기 위해 필요한 기본 정보부터, 센티널의 정확한 작동 방식을 이해하기 위해 더 복잡한 정보까지 점진적으로 설명한다.

Quick Start

Obtaining Sentinel

지금 버전의 센티널은 센티널2라고 부른다.

이 버전은 더 강력하고 단순한 알고리즘을 사용해서 초기의 센티널을 다시 구현한 것이다. 레디스 2.8 버전 이후 안정적인 레디스 센티널을 출시했고, 불안정한 브랜치에서 새로운 개발이 발생하고, 뉴 피처들이 안정적이라고 여겨지는 최신의 브랜치에 다시 포팅되기도 한다.

레디스 2.6과 함께 제공된 레디스 센티널 버전1은 더이상 사용되지 않으므로 사용해서는 안된다.

Runing Sentinel

다음 명령줄을 사용하여 Sentinel을 실행할 수 있다.

redis-sentinel /path/to/sentinel.conf

아니면 다음과 같이 레디스 서버를 센티널 모드에서 사용할 수도 있다.

redis-server /path/to/sentinel.conf --sentinel

두가지는 방법 모두 똑같이 적용된다.

그러나 센티널을 실행할 때에는 반드시 구성 파일(configuration file)을 사용 해야 하는데, 이 파일은 재시작 시 다시 로드되는 현재 상태를 저장하기 위해 시스템에서 사용하기 때문이다. 센티널은 구성 파일이 없거나 해당 경로에 쓸 수 없는 경우 시작되지 못한다.

센티널은 기본적으로 TCP 포트 26379 에 대한 연결을 수신하기 위해 실행되므로, 센티널이 작동하려면 서버의 포트 26379가 열려 있어야 다른 센티널 인스턴스로부터 연결을 받을 수 있다. 그렇지 않으면 센티널은 다른 서버와 연결되지 못해, 무엇을 해야 하는지에 대해 동의할 수 없으므로 페일오버도 수행할 수 없다.

센티널에 대해 알아야 할 기본 사항

  1. 튼튼한 구성을 위해서는 센티널 인스턴스가 세개 이상 필요하다.
  2. 세 개의 센티널 인스턴스는 서로 물리적으로 영향받지 않는 컴퓨터나 가상 머신에 설치되어야 한다. 서로 다른 물리적 서버 또는 가상 시스템이란 서로 다른 가용성 영역(availability zone)에서 실행되는 경우를 의미한다.
  3. 센티널 + 레디스 구조의 분산 시스템은 레디스가 비동기 복제를 사용하기 때문에 장애가 발생하는 동안 썼던 내용들이 유지됨을 보장할 수 없다. 그러나 특정 순간으로 쓰기 손실을 제한시키는 방법들이 존재한다.
  4. 클라이언트에서 센티널을 지원할 수 있어야 한다. 유명한 클라이언트 라이브러리들은 센티널을 지원하지만 모든 클라이언트에서 지원해주지는 않는다.
  5. 개발 환경(운영 환경에서 한다면 더 좋지만)에서 테스트를 해보지 않는다면 안전한 HA 구성이라고 말할 수 없다.
  6. 센티널, 도커 또는 다른 형태의 NAT 이나 포트 매핑을 주의해서 사용해야 한다. 도커는 포트 재매핑을 통해 다른 센티널 프로세스의 자동 발견과, 마스터의 슬레이브 목록을 깨트릴 수 있다.

센티널 구성하기

레디스 소스에는 센티널을 구성하는 데 사용할 수 있는 파일인 sentinel.conf 파일이 존재하고, 센티널을 사용하기 위한 최소의 구성 파라미터는 아래와 같다.

sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1

sentinel monitor resque 192.168.1.3 6380 4
sentinel down-after-milliseconds resque 10000
sentinel failover-timeout resque 180000
sentinel parallel-syncs resque 5

모니터링을 할 마스터를 지정하고, 각 분리된 마스터(슬레이브가 있을 수 있음)에 다른 이름을 부여해야 한다. 슬레이브는 자동으로 발견되기 때문에 굳이 명시하지 않아도 된다. 센티널은 (재시작시 정보를 유지하기 위해) 슬레이브에 대한 추가 정보를 conf 파일에 자동으로 업데이트한다. 또한 페일오버시 슬레이브가 마스터로 승격될 때마다, 그리고 새로운 센티널이 발견될 때마다 구성파일이 다시 작성된다.

위의 예제 구성은 기본적으로 각각 마스터와, 존재할 수 도 있는 슬레이브로 구성된 두 세트의 레디스 인스턴스를 모니터링한다. 한 세트의 이름이 mymaster 이고, 다른 한 가지가 resque이다.

sentinel monitor 구문의 인자에 대한 설명은 아래와 같다.

sentinel monitor <master-group-name> <ip> <port> <quorum>

위 예제의 첫번째 줄은 레디스에게 mymaster 라고 부를 마스터 (127.0.0.1) 와 포트 6379번을 쿼럼이 2인 상태로 모니터링 하라는 것을 의미한다.

  • 쿼럼(quorum) 은 마스터에 도달할 수 없다는 것에 동의해야 하는 센티널의 수로, 이 수를 만족 하는 경우 페일오버를 시작한다.
  • 쿼럼은 오직 실패를 탐지하는 데에만 사용된다. 페일오버를 실제로 수행하려면 센티널 중 하나가 페일오버의 리더로 선출되고, 계속 진행할 수 있는 권한이 있어야 한다. 이는 센티널의 과반수 득표 프로세스에 의해 발생한다.

예를 들어 5개의 센티널 프로세스가 있고, 마스터에 대해 쿼럼이 2로 설정된 경우

  • 두 개의 센티널이 마스터에 연결할 수 없다는 것에 동시에 동의하는 경우, 둘 중 하나가 페일오버를 시작하려고 할 것이다.
  • 적어도 세개의 센티널이 살아있는 경우여야지만 페일오버가 승인되어 실제 실행될 것이다.

다른 센티널 옵션

sentinel <option_name> <master_name> <option_value>
  • down-after-milliseconds 는 센티널이 다운되었다고 생각한 순간부터 인스턴스에 도달할 수 없는 시간 (핑에 응답하지 않거나 오류로 응답)을 의미한다.
  • parallel-syncs 는 장애조치를 마친 후 새 마스터를 사용하도록 동시에 재구성될 수 있는 슬레이브의 수를 의미한다. 숫자가 작을수록 페일오버 프로세스가 완료되는 데에 더 많은 시간이 걸린다. 복제를 수행하는 프로세스가 슬레이브에 의해 차단되지는 않지만, 마스터로부터 대용량 데이터를 로드하기 위해 순단이 발생한다. 슬레이브가 이전 데이터를 받아오도록 구성된 경우, 이 순단때문에 모든 마스터와 슬레이브가 동시에 다시 동기화 되는것을 원하지 않을 수 있다. 이 옵션을 1로 설정하면 한 번에 하나의 슬레이브만 중단되도록 설정할 수 있다.

센티널 구성 예제

이제 센티널에 대한 기본 정보를 파악했으니, 센티널 프로세스를 어디에 배치해야 하는지, 센티널 프로세스가 얼마나 필요한지 등을 궁금해 할 수 있다. 이 섹션은 몇 가지 예시 구성을 보여준다.

+-------------+               +-------------+
| Sentinel S1 |---------------| Sentinel S2 |
+-------------+               +-------------+

+-------------+                +-------------+
| Sentinel S1 |------ // ------| Sentinel S2 |
+-------------+                +-------------+

이런 식으로 네트워크의 연결과 단절을 표현한다.

  • 마스터들은 M1, M2, M3… Mn으로 표시
  • 슬레이브(리플리카)는 R1, R2, R3… Rn으로 표시
  • 센티널은 S1, S2, S3,,, Sn
  • 클라이언트는 C1, C2,,, Cn
  • Sentinel 동작으로 인해 인스턴스가 역할을 변경하면 대괄호 안에 넣으므로 [M1]은 센티널의 개입으로 인해 마스터로 변한 인스턴스를 의미한다.

센티널은 페일오버를 시작하기 위해 항상 다수와 통신해야 하기 때문에 단 두개의 센티널만 사용하는 설정은 없다는 것에 유의해라.

예제1: 두개의 센티널만 사용하는 경우. 절대 사용 금지.

+----+         +----+
| M1 |---------| R1 |
| S1 |         | S2 |
+----+         +----+

Configuration: quorum = 1
  • 이 구성에서 M1이 실패하는 경우, 두 센티널이 failure의 합의에 도달하게 되고, (쿼럼이 1이기 때문에) 과반수를 넘었기 때문에 페일오버를 승인해 R1이 마스터로 승격된다.
  • M1이 실행중인 박스가 작동을 멈추면 S1도 작동을 멈춘다. 다른 박스 S2에서 실행되는 센티널은 혼자서 페일오버를 승인할 수 없으므로 시스템을 사용할 수 없게 된다.

다른 페일오버를 명령하기 위해 과반수 이상의 동의가 필요하고, 그 후에 마지막 구성에 대해 모든 센티널에게 전파한다는 것에 유의해야 한다. 그리고, 한 쪽에서 아무런 동의 없이 페일오버를 수행하는 것은 매우 위험하다는 것은 다음 예제를 통해 알 수 있다.

+----+           +------+
| M1 |----//-----| [M1] |
| S1 |           | S2   |
+----+           +------+

위의 구성은 대칭적인 두 개의 마스터를 갖게 된 구성이다. (S2는 권한 없이 페일오버 될 수 있다고 가정) 클라이언트는 두 마스터에 계속해서 쓸 수 있고, 이는 split brain condition 을 초래하게 된다.

예제2: 세개의 박스를 이용한 기본 구성

아래는 기본적인 센티널 구성이다. 이 구성에 안정성을 위해 다른 요소들을 쉽게 추가할 수 있다. 한 개의 박스에서 레디스와 센티널 프로세스를 동시에 실행시킨다.

       +----+
       | M1 |
       | S1 |
       +----+
          |
+----+    |    +----+
| R2 |----+----| R3 |
| S2 |         | S3 |
+----+         +----+

Configuration: quorum = 2

마스터 M1이 실패하면 S2와 S3은 실패에 대해 동의하고, 페일오버를 승인하여 클라이언트가 계속 진행할 수 있게 한다.

모든 센티널 설정에서 레디스는 비동기식으로 복제되므로, 도중에 write 한 내용을 잃을 위험이 항상 존재한다. 그러나 위의 설정에서 다음 그림과 같이 이전의 마스터에 연결된 클라이언트로 인해 높은 위험이 존재한다.

         +----+
         | M1 |
         | S1 | <- C1 (writes will be lost)
         +----+
            |
            /
            /
+------+    |    +----+
| [M2] |----+----| R3 |
| S2   |         | S3 |
+------+         +----+

위의 경우, 네트워크 파티션은 이전의 마스터 M1와 통신을 하지 못하기 때문에 슬레이브 R2가 마스터로 승격한다. 그러나 기존 마스터와 동일한 파티션에 있는 C1같은 클라이언트는 계속해서 이전 마스터에게 데이터를 쓴다. 파티션이 복구될 때, 마스터는 데이터셋을 버리고, 새로운 마스터의 슬레이브로 재구성될 것이기 때문에 C1이 작성했던 이 데이터는 손실될 것이다.

이 문제는 마스터가 더이상 지정된 수의 슬레이브에게 쓰기를 전송할 수 없는 것을 감지했을 경우 쓰기 전송을 중지할 수 있는 다음과 같은 레디스 복제 기능을 사용해서 이 문제를 완화시킬 수 있다.

min-slaves-to-write 1
min-slaves-max-lag 10

마스터가 이런 구성으로 이루어져 있을 때, 레디스 인스턴스가 적어도 하나의 슬레이브에 쓸 수 없는 경우 쓰기를 중지한다. 복제는 비동기로 이루어지기 때문에 슬레이브는 연결이 끊어졌거나, 지정된 시간 이상 ack를 보내지 않음을 의미한다.

이 구성을 사용하면 위의 예제에서 이전 레디스 마스터 M1을 10초 후에 사용할 수 없게 된다. 파티션이 복구되면 C1은 유효한 값으로 새 마스터에 연결을 진행한다.

하지만 두개의 슬레이브가 다운될 경우 마스터는 살아있음에도 불구하고, 마스터에 쓰기를 진행할 수 없다. 이는 트레이드 오프이므로 감당해야 한다.

예제 3: 클라이언트 내부의 센티널 구성

때때로 마스터에 연결된 슬레이브가 하나만 필요할 경우도 존재한다. 예제2는 이럴 때에는 사용할 수 없으므로 센티널이 클라이언트가 있는 곳에 배치하여 다음처럼 사용할 수 있다.

            +----+         +----+
            | M1 |----+----| R1 |
            |    |    |    |    |
            +----+    |    +----+
                      |
         +------------+------------+
         |            |            |
         |            |            |
      +----+        +----+      +----+
      | C1 |        | C2 |      | C3 |
      | S1 |        | S2 |      | S3 |
      +----+        +----+      +----+

      Configuration: quorum = 2

위의 구성에서, 센티널은 클라이언트 수와 같다. C1, C2, C3은 레디스에 연결된 단일 클라이언트를 의미하는게 아니라 앱서버, 레디스 어플리케이션과 같은 것을 의미한다.

M1과 S1이 실행중인 박스에 장애가 발생하면 문제없이 페일오버를 할 수 있지만, 다른 네트워크 파티션이 발생할 경우에는 다른 동작을 유발한다는 것을 알 수 있다. 예를 들어, 레디스 클라이언트와 슬레이브간에 네트워크 단절이 일어난 경우 센티널을 이용할 수 없다.

C3과 M1이 분리되는 경우(위의 예제에서는 어렵겠지만, 다른 레이아웃이나 소프트웨어 계층에서 장애가 발생하면 가능) 예제2에서와 비슷한 이슈를 가질 수 있다. 마스터는 슬레이브와 연결이 끊어졌을 때 쿼리를 받아들이는 걸 멈출 수 없고, split brain 문제가 일어날 수 있다.

따라서 이것은 유효한 설정이지만, 예제2에서의 구성과 같은 HA 시스템이 관리하기 더 간단하다는 장점이 있고, 쓰기를 위한 시간이 존재한다.

예제 4: 클라이언트쪽이 3개 이하인 센티널 클라이언트

예제 3의 경우에서, 클라이언트가 3개 이하일 경우 다음과 같이 혼합된 설정을 사용해야 한다.

            +----+         +----+
            | M1 |----+----| R1 |
            | S1 |    |    | S2 |
            +----+    |    +----+
                      |
               +------+-----+
               |            |  
               |            |
            +----+        +----+
            | C1 |        | C2 |
            | S3 |        | S4 |
            +----+        +----+

      Configuration: quorum = 3

예제 3의 설정과 비슷하지만, 여기서는 사용할 수 있는 네개의 서버에서 네 개의 센티넬을 실행한다. 마스터 M1이 사용가능하지 않으면, 다른 세 개의 센티널이 페일오버를 수행한다.

이론적으로 C2.S4가 실행되는 박스를 제거하고, 쿼럼을 2로 설정하는 편이 보편적이다. 그러나 어플리케이션 계층을 포함하지 않고 레디스쪽에서만 HA를 원할 일은 거의 없다.

센티널, 도커, NAT 에서 가능한 이슈

도커는 port mapping 을 사용한다. 도커 컨테이너 내에서 돌아가는 프로그램은 실제 프로그램이 사용하는 것과 다른 포트로 노출될 수 있다. 이는 동일한 포트를 사용해서 동시에 동일한 서버에서 여러 개의 컨테이너를 실행할 때에 유용하다.

도커뿐 아니라 다른 NAT 시스템에 의해 포트나, IP 주소가 재매핑되는 경우가 발생할 수 있다.

센티널에서 IP와 포트를 재매핑하여 사용할 경우 다음과 같은 문제가 발생할 수 있다.

  1. 센티널은 각 센티널이 연결을 위해 수신하는 포트와 IP 주소를 알려주는 hello 메시지를 기반으로 auto-discovery 하기 때문에, 센티널 자동 동작은 더이상 작동하지 않을 수 있다. 그러나 센티널은 주소나 포트가 재매핑되었다는 것을 알 수 없기 때문에, 다른 센티널이 접속할 때 허용하지 않게 된다.
  2. 레디스 마스터에서 INFO 명령어를 통해 나열되는 슬레이브의 정보가 잘못될 수 있다.

센티널은 마스터의 INFO 정보를 사용해서 슬레이브를 자동으로 탐지하므로, 탐지된 슬레이브에 도달할 수 없을 것이며, 마스터가 죽었을 때 좋은 페일오버가 가능한 슬레이브가 존재함에도 불구하고, 절대 페일오버 할 수 없을 것이다. 도커가 포트를 1:1로 매핑하지 않는 이상.

첫번째 문제를 해결하기 위해, 아래 명령어를 사용해서 매핑하는 작업이 필요하다.

sentinel announce-ip <ip>
sentinel announce-port <port>

A quick tutorial

이 문서의 다음 섹션에서는 센티널 API, 구성과 semantic에 대한 모든 세부 사항을 점진적으로 다룬다. 그러나 이 섹션은 최대한 빨리 시스템을 사용하고자 하는 사람들을 위해 세개의 센티널 인스턴스를 구성하고, 상호작용하는 방법을 보여준다.

여기서는 인스턴스가 포트 5000, 5001, 5002에서 실행된다고 가정한다. 포트 6379에서는 레디스 마스터가 실행되고 있고, 6380에서는 슬레이브가 실행되고 있다고 가정한다. IP는 루프백 IP인 127.0.0.1을 사용했다.

세 가지 센티널의 설정 파일은 다음과 같다.

port 5000
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
sentinel parallel-syncs mymaster 1

나머지 두 개의 설정 파일은 동일하지만 포트 번호로 5001과 5002를 사용한다.

위의 설정 파일에서 다음과 같은 몇가지 사항을 유의해야 한다.

  1. 이 마스터 셋은 mymaster라 부른다. 이는 마스터와 슬레이브를 식별하는 정보이다. 마스터셋의 이름이 다르면, 센티널은 한번에 여러 마스터와 슬레이브들을 감시할 수 있다.
  2. 쿼럼은 2로 설정되어있다. (sentinel monitor 의 마지막 인자값)
  3. down-after-milliseconds 는 5000밀리초, 즉 5초이므로 마스터가 이 시간 내에 ping에 대해 어떠한 응답도 하지 않는다면 실패로 감지한다.

세개의 센티널을 시작하면 다음과 같은 로그가 남는다.

+monitor master mymaster 127.0.0.1 6379 quorum 2

이는 센티널 이벤트이고, 나중에 지정한 이름으로 SUBSCRIBE를 사용할 경우 Pub/Sub을 통해 이러한 이벤트를 수신할 수 있다.

센티널은 장애 감지와 페일오버 중에 다양한 이벤트를 생성하고 기록한다.

센티널에게 마스터 상태에 대해 질문하기

센티널을 사용하는 가장 중요한 이유는 마스터가 잘 동작하고 있는지 모니터링하기 위함일 것이다.

$ redis-cli -p 5000
127.0.0.1:5000> sentinel master mymaster
 1) "name"
 2) "mymaster"
 3) "ip"
 4) "127.0.0.1"
 5) "port"
 6) "6379"
 7) "runid"
 8) "953ae6a589449c13ddefaee3538d356d287f509b"
 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) "735"
19) "last-ping-reply"
20) "735"
21) "down-after-milliseconds"
22) "5000"
23) "info-refresh"
24) "126"
25) "role-reported"
26) "master"
27) "role-reported-time"
28) "532439"
29) "config-epoch"
30) "1"
31) "num-slaves"
32) "1"
33) "num-other-sentinels"
34) "2"
35) "quorum"
36) "2"
37) "failover-timeout"
38) "60000"
39) "parallel-syncs"
40) "1"

위와 같이, 센티널에서 마스터에 관한 다양한 정보를 확인할 수 있다. 그 중에서도 특별히 확인해야 할 몇 가지 정보들이 존재한다.

  1. num-other-sentinels 의 값이 2이다. 해당 센티널은 이미 이 마스터를 위해 두 개의 센티넬을 더 감지했다는 사실을 인지하고 있다는 의미이다.
  2. flag 에 해당하는 값은 master이다. 마스터가 다운되었을 경우 이 값이 s_down 혹은 o_down 으로 바뀔 것이다.
  3. num-slaves의 값은 1로, 센티널이 마스터에 붙은 슬레이브가 존재한다는 것을 인지하고 있다는 의미이다.

이 인스턴스에 대해 더 자세히 알아보려면 다음의 명령어를 통해 확인할 수 있다.

SENTINEL slaves mymaster
SENTINEL sentinels mymster

첫번째는 마스터에 연결된 슬레이브에 대한 정보, 두번째는 마스터에 연결된 다른 센티널에 대한 정보를 제공한다.

현재 마스터의 주소 얻기

앞서 명시한 대로, 센티넬은 마스터셋에 연결하고자 하는 클라이언트의 구성 제공자 (configuration provider)와 같은 역할을 한다. 페일오버 혹은 재구성으로 인해 현재 마스터에 대해 알 수 없기 때문에 다음과 같은 API를 사용해서 마스터의 주소를 얻어올 수 있다.

127.0.0.1:5000> SENTINEL get-master-addr-by-name mymaster
1) "127.0.0.1"
2) "6379"

페일오버 테스트

위의 구성에서 마스터를 죽이고 구성이 변경되는지 확인하는 것으로 간단히 페일오버 테스트를 진행할 수 있다.

redis-cli -p 6379 DEBUG sleep 30

해당 명령어는 30초동안 마스터에 접근하지 못하게 한다. 이는 어떤 이유로 인해 마스터가 잠시 행에 걸려있는 상황을 시뮬레이션 한 것이다.

센티널 로그를 확인해 보면, 다음과 같은 많은 작업이 일어났음을 확인할 수 있다.

  1. 각각의 센티널은 마스터가 +sdown 이벤트와 함께 다운된 것을 감지한다.
  2. 이 이벤트는 나중에 +odown으로 에스컬레이션되며, 이는 다수의 센티널이 마스터에 도달할 수 없다는 사실에 동의했음을 의미한다.
  3. 센티널은 첫번째 페일오버를 시도할 센티널에 투표한다.
  4. 페일오버 발생.

다음 명령어를 통해 mymaster의 master가 누구인지 다시 질문한다면, 아까와는 다른 다음과 같은 응답을 받을 수 있게 된다.

127.0.0.1:5000> SENTINEL get-master-addr-by-name mymaster
1) "127.0.0.1"
2) "6380"

지금까지 이해했다면, 센티널 구성을 위해 점프하거나, 더 자세한 구성을 위해 아래 내용을 읽으면 된다.

Sentinel API

센티널은 API를 제공해서 상태를 검사하고, 모니터링되는 마스터와 슬레이브의 상태를 확인하고, 특정 알림을 수신하기 위해 구독하며, 런타임 중 센티널 구성을 변경한다.

기본적으로 센티널은 TCP 포트 26379를 사용해서 실해오딘다. 센티널은 레디스 프로토콜을 사용해서 명령을 수신하므로, redis-cli 나 다른 레디스 클라이언트를 사용해서 센티널과 통신할 수 있다.

센티널에 직접 쿼리해서 모니터링되는 레디스 인스턴스의 상태를 센티널의 관점에서 확인하고, 다른 센티널이 알고 있는 것을 확인하는 등의 작업을 수행할 수 있다. 또는 Pub/Sub를 사용해서 센티널로부터 push style 알람을 수신할 수 있으며, 페일오버와 같은 일부 이벤트가 발생할 때마다 error condition을 입력하는 인스턴스 등을 사용할 수 있다.

Sentinel commands

PING

  • PONG을 리턴

SENTINEL masters

  • 모니터링되는 마스터들과 상태를 나열

SENTINEL master <master name>

  • 특정 마스터의 상태를 나열

SENTINEL slaves <master name>

  • 특정 마스터에 연결된 슬레이브들과, 그 상태를 나열

SENTINEL sentinels <master name>

  • 해당 마스터에 연결된 센티넬들과, 그 상태를 나열

SENTINEL get-master-addr-by-name <master name>

  • ip와 port 정보를 리턴.
  • 페일오버가 진행중이거나, 진행 완료된 경우 승격된 슬레이브의 IP와 port를 리턴.

SENTINEL reset <pattern>

  • 일치하는 이름을 가진 모든 마스터를 재설정.
  • 패턴은 글로벌 스타일의 패턴이다.
  • 재설정 프로세스는 마스터의 이전 상태를 삭제하고 (진행중인 페일오버 상태 포함) 마스터가 인지하고 있던 모든 슬레이브와 센티널의 연결을 제거한다.

SENTINEL ckquorum <master name>

  • 현재 센티널 구성이 마스터를 페일오버하는데 필요한 쿼럼과, 페일오버를 승인하는데에 필요한 과반수에 도달하는지 확인.
  • 센티널 구성이 정상인지 확인하기 위해 모니터링 시스템에서 사용해야 한다.

SENTINEL flushconfig

  • 센티널이 현재 상태를 포함해서 디스크에 config 파일을 다시 쓰도록 강제 설정.
  • 일반적으로 센티널은 상태가 변경될 때마다 config을 다시 기록.
  • 때때로 작업 오류, 디스크 오류 등으로 해당 config 파일이 손상될 수 있기 때문에, 이럴 경우 강제하는 방법이 유용하다.
  • 이전 config 파일이 완전히 누락된 경우에도 작동됨.

Reconfiguring Sentinel at Runtime

레디스 2.8.4버전 이상부터는 센티널은 주어진 마스터의 구성을 추가, 제거, 변경할 수 있는 API를 제공한다. 여러 개의 센티널이 있는 경우, 레디스 센티널이 제대로 작동하려면 모든 인스턴스에서 변경 사항을 적용해야 한다는 것에 유의해야 한다. 이는 단일 센티널의 구성을 변경한다고 해서 네트워크의 다른 센티널에 변경사항이 자동으로 전파되는 것은 아님을 의미한다.

다음은 센티널 인스턴스의 config을 업데이트하기 위해 사용되는 SENTINEL의 하위 명령이다.

SENTINEL MONITOR <name> <ip> <port> <quorum>

  • 지정된 이름, ip, 포트, 쿼럼을 가진 새 마스터의 모니터링 시작
  • IP에 hostname을 사용할 수는 없다.

SENTINEL REMOVE <name>

  • 지정된 마스터를 제거하기 위해 사용.
  • 마스터는 더이상 모니터링되지 않고, 센티널의 내부 상태에서 완전히 제거되므로, SENTINEL masters 등의 명령어에 더이상 나열되지 않음.

SENTINEL SET <name> <option> <value>

  • 레디스 서버의 CONFIG SET 커맨드와 비슷.
  • 특정 마스터의 파라미터를 변경하기 위해 사용됨.

다음은 object-cache 라는 이름의 마스터의 down-after-milliseconds 구성을 변경하기 위해 사용한 SENTINEL SET 명령어의 예시이다.

SENTINEL SET objects-cache-master down-after-milliseconds 1000

SENTINEL REMOVE 이후에 SENTINEL MONITOR 명령어로 재구성할 필요 없이, SENTINEL SET 명령어를 통해 아래처럼 쿼럼 구성만 변경할 수도 있다.

SENTINEL SET objects-cache-master quorum 5

SENTINEL MASTER 명령어가 모든 구성 파라미터를 필드/쌍 배열로 제공하므로 SENTINEL GET 명령어는 존재하지 않는다.

Adding or removing Sentinels

센티넬의 자동 검색 매커니즘 때문에 구성에 새로운 센티널을 추가하는 것은 간단하다. 현재의 마스터를 모니터링하도록 구성된 새 센티널을 시작하면 된다. 10초 이내에 센티널은 다른 센티널 목록과 마스터에 부착된 슬레이브 집합을 알게 될 것이다.

한 번에 여러 센티널을 추가해야 하는 경우, 센티널이 인식되기를 기다리면서 차례로 하나씩 추가하는 것이 좋다. This is useful in order to still guarantee that majority can be achieved only in one side of a partition, in the chance failures should happen in the process of adding new Sentinels.

네트워크 단절이 발생하지 않았을 경우, 30초 정도의 지연으로 모든 센티널을 추가할 수 있다. 추가하는 프로세스가 끝나면 SENTINEL MASTER mastername 명령을 사용해서 모든 센티널이 마스터를 모니터링하는 총 센티널 수에 동의하는지 확인할 수 있다.

센티널을 제거하는 방법은 좀 더 복잡하다. 센티널은 이미 등록된 센티널에 오랫동안 연결되지 않더라도 지우지 않는다. 왜냐하면 페일오버와 새로운 구성을 만드는데 필요한 대부분의 것들을 동적으로 변경하고 싶지 않아서이다. 따라서 센티널을 제거하려면 다음과 같은 스텝을 따라야 한다. (네트워크 단절이 없는 경우)

  1. 제거할 센티널의 프로세스 중지
  2. SENTINEL RESET * 명령을 다른 모든 센티널 인스턴스로 전송 (인스턴스간에 최소 30초 이상 대기하면서 기다려야 함)
  3. 모든 센티널의 SENTINEL MASTER mastername 출력을 검사하여 모든 센티널이 현재 활성화된 센티널의 수와 맞는지 확인

Removing the old master or unreachable slaves

센티널은 오랫동안 연결이 되지 않을 때에도, 마스터의 슬레이브에 대해 확인하지 않는다. 이는 센티널이 네트워크 파티션 또는 장애 이벤트 후에 반환되는 슬레이브를 올바르게 재구성할 수 있어야 하기 때문에 유용하다.(이해안됨)

또한 페일오버 이후에, 실패한 마스터는 자동적으로 새로운 마스터의 슬레이브로 추가된다. 이렇게 되면 다시 사용할 수 있게 되는 즉시 새로운 마스터를 복제하도록 재구성될 것이다.

그러나 때때로 센티널이 감시하는 슬레이브의 목록에서 영원히 해당 슬레이브 (옛 마스터일 수 있음)를 제거하길 원할 수 있다.

이 작업을 수행하려면 SENTINEL RESET mastername 명령을 모든 센티널에게 보내야 한다. SENTINEL RESET mastername 명령은 10초 이내에 슬레이브 목록을 새로 고치고, 현재 마스터의 INFO 출력에서 올바르게 복제된 것으로 나열된 슬레이브만 추가한다.

Pub/Sub Messages

클라이언트는 채널에 SUBSCRIBE 또는 PSUBSCRIBE 를 적용해서 특정 이벤트에 대한 알림을 받기 위해 Redis와 호환되는 Pub/Sub 서버 (PUBLISH를 사용할 수는 없음) 인 센티널을 사용할 수 있다.

채널의 이름은 이벤트 이름과 동일하다. 예를 들어, +sdown이라는 채널은 SDOWN을 입력하는 인스턴스와 관련된 모든 알림을 수신한다. (SDOWN은 쿼리 중인 센티널의 관점에서 인스턴스에 더 이상 연결할 수 없음을 의미한다.)

모든 메시지를 받으려면 PSUBSCRIBE * 을 사용하면 된다.

다음은 이 API를 사용해서 수신할 수 있는 채널의 메시지 및 형식 목록이다. 첫 번째 단어는 채널/이벤트 이름이고, 나머지는 데이터의 형식이다.

NOTE: 인스턴스의 세부 정보가 지정된 경우, 대상 인스턴스를 식별하기 위해 다음과 같은 인수가 제공됨을 의미.

<instance-type> <name> <ip> <port> @ <master-name> <master-ip> <master-port>

마스터를 식별하는 부분(@ 인수부터 끝까지)은 선택 사항이며, 인스턴스 자체가 마스터가 아닌 경우에만 지정된다.

### +reset-master <instance details>

  • 마스터가 리셋됨.

+slave <instance details>

  • 새로운 슬레이브가 감지되어 추가됨.

+failover-state-reconf-slaves <instance details>

  • 페일오버 상태가 reconf-slaves 상태로 변경됨

+failover-detected <instance details>

  • 페일오버가 다른 센티넬에 의해 시작되었거나, 다른 엔티티가 발견됨(연결된 슬레이브가 마스터가 되었다던지)

+slave-reconf-sent <instance details>

  • 리더 센티널이 새로운 슬레이브를 해당 인스턴스에 구성하기 위해 SLAVE OF 명령어를 보냄.

+slave-reconf-inprog <instance details>

  • 재구성된 슬레이브가 새로운 마스터 ip:port 쌍의 슬레이브로 발견되었지만, 싱크 프로세스가 완료되지는 않음.

-dup-sentinel <instance details>

  • 하나 이상의 센티널이 복제에 의해 제거됐음으로 확인. (센티널 인스턴스가 다시 시작될 때 발생)

+sentinel <instance details>

  • 같은 마스터를 보고 있는 새로운 센티널이 발견되어 연결됨.

+sdown <instance details>

  • 특정 인스턴스가 주관적인(Subjectively) down 상태가 됨.

-sdown <instance details>

  • 특정 인스턴스가 주관적인(Subjectively) down 상태에서 벗어남.

+odown <instance details>

  • 특정 인스턴스가 객관적인 (Objectively) down 상태가 됨.

-odown <instance details>

  • 특정 인스턴스가 객관적인 (Objectively) down 상태에서 벗어남.

+new-epoch <instance details>

  • 현재 상태가 업데이트됨
  • The current epoch was updated.

+try-failover <instance details>

  • 새로운 페일오버의 진행을 위해 다수결에 의해 선출되기를 기다리는 중.

+elected-leader <instance details>

  • 선거에서 이겼으며, 페일오버 실행 가능함.

+failover-state-select-slave <instance details>

  • 새로운 페일오버의 상태는 select-slave. 마스터로 승격시키기 위해 적합한 슬레이브를 찾고 있는 상태.

no-good-slave <instance details>

  • 승격시킬만한 슬레이브가 존재하지 않음. 현재 상태에서 몇분동안 더 시도해 보고, 아마도 이 상태는 바뀌고 state machine은 이 경우 페일오버를 중단함.

이 상태에서 성공적으로 바껴서 페일오버가 중단된다는 건지, 없으면 기다리다가 아예 상태가 변경된다는 건지 확인이 필요함.

selected-slave <instance details>

  • 승격시킬만한 좋은 슬레이브를 발견함

failover-state-send-slaveof-noone <instance details>

  • 승격된 슬레이브를 마스터로 재구성하여 전환되길 기다리는 중

failover-end-for-timeout <instance details>

  • 페일오버가 타임아웃으로 인해 중단되었지만, 슬레이브는 결국 새로운 마스터로 구성될 것임

failover-end <instance_details>

  • 페일오버가 성공적으로 완료됨. 모든 슬레이브가 마스터의 복제본으로 구성 완료됨.

switch-master <master name> <oldip> <oldport> <newip> <newport>

  • 마스터의 새 IP와 주소는 구성이 변경된 후 지정된 새 주소를 의미. 대부분의 외부 사용자가 제일 관심을 가질 정보.

+tilt

  • Tilt 모드 시작

-tilt

  • Tilt 모드 해제

-BUSY 상태 처리

루아 스크립트가 지정된 루아 스크립트 시간제한 시간보다 오래 걸릴 경우, 레디스 인스턴스는 -BUSY 에러를 리턴한다. 페일오버를 트리거하기 전에 레디스 센티널이 SCRIPT KILL 커맨드를 날렸을 때 이런 일이 발생한다. 스크립트가 읽기전용일 때에만 성공한다.

인스턴스가 이 시도 이후에도 계속 에러 상태로 남아있다면, 결국 페일오버될 것이다.

슬레이브 우선순위

  • 레디스 인스턴스는 slave-priority 라는 구성 파라미터를 가지고 있고, 슬레이브 인스턴스에서 INFO 명령어를 통해 확인 가능하다. 센티널은 이 정보를 페일오버시 마스터로 승격시킬 슬레이브를 정할 때 사용한다.
  1. 만약 슬레이브의 우선순위가 0으로 설정되었다면, 해당 슬레이브는 절대 마스터로 승격되지 않음.
  2. 낮은 우선순위의 슬레이브일 수록 마스터로 승격시킬 때 센티널이 선호함.

예를 들어 마스터와 동일한 데이터 센터를 가지고 있는 슬레이브 S1이 존재하고, 다른 데이터 센터를 가지고 있는 슬레이브 S2가 있다고 하자. S1의 우선순위가 10이고, S2가 100이라면 마스터가 죽었을 때 S1, S2 모두 마스터로 승격이 가능하지만 S1이 더 선호된다.

센티널과 레디스 인증

마스터가 보안을 위해 클라이언트의 암호를 필요로 하도록 구성되면, 슬레이브에서도 마스터와 인증하고, 비동기 복제 프로토콜에 사용되는 마스터-슬레이브 연결을 만들기 위해 이 암호를 사용해야 한다.

이는 다음 구문을 통해 사용된다.

  • 마스터의 requirepass 를 사용해서 인증 비밀번호를 설정하고, 인스턴스가 인증되지 않은 클라이언트에 대한 요청을 처리하지 않도록 한다.
  • 슬레이브의 masterauth 를 사용해서 마스터에서 인증을 받아서 데이터를 올바르게 복제할 수 있도록 한다.

센티널을 사용할 때, (싱글 마스터 구조가 아니라면) 페일오버가 끝나면 슬레이브는 마스터의 역할을 하고, 마스터는 슬레이브처럼 구성될 수 있다. 따라서 위의 구문은 슬레이브와 마스터 두 개의 인스턴스 모두에 설정해 주어야 한다.

또한, 슬레이브도 동일한 데이터를 복제받아 가지고 있는데. 마스터에서만 데이터를 보호하길 원하지 않을 것이기 때문에 이는 일반적으로 정상적인 설정이다.

그러나 인증 없이 액세스할 수 있는 슬레이브가 필요한 드문 경우에는, 해당 슬레이브의 우선순위를 0으로 설정하여 마스터로 승격되지 않도록 하고, 이 슬레이브에서 masterauth 구문만 사용하면 된다. 이 구성에서는 requirepass 지시문을 사용하지 않기 때문에, 데이터를 인증되지 않은 클라이언트가 읽을 수 있도록 한다.

센티널 구성에서 requirepass를 사용해서 구성할 때, 감시하는 서버가 레디스 인스턴스에 연결되도록 하려면, 다음과 같은 구문을 이용해야 한다.

sentinel auth-pass <master-group-name> <pass>

인증을 사용한 센티널 인스턴스의 구성

AUTH 명령어를 통해 클라이언트의 인증을 요구하도록 센티널 인스턴스 자체를 구성할 수도 있다. 하지만 이 기능은 레디스 5.0.1부터 사용 가능하다.

이를 가능하게 하려면 모든 센티널 인스턴스에 다음과 같은 구문을 추가하면 된다.

requirepass "your_password_here"

이렇게 구성할 경우, 센티널은 다음과 같은 두 가지 일을 하게 된다.

  1. 클라이언트가 센티널에 명령어를 전송하기 위해 패스워드가 필요하다. 이는 레디스에서 이런 설정 지시어가 작동하는 방식이므로 분명한 일이다.
  2. Moreover the same password configured to access the local Sentinel, will be used by this Sentinel instance in order to authenticate to all the other Sentinel instances it connects to.

즉, 모든 센티널 인스턴스에서 동일한 requirepass 패스워드를 구성해야 한다. 모든 센티널은 모든 다른 센티널과 통신할 수 있으므로, 모든 센티널에 액세스하기 위해 각 센티널에 암호를 구성할 필요가 없으며, 그렇지 않는다면 매우 매우 실용적이지 않았을 것이다.

이 구성을 사용하기 전에 클라이언트 라이브러리가 AUTH 명령을 센티널 인스턴스로 보낼 수 있는지 확인하여야 한다.

센티널 클라이언트의 구현

  • 센티널은 새 마스터 인스턴스 (VIP 또는 비슷한 시스템) 에 대한 모든 요청을 리디렉션 하는 스크립트를 실행하도록 시스템을 구성하지 않는 한 명시적으로 클라이언트의 지원이 필요하다. 클라이언트 라이브러리의 구현 주체는 센티널 클라이언트 가이드 를 참고하면 된다.

More advanced concepts

이 장에서는 어떻게 센티널이 작동하는지에 대한 디테일에 대해 알아볼 것이다.

SDOWN, ODOWN 실패 상태

레디스 센티널은 서버 다운 에 대해 두 가지의 다른 개념을 가지고 있다. 하나는 주관적 다운(Subjectively Down) 이라 부르는 SDOWN이고, 이 다운 상태는 하나의 센티널 인스턴스가 가지고 있는 상태이다. 다른 하나는 객관적 다운(Objectively Down) 이라 불리는 ODOWN이고, 이는 충분한 센티널이 (적어도 모니터링하는 마스터에 대해 설정된 쿼럼 파라미터 이상을 만족하는 수) SDOWN 조건을 가지고 있을 때, 그리고 다른 센티널들에게 SENTINEL is-master-down-by-addr 커맨드를 받았을 때 발생한다.

센티널의 관점에서 is-master-down-after-milliseconds 파라미터에 지정된 구성 시간동안 PING에 대해 유효한 응답을 받지 못하면 SDOWN 조건에 도달하게 된다.

PING에 대한 유효한 반환값은 아래와 같다.

  • PING 명령어가 +PONG 명령어를 반환함.
  • PING 명령어가 -LOADING 명령어를 반환함.
  • PING 명령어가 -MASTERDOWN 명령어를 반환함.

다른 반환값(혹은 반환값이 없는 경우 포함)은 모두 유효하지 않다. 하지만 INFO 출력에서 자신을 슬레이브라고 표현하는 마스터는 다운된 것으로 고려한다.

SDOWN은 전체 구성된 간격 동안 유효한 반환값을 받지 못할 경우를 의미하므로, 인터벌이 30초인 인스턴스가 ping 반환값을 29초마다 답변한다고 하면 이 인스턴스는 작동중인 것이라고 간주한다.

SDOWN은 페일오버에 대한 충분한 트리거는 아니다. 이는 단지 하나의 센티널이 레디스 인스턴스가 유효하지 않다고 판단한 것을 의미한다. 페일오버를 위해서는 ODOWN 상태에 도달해야 한다.

SDOWN에서 ODOWN이 되기 위해서는 동의에 대한 어려운 게 아닌, 가십 형태의 알고리즘을 사용한다. 주어진 기간 안에 마스터가 작동을 하지 않는다는 리포트를 여러 센티널에서 파악했다는 리포트를 받으면 SDOWN에서 ODOWN으로 승격된다. 이 응답이 후에 사라진다면, 플래그가 초기화된다.

페일오버를 시작하기 위해 과반수를 이용한 엄격한 인증작업이 필요하지만, 페일오버는 ODOWN 상태에 도달하지 않고서는 시작될 수 없다. ODOWN 상태는 마스터에만 적용된다. 슬레이브와 같은 마스터가 아닌 인스턴스들이 작동하지 않을 때에 센티널은 SDOWN 형태로 인식하지만, ODOWN 상태로는 절대 도달하지 않는다.

SDOWN 은 하지만 의미적인 암시를 포함한다. 예를 들어 SDOWN 상태의 슬레이브는 센티널이 페일오버를 진행할 때 마스터로 승격되도록 선택되지 않는다.

센티널과 슬레이브의 자동 발견

센티널은 서로의 가용성을 상호 점검하고, 메시지를 교환하기 위해 다른 센티널과 연결되어 있다. 하지만 센티널은 레디스 인스턴스의 Pub/Sub 기능을 사용하여 동일한 마스터 및 슬레이브를 모니터링하는 다른 센티널을 검색하므로, 실행중인 모든 센티널 인스턴스에서 다른 센티널 주소 목록을 구성할 필요가 없다.

이 기능은 hello 라는 메시지를 __ sentinel __ :hello 라는 채널에 전송하면서 실행된다.

마찬가지로 센티널은 마스터에 연결된 슬레이브의 목록을 구성할 필요가 없다. 센티널이 자동으로 레디스에 쿼리하여 알아내기 때문이다.

  • 모든 센티널은 모니터링된 모든 마스터와 슬레이브의 Pub/Sub 채널 __ sentinel __ :hello 에 매 2초마다 ip, port, runid를 포함한 메시지를 발송한다.
  • 모든 센티널은 Pub/Sub 채널 __ sentinel __ : hello 를 구독하면서 마스터에 연결된 알지 못한 센티널에 대해 찾는다. 새로운 센티널을 발견하면, 이 마스터에 대한 센티널 목록에 해당 인스턴스를 추가한다.
  • Hello 메시지는 현재 마스터에 대한 정확한 전체 구성 정보를 포함한다. 이 정보를 수신한 센티널이 가지고 있는 마스터에 대한 데이터가 오래되었다면 이를 새로운 구성으로 바로 업데이트한다.
  • 새로운 센티널을 리스트에 추가하기 전에 항상 이 센티널이 이미 존재하는 인스턴스인지 같은 runid나 주소(ip와 port 쌍)를 확인한다. 이 경우에 같은 정보를 가지고 있던 센티널은 제거되고, 새로운 것이 등록된다.

인스턴스에 대한 센티널 재구성 (페일오버 프로시저 제외)

장애 조치가 진행중이지 않을 때에도 센티널은 항상 모니터링되는 인스턴스에 대해 현재의 구성으로 업데이트하려고 시도한다. 구체적으로는 다음과 같다.

  • 슬레이브 (현재 구성에 의한) 가 마스터가 되어야 한다고 주장할 때, 현재 마스터로 복제 할 슬레이브로 구성됨.
  • 잘못된 마스터에 연결된 슬레이브는 새로운 마스터의 데이터를 복제해 가도록 재구성됨.

센티널이 슬레이브를 재구성하려면, 새로운 구성을 브로드캐스트 하는 데에 사용되는 기간보다 긴 시간 동안 잘못된 구성을 발견하고 있어야 한다.

이렇지 않으면 센티널이 오래된 구성으로 인해 (에를 들어 방금 분리된 파티션 문제로 인해 다시 연결되었기 때문에) 업데이트를 받기 전에 슬레이브 구성을 변경하려고 시도할 수 있기 때문이다.

Also note how the semantics of always trying to impose the current configuration makes the failover more resistant to partitions:

  • 페일오버된 마스터는 그것들이 사용 가능해 졌을 때 슬레이브로 재구성됨.
  • 파티션중에 분할된 슬레이브는 도달할 수 있게 되면 재구성한다.

여기서 기억해야 할 중요한 교훈은 다음과 같다. 센티널은 각 프로세스가 항상 마지막 논리적 구성을 모니터링되는 인스턴스 세트에 적용하려고 하는 시스템이라는 것이다.

슬레이브 선출과 우선순위

마스터가 ODOWN 상태이고, 센티널이 과반수의 센티널으로부터 페일오버에 대한 권한을 얻어서 페일오버를 수행하기 위해 준비가 되었을 때, 센티널 인스턴스는 적당한 슬레이브를 선출해야 한다.

슬레이브 선출 프로세스는 슬레이브를 다음과 같은 정보들로 평가한다.

  1. 마스터로부터 분리된 시간
  2. 슬레이브 우선순위
  3. 복제 offset 진행시간
  4. run ID

마스터 타임아웃이 발생한 시간 (down-after-milliseconds) 의 10배의 시간에, 페일오버를 수행중인 센티널의 관점에서 마스터를 사용할 수 없는 시간을 더한 것보다 마스터로부터 연결이 해제된 시간이 큰 슬레이브는 페일오버에 적합하지 않은 것으로 간주되어 마스터로 선택받지 못하고 건너 뛰게 된다.

좀 더 엄격한 조건에서, INFO 출력으로 마스터로부터 연결을 할 수 없는 슬레이브를 다음 수식으로 구할 수 있다.

(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state

슬레이브는 위의 조건을 만족한 인스턴트만 고려하며, 기준에 따라 다음과 같은 순서로 정렬된다.

  1. 슬레이브는 레디스 인스턴스의 redis.conf 파일에 구성된 슬레이브 우선순위에 따라 정렬된다. 우선순위가 낮을수록 선호된다.
  2. 우선순위가 같다면, 슬레이브가 처리한 replication offset을 확인하여, 마스터로부터 더 많은 데이터를 받은 슬레이브가 선택된다.
  3. 다수의 슬레이브의 우선순위가 같고, 마스터에서 동일한 데이터를 받아왔다면, 사전순으로 runID가 작은 슬레이브를 선택한다. 작은 run ID를 갖는다는게 슬레이브에게 실질적인 이점은 없지만, 임의의 슬레이브를 선택하는 과정 없이 하나의 슬레이브를 바로 결정할 수 있다는 점에서 유용하다.

물리적으로 선호되는 서버가 있다면 마스터, 슬레이브 모두 slave-priority 변수를 구성해야 한다. 아니라면, 모든 인스턴스가 기본 실행ID 으로 실행될 수 있다 (?)

slave-priority 값이 0인 레디스 인스턴스는 절대 센티널에 의해 새로운 마스터로 선택되지 않는다. 이 차이만 빼면, 페일오버 이후 재구성된 마스터에 슬레이브로 붙는다는 사실은 차이가 없다.

Algorithm and internals

이 장에서는 센티널의 동작에 대해 자세히 알아본다. 사용자가 모든 세부 사항을 알고 있어야 하는건 아니지만, 센티널에 대한 깊은 이해는 센티널을 보다 효과적으로 배치하고 운영하는 데에 도움이 될 수 있을 것이다.

Quorum

이전 섹션에서 센티널에서 모니터링되어지는 모든 마스터는 쿼럼과 관련되어 있다는 것을 확인했다. 쿼럼은 마스터에 닿을 수 없거나, 장애상황에 처해 페일오버가 일어나야 한다고 동의하는 센티널 프로세스의 수를 명시적으로 설정한다.

하지만, 페일오버가 시작된 이후, 실제로 수행되기 위해서는, 센티널의 과반수 이상이 페일오버 권한을 가지고 있어야 한다. 소수의 센티널이 있는 파티션에서는 절대 슬레이브를 마스터로 승격시키지 않는다.

조금 더 명확하게 설명하자면 다음과 같다.

  • 쿼럼(Quroum): 마스터가 ODOWN으로 표시될 수 있도록 장애상태를 파악해야 하는 센티널 프로세스의 수.
  • 페일오버는 ODOWN 상태에 의해 트리거된다.
  • 페일오버가 트리거되면, 페일오버를 시도하는 센티널은 과반수 이상의 센티널에게 권한을 요청해야 한다.

차이는 미묘해보일 수 있지만, 실제로 이해하고 사용하기에는 사실 간단하다. 예를 들어 당신이 5개의 센티널 인스턴스를 가지고 있고, 쿼럼이 2일 때, 두개의 센티널이 마스터에 도달가능하지 않다고 인식하자마자 페일오버는 트리거될 것이다. 하지만, 두개의 센티널 중 권한을 가진 하나의 센티널에서만 페일오버가 가능하다.

쿼럼이 5로 구성된 경우, 모든 센티널에서 마스터가 장애상황이라는 것을 판단하고, 모든 센티널에게 권한을 받아야 한다.

이것은 쿼럼이 두 가지 방법으로 센티널을 조정하는 데에 사용될 수 있음을 의미한다.

1, 쿼럼이 센티널의 과반수보다 작게 설정된 경우, 기본적으로 센티널이 마스터의 장애에 더 민감해지도록 하여, 소수의 센티널이 마스터와 더이상 연결될 수 없도록 즉시 페일오버를 수행한다.

  1. 쿼럼이 센티널의 과반수보다 큰 값으로 설정되면, 마스터가 다운되는 것에 동의하는 연결된 센티널의 수가 많을 때에만 장애 조치를 수행할 수 있다.

Configuration epochs

센티널은 다음과 같은 몇 가지 중요한 이유로 인해 페일오버를 시작하기 위해 다수로부터 인증을 받아야 한다.

센티널이 인증되면, 페일오버된 마스터에 대한 고유한 구성 epoch를 얻는다. 이는 페일오버가 완료된 후, 새 구성의 버전을 지정하는데 사용되는 번호이다. 과반수 이상이 특정 버전이 특정 센티널에 할당되었다는 것을 동의했기 때문에, 다른 센티널이 이 번호를 사용할 수 없다. 이는 페일오버로 인한 모든 구성이 유니크한 버전이라는 것을 의미한다. 이게 얼마나 중요한지 알아보자.

센티널은 다음과 같은 규칙이 있다. 센티널이 특정 마스터의 페일 오버를 위해 다른 센티널을 투표하면, 해당 마스터를 페일오버하기 위해 잠시 기다린다. 이 시간을 failover-timeout 이라 하고, sentinel.conf 에서 구성할 수 있다.이는 동일한 마스터를 동시에 페일오버하지 않는다는 것을 의미하며, 먼저 권한이 있는지 묻는 첫 번째 시도 이후, 잠시 시간이 지난 후 두 번째 시도가 일어남을 의미한다.

레디스 센티널은 만약 마스터가 죽은 경우, 과반수 이상의 센티널이 연결 가능하고, 결국 한개의 센티널이 페일오버를 시도하여 liveness한 속성이 있다는 것을 보장한다.

또한, 센티널은 모든 센티널이 다른 구성 epoch를 사용하여 동일한 마스터를 페일오버할 수 있는 safety 속성을 보장한다.

Configuration propagation

센티널이 마스터를 성공적으로 페일오버시킬 수 있게 되면, 새로운 센티널은 주어진 마스터에 대한 정보를 업데이트 할 수 있도록 새로운 구성을 브로드캐스팅하기 시작한다.

페일오버가 성공한 것으로 간주되려면, 센티널이 선택된 슬레이브에게 SLAVE OF NO ONE 명령어를 보내야 하고, 해당 슬레이브가 마스터로 전환된 것을 나중에 INFO 명령어로 확인할 수 있어야 한다.

이 시점에서, 슬레이브의 재구성이 진행중이더라도, 페일오버는 성공적으로 수행된 것으로 고려되고, 모든 센티널은 새로운 구성을 보고받기 시작한다.

새로운 구성이 전파되는 방식은 모든 센티널의 페일오버가 다른 버전 넘버(구성 epoch)로 인증되어야 하는 이유이다.

모든 센티널은 마스터 및 모든 센티널에서 레디스 Pub/Sub 메시지를 사용하여 마스터 버전의 구성을 지속적으로 브로드캐스트한다. 동시에 모든 센티널은 다른 센티널이 어떤 구성으로 이루어져 있는지 보내는 메시지를 기다린다.

구성은 _sentinel__:hello 라는 Pub/Sub 채널에서 브로드캐스트된다.

모든 구성이 다른 버전번호를 가지고 있기 때문에, 큰 버전은 항상 작은 버전보다 우선된다.

예를 들어 마스터 mymaster의 구성은 마스터가 192.168.1.50:6379라고 생각하는 모든 센티널 에 의해 시작된다.이 구성을 버전 1이라 하자. 잠시 뒤, 센티널은 페일오버로 인해 버전 2로 구성된다. 페일오버가 성공적으로 완료되면 새로운 구성을 브로드캐스팅 하기 시작할 것이다. 버전 2의 새로운 마스터를 192.168.1.50:9000 이라 하자. 모든 다른 인스턴스는 이 구성을 보고, 새로운 구성의 버전정보가 더 높기 때문에 본인의 구성을 업데이트 할 것이다.

이것은 센티널이 두번째 liveness라는 속성을 보장한다는 것을 의미한다. 통신할 수 있는 센티널 세트는 모두 더 높은 버전 번호와 동일한 구성으로 수렴된다.

예를 들어, 마스터 mymaster의 구성은 마스터가 192.168.1.50:6379라고 생각하는 모든 Sentinels로 시작합니다. 이 구성에는 버전 1이 있습니다. 얼마 후 Sentinel이 버전 2로 페일 오버 할 수있는 권한이 부여됩니다. 페일 오버가 성공하면 버전 2를 사용하여 192.168.1.50:9000이라고 새 구성을 브로드 캐스트하기 시작합니다. 다른 모든 인스턴스 새 구성에는 더 큰 버전이 있으므로이 구성이 표시되고 그에 따라 구성이 업데이트됩니다.

기본적으로 네트워크가 파티션된 경우, 많은 파티션은 높은 로컬 구성으로 수렴된다. 파티션이 없는 특별한 경우에는 각각이 싱글 파티션이고, 모든 센티널이 해당 구성에 동의해야 한다.

Consistency under partitions

레디스 센티널 구성은 결국 일관적으로, 모든 파티션을 사용 가능한 상위 구성으로 수렴시키는 역할을 한다. 하지만 센티널을 사용하는 real-world 시스템에서는 세 가지의 다른 플레이어가 존재한다.

  • 레디스 인스턴스
  • 센티널 인스턴스
  • 클라이언트

시스템의 동작을 정의하려면 위의 셋을 모두 고려해야 한다.

다음은 각각 레디스 인스턴스와 센티널 인스턴스를 실행하는 세개의 노드가 있는 간단한 네트워크 구성이다.

            +-------------+
            | Sentinel 1  |----- Client A
            | Redis 1 (M) |
            +-------------+
                    |
                    |
+-------------+     |          +------------+
| Sentinel 2  |-----+-- // ----| Sentinel 3 |----- Client B
| Redis 2 (S) |                | Redis 3 (M)|
+-------------+                +------------+

이 시스템에서 원래 상태는 레디스 3이 마스터이고, 1과 2는 슬레이브였다. 이전 마스터와 슬레이브를 분리하는 파티션이 발생하여 센티널 1과 2는 레디스1을 새로운 마스터로 승격시키는 페일오버를 시작했다.

센티널은 센티널 1과 2가 이제 마스터에 대한 새로운 구성을 갖도록 한다. 하지만 센티널 3은 다른 파티션에 존재하기 때문에 오래된 구성을 유지한다.

네트워크 파티션이 회복되었을 때 센티널3의 구성이 업데이트된다는 것을 알고 있다. 하지만 이전 마스터에 붙어 있는 클라이언트가 있는 경우 파티션동안 어떤 일이 발생할까?

클라이언트는 계속 레디스 3에 쓸 수 있다. 파티션이 다시 결합되면 레디스 3은 레디스 1의 슬레이브로 바뀌고, 파티션 중에 작성된 모든 데이터는 손실된다.

구성에 따라 다음과 같은 시나리오를 원할 수도, 원하지 않을 수도 있다.

  • 레디스를 캐시로 사용하는 경우, 데이터가 손실되더라도 클라이언트 B가 여전히 이전 마스터에 쓸 수 있는 것이 편리할 수 있다.
  • 레디스를 저장소로 사용하는 경우,이는 좋지 않으므로 이 문제를 방지하기 위한 시스템을 구성해야 한다.

레디스는 비동기식으로 복제되므로, 시나리오에서는 데이터 손실을 완전히 방지할 방법이 없지만, 다음 레디스의 구성 옵션을 사용하여 레디스 3과 레디스 1의 차이를 좁힐 수 있다.

min-slaves-to-write 1
min-slaves-max-log 10

위의 구성을 통해, 레디스 인스턴스는 마스터로 작동할 때, 적어도 하나의 슬레이브에 쓸 수 없는 경우 쓰기 허용을 중지한다. 복제가 비동기적으로 쓸 수 없다는 것은 실제로 슬레이브의 연결이 끊어졌거나, 지정된 지연 시간동안 비동기 승인을 보내지 않았음을 의미한다.

이 구성을 사용하면 위의 예에서 레디스는 10초 후에 사용할 수 없게 된다. 파티션이 회복되면 센티널3이 새로운 구성을 받아들이고, 센티널 B는 유효한 구성을 받아와서 계속 진행할 수 있다.

일반적으로 레디스+센티널 조합은 마지막 페일오버의 구성으로 머지되는, 결론적으로 일관성있는 시스템이다. 그리고, 이전 마스터의 데이터는 버려지고, 새로운 마스터의 데이터를 복제해 온다. 따라서 인정된 쓰기를 잃을 수 있는 가능성이 항상 존재한다. 이는 레디스의 비동기식 복제와 시스템의 “가상” 머지 기능의 폐기 특성 때문이다. 이는 센티널에 국한된 문제가 아니며, 일관성을 중요시 여기는 복제 상태 시스템에서 페일오버가 발생할 때 같은 문제가 밠애할 수 있다. 승인 된 쓰기가 손실되는 것을 피할 수 있는 방법은 다음 두가지 뿐이다.

  1. 동기 복제 (혹은 적절한 일치 알고리즘을 사용하는 상태 시스템)을 사용
  2. 동일한 객체의 다른 버전을 머지할 수 있는 시스템을 사용

레디스는 현재 위의 시스템 중 하나를 사용할 수 없으며, 이는 개발 목표에 존재하지 않음. 그러나 Netflix Dynomite와 같은 레디스 솔루션에 2번을 구현하는 프록시가 존재한다.

Sentinel persistent state

센티널의 상태는 구성 파일에 유지된다. 예를 들어 마스터에 대해 새로운 구성이 수신되거나, 리더 센티널에 의해 작성될 때마다, 구성은 구성 epoch와 함께 디스크에 저장된다. 이는 센티널 프로세스를 중지하고 다시 시작하는 것이 안전하다는 것을 의미함.

TILT mode

레디스 센티널은 서버 자체의 시간에 크게 의존한다. 예를 들어 인스턴스가 사용 가능한지 이해하기 위해 PING 명령에 대한 최근의 성공적인 응답 시간을 기억하고, 현재 시간과 비교하여 얼마나 오래됐는지 이해한다.

하지만 서버 시간이 예기치 않은 방식으로 변경되거나, 서버 사용량이 많거나 어떤 이유로 프로세스가 차단된 경우, 센티널이 예기치 않은 방식으로 동작할 수 있다.

TILT 모드는 시스템의 안정성을 저하시킬 수 있는 이상한 것이 감지될 때, 센티널이 들어갈 수 있는 “보호” 모드이다. 센티널 타이머 인터럽트는 1초에 보통 10회 호출되므로, 타이머 인터럽트에 대한 두 호출 사이에 100 밀리초 이상이 소요될 것으로 예상한다.

센티널이 하는 일은 타이머 인터럽트가 호출된 이전 시간을 등록하고, 현재 호출과 비교하는 것이다. 시차가 음수이거나 예기치 않게 큰 경우(2초 이상), TILT 모드는 시작되고, 혹은 이미 TILT 모드인 경우 TILT 모드의 종료가 연기된다.

TILT 모드일 때, 센티널은 모든 것을 계속 모니터랑하지만, 다음과 같이 동작한다.

  • 모든 동작을 멈춘다.
  • 실패를 감지하는 기능이 신뢰되지 않으므로 SENTINEL is-master-down-by-addr 요청에 부정적으로 응답한다.

모든 것이 30초동안 정상인 것으로 보이면 TILT 모드가 종료된다.

어떤 식으로든 TILT 모드는 많은 커널이 제공하는 시계 API를 사용해서 대체될 수 있지만, 현재의 시스템이 스케줄러에 의해 오랫동안 일시 중단되거나, 실행되지 않는 경우의 문제를 피할 수 있는 프로세스이기 때문에 이게 좋은 해결책인지 확실하지 않다.