最近隔三差五就有人在群里问“我的 k8s 集群装好了Web UI 面板也部署进去了但是浏览器就是打不开到底是哪里出了问题”这个问题我前前后后帮人排查过几十次经验告诉我UI 面板进不去十有八九不是 UI 本身挂了而是整个访问链路上某个环节没打通。如果你现在也卡在这里别急着去翻面板配置先把下面这条链路捋一遍大概率能自己找到答案。这篇文章我按真实排查顺序来写把每一层“闸门”都拆开讲清楚适合刚入门 k8s 的运维开发也适合部署了 Dashboard 之后一脸茫然的部署新手。1. 排查前先弄清一件事UI 进不去不是“一个故障”是四层问题很多人的第一反应是去 Web UI 容器里一顿乱改结果越改越乱。实际上从你敲下回车到页面渲染出来中间要穿越好几道关卡浏览器发请求到某个 IP 和端口流量到达节点上的 Service 入口Service 按规则转发给后端 PodPod 再把请求交给容器里的业务进程。任何一段断了表现都不一样所以第一步是“先定位再动手”。1.1 完整的访问链路长什么样以最常见的 NodePort 暴露方式为例整条链路是这样的浏览器 -- 节点IP:NodePort -- Service(ClusterIP:Port) -- Pod:ContainerPort -- 容器内监听进程如果你用了 Ingress就再往前面插一段浏览器 -- 域名解析到 NodeIP -- Ingress Controller -- Service -- Pod这里面每一个箭头都是一层“可能出问题的地方”。我见过最离谱的一次用户折腾了一天最后发现他把 NodePort 端口号记错了一直访问的是另外一个服务。所以拿到问题先画链路把每一层的实际值列出来比瞎猜靠谱得多。1.2 不同报错信号对应不同的嫌疑层凭经验我把常见的“进不去”现象和嫌疑层位放在一起方便你对号入座浏览器表现最可能的故障层优先排查方向“无法访问此网站”“连接被拒绝”入口层端口没监听、防火墙拦截、协议写错一直转圈然后超时网络转发层Service 与 Pod 不通、NodePort 未生效502 Bad GatewayService 到 Pod 转发层Endpoints 为空、Pod 未 Ready、探针失败504 Gateway Timeout后端容器层容器内进程僵死、端口没起来403 / 401认证授权层Token 无效、RBAC 权限不足页面白屏 / 静态资源加载失败UI 应用层前端资源路径不对、代理方式错误这张表不是严格的“一道错题只有一种表现”但大方向不会错。接下来我从外往里逐层排查这也是每次排障最快的路径。2. 第一道闸门浏览器和服务暴露方式这里躺着一半的“进不去”这一层全是基础问题但也是最高发的一层。很多“面板进不去”根本不是 k8s 的问题而是访问姿势不对。2.1 地址和端口先当面确认清楚先说 NodePort 方式。你把 Dashboard 部署好之后肯定会看到类似这样的输出kubectl get svc -n kube-system | grep dashboardService 的 PORT(S) 列会显示类似443:31234/TCP意思是集群内的 ClusterIP 端口是 443映射到节点的 NodePort 是 31234。浏览器要访问的是节点 IP 加 NodePort不是 Pod IP也不是 ClusterIP。# 获取节点 IP kubectl get nodes -o wide看到 INTERNAL-IP 之后浏览器访问地址应该是https://节点IP:31234很多人会把 Pod IP 拿来访问Pod IP 是 10.x 的内网地址只在集群内部可达浏览器当然进不去。还有一种情况访问的是https://节点IP:443这也是错的443 是 ClusterIP 上的端口并没有绑定到节点上。验证节点端口有没有在监听ss -lntp | grep 31234如果没有任何输出说明 NodePort 没有暴露成功或者这条命令跑错了节点。NodePort 默认分配范围是 30000-32767如果你手动指定的端口不在这个范围内Service 创建时会直接报错这个也要留意。2.2 协议是 HTTPS 还是 HTTP别小看这个坑官方 Dashboard 默认跑在 HTTPS 上容器内部监听 8443 或者 443。所以浏览器地址栏一定要带上https://你用http://去访问大概率会得到“无法建立安全连接”或者直接连不上。即便带了 HTTPS因为是自签名证书浏览器还会弹一个大红警告页面。这不是故障这是证书校验提醒点“高级”再点“继续前往”就能进去。如果你用的是 Chrome某些情况下 HSTS 策略会不允许你跳过这时候可以换个浏览器或者临时用 curl 验证一下curl -k https://节点IP:31234如果 curl 能返回 HTML 内容或者 HTTP 状态码说明 Web 服务本身是通的问题只出在浏览器侧的证书处理上。2.3 Ingress 配了但 Ingress Controller 根本没装我见过一个特别典型的场景用户照着网上教程配了一个 Ingress 对象然后访问域名打不开就开始怀疑 DNS、怀疑证书、怀疑网络。其实原因很简单——集群里压根没有 Ingress ControllerIngress 对象建了也只是一个空壳规则根本没人去执行它。kubectl get ingress -A kubectl get pods -n ingress-nginx如果 Ingress 的 ADDRESS 列是空的或者根本没有 ingress-nginx 相关的 Pod 在运行那就说明这套网关组件没部署。先解决 Controller 的问题域名才能通。另外也别忽略 hosts 解析本地调试可以直接写/etc/hosts把域名指向节点 IP再用curl -H Host: dashboard.example.com https://节点IP/绕过 DNS 来验证。2.4 防火墙与安全组外网访问的最后一道坎集群节点本机访问没问题但别的机器访问不了这种情况下 90% 是防火墙。检查几个地方# 本机如果开了 firewalld 或 ufw看端口是否放行 firewall-cmd --list-all ufw status # 在另一台机器上发起连接测试 curl -k https://节点IP:31234如果你用的是云服务器还要去云控制台看安全组规则确认入方向放行了 31234 这类高端口。这个环节最容易忽略因为本地 curl 通的时候容易误以为“都正常”实际上只是本地到本地的回环畅通。3. 第二道链路Service 转发和标签匹配越查越露馅入口层确认没问题之后就该检查 Service 到底有没有把流量正确转发到后端 Pod 了。这一层的问题很隐蔽因为从表面看 Service 是“存在”的Pod 好像也是“Running”的但流量就是进不去。3.1 Endpoints 是空的还是满的一眼看穿Service 的转发目标全靠 Endpoints 这张表。如果这张表里没有地址说明 Service 和 Pod 之间根本没建立联系。kubectl -n kube-system get endpoints如果对应 Endpoints 的 ADDRESS 列为空或者describe出来显示No endpoints那问题基本锁定Service 的 selector 没有匹配到任何 Pod。kubectl -n kube-system describe svc 服务名注意看输出里的Selector和Endpoints两行。Selector 写的是一组标签Endpoints 是实际匹配到的 Pod IP 列表。如果 Selector 明明写着k8s-appdashboard但 Endpoints 为空就去看看 Pod 的标签到底写的什么kubectl -n kube-system get pods --show-labels经常有人把app: dashboard和k8s-app: dashboard混着写标签差一个字符串Service 就一个后端都匹配不到。3.2 端口名与 targetPort 的对不上Service 定义里targetPort可以写数字也可以写端口名。如果写的是端口名Pod 容器里必须声明同名端口否则也会出现转发异常# Service 片段 ports: - port: 443 targetPort: https对应的 Deployment 容器里ports: - containerPort: 8443 name: https这种写法本身挺常见但就怕两个文件不是一个人写的“https”这里写成“https-port”那就匹配不上。查起来也简单kubectl describe pod看容器的 Ports 一栏和 Service 的 targetPort 对照一下就行。3.3 同一个名字不同 Namespace最容易犯浑当我问你“服务名是什么”的时候一定要确认这是哪个 Namespace 里的服务。k8s 允许不同命名空间存在同名 Service很多人部署 Dashboard 时按教程写在了kube-system然后自己又在default命名空间建了一个同名 Service两者端口号还不一样。后面排查配置时眼睛一滑看漏了 Namespace就会一直拿错误的对象在分析。kubectl get svc -A | grep -i dashboard所有命名空间一起列出来一眼就能看到是否存在重复名字。排查时养成习惯所有命令都带上-n 命名空间能避免一大半混乱。4. 第三道闸门Pod 到底起没起好事件里全是答案入口和 Service 都正常接下来就要看容器本身了。这一层我见过的问题最多而且很多是光看 Pod 状态看不出来的。4.1 状态栏不等于 Running先看完整状态kubectl -n kube-system get pods -o wide看到Running不等于万事大吉还要看 READY 列比如1/1才是“容器起来了且探针通过”。如果看到0/1比如0/1 Running说明容器虽然活着但就绪探针没过Service 也不会把流量导给它。如果看到CrashLoopBackOff或者ImagePullBackOff就更直接了容器处于反复重启或镜像拉取失败的状态。这时候不要猜直接看事件的详细输出kubectl -n kube-system describe pod Pod名称events 部分会明确写失败原因比如Failed to pull image、Back-off restarting failed container、Liveness probe failed这些都是根因线索。4.2 镜像仓库的坑拉不下来全都白搭ImagePullBackOff这个状态我强调过很多次绝大多数原因就三类镜像地址写错、tag 写错、仓库需要认证没认证。kubectl -n kube-system logs Pod名称 --tail50如果日志显示pull access denied或者not found先检查镜像地址里的 tag 是否存在。有些人写latest本地节点上又缓存过旧版本导致更新之后拉的一直是旧镜像现象就是“版本对不上”。私有仓库则要提前创建imagePullSecret并在 Deployment 的imagePullSecrets字段里引用这个步骤经常被漏掉。另外不同网络环境访问默认镜像仓库的速度差别很大如果一直卡在拉取状态建议换成本地或集群内可达的镜像源再试。4.3 就绪探针失败最常见的“假活”状态有一种情况特别坑kubectl get pods显示 Pod 是 RunningREADY 却是0/1但容器本身没崩日志也看不出异常。你直接访问 Pod IP 的端口可能也能通但通过 Service 访问就是 502。这是就绪探针readinessProbe在拦截流量。探针检查失败k8s 就认为这个 Pod 还没准备好不会把它加入 Service 的 Endpoints。可 Pod 本身业务是正常的这就会造成“Pod 活着但访问不到”的假象。看探针配置kubectl -n kube-system get deploy Deployment名称 -o yaml | grep -A 15 readinessProbe常见原因是探针路径写得太严格。比如 Dashboard 这种 Web 应用根路径/可能会返回 302 重定向到登录页如果探针配置要求 HTTP 200那永远都不过。稳妥的做法是把探针路径指到一个真实存在的静态资源或者 API 上或者改用exec方式在容器内执行命令检查。这个细节排查要花点时间但一旦理解了探针语义这类问题就再也不会迷糊了。4.4 架构不匹配ARM 机器上尤其容易踩最近的个人服务器、开发板、本地虚拟化环境里ARM 架构很常见。但很多镜像只提供了 amd64 版本你在 ARM 节点上强行拉下来容器会出现典型的exec format error。kubectl describe pod Pod名称 | grep -A 3 Events如果事件里出现类似standard_init_linux.go: exec format error的字样基本就是架构不对。解决办法是换用该组件提供的 arm64 镜像 tag或者选支持多架构构建的镜像源拉取多架构版本。这个坑在 x86 机器上不会出现所以很多老运维偶尔也会被绊一下。5. 第四道闸门Web UI 自己的认证层登录页进不去也不能算“进去”链路全通之后有些人卡在登录界面或者登录进去之后一堆接口 403这属于 UI 应用自身的认证授权问题。这种问题跟网络没关系但同样可以让人抓狂一整天。5.1 Token 获取姿势不对登录永远失败Dashboard 默认支持 Token 登录和 kubeconfig 登录。Token 登录前你得先创建一个有权限的 ServiceAccount并把它绑定到集群管理角色上。注意只创建 ServiceAccount 不绑定角色Token 是能拿到但登录进去后所有接口都会 403。推荐直接用下面这一套# 创建 ServiceAccount kubectl -n kube-system create serviceaccount admin-user # 绑定 cluster-admin 集群角色 kubectl -n kube-system create clusterrolebinding admin-user --clusterrolecluster-admin --serviceaccountkube-system:admin-user # 当前版本推荐直接生成短期 token kubectl -n kube-system create token admin-user如果是老的 k8s 版本可以从 Secret 里取 Tokenkubectl -n kube-system get secret $(kubectl -n kube-system get sa admin-user -o jsonpath{.secrets[0].name}) -o go-template{{.data.token | base64decode}}拿到 Token 后复制到登录框里即可。这里提醒一句每次重启 Dashboard 或者集群组件后登录会话可能需要重新获取 Token别以为上次复制的一定还有效。5.2 自签名证书与浏览器的“对抗”Dashboard 默认用自己的自签名证书浏览器必然报警。如果你不想每次都点“高级-继续”可以给 Dashboard 挂上自己的证书通过启动参数或挂载 Secret 实现。但个人使用场景下我一般就是点一下“继续访问”没必要在这个环节浪费过多时间。有一种情况需要留意如果你用 NodePort 方式暴露 HTTPS前端页面加载的静态资源请求可能因为证书域名不匹配而被浏览器拦截表现为页面空白。解决办法是访问时用-k做 curl 验证浏览器端要选“仍然继续”并确保你访问的 IP 和端口就是 NodePort 给出的那一个。5.3 登录成功但接口 403RBAC 权限没给够登录后能看到大框架但每个列表都在转圈打开浏览器开发者工具发现请求返回 403。这种情况基本就是当前 Token 对应的 ServiceAccount 权限不足。用cluster-admin绑定是最省事的方案但如果你想做最小权限控制就需要给这个 ServiceAccount 授予对应 Namespace 的 get/list/watch 权限。新手阶段不要纠结最小权限先用 cluster-admin 把功能跑通再逐步收敛权限否则容易在排查时把“权限不足”误判成“系统故障”。6. 复盘一次“全都正常但就是进不去”的抓狂经过讲一个我印象很深的实例。某次被拉去帮同事看问题同事说“Pod 是 Running 的Service 端口也是对的浏览器连不上我真的没辙了。”我远程一看确实所有表面指标都正常。6.1 现场状态初检先复现一遍kubectl -n kube-system get svc | grep dashboard # 输出kubernetes-dashboard ClusterIP 10.100.x.x 443/TCP 31234Pod 状态kubectl -n kube-system get pods -o wide # 输出kubernetes-dashboard-xxx 1/1 Running节点上 curl 一下curl -k https://127.0.0.1:31234 # 输出502 Bad Gateway这个 502 很关键说明节点上的 NodePort 是有监听的但流量没有被后端 Pod 接住。6.2 一层一层往里钻先查 Endpoints正常再查 selector正常进入 Pod 容器内部Dashboard 进程也在监听 8443。到这里表面上“全链路正常”实际上流量就是到不了容器。我让他再检查一遍节点上的 kube-proxy 状态并列出当前节点的端口监听systemctl status kube-proxy ss -lntp | grep 31234走了几步之后发现节点上 31234 这个端口被另一个进程占用了而且那个进程根本不是 kube-proxy 动态监听出来的。原因是有个业务程序在宿主机上手动绑定了 31234 端口跟 Dashboard 的 NodePort 撞了。流量到达节点后直接被那个错误进程接收于是返回 502。Dashboard 的 Service 和 Pod 一直没问题问题是出在宿主机层面的端口冲突。6.3 这类型“隐性故障”的通用排查思路这个案例给我们的教训是不要只看 k8s 资源对象的状态节点操作系统层面的端口监听、iptables/ipvs 规则、进程状态同样在访问链路上。以后再遇到“表面全通但请求异常”的情况建议按这个顺序排除# 1. 查节点上的 NodePort 是否真实监听 ss -lntp | grep NodePort # 2. 查后端 Pod IP 和容器端口 kubectl -n ns get endpoints kubectl -n ns get pods -o wide # 3. 翻 kube-proxy 的转发规则 iptables -L -n -t nat | grep NodePort # 或者在 ipvs 模式下 ipvsadm -Ln | grep ClusterIP # 4. 在节点上用 curl 直接访问 Pod IP curl -k https://PodIP:容器端口这个流程走下来绝大多数“看起来正常”的问题都会露出马脚。现在每个同事再来问我面板打不开我都会先让他们贴三条命令的输出“svc 的端口映射、pods 的状态、节点上 ss -lntp 的结果”这比远程瞎猜高效得多。至于那条 31234 端口冲突的教训我后来也没删掉那个看起来没用的ss -lntp习惯——排查到深处反而是这些底层细节最救命。