개발놀이터

Redis Sentinel 도커 배포하기 본문

CS 지식/데이터베이스

Redis Sentinel 도커 배포하기

마늘냄새폴폴 2024. 7. 17. 21:24

대기열 만들기를 진행한게 벌써 10일 전이네요... Redis Sentinel 정말 골치아픈 녀석이었습니다. 

 

이번 포스팅에선 Redis Sentinel을 도커로 배포하면서 삽질했던 부분들을 정리하고자 글을 쓰게 되었습니다. 

 

한번 시작해보도록 하겠습니다. 

 

 

Redis Sentinel

Redis Sentinel은 Redis에서 제공해주는 HA (High Availability) 전략 중 하나입니다. Sentinel이라는 Redis 서버를 모니터링 하는 Redis Client를 두고 지속적으로 모니터링 하면서 Redis의 상태를 확인하고 장애에 대응하는 기능들을 제공합니다. 

 

Redis Sentinel에 대해서는 이번 포스팅에선 자세히 다루지 않도록 하겠습니다. 배포하는데 집중해보려고합니다. 자세한 내용은 아래의 링크에 설명이 되어있습니다. 

 

https://coding-review.tistory.com/472

 

Redis 장애시 RDBMS의 연쇄적인 장애에 대응하기 위한 전략

만약 캐싱 솔루션으로 Redis를 사용하고 있다면 Redis가 장애상황으로 죽어버리는 경우 RDBMS에 부하가 심하게 발생하여 RDBMS까지 연쇄적으로 장애가 발생하는 상황이 충분히 있을 수 있습니다. 저

coding-review.tistory.com

 

주의사항

제가 실습한 환경은 실제 상황이랑 많이 다른 환경입니다. 왜냐하면 Redis 서버와 Sentinel을 같은 서버에 둘 예정이기 때문이죠. 

 

때문에 제가 배포한 방법은 실제 환경에서는 딱히 중요하지 않을 수도 있습니다. 하지만 저는 클라우드 비용을 아끼기 위해 이 방법을 택했고 실전에선 배포 자체는 조금 더 수월할 것으로 예상됩니다. (하지만 관리가 힘들어지겠죠?)

 

위의 링크에도 자세히 설명되어 있지만 도커는 포트포워딩을 사용하기 때문에 Sentinel간 통신이 어려워 도커를 사용하기 위해서는 announce-ip과 announce-port 라는 설정을 해주어야합니다. 

 

하지만 저는 Redis 서버와 Redis Sentinel을 모두 같은 도커 네트워크로 묶어버렸기 때문에 넘어가는 부분이 몇개 있을 수 있습니다. 이 부분은 나중에 따로 포스팅을 하도록 하겠습니다. 

 

 

Sentinel 배포

먼저 docker-compose.yml 파일을 만들어줍니다. 

 

$ vim docker-compose.yml

 

version: '3.8'

services:
  redis-master:
    image: redis:latest
    command: redis-server
    container_name: "redis-master"
    networks:
      - redis-net

  redis-slave-1:
    image: redis:latest
    command: redis-server --slaveof redis-master 6379
    links:
      - redis-master
    container_name: "redis-slave1"
    networks:
      - redis-net

  redis-slave-2:
    image: redis:latest
    command: redis-server --slaveof redis-master 6379
    links:
      - redis-master
    container_name: "redis-slave2"
    networks:
      - redis-net

  sentinel-1:
    build: sentinel
    ports:
      - "5000:26379"
    env_file:
      - .env
    depends_on:
      - redis-master
      - redis-slave-1
      - redis-slave-2
    container_name: "sentinel1"
    networks:
      - redis-net

  sentinel-2:
    build: sentinel
    ports:
      - "5001:26379"
    env_file:
      - .env
    depends_on:
      - redis-master
      - redis-slave-1
      - redis-slave-2
    container_name: "sentinel2"
    networks:
      - redis-net

  sentinel-3:
    build: sentinel
    ports:
      - "5002:26379"
    env_file:
      - .env
    depends_on:
      - redis-master
      - redis-slave-1
      - redis-slave-2
    container_name: "sentinel3"
    networks:
      - redis-net
networks:
  redis-net:
    driver: bridge

 

뭐가 굉장히 많은데 주요한 부분만 보면 다음과 같습니다. 

 

  • Redis 컨테이너는 redis-server 명령어를 입력해야합니다. 때문에 command 설정을 통해 Redis를 구동했습니다.
  • --slaveof redis-master 6379 에서 redis-master는 마스터 노드의 컨테이너 이름입니다. 6379는 밖에 노출된 포트가 아닌 실제 구동되는 포트를 사용해야합니다. 

    도커 네트워크에서 bridge는 컨테이너들을 하나의 네트워크로 감싸는 역할을 해주고 이를 통해 컨테이너 이름을 DNS처럼 사용할 수 있습니다. 
  • links 설정을 통해 slave 노드와 master 노드를 연결시켜줘야합니다.
  • Sentinel은 sentinel이라는 폴더에 들어있는 Dockerfile을 이용해서 만들어줄 것이기 때문에 build 명령어를 사용했습니다. 
  • 환경설정 파일은 후에 작성할 것이고 .env 파일을 기반으로 환경설정을 할 것이라는 것을 env_file 명령어를 통해 알려줘야합니다. 
  • depends_on 명령어로 Sentinel이 어떤 Redis 서버를 모니터링할지 명시해줘야합니다. 

 

이제 순서대로 만들어보겠습니다. 

 

$ touch .env

$ vi .env

-- .env 파일 내부 --
SENTINEL_DOWN_AFTER=5000
SENTINEL_FAILOVER=500
SENTINEL_QUORUM=2

 

sentinel이라는 폴더를 만들고 안에 Dockerfile, sentinel.conf, entrypoint 파일을 만들어줍니다. 

 

$ mkdir sentinel

$ cd sentinel

$ touch Dockerfile

$ touch sentinel-entrypoint.sh

$ touch sentinel.conf

 

Dockerfile

FROM redis:latest

EXPOSE 26379

ADD sentinel.conf /etc/redis/sentinel.conf

RUN mkdir -p /var/lib/redis && \
    chmod 777 /var/lib/redis && \
    chown redis:redis /etc/redis/sentinel.conf

COPY sentinel-entrypoint.sh /usr/local/bin/

RUN chmod +x /usr/local/bin/sentinel-entrypoint.sh

ENTRYPOINT ["sentinel-entrypoint.sh"]

 

Dockerfile에선 Sentinel이 어떻게 빌드될 것인지를 결정하는데 Sentinel이 실행되기 위해 필요한 sentinel.conf를 이동시키고 권한을 부여해주고 entrypoint 파일을 실행시키는 것이 목표입니다. 

 

ENTRYPOINT 명령어를 이용해 Sentinel이 빌드되고 컨테이너가 동작한 다음 entrypoint 파일이 실행되는 것을 반드시 보장해줍니다. 

 

sentinel-entrypoint.sh

#!/bin/sh

sed -i "s/\$SENTINEL_QUORUM/$SENTINEL_QUORUM/g" /etc/redis/sentinel.conf
sed -i "s/\$SENTINEL_DOWN_AFTER/$SENTINEL_DOWN_AFTER/g" /etc/redis/sentinel.conf
sed -i "s/\$SENTINEL_FAILOVER/$SENTINEL_FAILOVER/g" /etc/redis/sentinel.conf

exec docker-entrypoint.sh redis-server /etc/redis/sentinel.conf --sentinel

 

entrypoint 파일에선 Sentinel의 설정을 적용하고 실행하는 역할을 합니다.

 

sentinel.conf

# Example sentinel.conf can be downloaded from http://download.redis.io/redis-stable/sentinel.conf

port 26379

dir /tmp

sentinel resolve-hostnames yes

sentinel monitor mymaster redis-master 6379 $SENTINEL_QUORUM

sentinel down-after-milliseconds mymaster $SENTINEL_DOWN_AFTER

sentinel parallel-syncs mymaster 1

sentinel failover-timeout mymaster $SENTINEL_FAILOVER

bind 0.0.0.0

 

굉장히 중요한 부분이 바로 sentinel resolve-hostnames yes 이부분입니다. 이게 없으면 그 다음 행에서 실행되는 sentinel monitor 명령어에서 redis-master를 찾을 수 없습니다. 여기서 redis-master는 마스터 노드의 컨테이너 이름입니다.

 

만약 redis-master를 찾을 수 없다면 마스터 노드와 레플리카가 서로 연동되지 않는 상황이 벌어져 절대 우리가 원하는 상황이 만들어지지 않습니다. 

 

이 설정때문에 며칠을 소모했는지 모르겠네요.. ㅜㅜ

 

아래의 링크에 자세한 설명이 나와있습니다. 

 

https://stackoverflow.com/questions/57464443/redis-sentinel-throws-error-cant-resolve-master-instance-hostname

 

redis-sentinel throws error: " Can't resolve master instance hostname."

I'm starting up redis and sentinel nodes using the below configurations. I start the redis node first and when I start Sentinel, if fails with the error: sentinel_node | sentinel_node | *** FATAL ...

stackoverflow.com

 

모든 준비가 끝났습니다. docker-compose.yml을 실행해주고 이제 확인단계입니다. 

 

꼭 확인해야 하는 세가지!

일단 모든 컨테이너가 떠있는건 당연하구요

 

 

1. 마스터 노드에 replication 설정이 잘 되어있는지 확인한다. 

$ docker exec -it redis-master redis-cli -p 6379

$ info replication

 

 

이렇게 슬레이브 노드가 등록되었는지 확인이 필요합니다. 

 

2. Sentinel에 마스터 노드와 슬레이브 노드, 그리고 다른 Sentinel들이 잘 설정되었는지 확인한다.

$ docker exec -it sentinel1 redis-cli -p 26379

$ sentinel masters

 

 

Sentinel 내부에서 마스터가 잘 등록되어있는지 슬레이브 노드가 잘 등록되어있는지, 다른 Sentinel이 등록되어있는지 확인합니다. 

 

3. 슬레이브 노드의 로그에서 마스터와 연결이 잘 되었는지 메세지를 확인하기

$ docker container logs redis-slave1

 

 

 

검증

실제로 동작하는지 확인해보겠습니다. 

 

우선 마스터 노드를 죽이고 Sentinel 내부 로그를 확인해보겠습니다. 

 

 

 

 

 

이런식으로 투표 과정을 거치는 것을 볼 수 있습니다. 

 

여기서 sdown은 subjectively down 즉, Sentinel이 주관적으로 마스터 노드가 다운되었다고 인지하고 마크를 남기는 것입니다. 이 sdown은 Sentinel이 마스터 노드에 ping을 보내고 pong이 오지 않으면 결정합니다.

 

또한, odown은 objectively down 즉, Sentinel이 객관적으로 봤을 때 마스터 노드가 죽었다고 판단한 경우입니다. 

 

이 odown이 마킹되면 Sentinel이 Pub/Sub 메세징을 통해 투표를 진행하고 새로운 마스터를 선정합니다. 

 

마치며

정말 오랫동안 골치를 썩었던 Redis Sentinel이 드디어 끝났습니다. Sentinel이 매력적이긴 하지만 정말 골치아프긴 하네요... 하지만 좋은 경험이 되었습니다. 

 

긴 글 읽어주셔서 감사합니다. 오늘도 즐거운 하루 되세요~