markdown
# 2026-07-23 K3s 基准环境与 Headlamp 浏览器访问复盘 SOP
本文记录 2026-07-23 的排障过程。今天暂时不关注 CAS 和业务页面,只关注 K3s 基准环境、Gateway、ops-prod 中间件入口,以及通过浏览器访问 worker 节点上的 Headlamp 控制台。
## 一、今天目标
今天目标从"业务页面打开"收敛为:
```text
K3s 集群健康
节点标签正确
Gateway 入口健康
ops-prod 基准控制台 Headlamp 可用
Windows 浏览器可以访问 192.168.3.*** 上的 NodePort
最终成功打开:
text
http://192.168.3.***:31116/headlamp/
这说明:
text
Windows 浏览器
-> 192.168.3.***:31116
-> K3s NodePort
-> ops-prod/headlamp Service
-> headlamp Pod
这条基准访问链路已经打通。
二、当前角色与访问方式
机器角色
text
192.168.3.*** = k3s-master-01
192.168.3.*** = k3s-worker-01
Windows 客户端 = 192.168.100.***
浏览器访问注意点
项目不是直接把服务暴露在 80 端口上,而是通过 NodePort 暴露。
所以不能只访问:
text
http://192.168.3.***
要访问:
text
http://192.168.3.***:<NodePort>/<path>
今天验证成功的是:
text
Headlamp: http://192.168.3.***:31116/headlamp/
注意末尾必须有:
text
/headlamp/
之前访问:
text
http://192.168.3.***:31116
返回 404,是因为 Headlamp 配置了:
text
-base-url=/headlamp
三、今天已确认健康的部分
1. K3s 节点健康
执行:
bash
cd /root/k8s-config
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
kubectl get nodes -o wide
现场输出:
text
NAME STATUS ROLES VERSION INTERNAL-IP
k3s-master-01 Ready control-plane v1.35.5+k3s1 192.168.3.***
k3s-worker-01 Ready <none> v1.35.5+k3s1 192.168.3.***
结论:
text
master Ready
worker Ready
K3s 集群基础状态正常
2. 节点标签正确
执行:
bash
kubectl get nodes --show-labels | grep -E 'ingress-ready|middleware-role'
现场确认:
text
k3s-master-01:
middleware-role=core
workload=control-plane
k3s-worker-01:
ingress-ready=true
middleware-role=general
workload=ingress
这些标签符合项目里的调度设计:
text
Nacos / RabbitMQ -> middleware-role=core
Redis / RocketMQ / MinIO / MongoDB / Headlamp -> middleware-role=general
Gateway 数据面 -> ingress-ready=true
如后续缺标签,可按实际节点名补:
bash
kubectl label node k3s-master-01 middleware-role=core workload=control-plane --overwrite
kubectl label node k3s-worker-01 ingress-ready=true middleware-role=general workload=ingress --overwrite
3. Gateway 健康
执行:
bash
kubectl get gateway -A
kubectl get svc private-gateway-nginx -n ops-prod
kubectl get pods -n ops-prod -l gateway.networking.k8s.io/gateway-name=private-gateway -o wide
kubectl get endpoints private-gateway-nginx -n ops-prod
现场输出关键点:
text
private-gateway PROGRAMMED=True
private-gateway-nginx NodePort 8080:30080/TCP
private-gateway-nginx Pod 1/1 Running
private-gateway-nginx Endpoint 10.42.1.***:8080
结论:
text
Gateway 入口已经健康
今天不需要继续改 Gateway
4. Headlamp Pod 和 Service 健康
执行:
bash
kubectl get pod,svc -n ops-prod | grep headlamp
现场输出:
text
pod/headlamp-6cdc4bc94d-bjz9c 1/1 Running
service/headlamp NodePort 80:31116/TCP
结论:
text
Headlamp 已部署
Headlamp Pod 正常
Headlamp NodePort 为 31116
四、今天遇到的问题与解决
问题 1:重新梳理项目目标,先不关注 CAS
现象
之前排障过程中混在一起的层级很多:
text
Gateway
Nacos
CAS
HTTPRoute
业务页面
中间件
浏览器访问
容易误以为浏览器打不开就一定是 CAS 或 Gateway 问题。
重新收敛后的目标
今天明确只关注:
text
K3s 基准环境
ops-prod 中间件底座
浏览器能访问 worker 上的基础控制台
暂时不处理:
text
CAS
业务服务
业务 UI
经验
排障时要先定层级,不要跨层乱查。
今天的最小闭环是:
text
节点 Ready
-> 标签正确
-> Headlamp Pod Running
-> Headlamp NodePort 存在
-> 服务器 curl 200
-> Windows TCP 端口可达
-> 浏览器打开 Headlamp
问题 2:浏览器访问 192.168.3.*** 超时
现象
浏览器显示:
text
This site can't be reached
192.168.3.*** took too long to respond.
ERR_CONNECTION_TIMED_OUT
判断
直接访问:
text
http://192.168.3.***
默认访问的是 80 端口。
但项目当前主要入口是 NodePort:
text
Headlamp: 31116
Gateway: 30080
Nacos: 30603
所以浏览器应访问:
text
http://192.168.3.***:31116/headlamp/
经验
K8s NodePort 服务必须带端口访问。
格式:
text
http://<worker-ip>:<nodeport>/<path>
问题 3:服务器上 curl 正常,但 Windows 访问超时
现象
服务器上测 NodePort 都返回 200:
bash
curl -I http://192.168.3.***:31116/headlamp/
curl -I http://127.0.0.1:31116/headlamp/
但是 Windows PowerShell 测试失败:
powershell
Test-NetConnection 192.168.3.*** -Port 31116
输出:
text
PingSucceeded : True
TcpTestSucceeded : False
判断
这说明:
text
IP 能 ping 通
TCP 端口 31116 不通
也就是说 K3s 和 Headlamp 自身没有坏,问题在 Windows 到 worker 的 TCP 端口访问。
解决
在 worker 192.168.3.*** 上检查 firewalld:
bash
firewall-cmd --state
firewall-cmd --list-ports
放行本次需要的端口:
bash
firewall-cmd --permanent --add-port=31116/tcp
firewall-cmd --permanent --add-port=30080/tcp
firewall-cmd --permanent --add-port=30603/tcp
firewall-cmd --reload
firewall-cmd --list-ports
现场最终端口列表:
text
8848/tcp
80/tcp
6443/tcp
8472/udp
10250/tcp
30000-32767/tcp
31116/tcp
30080/tcp
30603/tcp
说明:
text
31116 = Headlamp
30080 = Gateway
30603 = Nacos 控制台备用 NodePort
30000-32767/tcp = Kubernetes NodePort 默认范围
验证
回到 Windows PowerShell:
powershell
Test-NetConnection 192.168.3.*** -Port 31116
期望:
text
TcpTestSucceeded : True
随后浏览器访问:
text
http://192.168.3.***:31116/headlamp/
今天最终已经成功进入 Headlamp 页面。
问题 4:/nacos/ 访问 500,不是 Gateway 问题,而是 Nacos 没部署
现象
访问:
bash
curl -I -H "Host: jyjzhfw.qiantang.gov.cn" http://192.168.3.***:30080/nacos/
返回:
text
HTTP/1.1 500 Internal Server Error
查询 Nacos 资源:
bash
kubectl get pods -n ops-prod -l app=nacos -o wide
kubectl get svc nacos -n ops-prod -o wide
kubectl get endpoints nacos -n ops-prod
kubectl get endpointslice -n ops-prod -l kubernetes.io/service-name=nacos -o wide
输出:
text
No resources found
services "nacos" not found
endpoints "nacos" not found
查询 HTTPRoute:
bash
kubectl get httproute nacos-route -n ops-prod -o yaml
kubectl describe httproute nacos-route -n ops-prod
关键状态:
text
Accepted=True
ResolvedRefs=False
Reason=BackendNotFound
Message=spec.rules[0].backendRefs[0].name: Not found: "nacos"
判断
nacos-route 配置本身是对的:
text
Host: jyjzhfw.qiantang.gov.cn
Path: /nacos
Backend: nacos:8080
但后端 Service nacos 不存在。
结论
text
/nacos/ 500 的原因不是 Gateway
而是 Nacos 中间件尚未部署
后续处理方向
后面要部署 Nacos,需要先准备外部 MySQL:
text
导入 nacos.sql
配置 mysql-external
创建 nacos-secret
部署 Nacos
暴露 30603
浏览器访问 /nacos/
问题 5:nacos.sql 有用,但不是 kubectl apply 文件
发现
项目中有:
text
k3s-private/middleware/nacos/data/nacos.sql
确认该 SQL 中包含:
text
Data ID: tianyin-cas-prod.properties
Group: DEFAULT_GROUP
tenant_id: f54ff835-1dda-4b27-a174-fe40f416789e
type: properties
这说明它是一个 Nacos 数据库 dump,里面包含 Nacos 的配置表数据。
关键点
这个文件不是 Kubernetes YAML,不能执行:
bash
kubectl apply -f nacos.sql
它应该导入外部 MySQL 的 nacos 数据库:
bash
mysql -h <外部MySQL地址> -P 3306 -u <用户> -p nacos < /root/k8s-config/k3s-private/middleware/nacos/data/nacos.sql
风险
该 SQL 是完整 dump,包含:
text
DROP TABLE
CREATE TABLE
INSERT INTO
所以导入前必须确认:
- 是否是全新的 Nacos 库
- 如果已有数据,必须先备份
备份命令:
bash
mysqldump -h <外部MySQL地址> -P 3306 -u <用户> -p nacos > /root/nacos-backup-$(date +%F-%H%M).sql
五、可复用 SOP:从零确认 Headlamp 浏览器访问
SOP 1:固定 kubectl 环境
在 master 上执行:
bash
cd /root/k8s-config
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
SOP 2:检查节点
bash
kubectl get nodes -o wide
kubectl get nodes --show-labels | grep -E 'ingress-ready|middleware-role'
期望:
text
k3s-master-01 Ready middleware-role=core
k3s-worker-01 Ready ingress-ready=true middleware-role=general
缺标签时补:
bash
kubectl label node k3s-master-01 middleware-role=core workload=control-plane --overwrite
kubectl label node k3s-worker-01 ingress-ready=true middleware-role=general workload=ingress --overwrite
SOP 3:检查 Gateway 基准入口
bash
kubectl get gateway -A
kubectl get svc private-gateway-nginx -n ops-prod
kubectl get pods -n ops-prod -l gateway.networking.k8s.io/gateway-name=private-gateway -o wide
kubectl get endpoints private-gateway-nginx -n ops-prod
期望:
text
Gateway PROGRAMMED=True
private-gateway-nginx NodePort 8080:30080/TCP
private-gateway-nginx Pod 1/1 Running
Endpoint 非空,例如 10.42.1.***:8080
Gateway 只用于统一域名入口。
Headlamp 当前直接用 NodePort 31116 访问,不依赖 Gateway。
SOP 4:检查 Headlamp
bash
kubectl get pod,svc -n ops-prod | grep headlamp
期望:
text
pod/headlamp-xxx 1/1 Running
service/headlamp NodePort 80:31116/TCP
如果 Headlamp 不存在,单独部署:
bash
kubectl apply -f k3s-private/middleware/headlamp/rbac.yaml
kubectl apply -f k3s-private/middleware/headlamp/deployment.yaml
kubectl apply -f k3s-private/middleware/headlamp/service.yaml
如果 Service 不是 NodePort,可临时 patch:
bash
kubectl patch svc headlamp -n ops-prod -p '{"spec":{"type":"NodePort","ports":[{"name":"http","port":80,"targetPort":4466,"nodePort":31116}]}}'
观察 Pod:
bash
kubectl get pods -n ops-prod -l app=headlamp -w
看到 1/1 Running 后按 Ctrl + C。
SOP 5:在服务器上验证 NodePort
在 master 上执行:
bash
curl -I http://192.168.3.***:31116/headlamp/
在 worker 上执行:
bash
curl -I http://127.0.0.1:31116/headlamp/
curl -I http://192.168.3.***:31116/headlamp/
期望:
text
HTTP/1.1 200 OK
如果服务器上能返回 200,说明 K3s、Service、Pod 基本没问题。
SOP 6:在 Windows 上验证端口
Windows PowerShell:
powershell
Test-NetConnection 192.168.3.*** -Port 31116
判断:
text
TcpTestSucceeded=True -> Windows 到 worker 端口通
TcpTestSucceeded=False -> 网络或防火墙阻断
如果 PingSucceeded=True 但 TcpTestSucceeded=False,说明 IP 通但端口不通。
SOP 7:放行 worker 防火墙端口
在 worker 上执行:
bash
firewall-cmd --state
firewall-cmd --list-ports
放行常用端口:
bash
firewall-cmd --permanent --add-port=31116/tcp
firewall-cmd --permanent --add-port=30080/tcp
firewall-cmd --permanent --add-port=30603/tcp
firewall-cmd --reload
firewall-cmd --list-ports
如果提示:
text
Warning: ALREADY_ENABLED
不是错误,表示已经放行。
再次在 Windows 测:
powershell
Test-NetConnection 192.168.3.*** -Port 31116
成功后浏览器访问:
text
http://192.168.3.***:31116/headlamp/
SOP 8:生成 Headlamp 登录 Token
在 master 上执行:
bash
kubectl create token headlamp-admin -n ops-prod --duration=24h
浏览器中选择 Token 登录,粘贴完整 token。
说明:
text
Headlamp 没有独立用户名密码
使用 Kubernetes ServiceAccount Token 登录
SOP 9:如果仍不能访问,使用 MobaXterm SSH Tunnel
如果 Windows 到 worker 的高端口被上级网络策略阻断,可以用 SSH 隧道临时绕过。
MobaXterm Tunnel 配置:
text
Forwarded port: 31116
SSH server: 192.168.3.***
SSH login: root
Remote server: 127.0.0.1
Remote port: 31116
然后 Windows 浏览器访问:
text
http://127.0.0.1:31116/headlamp/
六、中间件下一步路线
今天已经验证 Headlamp,可继续推进中间件。
中间件部署顺序建议:
text
1. 准备外部 MySQL
2. 导入 nacos.sql 到外部 MySQL 的 nacos 库
3. 配置 mysql-external Service
4. 创建 nacos-secret
5. 部署 Nacos
6. 暴露 Nacos 控制台 30603
7. 浏览器访问 http://192.168.3.***:30603/nacos/
8. 再部署 Redis / RabbitMQ / RocketMQ / MinIO / MongoDB / ES 等
Nacos 最小部署命令草稿
先确认 SQL:
bash
ls -lh /root/k8s-config/k3s-private/middleware/nacos/data/nacos.sql
grep -o "tianyin-cas-prod.properties" /root/k8s-config/k3s-private/middleware/nacos/data/nacos.sql | head
备份已有 Nacos 库:
bash
mysqldump -h <外部MySQL地址> -P 3306 -u <用户> -p nacos > /root/nacos-backup-$(date +%F-%H%M).sql
导入:
bash
mysql -h <外部MySQL地址> -P 3306 -u <用户> -p nacos < /root/k8s-config/k3s-private/middleware/nacos/data/nacos.sql
验证配置是否存在:
bash
mysql -h <外部MySQL地址> -P 3306 -u <用户> -p nacos -e "select data_id,group_id,tenant_id from config_info where data_id='tianyin-cas-prod.properties';"
配置 MySQL IP 模式:
bash
vi k3s-private/middleware/mysql/service-endpoints.yaml
kubectl apply -f k3s-private/middleware/mysql/service-endpoints.yaml
创建 Nacos Secret:
bash
kubectl create secret generic nacos-secret -n ops-prod \
--from-literal=mysql.password='<外部MySQL密码>' \
--dry-run=client -o yaml | kubectl apply -f -
部署 Nacos:
bash
kubectl apply -f k3s-private/middleware/nacos/configmap.yaml
kubectl apply -f k3s-private/middleware/nacos/pvc.yaml
kubectl apply -f k3s-private/middleware/nacos/service.yaml
kubectl apply -f k3s-private/middleware/nacos/statefulset.yaml
kubectl apply -f k3s-private/middleware/nacos/nacos-console-nodeport.yaml
观察:
bash
kubectl get pods -n ops-prod -l app=nacos -w
检查:
bash
kubectl get svc nacos -n ops-prod
kubectl get endpoints nacos -n ops-prod
kubectl get svc nacos-console-nodeport -n ops-prod
浏览器访问:
text
http://192.168.3.***:30603/nacos/
七、今天沉淀的经验
1. 先验证最小闭环
不要一开始就追业务页面。
今天正确闭环是:
text
K3s 节点 -> Headlamp Pod -> NodePort -> Windows 端口 -> 浏览器页面
2. 服务器 curl 200 不等于浏览器可访问
服务器本机 curl 成功,只能证明集群内部或服务器本机链路可用。
Windows 浏览器还需要额外确认:
powershell
Test-NetConnection <ip> -Port <port>
3. Ping 通不等于端口通
今天 Windows 输出:
text
PingSucceeded=True
TcpTestSucceeded=False
说明路由可达,但 TCP 端口被拦。
4. Headlamp 需要完整路径
正确:
text
http://192.168.3.***:31116/headlamp/
错误或不完整:
text
http://192.168.3.***
http://192.168.3.***:31116
5. HTTPRoute 后端不存在时,不要误判为 Gateway 坏了
nacos-route 的状态:
text
Accepted=True
ResolvedRefs=False
BackendNotFound
含义是:
text
路由对象被 Gateway 接受
但后端 Service 不存在
这种情况要部署后端中间件,不要反复改 Gateway。
6. SQL 文件和 YAML 文件用途不同
nacos.sql 用于外部 MySQL:
bash
mysql ... < nacos.sql
K8s YAML 用于 Kubernetes:
bash
kubectl apply -f xxx.yaml
不要混用。
八、当前状态快照
截至本次复盘:
text
K3s 节点:Ready
节点标签:正确
Gateway:Programmed=True
Gateway NodePort:30080
Headlamp Pod:1/1 Running
Headlamp Service:NodePort 31116
Worker 防火墙:已放行 31116 / 30080 / 30603
Windows 浏览器:已成功打开 Headlamp
Nacos:尚未部署
Nacos SQL:已确认包含 tianyin-cas-prod.properties,可用于初始化外部 MySQL 的 nacos 库
要访问浏览器时候发现服务器端口无法访问,防火墙关了也没用。那就用mobaxterm ssh tulnel
间有网络策略 / 路由 / 上级防火墙拦截 TCP 高端口。这时直接用 MobaXterm SSH Tunnel:
本地端口: 3111X
SSH server: 192.168.x.251
远程地址: 127.0.0.1
远程端口: 3111x
然后浏览器访问:
http://127.0.0.1:3111x/headlamp/这一步具体在 mobaxterm 怎么操作
MobaXterm 图形界面配置【本地端口转发 Local port forwarding】(对应你的需求)
需求汇总:
本地端口:3111x
SSH 跳板机:192.168.x.251
远程目标:127.0.0.1:3111x(跳板机本机 Headlamp 服务)
访问地址:http://127.0.0.1:3111x/headlamp/
方式 A(独立隧道窗口,推荐,不依赖 SSH 会话)
打开MobaSSHTunnel
隧道参数界面
MobaXterm 顶部菜单:Tools → MobaSSHTunnel (port forwarding)
左下角点击 New SSH tunnel
选中:✅ Local port forwarding
按下面精确填写:
plaintext
Local forwarded port: 3111x
Remote server: 127.0.0.1
Remote port: 3111x
SSH server: 192.168.3.25x
SSH login: 填入登录192.168.x.251的账号(如root)
SSH port: 22(如果你的ssh不是22就改成对应端口)
点击 Save
在隧道列表选中这条规则,点上方 Start tunnel
弹出窗口输入服务器密码(或使用私钥),隧道成功建立
⚠️ 隧道窗口不能关闭,最小化即可;关闭隧道立刻断开。
隧道启动成功验证
Windows cmd 执行:
cmd
netstat -ano | findstr 31116
看到 127.0.0.1:31116 LISTENING 代表本地监听正常
- 浏览器打开:
plaintext
http://127.0.0.1:3111x/headlamp/
常见踩坑排查
3111x 端口被本地其他程序占用 → 修改 Local forwarded port 为别的端口
能 ssh 登录 192.168.3.251,但隧道不通:登录服务器执行
bash
curl http://127.0.0.1:311x/headlamp/
看 Headlamp 服务是否真的正常运行
-
防火墙拦截:确认 192.168.x.251 本地防火墙没有禁止 127.0.0.1:3x 访问
-
注意区分:Local port forwarding 不要选错成 Remote/Dynamic!