
Kubernetes集群搭建好之后很多人的第一个疑问就是我该用什么来看集群里的资源状态命令行kubectl虽然功能强大但十几个namespace、上百个Pod在手没有一张可视化面板根本没法快速定位问题。Kubernetes Dashboard就是官方提供的那张“脸”把集群里的节点、工作负载、存储、配置都变成网页卡片摆在你面前。这篇文章我就把k8s部署dashboard面板的完整过程讲透从环境准备、账号创建到对外暴露访问、常见问题排查全部按我真实操作过的顺序来写顺便把那些文档里不会写、只有踩过坑才懂的细节一并交代。先交代一个问题背景很多朋友在“k8s集群搭建”阶段就已经折腾了一轮好不容易把Master节点初始化成功接着就是在Master上部署Dashboard结果发现要么Pod一直起不来要么登录进不去要么登录进去了但页面弹Token输入框弹个不停。这些问题我全都遇到过所以这篇文章会刻意多花篇幅在“为什么”上而不是光给一串命令就完了。1. 部署前的准备与环境自检1.1 版本选型与镜像说明Dashboard本身的版本节奏不算快目前主流稳定版本是2.7.0对应的Kubernetes兼容范围是1.19。如果你的集群是1.20到1.27这个范围内直接用v2.7.0基本没问题。不要盲目追新Dashboard这种管理组件追求的是稳定而不是新功能。还有一点需要注意Dashboard的镜像和历史版本信息都存在自己的GitHub仓库里官方推荐清单文件里写的镜像地址是kubernetesui/dashboard和kubernetesui/metrics-scraper。很多生产环境走的是内网离线安装或者各节点访问外网镜像仓库不顺畅这时候就涉及一个离线处理逻辑先在一台能联网的机器上docker pull镜像再docker tagdocker save最后拷贝到各节点docker load。这个步骤虽然啰嗦但避免了大批量Pod卡在ImagePullBackOff的窘境。除了镜像本身清单文件里还包含几个必用的资源对象ServiceAccount、Secret、ConfigMap、Deployment、Service和Ingress。尤其要注意Ingress这一项官方默认创建了一个kubernetes-dashboard的Ingress但在很多自建集群里IngressController根本没有安装直接kubectl apply会把Ingress资源也创建出来只是没有任何负载均衡器响应它所以不影响其他组件运行但会给你留一个“幽灵入口”。我的建议是如果是临时测试环境应用之后顺手把Ingress删掉防止后期误用。1.2 集群健康检查清单动手部署之前先花两分钟确认集群状态是值得的。我见过很多次集群本身已经处于亚健康状态然后Dashboard部署不上去大家误以为是Dashboard问题排查半天方向全错。需要执行三条基础检查命令# 查看所有节点的Ready状态 kubectl get nodes -o wide # 查看已有Pod是否都正常运行尤其是CoreDNS kubectl get pods -n kube-system -o wide # 查看API Server健康状态 kubectl get --raw/healthz节点状态必须全部是ReadyCoreDNS至少要有Running状态的PodAPI Server的/healthz要返回ok。有时候kubectl get nodes看着正常但CoreDNS是CrashLoopBackOff这种情况下Dashboard可以部署但后续面板里的服务发现列表和网络相关页面会出现各种异样排查起来非常绕。还有一点很容易被忽略检查你的kubeconfig文件里面server地址是不是通的。如果你是在别的机器上用kubectl操作集群得确认~/.kube/config里的server:字段是控制平面可达的地址。Dashboard本身不需要kubeconfig但你这个“操作者”需要否则你根本没有权限创建下文里的ServiceAccount和ClusterRoleBinding后面所有步骤都无从谈起。1.3 关键NodePort参数说明部署Dashboard时最常用的外部访问方案是NodePort。Kubernetes对NodePort的默认分配范围是30000-32767这是kube-apiserver的启动参数--service-node-port-range决定的。如果你在Service里手动指定一个nodePort: 30001这个端口必须在上述范围内否则API Server会直接拒绝创建。如果确实想用其他端口段比如业务上习惯了20000这种号码需要改kube-apiserver的静态Pod清单sudo vim /etc/kubernetes/manifests/kube-apiserver.yaml在command参数列表里找到--service-node-port-range把它改成--service-node-port-range20000-32767然后kubelet会自动重建apiserver容器。这个操作意味着整个控制平面的API Server会短暂重启生产环境要安排在维护窗口做。我个人的建议是非必要不改NodePort这种方案本身就偏向临时访问端口号是3万段还是2万段并没有本质区别为了端口号去重启控制平面不划算。检查完以上三点就可以开始真正部署了。2. Dashboard核心组件部署2.1 获取官方YAML清单Dashboard的官方推荐部署文件是聚合在一起的大YAML包含了所有必选组件。执行方式非常简单# 下载官方清单 wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml # 或者直接应用远端文件 kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml如果你的网络环境访问GitHub不太顺畅相信我别硬等。可以找一台网络条件更好的机器把文件下载好再拷到能访问集群的机器上应用。下载完以后强烈建议先把这个YAML文件完整读一遍而不是直接一把梭apply。这个习惯我保持了很久任何“官方一键脚本”都必须先扫一遍里面的资源定义原因有两个一是确认版本对不对二是提前知道下面要改哪里。在2.7.0的recommended.yaml里核心资源有以下几个Namespacekubernetes-dashboardDeploymentkubernetes-dashboardServicekubernetes-dashboard类型为ClusterIP端口443Deploymentdashboard-metrics-scraper用于收集UI上展示的统计数据其中Service的资源定义就是我们下一步要动刀的地方。2.2 修改Service为NodePort官方默认创建的Service类型是ClusterIP也就是说只能在集群内部访问。测试环境最省事的暴露方式就是把它改成NodePort这样一来任何一个集群节点的IP加上指定端口就能访问面板。推荐的做法是先下载YAML到本地用sed或者直接手动编辑把Service段改掉。这段内容大概长这样kind: Service apiVersion: v1 metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard namespace: kubernetes-dashboard spec: type: NodePort ports: - port: 443 targetPort: 8443 nodePort: 30001 selector: k8s-app: kubernetes-dashboard注意几个关键点和坑port: 443和targetPort: 8443不是笔误。Dashboard容器内部监听的是8443端口Service对外暴露443端口然后通过NodePort的30001映射到节点上。所以浏览器里访问的一定是https://节点IP:30001协议必须是https用http访问会直接Connection Refused。nodePort: 30001这个值可以省略省略时集群会在30000-32767范围内随机分配一个。我习惯手动指定因为后续配置防火墙、安全组、Ingress转发规则都需要知道确切端口随机分配虽然也能查但每次部署都要多一步查询动作没有意义。端口443和30001之间的关系很多人迷糊NodePort监听的30001收到请求后会转发到Service的443端口再转给Pod的8443端口。这是一条完整的链路。2.3 应用YAML与Pod状态验证修改完成后执行应用kubectl apply -f recommended.yaml然后查看资源状态kubectl get pods -n kubernetes-dashboard -o wide kubectl get svc -n kubernetes-dashboard正常情况下两个Pod都会在几秒钟内进入Running状态一个是kubernetes-dashboard-xxx一个是dashboard-metrics-scraper-xxx。如果Pod一直ContainerCreating最常见的两个原因一是镜像拉取不下来二是节点资源不足。镜像问题我在1.1节说过离线环境要提前把kubernetesui/dashboard:v2.7.0和kubernetesui/metrics-scraper:v1.0.9两个镜像load到所有节点上。资源问题则要检查节点内存是否充足Dashboard本身吃资源不算夸张但如果你的节点是2G内存的轻量机器同时跑着CoreDNS、etcd和其他系统组件内存很容易紧张。Pod全部Running后先用集群内部的负载均衡验证一下服务链路通不通。可以临时起一个带curl的Pod或者直接在任意节点上执行curl -k https://127.0.0.1:30001/healthz注意在节点上用curl访问的是本机自己暴露出来的NodePort。如果返回非200说明Kube-Proxy规则或者Service转发有问题趁早排查不要急着打开浏览器。3. 管理员账号与Token鉴权3.1 为什么Dashboard不能直接输用户名密码Dashboard部署完毕浏览器打开后看到的是登录页。这里有两种登录模式Kubeconfig和Token。很多第一次接触的朋友会困惑怎么没有让我创建用户名密码官方Dashboard默认没有内置的“注册账号”功能它的身份验证完全走后端Kubernetes的RBAC体系。所谓登录本质上是向kube-apiserver证明“我是谁、我有什么权限”然后再把这份身份映射到Dashboard的UI操作上。所以想登录Dashboard你必须先在集群里创建一个ServiceAccount然后把相应的权限角色绑定给这个账号。这跟我们在Linux里给用户加到sudo组是一个道理。Dashboard自己没有“用户库”它只认Kubernetes的账号体系。3.2 创建ServiceAccount并绑定ClusterRoleBinding创建一个名为admin-user的ServiceAccount并绑定到cluster-admin这个集群角色这应该是测试环境里最常见的做法。集群角色cluster-admin是Kubernetes里权限最大的角色相当于Windows的Administrator。测试环境图省事可以这么做但在多人协作或生产环境必须按最小权限原则来拆分账号否则整个集群的安全边界形同虚设。创建账号和绑定权限的操作如下# 创建ServiceAccount kubectl -n kubernetes-dashboard create serviceaccount admin-user # 创建ClusterRoleBinding绑定admin-user到cluster-admin角色 kubectl create clusterrolebinding admin-user --clusterrolecluster-admin --serviceaccountkubernetes-dashboard:admin-user命令本身不复杂但要理解它背后的两个API对象ServiceAccountKubernetes里的身份标识。它对应着一把密钥这个密钥最终会生成一个Token。ClusterRoleBinding把身份和权限连接起来的“桥梁”。--clusterrolecluster-admin表示权限模板--serviceaccountkubernetes-dashboard:admin-user表示把这份权限赋予哪个命名空间下的哪个账号。如果只创建ServiceAccount而不绑定ClusterRoleBinding或者绑定了低权限角色登录Dashboard后会看到满屏的forbidden: User system:serviceaccount:kubernetes-dashboard:admin-user cannot list resource pods in API group in the namespace default之类的错误。这是新手最常踩的坑后面第5章会专门聊排查方式。3.3 生成登录Token创建完成账号之后获取Token有两种方式。第一种使用kubectl create token命令这是Kubernetes 1.24之后推荐的方式Token会带着过期时间kubectl -n kubernetes-dashboard create token admin-user执行后终端会输出一长串eyJ开头的字符串这就是登录Token。我的习惯是把它放到一个变量里方便后面重复查看TOKEN$(kubectl -n kubernetes-dashboard create token admin-user) echo $TOKEN第二种直接从Secret里提取。注意Kubernetes 1.24版本开始ServiceAccount默认不再自动创建长期有效的Secretkubectl get secret -n kubernetes-dashboard可能看不到预期的Secret。所以我不太推荐走Secret读取的路径直接用create token就好。这里还要补一个重要提醒create token生成的Token是有TTL的默认一小时。如果你只是临时登录看一眼这个没问题但如果你希望长期使用固定的Token可以给ServiceAccount手动创建Secret并绑定注解或者直接用--duration参数控制有效期。生产环境不建议搞长期TokenDashboard管理端本来就是低频操作场景每次用的时候再生成新Token就行。4. 让外部访问Dashboard的几种方式4.1 NodePort直接暴露如果你按照第2章把Service改成了NodePort那么现在就可以通过浏览器访问了。地址格式是https://任意节点IP:30001注意用https并且浏览器会提示证书不受信任这是因为Dashboard默认使用自签名证书。正常现象选择“继续访问”或“高级-继续前往”即可。NodePort方案的优点就是快一条Service改动立刻就能从外部访问。缺点也很明显不区分来源IP任何人知道这个地址和端口都能打开登录页。加上Dashboard默认没有失败锁定机制暴力猜Token虽然不现实但暴露面过大始终是不专业的做法。所以我给的建议是NodePort只适合在内网测试环境用或者配合安全组/防火墙把来源IP白名单限制在一个很小的范围。我在真实环境里还遇到过一个细节有时候明明已经改了NodePort浏览器却始终打不开。用ss -ltnp | grep 30001查看端口监听状态发现kube-proxy的监听是正常的但外部就是不通。排查下来是云厂商安全组只放行了常用端口没有放行30001。这种问题不属于Kubernetes本身但运维排查顺序一定要从外到内安全组、节点防火墙、kube-proxy、Pod健康状态一层层剥。4.2 kubectl proxy临时隧道如果不想给集群开任何额外端口可以用kubectl proxy的方式建立一个本地到集群的加密隧道kubectl proxy --port8001 然后浏览器访问http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/这个方案的好处是不需要修改任何Service也没有节点端口暴露数据流经过你的kubectl客户端认证后进入集群。缺点是每次访问都要保持kubectl proxy进程活着且浏览器地址很长不适合团队共享。我的定位是救急专用比如在公司电脑上快速看一眼集群状况完事就关掉。4.3 Ingress集中入口生产环境更推荐用Ingress做集中访问入口。Dashboard官方YAML里其实已经自带了一个Ingress定义只是默认Host是空壳你需要按自己的域名修改。假设你的域名是dashboard.example.com使用nginx-ingress-controller作为流量入口可以创建这样一个IngressapiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: kubernetes-dashboard namespace: kubernetes-dashboard annotations: nginx.ingress.kubernetes.io/backend-protocol: HTTPS nginx.ingress.kubernetes.io/ssl-passthrough: true spec: ingressClassName: nginx tls: - hosts: - dashboard.example.com secretName: kubernetes-dashboard-tls rules: - host: dashboard.example.com http: paths: - path: / pathType: Prefix backend: service: name: kubernetes-dashboard port: number: 443这里有两个注解非常关键nginx.ingress.kubernetes.io/backend-protocol: HTTPS告诉Ingress Controller后端Service是HTTPS协议转发时要用SSL。nginx.ingress.kubernetes.io/ssl-passthrough: true让TLS握手直接穿透到后端避免在Ingress层二次终止HTTPS导致证书错乱。ssl-passthrough这个注解我只推荐在Dashboard这种特殊场景使用因为它会绕过Ingress的TLS卸载功能让nginx无法解析HTTP层内容也就无法做更多基于路径或Header的路由规则。但Dashboard自签名证书的问题决定了它就是需要这种透传方式否则nginx和后端之间二次加密握手非常容易出问题。4.4 生产环境安全加固建议Dashboard这种集群管理界面安全怎么强调都不过分。生产环境我建议至少做以下几件事关闭默认的NodePort只用Ingress访问。在Ingress前面加一层认证最简单的是basic auth或者对接企业已有的OAuth2/OIDC。对来源IP做白名单限制比如只允许办公网段访问。替换默认自签名证书用正规CA签发的证书挂到Ingress上。不要使用cluster-admin账号做日常操作按namespace或资源维度拆分只读或受限权限账号。这里特别补充证书的替换方式。Dashboard默认走的是自己生成的CA浏览器不认。如果不想每次都在浏览器里点“继续访问”可以把你的正式证书塞进Dashboard所在命名空间的Secret里名字就叫kubernetes-dashboard-certs然后让Dashboard的Deployment挂载这个Secret。具体操作# 删除自带的自签名证书Secret kubectl -n kubernetes-dashboard delete secret kubernetes-dashboard-certs # 用正式证书创建同名Secret kubectl -n kubernetes-dashboard create secret generic kubernetes-dashboard-certs --from-filetls.crt你的证书.crt --from-filetls.key你的证书.key # 滚动重启Dashboard Pod kubectl -n kubernetes-dashboard rollout restart deployment kubernetes-dashboard证书文件必须严格命名为tls.crt和tls.key否则Dashboard容器启动时会找不到对应文件直接CrashLoopBackOff。5. 常见问题与排查实录5.1 集群初始化时报api server is not healthy这个报错虽然发生在kubeadm初始化阶段不是Dashboard部署的直接报错但我发现它和后面的面板访问问题高度相关。报错全文通常是[kubelet-check] The HTTP call equal to GET /healthz failed with error: Get https://127.0.0.1:10248/healthz: dial tcp 127.0.0.1:10248: connect: connection refused还有另一种形态就是在kubeadm init末尾卡住等待[kubelet-check] Initial timeout of 40s passed最常见的是把kubelet的cgroup驱动和容器运行时对不上。环境如果使用containerd作为容器运行时需要检查/etc/containerd/config.toml里的SystemdCgroup参数是否设置为true。还有一个容易踩的坑是镜像没拉全。kubeadm init会默认拉取一系列控制平面镜像比如kube-apiserver、kube-controller-manager、kube-scheduler、etcd、coredns等。如果这些镜像没下载完整kubelet起不来healthz自然不通。一旦Master初始化失败不要反复kubeadm init先kubeadm reset清理现场再检查环境和镜像否则残留文件会让问题更隐蔽。5.2 Dashboard面板打不开或一直转圈如果Pod状态正常但浏览器访问面板页面一直转圈或者直接提示拒绝连接按下面顺序排查确认浏览器访问的是https而不是http。Dashboard对外只提供HTTPS服务用http访问会直接失败。确认访问的节点IP能通。可以在本机执行curl -k https://节点IP:30001/healthz如果通说明网络链路没问题问题出在浏览器或证书如果不通检查防火墙和安全组。确认Service是NodePort类型。如果官方YAML没有修改就直接applyService仍然是ClusterIP只有集群内部能访问外部当然打不开。另外如果页面能打开但点击左侧菜单一直转圈或接口报错大概率是dashboard-metrics-scraper组件异常或者Metrics Server没有安装。Dashboard页面上的很多统计图数据都来自Metrics API如果集群里没有metrics-server页面就会缺少节点和Pod的资源曲线菜单可能加载缓慢。这个不影响登录和基本查看但影响体验建议装一个。5.3 Token登录后403 Forbidden这个问题的原因最直接ServiceAccount没有绑定足够权限的角色。用Token登录后如果页面弹出大量错误或者在某个功能模块下报Forbidden就要回到第3章去确认ClusterRoleBinding是否创建成功。验证命令kubectl -n kubernetes-dashboard get serviceaccount admin-user kubectl get clusterrolebinding admin-user如果ClusterRoleBinding存在再用kubectl auth can-i验证账号权限kubectl auth can-i list pods --assystem:serviceaccount:kubernetes-dashboard:admin-user返回yes说明权限正常返回no说明绑定不对或角色选错了。还有一种情况Token使用了一个过期的Secret。尤其是通过Secret方式提取的Token如果Secret被删除或者被轮转旧的Token自然失效。这里补充一个细节Dashboard的Token登录框如果提示Unauthorized建议第一时间重新生成Token再试。我在实际排障中遇到过一个案例用户反馈“Token绝对正确但就是登录不了”后来发现他在复制Token时把换行符也带上了粘贴进输入框导致认证失败。这种低级错误真的一抓一大把所以我后来都建议用echo $TOKEN输出再手动全选复制不要用鼠标框选终端输出很容易带进隐藏字符。5.4 镜像拉取失败ImagePullBackOffDashboard部署最烦人的问题莫过于Pod卡在ImagePullBackOff。产生原因无非两类镜像地址访问不了或者节点上根本没有这个镜像。先看具体报错kubectl describe pod -n kubernetes-dashboard pod-name如果Event里显示Failed to pull image kubernetesui/dashboard:v2.7.0: rpc error: code Unknown desc failed to pull and unpack image ...那说明网络层拉取不了。处理办法有几种配置镜像仓库加速或镜像代理。在能联网的机器上手动docker pull再打包导入。修改YAML里的image地址替换成自己内网镜像仓库的地址。如果Event里显示image kubernetesui/dashboard:v2.7.0 not found说明已经配置了私有仓库但仓库里没有这个镜像需要把镜像先推送到私有仓库。另外我还遇到过一种隐藏情况节点磁盘满了镜像拉取下来没有足够空间解包同样会报ImagePullBackOff。排查问题不要只盯着镜像地址也要看一眼节点磁盘使用率。df -h是最快的检查方式。5.5 问题排查速查表为了方便遇到问题时能快速定位方向我把上述问题和可能原因整理成一张速查表现象优先级检查点Pod启动失败1镜像是否存在、节点磁盘空间NodePort外部无法访问1安全组/防火墙2.Service类型是否NodePort3.kube-proxy是否正常页面无法打开1协议是否为https2.证书跳过3.Service类型登录Token被拒绝1Token是否完整2.Secret是否过期3.账号是否存在登录后大量Forbidden1ClusterRoleBinding是否绑定2.角色权限是否足够页面监控曲线为空2Metrics Server是否安装2.dashboard-metrics-scraper是否Running这张表是我自己排障时候的思维框架区别清楚“致命问题”和“体验问题”能节省大量时间。我个人在实际操作中的体会是Dashboard部署本身不是高难度的活真正的门槛在于对Kubernetes账号体系和网络转发模型的理解。如果你能说清楚ServiceAccount、ClusterRoleBinding、NodePort三层逻辑那整个部署过程就变成了一套行云流水的操作而不是背命令。最后再分享一个小技巧排查Dashboard容器日志很多时候报错信息比你想的有价值得多执行kubectl logs -n kubernetes-dashboard deployment/kubernetes-dashboard看末尾输出一些奇怪的反复重定向问题可能一眼就能定位。希望这份从坑里爬出来的经验能帮你少走几步弯路。