Docker 部署 Redis 集群完整指南
Spring Cloud全栈实战:手撸企业级项目,从入门到架构师!
一、Redis 集群架构设计
Spring Cloud全栈实战:手撸企业级项目,从入门到架构师!Spring Cloud全栈实战:手撸企业级项目,从入门到架构师!
1.1 集群架构
┌─────────────────────────────────────────────────────────────────────┐ │ Redis Cluster (6节点) │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ Master1(7001) ──── Slave1(7004) Master2(7002) ──── Slave2(7005) │ │ │ │ │ │ │ │ ───────┴──────────────────┴──────────────┴──────────────────┴──────│ │ │ │ Master3(7003) ──── Slave3(7006) │ │ │ └─────────────────────────────────────────────────────────────────────┘二、Docker Compose 部署方案
2.1 docker-compose.yml
version:'3.8'services:# Redis 节点 1 (主节点)redis-node-1:image:redis:7.2-alpinecontainer_name:redis-cluster-node-1command:>redis-server --bind 0.0.0.0 --port 6379 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes --appendfsync everysec --save 900 1 --save 300 10 --save 60 10000 --requirepass ${REDIS_PASSWORD:-Redis123456} --masterauth ${REDIS_PASSWORD:-Redis123456} --maxmemory 2gb --maxmemory-policy allkeys-lru --loglevel notice --logfile /data/redis.log --protected-mode noports:-"7001:6379"-"17001:16379"# 集群总线端口volumes:-redis-data-1:/data-./redis.conf:/usr/local/etc/redis/redis.conf-./logs/redis-node-1:/var/log/redisenvironment:-REDIS_PORT=6379-REDIS_PASSWORD=${REDIS_PASSWORD:-Redis123456}-REDIS_CLUSTER_REPLICAS=1networks:redis-cluster-network:ipv4_address:172.20.0.11healthcheck:test:["CMD","redis-cli","-a","${REDIS_PASSWORD:-Redis123456}","ping"]interval:30stimeout:10sretries:3start_period:40sdeploy:resources:limits:memory:2Greservations:memory:1Grestart:unless-stopped# Redis 节点 2 (主节点)redis-node-2:image:redis:7.2-alpinecontainer_name:redis-cluster-node-2command:>redis-server --bind 0.0.0.0 --port 6379 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes --appendfsync everysec --save 900 1 --save 300 10 --save 60 10000 --requirepass ${REDIS_PASSWORD:-Redis123456} --masterauth ${REDIS_PASSWORD:-Redis123456} --maxmemory 2gb --maxmemory-policy allkeys-lru --loglevel notice --logfile /data/redis.log --protected-mode noports:-"7002:6379"-"17002:16379"volumes:-redis-data-2:/data-./redis.conf:/usr/local/etc/redis/redis.conf-./logs/redis-node-2:/var/log/redisenvironment:-REDIS_PORT=6379-REDIS_PASSWORD=${REDIS_PASSWORD:-Redis123456}networks:redis-cluster-network:ipv4_address:172.20.0.12healthcheck:test:["CMD","redis-cli","-a","${REDIS_PASSWORD:-Redis123456}","ping"]interval:30stimeout:10sretries:3start_period:40srestart:unless-stopped# Redis 节点 3 (主节点)redis-node-3:image:redis:7.2-alpinecontainer_name:redis-cluster-node-3command:>redis-server --bind 0.0.0.0 --port 6379 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes --appendfsync everysec --save 900 1 --save 300 10 --save 60 10000 --requirepass ${REDIS_PASSWORD:-Redis123456} --masterauth ${REDIS_PASSWORD:-Redis123456} --maxmemory 2gb --maxmemory-policy allkeys-lru --loglevel notice --logfile /data/redis.log --protected-mode noports:-"7003:6379"-"17003:16379"volumes:-redis-data-3:/data-./redis.conf:/usr/local/etc/redis/redis.conf-./logs/redis-node-3:/var/log/redisenvironment:-REDIS_PORT=6379-REDIS_PASSWORD=${REDIS_PASSWORD:-Redis123456}networks:redis-cluster-network:ipv4_address:172.20.0.13healthcheck:test:["CMD","redis-cli","-a","${REDIS_PASSWORD:-Redis123456}","ping"]interval:30stimeout:10sretries:3start_period:40srestart:unless-stopped# Redis 节点 4 (从节点)redis-node-4:image:redis:7.2-alpinecontainer_name:redis-cluster-node-4command:>redis-server --bind 0.0.0.0 --port 6379 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes --appendfsync everysec --save 900 1 --save 300 10 --save 60 10000 --requirepass ${REDIS_PASSWORD:-Redis123456} --masterauth ${REDIS_PASSWORD:-Redis123456} --maxmemory 2gb --maxmemory-policy allkeys-lru --loglevel notice --logfile /data/redis.log --protected-mode noports:-"7004:6379"-"17004:16379"volumes:-redis-data-4:/data-./redis.conf:/usr/local/etc/redis/redis.conf-./logs/redis-node-4:/var/log/redisenvironment:-REDIS_PORT=6379-REDIS_PASSWORD=${REDIS_PASSWORD:-Redis123456}networks:redis-cluster-network:ipv4_address:172.20.0.14healthcheck:test:["CMD","redis-cli","-a","${REDIS_PASSWORD:-Redis123456}","ping"]interval:30stimeout:10sretries:3start_period:40srestart:unless-stopped# Redis 节点 5 (从节点)redis-node-5:image:redis:7.2-alpinecontainer_name:redis-cluster-node-5command:>redis-server --bind 0.0.0.0 --port 6379 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes --appendfsync everysec --save 900 1 --save 300 10 --save 60 10000 --requirepass ${REDIS_PASSWORD:-Redis123456} --masterauth ${REDIS_PASSWORD:-Redis123456} --maxmemory 2gb --maxmemory-policy allkeys-lru --loglevel notice --logfile /data/redis.log --protected-mode noports:-"7005:6379"-"17005:16379"volumes:-redis-data-5:/data-./redis.conf:/usr/local/etc/redis/redis.conf-./logs/redis-node-5:/var/log/redisenvironment:-REDIS_PORT=6379-REDIS_PASSWORD=${REDIS_PASSWORD:-Redis123456}networks:redis-cluster-network:ipv4_address:172.20.0.15healthcheck:test:["CMD","redis-cli","-a","${REDIS_PASSWORD:-Redis123456}","ping"]interval:30stimeout:10sretries:3start_period:40srestart:unless-stopped# Redis 节点 6 (从节点)redis-node-6:image:redis:7.2-alpinecontainer_name:redis-cluster-node-6command:>redis-server --bind 0.0.0.0 --port 6379 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes --appendfsync everysec --save 900 1 --save 300 10 --save 60 10000 --requirepass ${REDIS_PASSWORD:-Redis123456} --masterauth ${REDIS_PASSWORD:-Redis123456} --maxmemory 2gb --maxmemory-policy allkeys-lru --loglevel notice --logfile /data/redis.log --protected-mode noports:-"7006:6379"-"17006:16379"volumes:-redis-data-6:/data-./redis.conf:/usr/local/etc/redis/redis.conf-./logs/redis-node-6:/var/log/redisenvironment:-REDIS_PORT=6379-REDIS_PASSWORD=${REDIS_PASSWORD:-Redis123456}networks:redis-cluster-network:ipv4_address:172.20.0.16healthcheck:test:["CMD","redis-cli","-a","${REDIS_PASSWORD:-Redis123456}","ping"]interval:30stimeout:10sretries:3start_period:40srestart:unless-stopped# Redis Cluster 初始化服务redis-cluster-init:image:redis:7.2-alpinecontainer_name:redis-cluster-initdepends_on:-redis-node-1-redis-node-2-redis-node-3-redis-node-4-redis-node-5-redis-node-6networks:-redis-cluster-networkcommand:>sh -c " echo '等待Redis节点启动...'; sleep 30;echo '检查节点健康状态...'; for i in $$(seq 1 6); do redis-cli-h 172.20.0.1$$i-p 6379-a ${REDIS_PASSWORD:-Redis123456}ping 2>/dev/null||echo '节点 $$i 未就绪'; done; echo '创建Redis集群...'; echo 'yes'|redis-cli--cluster create 172.20.0.11:6379172.20.0.12:6379172.20.0.13:6379172.20.0.14:6379172.20.0.15:6379172.20.0.16:6379--cluster-replicas 1-a ${REDIS_PASSWORD:-Redis123456}; echo '验证集群状态...'; redis-cli--cluster check 172.20.0.11:6379-a ${REDIS_PASSWORD:-Redis123456}; echo '集群信息:'; redis-cli-h 172.20.0.11-p 6379-a ${REDIS_PASSWORD:-Redis123456}cluster info; echo '节点信息:'; redis-cli-h 172.20.0.11-p 6379-a ${REDIS_PASSWORD:-Redis123456}cluster nodes; echo 'Redis集群初始化完成!'; "restart:on-failure# Redis 集群管理工具redis-commander:image:rediscommander/redis-commander:latestcontainer_name:redis-cluster-webports:-"8081:8081"environment:-REDIS_HOSTS=local:redis-node-1:6379:0:Redis123456,local:redis-node-2:6379:0:Redis123456,local:redis-node-3:6379:0:Redis123456-HTTP_USER=admin-HTTP_PASSWORD=${REDIS_PASSWORD:-Redis123456}-REDIS_PORT=6379-REDIS_PASSWORD=${REDIS_PASSWORD:-Redis123456}depends_on:-redis-cluster-initnetworks:-redis-cluster-networkrestart:unless-stopped# Redis 监控工具redis-exporter:image:oliver006/redis_exporter:latestcontainer_name:redis-cluster-exporterports:-"9121:9121"command:-'--redis.addr=redis://redis-node-1:6379'-'--redis.addr=redis://redis-node-2:6379'-'--redis.addr=redis://redis-node-3:6379'-'--redis.password=${REDIS_PASSWORD:-Redis123456}'-'--log-format=json'-'--namespace=redis_cluster'environment:-REDIS_PASSWORD=${REDIS_PASSWORD:-Redis123456}depends_on:-redis-cluster-initnetworks:-redis-cluster-networkrestart:unless-stoppednetworks:redis-cluster-network:driver:bridgeipam:config:-subnet:172.20.0.0/16gateway:172.20.0.1volumes:redis-data-1:driver:localredis-data-2:driver:localredis-data-3:driver:localredis-data-4:driver:localredis-data-5:driver:localredis-data-6:driver:local2.2 环境变量配置 (.env)
Spring Cloud全栈实战:手撸企业级项目,从入门到架构师!
# Redis 集群环境变量配置REDIS_PASSWORD=Redis123456@ClusterREDIS_CLUSTER_NAME=production-clusterREDIS_MAXMEMORY=2gbREDIS_TIMEOUT=5000REDIS_APPENDONLY=yesREDIS_APPENDFSYNC=everysec# 网络配置REDIS_CLUSTER_SUBNET=172.20.0.0/16REDIS_CLUSTER_GATEWAY=172.20.0.1# 监控配置PROMETHEUS_ENABLED=trueGRAFANA_ENABLED=true# 端口映射REDIS_PORT_START=7001REDIS_CLUSTER_BUS_PORT_START=17001REDIS_COMMANDER_PORT=8081REDIS_EXPORTER_PORT=9121三、自定义 Redis 配置文件
3.1 redis.conf
# Redis 集群配置文件 # 基于 Redis 7.2 版本 # 网络配置 bind 0.0.0.0 protected-mode no port 6379 tcp-backlog 511 timeout 0 tcp-keepalive 300 # 常规配置 daemonize no pidfile /var/run/redis_6379.pid loglevel notice logfile /data/redis.log databases 16 always-show-logo yes set-proc-title yes proc-title-template "{title} {listen-addr} {server-mode}" # 快照配置 save 900 1 save 300 10 save 60 10000 stop-writes-on-bgsave-error yes rdbcompression yes rdbchecksum yes dbfilename dump.rdb rdb-del-sync-files no dir /data # 主从复制配置 replica-serve-stale-data yes replica-read-only yes repl-diskless-sync no repl-diskless-sync-delay 5 repl-diskless-load disabled repl-disable-tcp-nodelay no replica-priority 100 acllog-max-len 128 # 安全配置 requirepass ${REDIS_PASSWORD} masterauth ${REDIS_PASSWORD} # 客户端配置 maxclients 10000 maxmemory 2gb maxmemory-policy allkeys-lru maxmemory-samples 5 lazyfree-lazy-eviction no lazyfree-lazy-expire no lazyfree-lazy-server-del no replica-lazy-flush no lazyfree-lazy-user-del no oom-score-adj no oom-score-adj-values 0 200 800 # 持久化配置 appendonly yes appendfilename "appendonly.aof" appendfsync everysec no-appendfsync-on-rewrite no auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb aof-load-truncated yes aof-use-rdb-preamble yes # Lua 脚本配置 lua-time-limit 5000 # 慢查询日志 slowlog-log-slower-than 10000 slowlog-max-len 128 latency-monitor-threshold 0 # 事件通知 notify-keyspace-events "" # 高级配置 hash-max-ziplist-entries 512 hash-max-ziplist-value 64 list-max-ziplist-size -2 list-compress-depth 0 set-max-intset-entries 512 zset-max-ziplist-entries 128 zset-max-ziplist-value 64 hll-sparse-max-bytes 3000 stream-node-max-bytes 4096 stream-node-max-entries 100 activerehashing yes client-output-buffer-limit normal 0 0 0 client-output-buffer-limit replica 256mb 64mb 60 client-output-buffer-limit pubsub 32mb 8mb 60 hz 10 dynamic-hz yes aof-rewrite-incremental-fsync yes rdb-save-incremental-fsync yes jemalloc-bg-thread yes # 集群配置 cluster-enabled yes cluster-config-file /data/nodes.conf cluster-node-timeout 5000 cluster-replica-validity-factor 10 cluster-migration-barrier 1 cluster-require-full-coverage yes cluster-replica-no-failover no cluster-allow-reads-when-down no cluster-allow-pubsubshard-when-down no cluster-link-sendbuf-limit 0 cluster-announce-ip ${HOST_IP} cluster-announce-port 6379 cluster-announce-bus-port 16379 # 模块配置 loadmodule /usr/lib/redis/modules/redisearch.so loadmodule /usr/lib/redis/modules/rejson.so loadmodule /usr/lib/redis/modules/redisgraph.so loadmodule /usr/lib/redis/modules/redistimeseries.so loadmodule /usr/lib/redis/modules/redisbloom.so # TLS/SSL 配置 # tls-port 0 # tls-cert-file /etc/redis/certs/redis.crt # tls-key-file /etc/redis/certs/redis.key # tls-ca-cert-file /etc/redis/certs/ca.crt # tls-auth-clients no # tls-auth-clients optional # tls-protocols "TLSv1.2 TLSv1.3" # tls-ciphers DEFAULT:!MEDIUM # tls-ciphersuites TLS_CHACHA20_POLY1305_SHA256 # tls-prefer-server-ciphers yes # tls-session-caching no # tls-session-cache-size 20480 # tls-session-cache-timeout 300四、启动和部署脚本
Spring Cloud全栈实战:手撸企业级项目,从入门到架构师!
4.1 启动脚本 (start-cluster.sh)
#!/bin/bash# Redis Cluster 启动脚本# 支持 Docker 和 Docker Compose 部署set-e# 颜色定义RED='\033[0;31m'GREEN='\033[0;32m'YELLOW='\033[1;33m'BLUE='\033[0;34m'NC='\033[0m'# No Color# 日志函数log_info(){echo-e"${BLUE}[INFO]${NC}$1"}log_success(){echo-e"${GREEN}[SUCCESS]${NC}$1"}log_warn(){echo-e"${YELLOW}[WARN]${NC}$1"}log_error(){echo-e"${RED}[ERROR]${NC}$1"}# 检查依赖check_dependencies(){log_info"检查系统依赖..."# 检查 Dockerif!command-v docker&>/dev/null;thenlog_error"Docker 未安装"exit1fi# 检查 Docker Composeif!command-v docker-compose&>/dev/null;thenlog_error"Docker Compose 未安装"exit1filog_success"依赖检查通过"}# 创建目录结构create_directories(){log_info"创建目录结构..."mkdir-p ./data/{node1,node2,node3,node4,node5,node6}mkdir-p ./logs/{node1,node2,node3,node4,node5,node6}mkdir-p ./configmkdir-p ./ssl log_success"目录创建完成"}# 生成配置文件generate_configs(){log_info"生成 Redis 配置文件..."# 生成主配置文件cat>./config/redis.conf<<'EOF' # Redis 集群配置文件 bind 0.0.0.0 port 6379 cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 5000 appendonly yes requirepass ${REDIS_PASSWORD} masterauth ${REDIS_PASSWORD} EOF# 生成节点配置文件foriin{1..6};docat>./config/redis-node-$i.conf<<EOF # Redis 节点$i配置 bind 0.0.0.0 port 6379 cluster-enabled yes cluster-config-file /data/nodes.conf cluster-node-timeout 5000 appendonly yes appendfilename "appendonly-node-$i.aof" dbfilename "dump-node-$i.rdb" dir /data requirepass${REDIS_PASSWORD:-Redis123456}masterauth${REDIS_PASSWORD:-Redis123456}maxmemory 2gb maxmemory-policy allkeys-lru loglevel notice logfile /var/log/redis/redis.log protected-mode no EOFdonelog_success"配置文件生成完成"}# 启动 Redis 集群start_cluster(){log_info"启动 Redis 集群..."# 加载环境变量if[-f .env];thenlog_info"加载环境变量..."export$(grep-v'^#'.env|xargs)fi# 启动集群docker-compose up -d# 等待集群启动log_info"等待集群启动..."sleep10# 检查集群状态check_cluster_health log_success"Redis 集群启动完成"}# 检查集群健康状态check_cluster_health(){log_info"检查集群健康状态..."localretries=10localwait_time=5for((i=1;i<=retries;i++));dolog_info"尝试$i/$retries检查集群状态..."# 检查节点是否可访问ifdockerexecredis-cluster-node-1 redis-cli -a"${REDIS_PASSWORD:-Redis123456}"ping2>/dev/null|grep-q"PONG";then# 获取集群信息localcluster_info=$(dockerexecredis-cluster-node-1 redis-cli -a"${REDIS_PASSWORD:-Redis123456}"cluster info2>/dev/null)ifecho"$cluster_info"|grep-q"cluster_state:ok";thenlog_success"集群状态正常"# 显示集群信息echo"========================================"echo"Redis 集群信息:"echo"========================================"dockerexecredis-cluster-node-1 redis-cli -a"${REDIS_PASSWORD:-Redis123456}"cluster infoecho"========================================"echo"集群节点:"dockerexecredis-cluster-node-1 redis-cli -a"${REDIS_PASSWORD:-Redis123456}"cluster nodesecho"========================================"return0fifiif[$i-lt$retries];thenlog_info"等待${wait_time}秒后重试..."sleep$wait_timefidonelog_error"集群健康检查失败"return1}# 停止集群stop_cluster(){log_info"停止 Redis 集群..."docker-compose down log_success"Redis 集群已停止"}# 重启集群restart_cluster(){log_info"重启 Redis 集群..."stop_clustersleep5start_cluster}# 清理集群clean_cluster(){log_warn"清理 Redis 集群数据..."read-p"确认要删除所有数据吗?(y/n): "-n1-rechoif[[$REPLY=~ ^[Yy]$]];thendocker-compose down -vrm-rf ./data/* ./logs/* log_success"集群数据已清理"elselog_info"取消清理操作"fi}# 集群扩容scale_cluster(){log_info"集群扩容..."echo"选择扩容方式:"echo"1) 增加主节点"echo"2) 增加从节点"echo"3) 退出"read-p"请选择 (1-3): "choicecase$choicein1)add_master_node;;2)add_slave_node;;3)log_info"退出扩容";;*)log_error"无效选择";;esac}# 添加主节点add_master_node(){log_info"添加主节点..."# 获取下一个节点编号localnext_node=$(($(ls-d ./data/node*2>/dev/null|wc-l)+1))# 创建数据目录mkdir-p ./data/node$next_nodemkdir-p ./logs/node$next_node# 生成节点配置cat>./config/redis-node-$next_node.conf<<EOF bind 0.0.0.0 port 6379 cluster-enabled yes cluster-config-file /data/nodes.conf cluster-node-timeout 5000 appendonly yes dir /data requirepass${REDIS_PASSWORD:-Redis123456}masterauth${REDIS_PASSWORD:-Redis123456}EOF# 更新 docker-compose.yml# 这里需要手动更新 docker-compose.yml 文件log_warn"请手动更新 docker-compose.yml 文件添加新节点"log_info"然后运行: docker-compose up -d redis-node-$next_node"log_info"最后将新节点加入集群: docker exec redis-cluster-node-1 redis-cli --cluster add-node new_host:new_port existing_host:existing_port"}# 备份集群backup_cluster(){log_info"备份集群数据..."localbackup_dir="./backups/$(date+%Y%m%d_%H%M%S)"mkdir-p"$backup_dir"# 备份数据foriin{1..6};doifdockerps|grep-q"redis-cluster-node-$i";thenlog_info"备份节点$i数据..."dockerexecredis-cluster-node-$iredis-cli -a"${REDIS_PASSWORD:-Redis123456}"savecp-r ./data/node$i"$backup_dir/"fidone# 备份配置cp-r ./config"$backup_dir/"cpdocker-compose.yml"$backup_dir/"cp.env"$backup_dir/"2>/dev/null||true# 创建备份元数据cat>"$backup_dir/backup.info"<<EOF 备份时间:$(date)集群节点: 6 Redis 版本: 7.2 备份目录:$backup_dirEOF# 压缩备份tar-czf"$backup_dir.tar.gz"-C ./backups"$(basename$backup_dir)"log_success"集群备份完成:$backup_dir.tar.gz"}# 监控集群monitor_cluster(){log_info"启动集群监控..."echo"选择监控方式:"echo"1) 实时监控"echo"2) 查看节点信息"echo"3) 查看内存使用"echo"4) 查看慢查询"echo"5) 返回"read-p"请选择 (1-5): "choicecase$choicein1)dockerexec-it redis-cluster-node-1 redis-cli -a"${REDIS_PASSWORD:-Redis123456}"--stat;;2)dockerexecredis-cluster-node-1 redis-cli -a"${REDIS_PASSWORD:-Redis123456}"cluster nodes;;3)dockerexecredis-cluster-node-1 redis-cli -a"${REDIS_PASSWORD:-Redis123456}"info memory;;4)dockerexecredis-cluster-node-1 redis-cli -a"${REDIS_PASSWORD:-Redis123456}"slowlog get10;;5)return;;*)log_error"无效选择";;esac}# 主菜单show_menu(){clearecho"========================================"echo" Redis Cluster 管理工具"echo"========================================"echo"1) 启动集群"echo"2) 停止集群"echo"3) 重启集群"echo"4) 检查状态"echo"5) 集群扩容"echo"6) 备份集群"echo"7) 监控集群"echo"8) 清理集群"echo"9) 退出"echo"========================================"}# 主函数main(){# 检查依赖check_dependencies# 创建目录create_directories# 生成配置generate_configswhiletrue;doshow_menuread-p"请选择操作 (1-9): "choicecase$choicein1)start_cluster;;2)stop_cluster;;3)restart_cluster;;4)check_cluster_health;;5)scale_cluster;;6)backup_cluster;;7)monitor_cluster;;8)clean_cluster;;9)log_info"退出管理工具"exit0;;*)log_error"无效选择,请重新输入";;esacread-p"按 Enter 键继续..."done}# 执行主函数main"$@"4.2 集群测试脚本 (test-cluster.sh)
Spring Cloud全栈实战:手撸企业级项目,从入门到架构师!
#!/bin/bash# Redis Cluster 测试脚本set-e# 颜色定义RED='\033[0;31m'GREEN='\033[0;32m'YELLOW='\033[1;33m'BLUE='\033[0;34m'NC='\033[0m'# No Color# 日志函数log_info(){echo-e"${BLUE}[INFO]${NC}$1";}log_success(){echo-e"${GREEN}[SUCCESS]${NC}$1";}log_warn(){echo-e"${YELLOW}[WARN]${NC}$1";}log_error(){echo-e"${RED}[ERROR]${NC}$1";}# 加载环境变量if[-f .env];thenexport$(grep-v'^#'.env|xargs)fiREDIS_PASSWORD=${REDIS_PASSWORD:-Redis123456}REDIS_HOST=${REDIS_HOST:-localhost}PORTS=(700170027003700470057006)# 测试连接test_connection(){log_info"测试 Redis 集群连接..."forportin"${PORTS[@]}";doifredis-cli -h$REDIS_HOST-p$port-a"$REDIS_PASSWORD"ping2>/dev/null|grep-q"PONG";thenlog_success"节点$REDIS_HOST:$port连接成功"elselog_error"节点$REDIS_HOST:$port连接失败"return1fidonereturn0}# 测试集群状态test_cluster_status(){log_info"测试集群状态..."localcluster_info=$(redis-cli -h $REDIS_HOST -p7001-a"$REDIS_PASSWORD"cluster info2>/dev/null)ifecho"$cluster_info"|grep-q"cluster_state:ok";thenlog_success"集群状态正常"echo"$cluster_info"return0elselog_error"集群状态异常"echo"$cluster_info"return1fi}# 测试数据读写test_data_operations(){log_info"测试数据读写操作..."localtest_key="test:cluster:$(date+%s)"localtest_value="test_value_$(date+%s)"# 写入数据ifredis-cli -h$REDIS_HOST-p7001-a"$REDIS_PASSWORD"set"$test_key""$test_value"2>/dev/null|grep-q"OK";thenlog_success"数据写入成功:$test_key=$test_value"elselog_error"数据写入失败"return1fi# 读取数据localread_value=$(redis-cli -h $REDIS_HOST -p7001-a"$REDIS_PASSWORD"get"$test_key"2>/dev/null)if["$read_value"="$test_value"];thenlog_success"数据读取成功:$test_key=$read_value"elselog_error"数据读取失败"return1fi# 删除数据ifredis-cli -h$REDIS_HOST-p7001-a"$REDIS_PASSWORD"del"$test_key"2>/dev/null|grep-q"^1$";thenlog_success"数据删除成功"elselog_error"数据删除失败"return1fireturn0}# 测试集群重定向test_cluster_redirect(){log_info"测试集群重定向..."# 创建多个键,确保它们分布在不同节点localkeys=()foriin{1..100};dokeys+=("cluster:key:$i")donelocalsuccess_count=0localfail_count=0forkeyin"${keys[@]}";do# 尝试在随机节点上设置值localrandom_port=${PORTS[$RANDOM%${#PORTS[@]}]}localresult=$(redis-cli -h $REDIS_HOST -p $random_port -a"$REDIS_PASSWORD"set"$key""value_$key"2>&1)ifecho"$result"|grep-q"MOVED\|OK";then((success_count++))else((fail_count++))log_warn"键$key在端口$random_port设置失败:$result"fidonelog_info"重定向测试结果: 成功$success_count, 失败$fail_count"if[$fail_count-eq0];thenlog_success"集群重定向测试通过"return0elselog_error"集群重定向测试失败"return1fi}# 测试故障转移test_failover(){log_info"测试故障转移(模拟主节点故障)..."# 获取当前主节点localmaster_nodes=$(redis-cli -h $REDIS_HOST -p7001-a"$REDIS_PASSWORD"cluster nodes2>/dev/null|grep"master"|head-3)localfirst_master=$(echo"$master_nodes"|head-1|awk'{print$2}')if[-z"$first_master"];thenlog_error"无法获取主节点信息"return1filocalmaster_host=$(echo"$first_master"|cut-d: -f1)localmaster_port=$(echo"$first_master"|cut-d: -f2)log_info"模拟主节点$master_host:$master_port故障..."# 停止主节点容器localcontainer_name=$(dockerps--format"{{.Names}}"|grep"$master_port"|head-1)if[-n"$container_name"];thenlog_info"停止容器:$container_name"docker stop"$container_name"# 等待故障转移log_info"等待故障转移 (30秒)..."sleep30# 检查新的主节点localnew_master=$(redis-cli -h $REDIS_HOST -p7002-a"$REDIS_PASSWORD"cluster nodes2>/dev/null|grep"$master_host:$master_port"|grep"slave")if[-n"$new_master"];thenlog_success"故障转移成功: 原主节点$master_host:$master_port变为从节点"elselog_error"故障转移失败"fi# 恢复节点log_info"恢复节点..."docker start"$container_name"sleep10elselog_error"未找到对应的容器"return1fireturn0}# 性能测试test_performance(){log_info"运行性能测试..."# 使用 redis-benchmark 测试log_info"1. 测试 SET 操作..."redis-benchmark -h$REDIS_HOST-p7001-a"$REDIS_PASSWORD"-tset-n10000-c50log_info"2. 测试 GET 操作..."redis-benchmark -h$REDIS_HOST-p7001-a"$REDIS_PASSWORD"-t get -n10000-c50log_info"3. 测试流水线操作..."redis-benchmark -h$REDIS_HOST-p7001-a"$REDIS_PASSWORD"-t ping,set,get -n10000-c50-P16log_success"性能测试完成"}# 运行所有测试run_all_tests(){log_info"开始 Redis 集群完整测试..."localtests_passed=0localtests_failed=0# 测试1: 连接测试iftest_connection;then((tests_passed++))else((tests_failed++))fi# 测试2: 集群状态测试iftest_cluster_status;then((tests_passed++))else((tests_failed++))fi# 测试3: 数据操作测试iftest_data_operations;then((tests_passed++))else((tests_failed++))fi# 测试4: 集群重定向测试iftest_cluster_redirect;then((tests_passed++))else((tests_failed++))fi# 测试5: 性能测试test_performanceecho"========================================"echo"测试结果汇总:"echo"通过:$tests_passed"echo"失败:$tests_failed"echo"========================================"if[$tests_failed-eq0];thenlog_success"所有测试通过!Redis 集群运行正常。"return0elselog_error"有$tests_failed个测试失败"return1fi}# 主函数main(){echo"========================================"echo" Redis Cluster 测试工具"echo"========================================"echo"1) 运行完整测试"echo"2) 测试连接"echo"3) 测试集群状态"echo"4) 测试数据操作"echo"5) 测试集群重定向"echo"6) 测试故障转移"echo"7) 性能测试"echo"8) 退出"echo"========================================"read-p"请选择测试项目 (1-8): "choicecase$choicein1)run_all_tests;;2)test_connection;;3)test_cluster_status;;4)test_data_operations;;5)test_cluster_redirect;;6)test_failover;;7)test_performance;;8)log_info"退出测试工具"exit0;;*)log_error"无效选择"exit1;;esac}# 执行主函数main"$@"五、Kubernetes 部署方案
Spring Cloud全栈实战:手撸企业级项目,从入门到架构师!
5.1 Redis Cluster Kubernetes 部署文件
5.1.1 namespace.yaml
apiVersion:v1kind:Namespacemetadata:name:redis-clusterlabels:name:redis-clustercomponent:databaseenvironment:production5.1.2 configmap.yaml
apiVersion:v1kind:ConfigMapmetadata:name:redis-cluster-confignamespace:redis-clusterdata:redis.conf:|# Redis 集群配置 bind 0.0.0.0 port 6379 cluster-enabled yes cluster-config-file /data/nodes.conf cluster-node-timeout 5000 cluster-slave-validity-factor 10 cluster-migration-barrier 1 cluster-require-full-coverage yes appendonly yes appendfilename "appendonly.aof" appendfsync everysec auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb dir /data save 900 1 save 300 10 save 60 10000 stop-writes-on-bgsave-error yes rdbcompression yes rdbchecksum yes dbfilename dump.rdb maxmemory 2gb maxmemory-policy allkeys-lru maxmemory-samples 5 lazyfree-lazy-eviction no lazyfree-lazy-expire no lazyfree-lazy-server-del no replica-lazy-flush no lua-time-limit 5000 slowlog-log-slower-than 10000 slowlog-max-len 128 latency-monitor-threshold 0 notify-keyspace-events "" hash-max-ziplist-entries 512 hash-max-ziplist-value 64 list-max-ziplist-size -2 list-compress-depth 0 set-max-intset-entries 512 zset-max-ziplist-entries 128 zset-max-ziplist-value 64 hll-sparse-max-bytes 3000 stream-node-max-bytes 4096 stream-node-max-entries 100 activerehashing yes client-output-buffer-limit normal 0 0 0 client-output-buffer-limit replica 256mb 64mb 60 client-output-buffer-limit pubsub 32mb 8mb 60 hz 10 dynamic-hz yes aof-rewrite-incremental-fsync yes rdb-save-incremental-fsync yes5.1.3 secret.yaml
apiVersion:v1kind:Secretmetadata:name:redis-cluster-secretnamespace:redis-clustertype:OpaquestringData:redis-password:"Redis123456@K8s"requirepass:"Redis123456@K8s"masterauth:"Redis123456@K8s"5.1.4 service.yaml
apiVersion:v1kind:Servicemetadata:name:redis-clusternamespace:redis-clusterlabels:app:redis-clustercomponent:databasespec:ports:-name:clientport:6379targetPort:6379-name:clusterport:16379targetPort:16379clusterIP:Noneselector:app:redis-cluster---apiVersion:v1kind:Servicemetadata:name:redis-cluster-headlessnamespace:redis-clusterlabels:app:redis-clustercomponent:databasespec:ports:-name:clientport:6379targetPort:6379-name:clusterport:16379targetPort:16379clusterIP:Noneselector:app:redis-cluster5.1.5 statefulset.yaml
apiVersion:apps/v1kind:StatefulSetmetadata:name:redis-clusternamespace:redis-clusterlabels:app:redis-clustercomponent:databaseenvironment:productionspec:serviceName:redis-cluster-headlessreplicas:6selector:matchLabels:app:redis-clusterupdateStrategy:type:RollingUpdatepodManagementPolicy:OrderedReadytemplate:metadata:labels:app:redis-clustercomponent:databaseenvironment:productionannotations:prometheus.io/scrape:"true"prometheus.io/port:"9121"prometheus.io/path:"/metrics"spec:affinity:podAntiAffinity:preferredDuringSchedulingIgnoredDuringExecution:-weight:100podAffinityTerm:labelSelector:matchExpressions:-key:appoperator:Invalues:-redis-clustertopologyKey:kubernetes.io/hostnamecontainers:-name:redisimage:redis:7.2-alpineimagePullPolicy:IfNotPresentcommand:-redis-server-/etc/redis/redis.conf---requirepass-$(REDIS_PASSWORD)---masterauth-$(REDIS_PASSWORD)ports:-name:clientcontainerPort:6379-name:clustercontainerPort:16379env:-name:REDIS_PASSWORDvalueFrom:secretKeyRef:name:redis-cluster-secretkey:redis-password-name:POD_IPvalueFrom:fieldRef:fieldPath:status.podIPresources:requests:memory:"2Gi"cpu:"1000m"limits:memory:"4Gi"cpu:"2000m"volumeMounts:-name:redis-configmountPath:/etc/redis-name:redis-datamountPath:/data-name:redis-tmpmountPath:/tmplivenessProbe:exec:command:-redis-cli--a-$(REDIS_PASSWORD)-pinginitialDelaySeconds:30periodSeconds:10timeoutSeconds:5failureThreshold:3readinessProbe:exec:command:-redis-cli--a-$(REDIS_PASSWORD)-pinginitialDelaySeconds:5periodSeconds:5timeoutSeconds:3failureThreshold:3startupProbe:exec:command:-redis-cli--a-$(REDIS_PASSWORD)-pinginitialDelaySeconds:0periodSeconds:5timeoutSeconds:3failureThreshold:30securityContext:privileged:falsereadOnlyRootFilesystem:trueallowPrivilegeEscalation:falsecapabilities:drop:-ALLvolumes:-name:redis-configconfigMap:name:redis-cluster-config-name:redis-tmpemptyDir:{}volumeClaimTemplates:-metadata:name:redis-dataspec:accessModes:-ReadWriteOncestorageClassName:standardresources:requests:storage:20Gi5.1.6 job-init.yaml
apiVersion:batch/v1kind:Jobmetadata:name:redis-cluster-initnamespace:redis-clusterspec:template:spec:containers:-name:redis-cluster-initimage:redis:7.2-alpinecommand:-/bin/sh--c-|echo "等待 Redis 节点启动..." sleep 30echo "获取 Pod IP 地址..." POD_IPS=() for i in{0..5}; do IP=$(getent hosts redis-cluster-$i.redis-cluster-headless.redis-cluster.svc.cluster.local|awk '{print $1}') if[-n "$IP"]; then POD_IPS+=("$IP:6379")echo "节点 $i IP:$IP" fi done echo "创建 Redis 集群..." echo "yes"|redis-cli--cluster create \ ${POD_IPS[@]}\--cluster-replicas 1 \-a $(REDIS_PASSWORD) echo "验证集群状态..." redis-cli--cluster check ${POD_IPS[0]}-a $(REDIS_PASSWORD) echo "集群信息:" redis-cli-h ${POD_IPS[0]%:*}-p 6379-a $(REDIS_PASSWORD) cluster info echo "节点信息:" redis-cli-h ${POD_IPS[0]%:*}-p 6379-a $(REDIS_PASSWORD) cluster nodes echo "Redis 集群初始化完成!"env:-name:REDIS_PASSWORDvalueFrom:secretKeyRef:name:redis-cluster-secretkey:redis-passwordrestartPolicy:OnFailurebackoffLimit:3activeDeadlineSeconds:300六、监控和告警配置
Spring Cloud全栈实战:手撸企业级项目,从入门到架构师!
6.1 Prometheus 监控配置
# prometheus-config.ymlglobal:scrape_interval:15sevaluation_interval:15sscrape_configs:-job_name:'redis-cluster'static_configs:-targets:-'redis-cluster-exporter:9121'metrics_path:/metricsrelabel_configs:-source_labels:[__address__]target_label:instanceregex:'(.*):.*'replacement:'${1}'-job_name:'redis-cluster-nodes'kubernetes_sd_configs:-role:podnamespaces:names:-redis-clusterrelabel_configs:-source_labels:[__meta_kubernetes_pod_label_app]regex:redis-clusteraction:keep-source_labels:[__meta_kubernetes_pod_container_port_number]regex:6379action:keep-source_labels:[__meta_kubernetes_pod_ip]target_label:instancereplacement:'${1}:6379'-source_labels:[__meta_kubernetes_pod_name]target_label:pod-source_labels:[__meta_kubernetes_namespace]target_label:namespace6.2 Grafana 仪表板
{"dashboard":{"id":null,"title":"Redis Cluster Monitoring","tags":["redis","cluster","database"],"timezone":"browser","panels":[{"id":1,"title":"Cluster Health","type":"stat","targets":[{"expr":"redis_up","legendFormat":"{{instance}}"}],"fieldConfig":{"defaults":{"thresholds":{"steps":[{"color":"red","value":null},{"color":"green","value":1}]}}}},{"id":2,"title":"Memory Usage","type":"graph","targets":[{"expr":"redis_memory_used_bytes / 1024 / 1024","legendFormat":"Used Memory (MB) - {{instance}}"},{"expr":"redis_memory_max_bytes / 1024 / 1024","legendFormat":"Max Memory (MB) - {{instance}}"}]},{"id":3,"title":"Commands per Second","type":"graph","targets":[{"expr":"rate(redis_commands_processed_total[5m])","legendFormat":"Commands/s - {{instance}}"}]},{"id":4,"title":"Connections","type":"graph","targets":[{"expr":"redis_connected_clients","legendFormat":"Connected Clients - {{instance}}"}]},{"id":5,"title":"Keyspace","type":"graph","targets":[{"expr":"redis_db_keys","legendFormat":"Keys - db{{db}}"}]},{"id":6,"title":"CPU Usage","type":"graph","targets":[{"expr":"rate(redis_cpu_sys_seconds_total[5m]) * 100","legendFormat":"System CPU % - {{instance}}"},{"expr":"rate(redis_cpu_user_seconds_total[5m]) * 100","legendFormat":"User CPU % - {{instance}}"}]}],"time":{"from":"now-6h","to":"now"}}}七、使用说明
7.1 快速开始
# 1. 克隆仓库gitclone https://github.com/your-repo/redis-cluster-docker.gitcdredis-cluster-docker# 2. 配置环境变量cp.env.example .env# 编辑 .env 文件,设置密码和其他配置# 3. 启动集群chmod+x start-cluster.sh ./start-cluster.sh# 4. 测试集群chmod+x test-cluster.sh ./test-cluster.sh# 5. 访问管理界面# Redis Commander: http://localhost:8081# 用户名: admin# 密码: 你的REDIS_PASSWORD7.2 常用命令
# 查看集群状态dockerexecredis-cluster-node-1 redis-cli -a"你的密码"cluster info# 查看集群节点dockerexecredis-cluster-node-1 redis-cli -a"你的密码"cluster nodes# 查看集群槽分配dockerexecredis-cluster-node-1 redis-cli -a"你的密码"cluster slots# 重启集群docker-compose restart# 查看日志docker-compose logs -f redis-node-1# 进入容器dockerexec-it redis-cluster-node-1sh# 备份集群docker-composeexecredis-cluster-node-1 redis-cli -a"你的密码"save7.3 故障处理
# 1. 节点故障恢复# 如果某个节点故障,重启该节点即可docker-compose restart redis-node-1# 2. 集群重新初始化# 如果集群配置损坏,可以重新初始化docker-compose down -v ./start-cluster.sh# 3. 数据恢复# 从备份恢复数据tar-xzf backups/20240101_120000.tar.gzcp-r backups/20240101_120000/data/node1/* ./data/node1/# 重启集群# 4. 性能调优# 调整内存限制# 在 .env 文件中设置 REDIS_MAXMEMORY=4gb# 重启集群八、安全和优化建议
Spring Cloud全栈实战:手撸企业级项目,从入门到架构师!
8.1 安全配置
# 安全配置最佳实践security:# 1. 强密码策略requirepass:"复杂密码@包含大小写数字特殊字符"# 2. 禁用危险命令rename-command:FLUSHDB:""FLUSHALL:""CONFIG:""KEYS:""SHUTDOWN:""# 3. 网络限制bind:0.0.0.0# 生产环境应绑定特定IPprotected-mode:yes# 4. SSL/TLS 加密tls-port:6380tls-cert-file:/path/to/redis.crttls-key-file:/path/to/redis.keytls-ca-cert-file:/path/to/ca.crt# 5. 客户端限制maxclients:10000timeout:300# 6. 慢查询日志slowlog-log-slower-than:10000slowlog-max-len:1288.2 性能优化
# 性能优化配置performance:# 1. 内存优化maxmemory:"4gb"maxmemory-policy:"allkeys-lru"maxmemory-samples:5# 2. 持久化优化appendonly:yesappendfsync:everysecauto-aof-rewrite-percentage:100auto-aof-rewrite-min-size:64mb# 3. 网络优化tcp-keepalive:300tcp-backlog:511# 4. 集群优化cluster-node-timeout:5000cluster-replica-validity-factor:10# 5. 内核参数优化# 在宿主机上设置# echo never > /sys/kernel/mm/transparent_hugepage/enabled# sysctl -w net.core.somaxconn=65535# sysctl -w vm.overcommit_memory=1这个 Redis 集群部署方案提供了完整的生产级解决方案,包含 Docker Compose 部署、Kubernetes 部署、监控告警、备份恢复等功能,可以直接用于生产环境。