Chuyện xảy ra lúc 2 giờ sáng
Khoảng 3 năm trước, mình nhận được cảnh báo lúc 2 giờ sáng: toàn bộ tính năng giỏ hàng và session của hệ thống e-commerce bị treo. Nguyên nhân? Redis master chết do OOM killer, và không có gì tự động phục hồi cả. Dev team phải SSH vào tay promote replica lên master, cập nhật config ứng dụng, rồi restart service — mất gần 40 phút downtime.
Sau sự cố đó, mình bắt đầu nghiên cứu Redis Sentinel nghiêm túc. Bài này viết lại từ kinh nghiệm triển khai thực tế, không phải copy-paste từ docs.
Tại sao Redis standalone không đủ cho production?
Redis replication (master-replica) giải quyết được vấn đề đọc scale-out và backup, nhưng nó không tự xử lý failover. Khi master die:
- Các replica biết master down nhưng không tự promote
- Ứng dụng tiếp tục kết nối vào IP cũ → lỗi hàng loạt
- Cần can thiệp thủ công: promote replica, cập nhật connection string
So sánh nhanh: MongoDB có replica set tự elect primary, MySQL 8 có Group Replication, PostgreSQL dùng Patroni hoặc Repmgr. Redis cũng cần cơ chế tương tự — Sentinel chính là câu trả lời đó.
Redis Cluster là lựa chọn khác, nhưng đòi hỏi data sharding và setup phức tạp hơn đáng kể. Với phần lớn hệ thống (session, cache, pub/sub), Sentinel là đủ dùng — và dễ debug hơn rất nhiều khi có sự cố.
Redis Sentinel hoạt động như thế nào?
Sentinel là một tiến trình riêng biệt — không phải Redis server — chạy bên cạnh Redis. Bình thường nó chỉ ngồi theo dõi, không can thiệp gì. Khi bạn chạy nhiều Sentinel cùng nhau, chúng tạo thành quorum để đưa ra quyết định failover một cách phân tán.
Luồng hoạt động khi master die:
- Mỗi Sentinel liên tục ping master mỗi 1 giây
- Khi 1 Sentinel không nhận được phản hồi → đánh dấu master là
SDOWN(subjectively down) - Sentinel này hỏi các Sentinel khác — nếu đủ quorum đồng ý → nâng lên
ODOWN(objectively down) - Các Sentinel bầu chọn leader trong nhóm
- Sentinel leader chọn replica phù hợp nhất để promote thành master mới
- Cập nhật config tất cả replica còn lại để replication từ master mới
- Thông báo cho client qua pub/sub channel
Quorum cần số lẻ Sentinel, thường là 3. Với 2 Sentinel mà network partition xảy ra, cả 2 đều không thể đạt quorum một mình — deadlock, failover không chạy được.
Chuẩn bị môi trường: 3 server, 1 Sentinel cluster
Mô hình mình dùng thực tế trên production:
192.168.1.10— Redis Master + Sentinel 1192.168.1.11— Redis Replica 1 + Sentinel 2192.168.1.12— Redis Replica 2 + Sentinel 3
Cài Redis trên cả 3 server (Ubuntu 22.04):
sudo apt update && sudo apt install -y redis-server
sudo systemctl enable redis-server
Cấu hình Redis Master và Replica
Trên server master (192.168.1.10), chỉnh /etc/redis/redis.conf:
bind 0.0.0.0
protected-mode no
port 6379
requirepass "your_strong_password"
masterauth "your_strong_password"
Trên 2 server replica (192.168.1.11 và 192.168.1.12), thêm dòng replication:
bind 0.0.0.0
protected-mode no
port 6379
replicaof 192.168.1.10 6379
requirepass "your_strong_password"
masterauth "your_strong_password"
Restart Redis trên cả 3 server:
sudo systemctl restart redis-server
# Kiểm tra replication đã hoạt động chưa
redis-cli -a your_strong_password info replication
Output mong đợi trên master sẽ có role:master và connected_slaves:2.
Cấu hình Redis Sentinel
Tạo file config Sentinel trên cả 3 server. Lưu ý quan trọng: Sentinel sẽ tự ghi vào file này khi failover xảy ra, nên process cần có quyền ghi vào file:
sudo nano /etc/redis/sentinel.conf
Nội dung file — giống nhau trên cả 3 server, chỉ đổi sentinel announce-ip thành IP của từng server:
port 26379
daemonize yes
logfile /var/log/redis/sentinel.log
pidfile /var/run/redis/redis-sentinel.pid
# Đặt tên cluster theo tên project cho dễ nhớ
sentinel monitor mymaster 192.168.1.10 6379 2
sentinel auth-pass mymaster your_strong_password
# Sau bao nhiêu ms không phản hồi thì coi là down
sentinel down-after-milliseconds mymaster 5000
# Số replica được sync lại cùng lúc sau failover (nên để 1)
sentinel parallel-syncs mymaster 1
# Timeout failover (ms)
sentinel failover-timeout mymaster 10000
# Khai báo IP thực của server này — bắt buộc trong môi trường NAT/Docker
sentinel announce-ip 192.168.1.10
sentinel announce-port 26379
Khởi động Sentinel:
sudo redis-sentinel /etc/redis/sentinel.conf
# Hoặc dùng systemd (khuyên dùng cho production)
sudo nano /etc/systemd/system/redis-sentinel.service
[Unit]
Description=Redis Sentinel
After=network.target
[Service]
Type=forking
ExecStart=/usr/bin/redis-sentinel /etc/redis/sentinel.conf
PIDFile=/var/run/redis/redis-sentinel.pid
Restart=always
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now redis-sentinel
Kiểm tra Sentinel hoạt động
# Kết nối vào Sentinel
redis-cli -p 26379
# Xem thông tin cluster
127.0.0.1:26379> SENTINEL masters
127.0.0.1:26379> SENTINEL replicas mymaster
127.0.0.1:26379> SENTINEL sentinels mymaster
# Lấy địa chỉ master hiện tại (ứng dụng dùng câu này)
127.0.0.1:26379> SENTINEL get-master-addr-by-name mymaster
Test failover thực tế
Bước này mình luôn làm ngay sau khi setup xong — không bao giờ đưa vào production mà chưa test failover một lần:
# Trên server master, kill Redis
sudo systemctl stop redis-server
# Theo dõi log Sentinel trên server khác
tail -f /var/log/redis/sentinel.log
# Sau ~10-15 giây, check master mới
redis-cli -p 26379 SENTINEL get-master-addr-by-name mymaster
Nếu mọi thứ đúng, log Sentinel sẽ ghi lại toàn bộ quá trình bầu chọn và promote. Master mới sẽ là 192.168.1.11 hoặc 192.168.1.12, tùy replica nào có replication offset cao hơn tại thời điểm đó.
Kết nối ứng dụng qua Sentinel
Đây là điểm nhiều người hay nhầm: ứng dụng không kết nối trực tiếp vào Redis master. Client hỏi Sentinel “master hiện tại ở đâu?”, rồi mới kết nối vào đó. Khi failover xảy ra, client tự discover master mới — không cần restart ứng dụng.
Python với redis-py:
from redis.sentinel import Sentinel
sentinel = Sentinel(
[
('192.168.1.10', 26379),
('192.168.1.11', 26379),
('192.168.1.12', 26379),
],
socket_timeout=0.5,
password='your_strong_password'
)
# Lấy master connection (ghi)
master = sentinel.master_for('mymaster', socket_timeout=0.5)
# Lấy replica connection (đọc)
replica = sentinel.slave_for('mymaster', socket_timeout=0.5)
master.set('key', 'value')
print(replica.get('key'))
Node.js với ioredis:
const Redis = require('ioredis');
const redis = new Redis({
sentinels: [
{ host: '192.168.1.10', port: 26379 },
{ host: '192.168.1.11', port: 26379 },
{ host: '192.168.1.12', port: 26379 },
],
name: 'mymaster',
password: 'your_strong_password',
sentinelPassword: 'your_strong_password',
});
Những lỗi mình từng gặp
Một số pitfall thực tế khi vận hành Sentinel:
- Quên set
masterauth: Khi failover, replica mới promote lên master nhưng replica còn lại không replication được vì thiếu auth. Triệu chứng hay thấy: replication lag tăng vọt, data giữa các node bắt đầu lệch nhau. Luôn set cảrequirepasslẫnmasterauthtrên mọi node. - Sentinel không announce IP: Trong môi trường NAT hoặc Docker, Sentinel tự report IP nội bộ sai — client không kết nối được sau failover. Luôn set
sentinel announce-ipvới IP thực tế mà client có thể reach được. - File sentinel.conf bị locked quyền: Sentinel cần ghi vào file config khi failover. Nếu file thuộc quyền root nhưng Sentinel chạy dưới user
redis, failover thất bại im lặng — không có error rõ ràng, chỉ thấy timeout rồi tự recover. - down-after-milliseconds quá thấp: Trên môi trường cloud, network latency đôi khi spike lên 500ms-1s. Giá trị 1000ms có thể trigger false positive failover. Mình thường để 5000ms trên production.
Monitoring Sentinel trong production
Tích hợp Prometheus thông qua redis_exporter — nó hỗ trợ Sentinel mode sẵn. Nếu chưa có Prometheus stack, script bash check định kỳ là đủ dùng:
#!/bin/bash
MASTER=$(redis-cli -p 26379 SENTINEL get-master-addr-by-name mymaster | head -1)
echo "Current master: $MASTER"
# Alert nếu không lấy được master
if [ -z "$MASTER" ]; then
echo "ALERT: Cannot determine Redis master!" | mail -s "Redis Sentinel Alert" [email protected]
fi
Redis Sentinel không giải quyết được mọi bài toán — với data lớn và cần sharding thực sự, Redis Cluster mới là bước tiếp theo. Nhưng với 90% use case thông thường (session, cache, queue), Sentinel đủ mạnh, đủ đơn giản để debug lúc nửa đêm, và quan trọng nhất: nó sẽ không khiến bạn bị gọi điện lúc 2 giờ sáng nữa.

