Redis连接失败排查实录:配置文件加载陷阱与bind监听地址定位 Redis连接失败我遇到过的次数不算少了但这回是真把我折腾够呛。前后查了三天应用的报警一直在那跳Redis进程明明活着本地redis-cli ping也是秒回 PONG防火墙和安全组查了个底朝天密码也没改过可应用服务器就是连不上。最后定位到根因的时候我自己都愣了半天——不是网络、不是权限、不是代码就是配置文件的锅而且是一个极其隐蔽的配置加载问题。这篇文章我把我完整的排查思路、踩过的坑、最后怎么定位到根因的过程都写出来如果你也遇到过这种看似什么都没问题但就是连不上的诡异场景希望能帮你少走几天弯路。1. 问题现场Redis进程活着应用就是连不上1.1 先说现场情况这次出问题的环境是公司内网的一套老业务系统Redis 6.x 跑在一台 Linux 服务器上另外有三台应用服务器通过网络访问它做缓存。系统本身已经稳定跑了挺长时间不是新上线的项目之前从来没出过连接问题。故障的触发点其实很普通某天早上业务方反馈页面打开很慢我上去看应用日志发现里面刷了一堆连接Redis超时的报错大概长这样redis.clients.jedis.exceptions.JedisConnectionException: Could not connect to Redis at 192.168.x.x:6379 (connect timed out)第一反应当然是怀疑Redis挂了赶紧ssh上Redis服务器看了一眼发现进程是活着的ps aux | grep redis redis 12345 0.1 0.5 52140 10240 ? Ssl Mar12 10:12 /usr/local/bin/redis-server 127.0.0.1:6379然后我顺手在服务器本地测了一下redis-cli -h 127.0.0.1 -p 6379 ping PONG本地是通的进程也是好的但应用服务器就是连不上。这就很矛盾了也是最折磨人的地方你手里同时握着服务正常和连接失败两条相互矛盾的信息如果不按层级一步步排查很容易在哪一层上钻牛角尖出不来。我这次踩的坑恰恰就是在网络层和服务层来回折腾太久完全没想到问题会出在一个配置文件上。1.2 为什么这个Case特别难查这个Case难查主要有两个原因。第一个原因是信息矛盾如果Redis进程挂了那问题很简单服务层重启就行如果防火墙拦了那问题也简单配置防火墙或安全组就行。但这回所有常规检查项都是正常的本地也通远程不通指向像网络问题可网络设备查了一圈又什么都没查出来。第二个原因是技术债。这套服务器是前一个同事交接过来的当时留下的文档里只写了Redis安装在 /usr/local/redis/ 目录下配置文件也在这个目录里我后面查了三天发现这个信息是导致整个排查过程走偏的关键——那个目录下确实有一份redis.conf但它压根不是服务实际加载的配置文件。后来我总结了一套自己的排查框架遇到类似问题就按这个顺序走网络层ping、telnet、nc确认主机连不连得通、端口通不通。服务层ps、ss、Redis日志确认进程是否存活、是否监听在期望的地址。配置层systemctl启动参数、redis-cli CONFIG GET确认服务实际加载的是哪个配置、配置的哪些参数是生效的。我这次的问题是第一层和第二层花了太多时间反复确认第三层的配置排查反而放在了最后这是最大的教训。2. 第一天网络排查差点误判成防火墙的锅2.1 从应用服务器到Redis先测三层链路排查一开始我的思路很常规先确认网络通不通。这里说的通分好几个层次从下往上分别是主机可达、端口可达、应用层可达。很多人只测一个ping就下一个网络没问题的结论其实远远不够。我当时的操作是从一台应用服务器ssh上去依次执行# 1. 主机层面有没有通 ping 192.168.x.x # 2. 端口层面6379端口是否对外开放 telnet 192.168.x.x 6379 nc -vz 192.168.x.x 6379结果很有意思ping是通的ICMP协议走到Redis服务器没有问题但telnet 192.168.x.x 6379直接超时nc也是同样的结果提示connect timed out。当时我的第一判断是防火墙把6379端口拦了。这在逻辑上是说得通的——主机能ping通说明主机在线、路由没问题但端口连不上那八成是中间某个地方把TCP给丢了。于是接下来的一整天我基本上都在跟防火墙和安全组较劲。2.2 防火墙和安全组查了个遍我们先查的是系统防火墙。因为服务器发行版是Ubuntu所以先看了ufwufw status Status: inactive系统防火墙压根没开。接着又查了iptablesiptables -L -n结果也是干干净净没有任何拦截规则。然后我又跑到云控制台看安全组入方向规则里6379端口是明确放行的来源也覆盖了应用服务器的网段。到这里我陷入了一个死胡同防火墙全都没问题可端口就是不通这不科学。那天我反复做的操作就是看安全组规则、改安全组规则、再看规则、再telnet一次来回折腾。中途甚至还试了把防火墙整个关掉来验证结果当然还是连不上。因为问题压根不在网络这一层Redis根本没有把端口监听在对外网卡上流量走到服务器就被拒了防火墙放行再多条规则也没用。2.3 第一天排查小结第一天的结论是排除了云安全组、系统防火墙和主机连通性的问题但没有实际定位到根因。回过头看这一天最大的问题是我没有在第一时间去看服务到底监听在哪个地址上。这里给所有遇到同类问题的朋友一个很直接的建议当防火墙规则全部正常、端口却仍然不通的时候下一个动作一定要去看服务监听地址而不是反复怀疑防火墙。ping通只代表主机可达telnet不通也不一定就是防火墙拦截因为服务如果只监听在127.0.0.1上它压根就不会去响应外网来的TCP请求效果跟被防火墙挡了一模一样。3. 第二天服务层排查监听地址泄露了天机3.1 进程、日志、连通性三维检查第二天我换了个思路既然网络层看起来没问题那就回到Redis服务器上把服务本身查个明白。首先是进程检查前面提到过了进程是活着的。接着看日志Redis的日志默认会输出到配置指定的logfile我用tail跟踪了一段日志tail -f /var/log/redis/redis-server.log日志里除了启动时的常规信息没有任何error级别或者fatal的记录连接不上也没有产生异常日志。这就很诡异了如果是因为密码错误、客户端发起了非法命令之类的日志里应该能看到告警但这里什么都没有说明连接请求可能压根就没到达Redis的应用层。然后我又在Redis服务器上本地测了一下redis-cli -h 127.0.0.1 -p 6379 ping返回 PONG说明Redis本身处理本地请求是没问题的。到了这一步我心里隐隐觉得问题可能出在监听地址上因为本机能连、外网不能连最经典的解释就是服务没有监听在外部网卡上。3.2 ss -lntp 一锤定音只监听了127.0.0.1顺着这个猜测我执行了这条命令看到结果的一瞬间整个排查方向就明朗了ss -lntp | grep 6379 LISTEN 0 511 127.0.0.1:6379 0.0.0.0:* users:((redis-server,pid12345,fd6))注意看127.0.0.1:6379这一段——Redis确实在监听6379端口但它只绑定了回环地址没有绑定服务器对外的内网IP。这种情况下别说是外网就连同一台服务器上通过非回环IP比如内网IP去访问6379都不会成功。这也就解释了为什么防火墙和安全组都放行了仍然连不上防火墙检查的是外部请求到达服务器时是否放行而Redis的监听地址决定了内核是否把收到的请求交给Redis进程。如果服务只监听回环地址即使防火墙把流量放进来内核也会因为目标地址不匹配而直接拒绝最终表现就跟连接被切断一样客户端看起来就是超时。3.3 bind指令详解以及和protected-mode的联动定位到监听地址的问题之后我自然想到了Redis配置里的bind参数。这里给不太熟悉Redis配置的朋友简单拆解一下。bind指令控制Redis监听在哪些网络接口上常见写法有这么几种配置写法含义bind 127.0.0.1只监听本机回环地址只能本机访问bind 0.0.0.0监听所有IPv4网卡任意地址都可以访问需要配合访问控制bind 192.168.x.x只监听指定内网IP只有能访问到这个IP的机器才能连bind 127.0.0.1 -::1同时监听IPv4和IPv6回环地址另外必须要提一下protected-mode。Redis从3.2版本开始默认开启了保护模式这个参数和bind、requirepass是联动的如果protected-mode yes且没有配置bind默认监听所有地址也没有配置requirepass密码那么Redis只允许本机通过回环地址访问外部机器的连接请求会被直接拒绝。如果配置了bind 0.0.0.0但没设密码protected-mode会拒绝外部访问这就是保护模式的兜底逻辑。我当时看到这个联动机制之后信心十足地认为已经找到了根因一定是Redis配置里写死了bind 127.0.0.1导致监听地址不对。我立刻去改了配置结果后面的一堆操作反而让我又折腾了一整天。3.4 我以为找到根因了改配置、重启、失败我找到redis.conf打开一看里面确实写着bind 127.0.0.1 -::1当时心里还想果然是这个。我把它改成了bind 0.0.0.0保存之后执行了重启操作systemctl restart redis然后满怀期待地再执行ss -lntp结果让我傻眼了ss -lntp | grep 6379 LISTEN 0 511 127.0.0.1:6379 0.0.0.0:* users:((redis-server,pid22345,fd6))监听地址还是127.0.0.1:6379压根没变。明明改的是redis.conf重启后居然不生效到了这一刻这个Case才真正进入查了三天的下半场。4. 第三天配置层深挖真正的锅是加载了错误的配置文件4.1 改造重启后不生效问题出在哪里重启不生效大部分人第一反应是配置写错了或者缓存没刷新。但Redis的配置加载是每次启动时重新读取的不存在缓存这回事。所以我的猜测方向变成了两个改的这个文件不是服务实际加载的文件配置文件里存在多个bind指令后面的覆盖了前面的。第二种可能性我马上排除了因为我在文件里搜索过只有一处bind。那剩下的就是我压根没找对文件。查这个问题的关键命令是看systemd服务单元文件里到底是怎么启动Redis的systemctl cat redis然后我看到了两个被忽略掉的配置路径[Service] ExecStart/usr/local/bin/redis-server /etc/redis/redis.confExecStart里清清楚楚地写着systemd启动Redis时加载的配置文件是/etc/redis/redis.conf而不是我前面改的那个 /usr/local/redis/redis.conf。这里就体现出了这套服务器交接文档的坑文档说Redis安装在 /usr/local/redis/ 目录下我潜意识里就认为配置也在那于是去了那个目录下找redis.conf。而实际生产环境里systemd服务管理器早就把启动路径指到了 /etc/redis/ 目录下。我为什么改了三天都没发现因为/usr/local/redis/redis.conf 是前同事安装时保留的初始配置文件后来配置变更都是通过/etc/redis/redis.conf做的默认/etc/redis/redis.conf才是真正生效的那个文件而我一直在改另一个废弃的配置文件自然怎么改都没用。4.2 找到真正的配置文件修复只花了三分钟确认状态之后我重新去检查 /etc/redis/redis.conf打开一看bind 127.0.0.1 -::1问题一目了然。我做了个备份然后修改配置cp /etc/redis/redis.conf /etc/redis/redis.conf.bak.20241222 vim /etc/redis/redis.conf把bind 127.0.0.1 -::1改成了bind 0.0.0.0因为这台Redis在内网且应用层也有密码保护所以当时觉得绑定所有IPv4接口问题不大。然后重启systemctl restart redis ss -lntp | grep 6379 LISTEN 0 511 0.0.0.0:6379 0.0.0.0:* users:((redis-server,pid33456,fd6))这次监听地址终于变成了0.0.0.0:6379。我立刻回到应用服务器上重试telnet 192.168.x.x 6379 Trying 192.168.x.x... Connected to 192.168.x.x. Escape character is ^].端口通了。再刷新应用日志里Redis连接报错消失业务恢复正常。整个修复过程其实只有三分钟但定位这个根因花了整整三天。4.3 通用套路如何快速确认服务实际加载的配置这个Case说到底是一个配置加载路径的坑不是Redis参数本身的坑。回头看如果第一天就能确认这个进程到底加载的是哪个配置文件后面两天完全可以省掉。这里分享一个最简单的确认方法对任何用systemd管理的服务都有效# 1. 看进程启动命令和参数 ps -ef | grep redis-server # 2. 看systemd单元文件里的ExecStart systemctl cat redis # 3. 看运行时实际生效的配置 redis-cli -p 6379 CONFIG GET bind redis-cli -p 6379 CONFIG GET protected-mode redis-cli -p 6379 INFO server尤其是第三步CONFIG GET查的是Redis运行时实际使用的配置值而不是文件里的文本。如果文件里的值和CONFIG GET返回的值不一致那说明文件改错了或者加载的根本不是这个文件。另外提一个很容易被忽略的细节Redis配置文件里如果有多个相同的指令后面出现的会覆盖前面的。比如同一个文件里写了两行bind第一行bind 127.0.0.1后面又写了一个bind 0.0.0.0最终生效的是0.0.0.0。我的建议是遇到问题先用CONFIG GET确认运行时状态再回头看文件不要光凭文件内容下结论。5. Redis连接失败常见配置陷阱与排查对照表5.1 高频配置类故障速查这次踩坑之后我专门整理了一份Redis连接失败的速查表这里贴出来给各位参考。以后遇见连接不上按表里对应情况去查效率会高很多。现象特征可能的配置原因快速定位方法本地redis-cli正常远程连不上bind只配了127.0.0.1服务没监听外部网卡ss -lntp看监听地址CONFIG GET bind看运行时值改完配置重启后不生效systemd或脚本加载了另一个配置文件systemctl cat 服务名看 ExecStartps -ef看启动参数连接报NOAUTH或ERR Client sent AUTH密码没配置或客户端密码配置不一致Redis 6.0后ACL用户权限问题CONFIG GET requirepass检查客户端连接池的 password 配置端口是通的连上后立刻断开timeout或tcp-keepalive配置过短CONFIG GET timeout查看客户端连接池的空闲检测配置并发一高就报连接失败maxclients达到上限或tcp-backlog过小INFO clients看 connected_clients查看 /proc/sys/net/core/somaxconn用Docker部署后宿主机连不上容器端口映射没做或挂载的配置文件和容器内路径不对docker ps看端口映射检查挂载卷路径用Redis Desktop Manager等图形工具连不上保护模式开启、bind限制、SSH隧道未启用工具箱所在机器先telnet一下再逐项核对 bind 和 protected-mode从热词里也可以看到很多人用 Redis Desktop Manager、Another Redis Desktop Manager 这类可视化工具连接时最容易撞上的就是bind和protected-mode的问题。图形化工具本质上也只是一个Redis客户端它能不能连上取决于网络可达性和服务端配置跟工具本身关系不大。出问题的时候先用命令行redis-cli -h 地址 -p 端口 ping测一遍能帮你快速判断是服务端问题还是工具配置问题。5.2 容易被忽略的配置细节除了上面表格里的常见问题我这次排查还积累了一些细节经验。第一个是配置文件里的include指令。Redis支持用include /path/to/other.conf的方式引入其他配置文件而且include可以放在文件的任意位置。如果主配置文件和被引入的文件里有相同的指令后加载的那个会覆盖先加载的。这意味着你看到的配置文件内容可能不是最终生效的内容这种情况最坑。第二个是protected-mode。我见过不少同学为了图方便直接bind 0.0.0.0但又没设密码结果外部连接被保护模式拒绝。Redis的保护模式是一个安全兜底它的设计初衷是如果你明确暴露到了非回环地址又没有配密码那就只允许本机访问。所以生产环境里如果必须绑定0.0.0.0请务必同时配置requirepass或使用ACL用户认证否则要么连不上要么不安全。第三个是Docker部署时的配置路径问题。用Docker跑Redis配置文件是通过-v挂载进容器的命令长这样docker run -d \ --name redis \ -p 6379:6379 \ -v /opt/redis/redis.conf:/etc/redis/redis.conf \ redis:7.0这种情况下坑点在于/opt/redis/redis.conf里的bind配置、protected-mode配置、以及容器内Redis是否以daemonize yes启动都会影响最终行为。尤其是配置文件权限如果宿主机上文件权限是600且属主不是容器内Redis进程的用户容器里可能连读都读不到导致启动时静默使用默认配置。建议用Docker部署时先起一个不带挂载的容器确认能连再加挂载逐个排查。5.3 日志与运行时状态是排查的指南针这次三天排查里日志给了我很多信息只不过当时没注意到。Redis默认日志路径因安装方式不同有差异常见的有/var/log/redis/redis-server.log和编译安装时的/usr/local/redis/log/redis.log。如果配置里写的是logfile 日志会输出到标准输出当daemonize yes的时候标准输出可能被重定向到/dev/null这时候日志反而是看不到的。排查连接问题时日志重点看两类信息启动阶段的加载信息Redis启动时会打印当前配置文件的路径、监听端口、是否开启了保护模式等这会直接告诉你系统加载的是哪个conf。运行阶段的连接告警比如Possible SECURITY ATTACK detected、AUTH password called without any password configured等这些信息能帮你快速定位认证和保护模式的问题。另外redis-cli本身也是一个很好的诊断工具# 查看当前的连接数 redis-cli -p 6379 INFO clients # 查看所有可写的配置项当前值 redis-cli -p 6379 CONFIG GET * # 临时修改配置重启后失效 redis-cli -p 6379 CONFIG SET maxclients 10000 # 把当前运行时配置持久化到配置文件 redis-cli -p 6379 CONFIG REWRITE其中CONFIG REWRITE是把运行时内存中的配置写回配置文件这个命令适合临时用CONFIG SET改完参数后确认没问题再持久化。要注意CONFIG REWRITE只会修改文件里已有的指令不会把所有的默认配置都写进去。6. 三天教训换来的排查流程与实操心得6.1 我现在固定使用的排查四步踩过这次坑之后我把Redis连接失败的排查流程固化成了四步现在基本能在10分钟内定位问题。第一步看进程启动参数ps -ef | grep redis-server这一步是为了确认Redis到底是怎么起起来的是systemd管的还是手动redis-server redis.conf起的还是Docker容器里的。启动方式直接决定了配置文件路径在哪。第二步看监听地址ss -lntp | grep 6379这一步确认Redis监听在哪个网卡上。如果看到127.0.0.1直接锁定bind问题如果看到0.0.0.0那问题大概率在防火墙或者认证配置上。第三步查运行时配置redis-cli -p 6379 CONFIG GET bind redis-cli -p 6379 CONFIG GET protected-mode redis-cli -p 6379 CONFIG GET requirepass这一步是确认运行时真正生效的值与文件内容做对比能快速发现改错文件或者include覆盖的问题。第四步查服务编排文件的配置加载路径systemctl cat redis # 或 docker inspect redis这一步就是把流程走完的最后一块拼图。尤其是systemd场景ExecStart里的配置文件路径才是真正的生效路径。6.2 一些长期的配置管理建议查了三天配置根源是一个文档信息不准配置路径不统一的组合问题。事后我做了一系列改进也算是对这次事故的一个交代。第一配置管理要收敛。不要同一套环境存在多份redis.conf尤其是安装目录一份、/etc/redis/ 一份这样早晚会有人改错。如果不方便重构至少在文档里写明实际生效的配置是哪个文件。第二配置文件变更要有记录。我现在的习惯是每次改配置之前先cp做备份改完之后重启并立即执行ss -lntp和CONFIG GET验证一遍确认没有改了没生效的情况。第三安全上不要裸奔。Redis如果确实需要对外提供服务建议绑定明确的内网IP不要图省事直接bind 0.0.0.0同时必须配置requirepass。如果用了Redis 6.0以上的版本还可以用ACL为不同业务创建独立用户给每个用户只开最小权限。绑定IP加密码认证这两条能挡掉绝大多数外部风险。第四Docker和云环境额外注意。云上服务器除了系统防火墙还要留意安全组和网络ACLDocker部署要确认端口映射、挂载路径和配置文件权限。这类问题看似是Redis连接失败其实根源在编排配置排查时不要只盯着Redis本身。6.3 最后再分享一个小技巧如果你手头有应用服务器的访问权限有一个判断手法特别管用在应用服务器上执行telnet Redis服务器IP 6379和nc -vz Redis服务器IP 6379。如果在应用服务器上telnet不通但在Redis服务器本机telnet 127.0.0.1 6379是通的那99%的问题出在服务没监听在外部地址或者中间网络设备拦截上。这时候用ss -lntp看一眼如果Redis监听的是127.0.0.1那网络设备也不用查了直接改bind配置就行。我个人现在接手任何一台Redis服务器第一件事永远是先跑一遍启动参数、监听地址、运行时配置这三条命令确认心里有底再去谈后续优化。这次查了三天的问题本质上不是Redis多复杂而是默认配置和管理习惯埋下的雷。希望这篇记录能帮你把同类问题变成十分钟内就能定位的常规Case。