지난달 새벽 2시 43분에 알람이 울렸다. Redis primary 노드가 죽었다는 알람이었는데, 문제는 Sentinel이 자동 failover를 안 해줬다는 거였다. 5분 정도 쳐다보다가 결국 손으로 promote 시켰다. 서비스는 그 사이 캐시 미스 폭탄으로 DB 커넥션 풀이 다 터졌고, 우리 팀 슬랙에는 "왜 자동 failover가 안 되냐"는 질문이 5개쯤 쌓였다.
이 글은 그 원인을 찾아가는 과정과, 결국 뭐가 문제였는지에 대한 회고다.
처음 봤을 때
우리 구성은 이랬다. Redis 6.2에 primary 1대, replica 2대, Sentinel 3대. Sentinel은 Redis 노드와 같은 VM에 얹혀있었다 (사실 이게 나중에 원흉으로 밝혀진다). quorum은 2로 설정돼 있어서 이론상 Sentinel 2대만 살아있으면 failover가 트리거돼야 했다.
새벽에 눈 비비면서 접속해서 제일 먼저 확인한 게 이거였다.
$ redis-cli -h sentinel-01 -p 26379 SENTINEL ckquorum mymaster
(error) NOQUORUM 1 usable Sentinels. Not enough available Sentinels...
NOQUORUM이라는 문자열을 새벽에 보면 정신이 살짝 나간다. Sentinel이 세 대인데 왜 하나만 살아있다는 거야?
첫 번째 삽질: 네트워크부터 의심
일단 다른 Sentinel들이 살아있는지 봤다.
$ redis-cli -h sentinel-02 -p 26379 PING
PONG
$ redis-cli -h sentinel-03 -p 26379 PING
PONG
프로세스는 살아있다. 그럼 서로를 못 보는 건가 싶어서 확인.
$ redis-cli -h sentinel-01 -p 26379 SENTINEL sentinels mymaster
결과는... sentinel-01은 자기 자신만 알고 있었다. sentinel-02와 03은 목록에 없었다. 반대로 sentinel-02에서 조회하면 02와 03만 보이고 01은 없었다. 세 놈이 파벌을 나눠서 서로를 못 보고 있는 상태였다.
방화벽부터 의심했는데 nc -zv sentinel-02 26379가 다 잘 됐다. Port는 열려있다. 그럼 뭐지?
진짜 원인은 TILT였다
Sentinel 로그를 뒤지다가 발견한 게 이거였다.
+tilt #tilt mode entered
TILT 모드. Sentinel이 자기 자신의 상태를 신뢰할 수 없다고 판단하면 들어가는 모드다. 이 상태에서는 어떤 failover 결정도 내리지 않는다.
Redis 공식 문서에 따르면 TILT는 시스템 시계가 크게 점프하거나 Sentinel 프로세스가 잠시 멈췄다가 다시 실행됐을 때 트리거된다. 기본 지속 시간은 30초.
그럼 왜 TILT에 들어갔지? 로그를 더 뒤졌다.
14:32:11.892 - 14:32:47.109 - gap detected in monitoring loop
35초짜리 gap. Sentinel 이벤트 루프가 35초 동안 아무것도 못 했다는 소리다. 이 정도면 프로세스가 완전히 멈춘 거고, 원인은 뻔했다. 같은 VM에 얹혀있는 Redis primary가 fsync를 하느라 IO 대기가 걸렸고, Sentinel도 같이 물려서 이벤트 루프가 못 돌았다.
primary가 죽기 직전에 뭔가 큰 write가 몰렸고, BGREWRITEAOF가 돌면서 디스크가 완전히 잡혀버렸다. 그 상태에서 primary가 OOM으로 죽었는데, Sentinel도 이미 TILT 모드에 들어가 있어서 failover 결정을 못 했다. 30초 후에 TILT는 풀렸지만, 그때는 이미 다른 Sentinel들도 서로 통신이 밀리면서 상태가 꼬여있었다.
정리하면 원인은 세 겹이었다.
첫째, Sentinel을 Redis 노드와 같은 VM에 배치한 것. 리소스 격리가 안 됐다.
둘째, AOF fsync가 걸리면서 디스크 IO가 포화 상태였던 것.
셋째, 시스템 로드가 올라가면서 Sentinel 프로세스의 CPU quota가 밀렸고, 이벤트 루프에 gap이 생겨서 TILT 트리거.
그래서 뭘 바꿨나
당일 오전에 급하게 몇 가지를 정리했다.
Sentinel 분리. Sentinel 3대를 완전히 별도 VM으로 옮겼다. 사실 Redis 공식 문서에도 "Sentinel과 Redis를 같은 노드에 두지 말라"는 얘기가 있는데, 초기 세팅할 때 리소스 아낀다고 얹어놓은 게 이렇게 돌아왔다. 인프라 비용 몇 푼 아끼려다 새벽에 손으로 promote 하는 게 훨씬 비싸다.
down-after-milliseconds 조정. 기존에 5000ms로 잡혀있었는데, 이게 너무 짧으면 짧은 GC pause에도 down으로 판단해서 flapping이 생긴다. 30000ms로 늘렸다.
모니터링 추가. Sentinel의 TILT 진입 여부를 별도로 감시하는 알람을 붙였다. 기존엔 primary down만 봤는데, Sentinel이 판단 불능 상태에 빠지는 것도 사실상 장애의 전조다. 최근에 oneuptime 블로그에서도 비슷한 얘기를 봤는데, TILT 모니터링은 의외로 놓치기 쉬운 포인트라고 한다.
AOF everysec 유지, 하지만. appendfsync를 always로 바꿀까 고민했는데, 결국 everysec을 유지하기로 했다. 대신 디스크를 gp3에서 io2로 올려서 IOPS를 확보했다. 여기서 always는 오버킬이었다.
남은 질문들
솔직히 아직 100% 확신하지 못하는 부분이 있다. Sentinel을 완전히 분리했으니 같은 상황이 다시 재현되긴 어렵겠지만, 만약 Sentinel VM 자체에 문제가 생기면 어떻게 될까? 그래서 요즘은 Redis Cluster로 넘어가는 걸 고민 중이다. Sentinel은 분명 단순하고 좋은 도구지만, 우리처럼 트래픽이 커지는 팀에는 슬슬 한계가 느껴진다.
혹시 비슷한 상황 겪으신 분들, Sentinel 유지하시는지 Cluster로 넘어가셨는지 궁금하다. 아니면 KeyDB나 Dragonfly 같은 대안으로 넘어가신 분도 있을지.
'IT > DB 운영' 카테고리의 다른 글
| Postgres autovacuum, 내부는 어떻게 도는가 (0) | 2026.07.19 |
|---|---|
| idle_in_transaction_session_timeout, 이거 하나로 커넥션 풀이 살아난다 (0) | 2026.07.16 |
| PostgreSQL 17 autovacuum, 이제 maintenance_work_mem 상한 신경 안 써도 된다 (0) | 2026.07.12 |
| session pooling에서 transaction pooling으로 갈아탄 후 3주간 벌어진 일 (0) | 2026.07.05 |
| PgBouncer transaction vs session 모드, 뭘 쓸까 (0) | 2026.06.29 |