简介一份面向 CentOS 7.x 环境搭建 Redis 集群的自动化部署脚本包服务于需要快速验证集群方案的运维与后端开发者把主从复制、16384 个槽位分配、故障转移等集群要点固化为可一键执行的流程免去逐台手工编译与配置的重复劳动。压缩包共 5 个文件、约 1.86MB以 3 个 sh 脚本为主体分别承担主流程调度、Redis 安装配置与集群卸载清理另含 redis-5.0.0.tar.gz 源码包和一份 txt 使用说明覆盖从解压编译到集群成型的完整环节已有 777 人学习。按说明修改 IP 与端口变量后依次执行即可完成依赖安装、编译、节点配置与集群创建卸载脚本便于回收实例与残留配置脚本中的变量定义、条件判断与循环结构也可作为编写同类自动化部署脚本的参考。对初次接触集群部署的读者还可借此理解节点角色划分与槽位分配在真实环境中的含义并沿着脚本顺序梳理常见故障的排查方向。1. 手工搭三主三从熬到凌晨这份 Redis 集群一键部署脚本省掉的四件事三台 CentOS 机器、六个 Redis 实例我第一次手工搭集群从下午两点折腾到凌晨配置文件手改六遍meet 完节点卡在 handshake最后发现是 17000 端口没放行。后来拿到这份 redis 集群一键自动部署脚本.rar真正省事的不是redis-cli --cluster create那一行而是它前面那堆重复劳动——批量生成 conf、按顺序拉起实例、把客户端端口和集群总线端口一起放通、创建完再跑一遍 check 把状态确认下来。这套东西是 shell 写的贴着 CentOS 的 systemctl、firewalld、sysctl 走没有额外运行时依赖几百行读一遍就能按自己的机器改。它适合三类人手上只有两三台测试机、需要反复重建集群做课程设计或压测的线上出过槽位故障、想在预发环境原样复现的以及刚接触 Redis Cluster、想先跑通再回头补原理的。下面从机制讲到执行链路再讲翻车点。2. 槽位、总线与选举一键脚本必须替你做对的四个集群机制脚本只做「生成配置、拉进程、建集群」三件事但每一件背后都压着 Redis Cluster 的运行时规则。不理解这四条脚本跑挂了连日志该往哪看都不知道。反过来理解了这四条脚本里那些看起来多余的 sleep、循环和判断你就知道它在防什么。2.1 16384 个槽位怎么分脚本为什么默认给三主Redis Cluster 把整个键空间切成 16384 个槽key 落到哪个槽由CRC16(key) mod 16384决定槽再归某个主节点管。客户端拿到 MOVED 重定向后自己缓存槽位表所以集群的核心不是「数据分片存储」这件事本身而是「槽位归属必须全集群一致」。任何一个槽没有主人集群在cluster-require-full-coverage yes默认值下会直接拒绝所有写请求。一键脚本之所以默认三主三从不是审美选择是硬约束--cluster-replicas 1要求主节点数至少 3因为故障转移要靠主节点多数派投票两个主节点挂一个就凑不出多数。三主是最小可用拓扑脚本里如果写成「节点数 / 2」来算主节点数节点少于 6 个时就会算出 2 主create 阶段直接报*** ERROR: Invalid configuration for cluster creation。槽位分配默认是均分再取余16384 / 3 5461、5461、5462第三个主节点多拿 2 个槽。这个数字在很多巡检脚本里被写死成5461做断言一旦集群做过 reshard 就会误报。我的做法是只看cluster info里的cluster_slots_assigned:16384不校验单节点槽数。另外要记住集群模式只支持 db 0配置里写databases 16不会报错但SELECT命令会被拒绝返回ERR SELECT is not allowed in cluster mode。涉及多 key 的操作必须落在同一个槽用 hash tag 把 key 的一部分用{}包起来比如user:{1001}:name和user:{1001}:age会被强制分到同一槽这是设计键名时就要定下来的事不是部署阶段能补救的。2.2 cluster bus 与握手meet 之后到底发生了什么每个集群节点会额外监听一个「客户端端口 10000」的总线端口7000 对应 17000。节点之间交换 gossip 消息、投票、传播槽位表都走这个端口用的是二进制协议和客户端协议不是一回事。手工搭集群最常见的卡点就在这里6379 能 telnet 通17000 不通cluster nodes里对方节点永远是handshake状态日志里反复刷Unable to connect to MASTER。握手的完整链路是脚本对 A 节点执行CLUSTER MEET B_IP B_PORTA 通过总线端口连 B交换节点信息B 再把 A 的信息 gossip 给已知节点。所以只要有一个节点的总线端口不通整个集群就可能分裂成两个互不可见的孤岛而cluster_state在每个孤岛内部都显示 ok。这就是为什么脚本里的防火墙规则必须成对出现客户端端口段和总线端口段一起放少写一行就是几小时的排查。还有个容易被忽略的点节点的 node id 存在cluster-config-file指定的文件里默认叫nodes.conf。这个文件是 Redis 自己维护的脚本绝对不能删、不能提前创建、不能手改。删掉它的后果是节点重启后生成新 id其他节点还拿着旧 id 做 gossip集群视图彻底错乱。我在脚本里见过用rm -rf $DATA_DIR/*做「清理重来」的写法看着干净实际上把 nodes.conf 一起干掉了第二次执行必翻车。2.3 主从复制与自动故障转移的判定链路从节点默认每秒向主节点发REPLCONF ACK主节点把复制偏移量记在内存里。某个主节点失联后其余主节点在cluster-node-timeout默认 15000 毫秒之后把它标记为fail然后由持有该主节点槽位的从节点发起选举拿到多数主节点投票后提升为主接管槽位。整个过程脚本管不了但脚本里的两个参数直接决定它能不能成。一个参数是cluster-node-timeout。设太小网络抖一下就触发主从切换切完数据还没同步完又切回来设太大真挂了要等半分钟以上才恢复。生产上给 15000 到 30000 之间比较稳测试机可以给 5000 加快演练节奏。另一个是cluster-replica-validity-factor默认 10从节点断线时间超过node-timeout × factor就失去竞选资格这是防止用一份过期数据顶上去的保护。把它设成 0 表示不管断多久都能选只在「宁可丢数据也要保可用」的场景下才这么干。从节点能不能被选中还要看它的复制偏移量和主节点差多少。如果脚本为了让启动快把repl-diskless-sync之类的东西乱调或者从节点刚起来还没完成全量同步就被拉进集群选举时它的 offset 落后太多同样会被其他主节点拒绝。所以脚本里「拉起实例」和「cluster create」之间应该留几秒让复制链路建立这个 sleep 不是玄学是在等master_link_status:up。2.4 节点顺序与副本错位脚本里最容易写错的一段逻辑redis-cli --cluster create的节点列表顺序是有语义的前 N 个被当作主节点后面按顺序分配给它们做从节点。如果参数写成--cluster-replicas 1列表是A B C D E F那 A B C 是主D E F 依次是 A B C 的从。但这个「依次」是按主机和端口的排列算的脚本如果用两层 for 循环先生成所有 7000 端口再生成所有 7001 端口最终可能出现三个主节点全在同一台物理机上另一台机器上的三个从节点全在等它挂——机器一断电集群直接没了一半。正确的做法是按机器交错排列每台机器出两个实例列表写成A:7000 A:7001 B:7000 B:7001 C:7000 C:7001这样主节点天然分散在三台机器上从节点也落在不同的物理机。脚本里如果做了「按 IP 分组再拼接」的处理读的时候要确认它是交错而不是分段。这一点在验收时可以用redis-cli --cluster check输出的主从对应关系直接看出来从节点和它的主节点在同一台机器上就是错位了。还有一种错位是端口和目录没对上。同一个 IP 上跑 7000 和 7001 两个实例配置里的dir必须是两个不同目录logfile、pidfile、dbfilename也都得带端口后缀。脚本如果只替换了port而没替换dir两个实例会往同一个 dump 文件里写AOF 也会互相覆盖症状是集群能建起来但一重启数据就乱。3. 解包看结构CentOS 上的目录、系统参数与端口三件套拿到 rar 之后第一件事不是急着bash deploy.sh而是先看清包里有什么、它假设你的机器处于什么状态。这类脚本的失败八成不是脚本逻辑错而是它默认的前置条件在你的机器上不成立。3.1 压缩包里的文件各管什么rar 在 CentOS 上默认解不开需要先装unrar很多最小化安装的镜像里没有这个包得先配好软件源再装。解出来的目录结构各家写法不同但职责基本是固定的几类读之前先按下面这张表对一遍缺哪个文件就知道脚本会缺哪一步文件/目录典型职责缺了会怎样deploy.sh主入口串起检查、生成、启动、建集群无法一键执行redis.conf.tpl配置模板用变量占位端口、目录、密码生成的配置全是默认值nodes.list/hosts.conf节点 IP 与端口清单脚本不知道往哪几台机器铺start_all.sh/stop_all.sh批量拉起与停止实例只能手工 ps 后 killcheck_cluster.sh建完集群后的状态校验出问题没有统一出口redis-*.tar.gz源码包或二进制包需要自己准备同版本安装包README端口、密码、目录约定参数只能靠读脚本猜读脚本时优先确认三件事REDIS_HOME装在哪、DATA_DIR数据落在哪、密码是写死在脚本里还是从外部传入。写死密码的脚本在多人共用的测试环境里很常见改的时候记得同步改requirepass和masterauth两处只改一处集群建不起来。3.2 CentOS 7/8 必须先改的四个内核与资源项Redis 在集群模式下对系统资源更敏感因为多了总线连接和节点间心跳。以下四项如果脚本里没做建议在执行前手工补上用sysctl落成配置文件比sysctl -w临时改可靠重启不丢# /etc/sysctl.d/99-redis-cluster.conf # 允许内存过量提交否则 fork 做 RDB 落盘时可能直接失败 vm.overcommit_memory 1 # 监听队列长度低于 511 时高并发下会出现连接被丢弃 net.core.somaxconn 1024 # 尽量不使用 swap避免节点间心跳因换页抖动而超时 vm.swappiness 1 # 关闭透明大页THP 会造成 Redis 的 fork 延迟 spikes echo never /sys/kernel/mm/transparent_hugepage/enabled echo never /sys/kernel/mm/transparent_hugepage/defrag sysctl --systemvm.overcommit_memory这一项是最容易被忽略的日志里表现为Background save is starting之后跟一条fork: Cannot allocate memory进程直接没了而这台机器内存看起来还很空闲。原因不是内存不够是内核在 fork 时按 overcommit 策略拒绝了地址空间申请。somaxconn则对应TCP: request_sock_TCP: Possible SYN flooding这类告警连接数一上来就丢包。文件描述符也得提。Redis 默认建议ulimit -n不低于 10032systemd 拉起的服务要在 unit 文件里写LimitNOFILE10032光改/etc/security/limits.conf对 systemd 服务不生效这是 CentOS 7 之后非常常见的坑。脚本如果用redis-server xxx.conf 的方式启动继承的是当前 shell 的限制换个终端执行结果就不一样我一般让脚本显式ulimit -n 10032再拉进程。3.3 端口放行7000-7005 只是其中一半六个实例用 7000 到 7005客户端连这些端口没问题但节点之间要走 17000 到 17005只放前半段的表现是cluster nodes里其他节点一直是handshake或者已经connected但cluster_state:fail日志刷Cluster bus: connection refused。firewalld 的写法是一次把两段都加上# 客户端端口段 firewall-cmd --permanent --add-port7000-7005/tcp # 集群总线端口段 客户端端口 10000 firewall-cmd --permanent --add-port17000-17005/tcp firewall-cmd --reload firewall-cmd --list-ports如果机器在云上安全组是另一道门firewall-cmd --list-ports有输出不代表外部能进来。验证方式是换一台机器用telnet 目标IP 17000或者nc -zv 目标IP 17000试本地ss -lntp | grep 1700看到监听只说明本机进程起来了跟网络可达是两件事。还有个细节如果同一台机器上跑多个实例bind建议写内网 IP 而不是0.0.0.0同时把protected-mode关掉集群模式和多节点访问下通常要关并且显式设置cluster-announce-ip为内网地址。多网卡机器上不设这一项节点会把自己广播成 127.0.0.1 或者某块业务网卡的地址其他节点按这个地址连总线端口永远连不上。4. 从零跑一遍配置生成、实例拉起、cluster create 的完整链路这一章按执行顺序走一遍每一步给出关键命令和需要核对的输出。脚本再自动这四步的中间产物你都应该知道长什么样否则出问题只能干等。4.1 生成六份 redis.conf用循环替掉手改配置模板的核心是集群相关的那几行端口、目录、密码用变量替换。手写六份的失败率远高于想象一份漏了cluster-enabled yescreate 阶段就报Node xxx is not configured as a cluster node。用循环生成模板只维护一份#!/usr/bin/env bash set -euo pipefail BASE_PORT7000 DATA_ROOT/data/redis-cluster NODES(10.0.0.11 10.0.0.12 10.0.0.13) PASSWORDChangeMe_2024 for ip in ${NODES[]}; do for idx in 0 1; do port$((BASE_PORT idx)) dir${DATA_ROOT}/${port} mkdir -p ${dir} cat ${dir}/redis.conf EOF port ${port} bind ${ip} protected-mode no daemonize yes pidfile ${dir}/redis.pid logfile ${dir}/redis.log dir ${dir} dbfilename dump-${port}.rdb appendonly yes appendfilename appendonly-${port}.aof # 集群三件套开关、节点状态文件、失联判定毫秒数 cluster-enabled yes cluster-config-file nodes-${port}.conf cluster-node-timeout 15000 cluster-announce-ip ${ip} cluster-announce-port ${port} cluster-announce-bus-port $((port 10000)) # 密码必须两处一致否则从节点同步和集群创建都会失败 requirepass ${PASSWORD} masterauth ${PASSWORD} EOF done done几个参数值得单独说。cluster-config-file带上端口后缀避免同机多实例互相覆盖这个文件由 Redis 维护脚本只负责给路径。cluster-announce-ip显式写成当前机器的内网 IP多网卡环境下这是唯一可靠的广播地址来源。appendonly yes在集群里建议打开从节点提升为主后如果只有 RDB最后一个 RDB 之后的数据就丢了。daemonize yes让实例后台跑脚本里要跟一个循环等待端口就绪不能生成完就立刻建集群。路径权限也要顺手检查${dir}必须对运行 Redis 的用户可写用 root 生成目录、用 redis 用户起进程是常见组合权限没给对的表现是启动即退出日志里一行Cant open the append-only file: Permission denied。4.2 拉起实例并确认端口真的在监听启动之后不要相信daemonize的返回值它只表示 fork 成功不表示配置正确。判断标准是端口在监听且PING有回应这一步放在脚本里就是一个带超时的轮询#!/usr/bin/env bash # 逐台启动并按端口探活超过 30 秒未就绪就打印日志尾巴退出 for ip in 10.0.0.11 10.0.0.12 10.0.0.13; do for port in 7000 7001; do ssh ${ip} redis-server ${DATA_ROOT}/${port}/redis.conf for i in $(seq 1 30); do if redis-cli -h ${ip} -p ${port} -a ChangeMe_2024 --no-auth-warning ping 2/dev/null | grep -q PONG; then echo OK ${ip}:${port} break fi sleep 1 done redis-cli -h ${ip} -p ${port} -a ChangeMe_2024 --no-auth-warning ping \ | grep -q PONG || { tail -n 30 ${DATA_ROOT}/${port}/redis.log; exit 1; } done done这里用--no-auth-warning只是避免密码提示刷屏脚本里不要把密码明文写进 history正式环境建议用REDISCLI_AUTH环境变量传。探活只看PING还不够集群模式下要再确认cluster-enabled:yes和cluster_state的初始值刚启动的节点应该是cluster_state:fail、cluster_known_nodes:1这属于正常状态说明它以单节点身份起来了等着被 meet。如果某个端口一直起不来先看日志最后 30 行再看ss -lntp | grep 端口有没有别的进程占着。测试机上经常出现上一次的实例没杀干净pidfile 残留导致新进程以为自己在跑daemonize yes下这种问题特别隐蔽。4.3 cluster create 的节点顺序与副本分配六个节点都活了之后才能建集群节点列表按「主机交错、主节点在前」的顺序排。Redis 5 之后--cluster子命令是内建的不需要额外装 ruby 的 redis-tribredis-cli --cluster create \ 10.0.0.11:7000 10.0.0.12:7000 10.0.0.13:7000 \ 10.0.0.11:7001 10.0.0.12:7001 10.0.0.13:7001 \ --cluster-replicas 1 \ -a ChangeMe_2024 --cluster-yes前三个是主节点后三个按顺序做它们的从节点所以 7001 的三个实例分别跟着三个 7000 主节点且从节点都不在同一个 IP 上——这就是 2.4 里说的交错排列。--cluster-yes跳过多余的交互确认脚本里必须加否则会卡在Can I set the above configuration? (type yes to accept)等人输入。-a在带密码集群里要给且必须和配置里的requirepass一致。执行过程中会看到每个主节点分到5461/5461/5462个槽接着是Waiting for the cluster to join这一步耗时取决于总线端口是否通。如果卡在这里超过一分钟别等直接去另一台机器上看telnet 目标IP 17000是否可达九成是端口或cluster-announce-ip的问题。4.4 用 cluster check 把状态钉死创建命令返回成功不代表集群健康必须再跑一遍 check它会逐个节点连过去核对槽位覆盖和主从关系redis-cli --cluster check 10.0.0.11:7000 -a ChangeMe_2024 # 关键看三行 # [OK] All 16384 slots covered. # 每个 master 下列出的 slots 区间与 replica 数量 # [OK] All nodes agree about slots configuration.All nodes agree about slots configuration这句是核心它表示各节点的槽位视图已经收敛一致。如果这里报Different slots configuration说明有节点的信息还没 gossip 到等几秒重跑如果一直不一致检查是不是有节点被 meet 过两次、在集群里留了重复条目。验收时我会把 check 的输出存一份到日志目录后面扩容、迁移、重启都可以拿来对比。5. 避坑与排查cluster_state:ok 之后仍然会翻车的五个场景集群建起来、状态 ok只说明拓扑成立不代表业务能用。下面五个是我和同事在不同机器上真实踩过的症状看起来都不像集群问题根因全在配置和端口上。5.1 客户端一直报 MOVED状态却是 ok现象应用连上 7000 写一个 key报MOVED 12345 10.0.0.12:7000程序里没做重定向处理直接抛异常。集群侧CLUSTER INFO显示一切正常。原因客户端没开集群模式把 Redis 当成单机在用。MOVED 不是错误是告诉客户端这个槽在别的节点集群感知客户端会自动跟随并缓存槽位表单机客户端则把重定向当异常抛出来。另一种变体是连的是代理或 VIP请求被固定转发到某一台槽位表永远变不过来。解决换成客户端库的集群模式初始化方式连接时给出全部种子节点而不是一个 VIP。如果确实只能给单地址必须在中间层做槽位感知转发不能做四层负载均衡——四层会把同一个连接的所有请求固定到同一节点跨槽访问必然失败。5.2 节点卡在 handshake 半天不动现象cluster nodes里某个节点状态是handshakecluster info的cluster_known_nodes比实际节点数少日志反复刷Unable to connect to node。原因总线端口不通或者节点广播的地址是 127.0.0.1、某块业务网卡地址。前者是防火墙只放了 7000 段后者是多网卡机器没设cluster-announce-ip节点按默认网卡地址互相通告对方连过去自然不通。解决把总线端口段补进防火墙和安全组cluster-announce-ip显式写内网 IP 后重启实例。已经进过集群的错误节点不能直接改配置重启要先CLUSTER RESET HARD清掉它的节点状态再重新CLUSTER MEET否则旧地址会一直留在其他节点的视图里。5.3 重启之后集群整体 fail现象三台机器重启实例都起来了cluster_state:failcluster_slots_assigned远小于 16384部分主节点显示fail?。原因脚本的清理逻辑把nodes-*.conf删了节点重新生成 id全集群不认它或者数据目录权限变了节点读不到原状态文件只能当新节点启动还有一种是从节点比主节点先起来且主节点长时间未就绪从节点把自己降级成孤立状态。解决nodes.conf绝对不删。重启顺序先主后从脚本里加等待逻辑确认所有主节点cluster_state恢复到 ok 再拉起从节点。真出现了 id 变更只能在剩下节点里用CLUSTER FORGET把旧条目录掉再把故障节点以干净状态重新加回并迁移槽位这个操作要停机窗口。5.4 带密码的集群创建直接失败现象redis-cli --cluster create输出Node xxx replied with error: NOAUTH Authentication required或者从节点一直master_link_status:down。原因requirepass配了masterauth没配或者值不一样。requirepass管客户端连它masterauth管它连主节点时用什么密码集群里两个方向都要通只配一处就是半通状态。解决两处写成同一个值redis-cli --cluster相关命令带-a或用REDISCLI_AUTH。改配置要六个节点全改一遍再重启改完先用redis-cli -a 密码 info replication确认从节点master_link_status:up再跑--cluster check。5.5 扩容加节点后槽位覆盖不全现象新加两个节点、迁移了部分槽过去--cluster check报Not all 16384 slots are covered但每个节点单独看都正常。原因迁移过程中断或者用了手工CLUSTER SETSLOT但漏了IMPORTING/MIGRATING两步的收尾源节点把槽标成迁移中目标节点没接管这个槽就悬空了。cluster-require-full-coverage yes下整个集群拒写。解决用redis-cli --cluster reshard走完整流程不要手工拼SETSLOT。已经悬空的槽用CLUSTER SETSLOT slot NODE node-id在相关节点上逐一修正修完跑 check 确认All 16384 slots covered。注意以上所有涉及CLUSTER RESET、FORGET、SETSLOT的操作都有数据丢失风险先在测试环境完整走一遍再上预发。6. 把一键脚本用成可维护工具故障演练与巡检的固定动作脚本能建集群只是起点。真正决定这套东西能不能长期用的是你能不能在一次执行之后回答两个问题故障切换要多久、下次重建要改哪几行。我给自己的固定动作是每次部署完做一次小型故障演练把切换时间记进部署记录后面遇到「集群是不是变慢了」这类问题时至少有基线可比。演练很简单找到某个主节点kill -9掉它的进程然后每两秒查一次cluster info里的cluster_state和cluster_slots_assigned同时用redis-cli -c往这个主节点负责的槽写 key看多久恢复写入。默认cluster-node-timeout 15000下30 秒内完成切换属于正常超过 45 秒就要检查是不是只有两个主节点、投票凑不齐多数。演练完把杀掉的实例重新拉起它会以从节点身份回归用redis-cli --cluster check确认主从关系复原。巡检我习惯写成一个小循环把每个节点的关键指标拉成一行肉眼扫一遍就知道有没有异常#!/usr/bin/env bash # 逐节点采集关键状态输出一行一个实例便于 diff 和归档 PASSChangeMe_2024 for ip in 10.0.0.11 10.0.0.12 10.0.0.13; do for port in 7000 7001; do info$(redis-cli -h ${ip} -p ${port} -a ${PASS} --no-auth-warning cluster info 2/dev/null) role$(redis-cli -h ${ip} -p ${port} -a ${PASS} --no-auth-warning info replication 2/dev/null | awk -F: /^role/{print $2} | tr -d \r) state$(echo ${info} | awk -F: /^cluster_state/{print $2} | tr -d \r) assigned$(echo ${info} | awk -F: /^cluster_slots_assigned/{print $2} | tr -d \r) printf %-12s %-5s role%-6s state%-5s slots%s\n ${ip} ${port} ${role} ${state} ${assigned} done done这个脚本的价值在于输出稳定、可以直接 diff部署前存一份、演练后存一份cluster_slots_assigned从 16384 掉了就说明有槽悬空role变了就说明发生过切换。比翻日志快得多也不用记复杂命令。最后是一个参数化的习惯。拿到这类脚本我第一件事是把BASE_PORT、NODES、PASSWORD、DATA_ROOT四个变量抽到单独的cluster.env里脚本开头source进来第二件事是把生成的配置、check 输出、演练记录按时间戳落到logs/不做覆盖。那次重启后集群 fail 的经历之后我每次改脚本都会先在一台干净的虚拟机上从零跑两遍确认第二遍执行不会因为残留目录报错再看一遍nodes.conf有没有被动过——集群这东西一次省事的rm -rf能换来一整晚的恢复工作后悔药是没有的。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?