Tính sẵn sàng cao Redis với Sentinel
Redis là một phần mềm nguồn mở cung cấp khả năng lưu trữ dữ liệu trong RAM thường được sử dụng cho bộ nhớ đệm, trình trung chuyển tin nhắn và công cụ phát trực tuyến. Redis được sử dụng bởi các công cụ nổi bật khác:
- GitHub
- Snapchat
- danh sách Craigslist
- StackOverflow
Tùy từng trường hợp sử dụng mà Redis được sử dụng với các vai trò khác nhau trong một hệ thống. Và công ty của mình cũng vậy, Redis được sử dụng khá phổ biến trong hầu hết các ứng dụng. Vào một ngày đẹp trời, server Redis tự nhiên lăn ra thẳng, không thể truy cập và khôi phục lại được. Vậy là toàn bộ dữ liệu đang có trong Redis cứ thế không cánh mà bay.
Mất dữ liệu, dừng hoạt động của ứng dụng là những gì tôi phải đối mặt. Server bị trung thì có vô vàn lý do: loại trừ lý do chủ quan là code cùi, code lỗi, thì còn các lý do do khách quan mà không ai muốn xảy ra cả: các phần cứng bị lỗi, … Ở đây, mình đã xảy ra lỗi khi chạy cả ram trên máy chủ, không có bất kỳ ánh sáng nào cho bất kỳ hy vọng nào ở đây.
Sau công việc này, mình lại hì hục tìm cách làm thế nào để có thể cấu hình High Availability cho Redis để nhỡ có đen thì cũng sẽ lấy lại được những phần dữ liệu đã bị mất. Mình đã nghĩ đến Redis Replication
để có thể đồng bộ dữ liệu sang nhiều server khác nhau, để mà có đen server thì cũng vẫn còn lại dữ liệu. Tuy nhiên thì mình lại cần phải có Auto-Failover
nữa, vậy là mình đã tìm đến Redis Sentinel
. Vậy thì mình đã làm như thế nào và ứng dụng được gì?
1. Redis Sentinel là gì?
Redis Sentinel là một tính năng sử dụng cho việc phát triển Tính sẵn sàng cao cho Redis mà không cần thiết lập cụm. Theo mô tả thì đây là các tính năng của Sentinel:
- Giám sát. Sentinel liên tục kiểm tra xem phiên bản chính và bản sao của bạn có hoạt động như mong đợi hay không.
- Thông báo. Sentinel có thể thông báo cho quản trị viên hệ thống hoặc các chương trình máy tính khác thông qua API rằng có sự cố xảy ra với một trong các phiên bản Redis được giám sát.
- Tự động chuyển đổi dự phòng. Nếu bản gốc không hoạt động như mong đợi, Sentinel có thể bắt đầu quá trình chuyển đổi dự phòng trong đó bản sao được nâng cấp lên bản chính, các bản sao bổ sung khác được cấu hình lại để sử dụng bản gốc mới và các ứng dụng sử dụng máy chủ Redis sẽ được thông báo về địa chỉ mới sẽ sử dụng. - khi kết nối.
- Nhà cung cấp cấu hình. Sentinel đóng vai trò là nguồn cấp quyền cho việc khám phá dịch vụ của khách hàng: khách hàng kết nối với Sentinels để hỏi địa chỉ của chủ Redis hiện tại chịu trách nhiệm về một dịch vụ nhất định. Nếu xảy ra chuyển đổi dự phòng, Sentinels sẽ báo cáo địa chỉ mới.
2. Khai báo Mô hình phát triển
Về cơ bản thì để phát triển khai Sentinel cũng tương đối khá dễ dàng. Mình đã và đang phát triển Redis Sentinel với 3 server và Redis thì chạy trong Docker(cái này thì có nhiều lý do, và dựa vào các ưu tiên của container nên mình thường xuyên cố gắng đưa mọi thứ vào container, miễn là phù hợp nhảy lò cò).
trong đó, các nút có IP địa chỉ tương ứng như sau: Tên nút | Địa chỉ IP | ———-|———— redis-xpg72e62 | 20.10.161.181 | redis5-repl-1 | 20.10.161.80 | redis5-repl-2 | 20.10.161.111 |
3. Cấu hình
- Trên các nút tạo các thư mục sau
mkdir -p /var/log/redis /var/run/redis /var/lib/redis /etc/redis
Tạo cấu hình tệp
/etc/redis/redis.conf
để cấu hình sao chép lại cấu hình như sau:- Trên nút 1
activerehashing yes always-show-logo yes aof-load-truncated yes aof-rewrite-incremental-fsync yes aof-use-rdb-preamble yes appendfilename appendonly.aof appendfsync everysec appendonly no auto-aof-rewrite-min-size 64mb auto-aof-rewrite-percentage 100 client-output-buffer-limit normal 0 0 0 client-output-buffer-limit pubsub 32mb 8mb 60 daemonize no databases 16 dbfilename dump.rdb dir /var/lib/redis/data dynamic-hz yes hash-max-ziplist-entries 512 hash-max-ziplist-value 64 hll-sparse-max-bytes 3000 hz 10 latency-monitor-threshold 0 lazyfree-lazy-eviction no lazyfree-lazy-expire no lazyfree-lazy-server-del no list-compress-depth 0 list-max-ziplist-size -2 logfile /var/log/redis/redis.log loglevel notice lua-time-limit 5000 maxmemory 870mb no-appendfsync-on-rewrite no notify-keyspace-events '' pidfile /var/run/redis/redis.pid port 6379 protected-mode no rdb-save-incremental-fsync yes rdbchecksum yes rdbcompression yes repl-disable-tcp-nodelay no repl-diskless-sync yes repl-diskless-sync-delay 10 save 300 10 save 60 10000 save 900 1 set-max-intset-entries 512 slowlog-log-slower-than 10000 slowlog-max-len 128 stop-writes-on-bgsave-error yes stream-node-max-bytes 4096 stream-node-max-entries 100 supervised no tcp-backlog 511 tcp-keepalive 300 timeout 0 unixsocket /var/run/redis/redis.sock unixsocketperm 700 zset-max-ziplist-entries 128 zset-max-ziplist-value 64 requirepass <password> masterauth <password>
- Trên nút 2
activerehashing yes always-show-logo yes aof-load-truncated yes aof-rewrite-incremental-fsync yes aof-use-rdb-preamble yes appendfilename "appendonly.aof" appendfsync everysec appendonly no auto-aof-rewrite-min-size 64mb auto-aof-rewrite-percentage 100 client-output-buffer-limit normal 0 0 0 daemonize no databases 16 dbfilename "dump.rdb" dir "/var/lib/redis/data" dynamic-hz yes hash-max-ziplist-entries 512 hash-max-ziplist-value 64 hll-sparse-max-bytes 3000 hz 10 latency-monitor-threshold 0 lazyfree-lazy-eviction no lazyfree-lazy-expire no lazyfree-lazy-server-del no list-compress-depth 0 list-max-ziplist-size -2 logfile "/var/log/redis/redis.log" loglevel notice lua-time-limit 5000 maxmemory 870mb no-appendfsync-on-rewrite no notify-keyspace-events "" pidfile "/var/run/redis/redis.pid" port 6379 protected-mode no rdb-save-incremental-fsync yes rdbchecksum yes rdbcompression yes repl-disable-tcp-nodelay no repl-diskless-sync no repl-diskless-sync-delay 5 save 300 10 save 60 10000 save 900 1 set-max-intset-entries 512 slowlog-log-slower-than 10000 slowlog-max-len 128 stop-writes-on-bgsave-error yes stream-node-max-bytes 4096 stream-node-max-entries 100 supervised no tcp-backlog 511 tcp-keepalive 300 timeout 0 unixsocket "/var/run/redis/redis.sock" unixsocketperm 700 zset-max-ziplist-entries 128 zset-max-ziplist-value 64 requirepass <password> masterauth <password> min-replicas-max-lag 10 min-replicas-to-write 1 replica-lazy-flush no replica-priority 100 replica-read-only yes replica-serve-stale-data yes replicaof 10.20.161.181 6379
- Trên nút 3
activerehashing yes always-show-logo yes aof-load-truncated yes aof-rewrite-incremental-fsync yes aof-use-rdb-preamble yes appendfilename "appendonly.aof" appendfsync everysec appendonly no auto-aof-rewrite-min-size 64mb auto-aof-rewrite-percentage 100 client-output-buffer-limit normal 0 0 0 daemonize no databases 16 dbfilename "dump.rdb" dir "/var/lib/redis/data" dynamic-hz yes hash-max-ziplist-entries 512 hash-max-ziplist-value 64 hll-sparse-max-bytes 3000 hz 10 latency-monitor-threshold 0 lazyfree-lazy-eviction no lazyfree-lazy-expire no lazyfree-lazy-server-del no list-compress-depth 0 list-max-ziplist-size -2 logfile "/var/log/redis/redis.log" loglevel notice lua-time-limit 5000 maxmemory 870mb no-appendfsync-on-rewrite no notify-keyspace-events "" pidfile "/var/run/redis/redis.pid" port 6379 protected-mode no rdb-save-incremental-fsync yes rdbchecksum yes rdbcompression yes repl-disable-tcp-nodelay no repl-diskless-sync no repl-diskless-sync-delay 5 save 300 10 save 60 10000 save 900 1 set-max-intset-entries 512 slowlog-log-slower-than 10000 slowlog-max-len 128 stop-writes-on-bgsave-error yes stream-node-max-bytes 4096 stream-node-max-entries 100 supervised no tcp-backlog 511 tcp-keepalive 300 timeout 0 unixsocket "/var/run/redis/redis.sock" unixsocketperm 700 zset-max-ziplist-entries 128 zset-max-ziplist-value 64 requirepass <password> masterauth <password> min-replicas-max-lag 10 min-replicas-to-write 1 replica-lazy-flush no replica-priority 100 replica-read-only yes replica-serve-stale-data yes replicaof 10.20.161.181 6379
Lưu ý: Cần thay đổi địa chỉ IP tương ứng của bạn vào
Tạo cấu hình tệp
/etc/redis/sentinel.conf
để cấu hình Sentinel như sau:bash daemonize no dir /var/lib/redis/data logfile /var/log/redis/sentinel.log pidfile /var/run/redis/sentinel.pid port 26379 protected-mode no unixsocket /var/run/redis/sentinel.sock unixsocketperm 700 requirepass <password> sentinel myid b16319cdc0783a0bf3b49e3f60759c3a173cbe7b sentinel deny-scripts-reconfig no sentinel monitor s6l 10.20.161.181 6379 2 sentinel down-after-milliseconds s6l 10000 sentinel failover-timeout s6l 10000 sentinel auth-pass s6l <password> sentinel config-epoch s6l 0 sentinel leader-epoch s6l 0 sentinel known-replica s6l 10.20.161.111 6379 sentinel known-replica s6l 10.20.161.80 6379 sentinel current-epoch 0
- Trên nút 2
daemonize no dir "/var/lib/redis/data" logfile "/var/log/redis/sentinel.log" pidfile "/var/run/redis/sentinel.pid" port 26379 protected-mode no unixsocket "/var/run/redis/sentinel.sock" unixsocketperm 700 requirepass <password> sentinel myid 2a88d7cbe2c3ae42505b7ced061b84e53cd353ce sentinel deny-scripts-reconfig no sentinel monitor s6l 10.20.161.181 6379 2 sentinel down-after-milliseconds s6l 10000 sentinel failover-timeout s6l 10000 sentinel auth-pass s6l <password> sentinel leader-epoch s6l 0 sentinel known-replica s6l 10.20.161.80 6379 sentinel known-replica s6l 10.20.161.111 6379 sentinel current-epoch 0
- Trên nút 3
daemonize no dir "/var/lib/redis/data" logfile "/var/log/redis/sentinel.log" pidfile "/var/run/redis/sentinel.pid" port 26379 protected-mode no unixsocket "/var/run/redis/sentinel.sock" unixsocketperm 700 requirepass <password> sentinel myid e3e9b126a9a6b17bbf1ccadda0d05315abe1dc28 sentinel deny-scripts-reconfig no sentinel monitor s6l 10.20.161.181 6379 2 sentinel down-after-milliseconds s6l 10000 sentinel failover-timeout s6l 10000 sentinel auth-pass s6l <password> sentinel config-epoch s6l 0 sentinel leader-epoch s6l 0 sentinel known-replica s6l 10.20.161.111 6379 sentinel known-replica s6l 10.20.161.80 6379 sentinel current-epoch 0
Lưu ý: Cần thay đổi địa chỉ IP tương ứng của bạn vào
- Trên nút 1
- Tạo cơ sở dữ liệu vùng chứa trên các nút
Trên các nút tiến hành tạo các container như sau:
- Tạo container chạy Redis
bash docker create --name database --network host \ -v /dev/shm:/dev/shm \ -v /etc/redis/etc/redis \ -v /var/lib/redis:/var/lib/redis \ -v /var/log/redis:/var/log/redis \ -v /var/run/redis:/var/run/redis \ redis:5.0.14 /etc/redis/sentinel.conf
- Tạo container chạy Sentinel
bash docker create --name sentinel --network host \ -v /dev/shm:/dev/shm \ -v /etc/redis/etc/redis \ -v /var/lib/redis:/var/lib/redis \ -v /var/log/redis:/var/log/redis \ -v /var/run/redis:/var/run/redis \ redis:5.0.14 --sentinel /etc/redis/sentinel.conf
4. Kiểm tra kết quả
Thực hiện kết nối tới một máy chủ redis bất kỳ trên cổng 26379
redis-cli -h <ip_address> - p 26379 auth <password> info sentinel sentinel_masters:1 sentinel_tilt:0 sentinel_running_scripts:0 sentinel_scripts_queue_length:0 sentinel_simulate_failure_flags:0 master0:name=s6l,status=ok,address=10.20.161.181:6379,slaves=2,sentinels=4
Lúc này, chúng tôi có thể tắt nút chính để kiểm tra quá trình chuyển đổi dự phòng tự động.
Mã ví dụ trong python
import redis def connect_to_redis_sentinel(): """ Connects to Redis Sentinel. Returns: A Redis connection object. """ sentinel = redis.sentinel.Sentinel([ ('localhost', 26379), ('localhost', 26380), ('localhost', 26381), ]) master = sentinel.master_for('s6l') return redis.Redis(connection_pool=master) # Example usage: redis = connect_to_redis_sentinel() # Set a value in Redis: redis.set('my_key', 'my_value') # Get a value from Redis: value = redis.get('my_key') print(value)