☰
K8s Web UI 访问故障排查:从访问链路到认证层逐层定位
2026/10/13 6:01:31 网站建设 项目流程

最近隔三差五就有人在群里问:“我的 k8s 集群装好了,Web UI 面板也部署进去了,但是浏览器就是打不开,到底是哪里出了问题?”这个问题我前前后后帮人排查过几十次,经验告诉我:UI 面板进不去,十有八九不是 UI 本身挂了,而是整个访问链路上某个环节没打通。如果你现在也卡在这里,别急着去翻面板配置,先把下面这条链路捋一遍,大概率能自己找到答案。这篇文章我按真实排查顺序来写,把每一层“闸门”都拆开讲清楚,适合刚入门 k8s 的运维开发,也适合部署了 Dashboard 之后一脸茫然的部署新手。

1. 排查前先弄清一件事:UI 进不去不是“一个故障”,是四层问题

很多人的第一反应是去 Web UI 容器里一顿乱改,结果越改越乱。实际上,从你敲下回车到页面渲染出来,中间要穿越好几道关卡:浏览器发请求到某个 IP 和端口,流量到达节点上的 Service 入口,Service 按规则转发给后端 Pod,Pod 再把请求交给容器里的业务进程。任何一段断了,表现都不一样,所以第一步是“先定位,再动手”。

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 dashboard

Service 的 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 Controller,Ingress 对象建了也只是一个空壳规则,根本没人去执行它。

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-app=dashboard,但 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名称> --tail=50

如果日志显示pull access denied或者not found,先检查镜像地址里的 tag 是否存在。有些人写latest,本地节点上又缓存过旧版本,导致更新之后拉的一直是旧镜像,现象就是“版本对不上”。私有仓库则要提前创建imagePullSecret,并在 Deployment 的imagePullSecrets字段里引用,这个步骤经常被漏掉。另外不同网络环境访问默认镜像仓库的速度差别很大,如果一直卡在拉取状态,建议换成本地或集群内可达的镜像源再试。

4.3 就绪探针失败,最常见的“假活”状态

有一种情况特别坑:kubectl get pods显示 Pod 是 Running,READY 却是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 --clusterrole=cluster-admin --serviceaccount=kube-system:admin-user # 当前版本推荐直接生成短期 token kubectl -n kube-system create token admin-user

如果是老的 k8s 版本,可以从 Secret 里取 Token:

kubectl -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 登录成功但接口 403,RBAC 权限没给够

登录后能看到大框架,但每个列表都在转圈,打开浏览器开发者工具,发现请求返回 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 31234

Pod 状态:

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习惯——排查到深处,反而是这些底层细节最救命。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询