Headlamp 是 Kubernetes 官方社区维护的 Web UI,适合用来查看工作负载、Service、Ingress、Pod 日志、事件等资源。本文记录在已经安装好 ingress-nginxMetalLBcert-manager 的集群里部署 Headlamp,并通过 https://headlamp.jihw.top 访问。

安装 Headlamp

添加 Helm 仓库并安装:

helm repo add headlamp https://kubernetes-sigs.github.io/headlamp/
helm repo update

kubectl create namespace headlamp

helm upgrade --install headlamp headlamp/headlamp \
  -n headlamp

查看运行状态:

kubectl -n headlamp get pod,svc -o wide

正常情况下可以看到 Headlamp Pod 为 Running,Service 类型为 ClusterIP

kubectl -n headlamp get pod
kubectl -n headlamp get svc

创建登录账号

为了先快速验证集群管理功能,可以创建一个 cluster-admin 权限的 ServiceAccount:

kubectl -n headlamp create serviceaccount headlamp-admin

kubectl create clusterrolebinding headlamp-admin-cluster-admin \
  --serviceaccount=headlamp:headlamp-admin \
  --clusterrole=cluster-admin

生成登录 Token:

kubectl -n headlamp create token headlamp-admin --duration=24h

打开 Headlamp 后选择 Token 登录,把上面命令输出的 Token 粘贴进去即可。

如果之前已经创建过同名 ClusterRoleBinding/headlamp-admin,需要注意它可能是 Helm 默认创建的绑定,绑定对象不一定是 headlamp-admin 这个 ServiceAccount。可以用下面的命令确认权限:

kubectl auth can-i list nodes.metrics.k8s.io \
  --as=system:serviceaccount:headlamp:headlamp-admin

如果输出是 no,重新创建一个独立名称的绑定:

kubectl create clusterrolebinding headlamp-admin-cluster-admin \
  --serviceaccount=headlamp:headlamp-admin \
  --clusterrole=cluster-admin \
  --dry-run=client -o yaml | kubectl apply -f -

创建长期登录 Token

kubectl create token 生成的是短期 Token,过期后 Headlamp 会要求重新登录。如果只是内网自用,可以手动创建一个 kubernetes.io/service-account-token 类型的 Secret,让 Kubernetes 为 headlamp-admin 生成长期 Token。

注意:这里给的是 cluster-admin 权限,只适合可信内网环境,不要暴露到公网。

先确认 ServiceAccount 和权限绑定已经存在:

kubectl -n headlamp get serviceaccount headlamp-admin
kubectl get clusterrolebinding headlamp-admin-cluster-admin

如果不存在,先创建:

kubectl -n headlamp create serviceaccount headlamp-admin

kubectl create clusterrolebinding headlamp-admin-cluster-admin \
  --serviceaccount=headlamp:headlamp-admin \
  --clusterrole=cluster-admin \
  --dry-run=client -o yaml | kubectl apply -f -

创建长期 Token Secret:

cat <<'EOF' | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
  name: headlamp-admin-token
  namespace: headlamp
  annotations:
    kubernetes.io/service-account.name: headlamp-admin
type: kubernetes.io/service-account-token
EOF

等待几秒后获取 Token:

kubectl -n headlamp get secret headlamp-admin-token \
  -o jsonpath='{.data.token}' | base64 -d

把输出粘贴到 Headlamp 的 Token 登录页。以后如果浏览器丢失登录状态,可以继续用这个 Token 登录。

确认权限:

TOKEN=$(kubectl -n headlamp get secret headlamp-admin-token \
  -o jsonpath='{.data.token}' | base64 -d)

kubectl auth can-i '*' '*' --all-namespaces --token="$TOKEN"
kubectl auth can-i list nodes.metrics.k8s.io --token="$TOKEN"

两个命令都应该输出:

yes

如果以后要废弃这个长期 Token,删除 Secret 即可:

kubectl -n headlamp delete secret headlamp-admin-token

配置 HTTPS 访问

这里使用 ingress-nginx 暴露 Headlamp,并使用之前配置好的生产证书签发器 letsencrypt-alidns-prod 自动申请证书。

创建 Ingress:

cat <<'EOF' | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: headlamp
  namespace: headlamp
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-alidns-prod
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - headlamp.jihw.top
      secretName: headlamp-jihw-top-tls
  rules:
    - host: headlamp.jihw.top
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: headlamp
                port:
                  number: 80
EOF

查看 Ingress 和证书状态:

kubectl -n headlamp get ingress,certificate -o wide

证书正常时可以看到:

certificate.cert-manager.io/headlamp-jihw-top-tls   True   headlamp-jihw-top-tls

配置 DNS

当前 ingress-nginx-controller 的 LoadBalancer IP 是 192.168.3.230

kubectl -n ingress-nginx get svc ingress-nginx-controller -o wide

所以需要把域名解析到这个 IP:

192.168.3.230 headlamp.jihw.top

如果是在 Windows 本机测试,可以先确认解析是否正确:

nslookup headlamp.jihw.top
Test-NetConnection headlamp.jihw.top -Port 443

连通后访问:

https://headlamp.jihw.top

需要注意:多个 Ingress 共用同一个 192.168.3.230 是正常的。这个 IP 属于 ingress-nginx-controller 这个 LoadBalancer Service,ingress-nginx 会根据访问的 Host,例如 headlamp.jihw.topk8s-ai.jihw.top,转发到不同后端服务。

三主节点集群的 MetalLB 注意事项

如果集群只有控制平面节点,没有单独的 Worker 节点,可能会遇到域名已经解析到 192.168.3.230,但 Windows 访问时报错:

TCP connect to 192.168.3.230:443 failed
Ping to 192.168.3.230 failed with status: DestinationHostUnreachable

原因是控制平面节点默认可能带有这个标签:

node.kubernetes.io/exclude-from-external-load-balancers=

MetalLB 默认不会在带有该标签的节点上宣告 LoadBalancer IP。如果所有节点都是控制平面节点,就会导致 192.168.3.230 没有真正对局域网宣告。

删除该标签:

kubectl label nodes --all node.kubernetes.io/exclude-from-external-load-balancers-

确认 MetalLB 已经分配并宣告 ingress-nginx-controller

kubectl get servicel2status -A -o wide

正常情况下可以看到类似输出:

NAMESPACE        NAME       ALLOCATED NODE   SERVICE NAME               SERVICE NAMESPACE
metallb-system   l2-wpcq7   k8s-master2      ingress-nginx-controller   ingress-nginx

此时再从 Windows 测试:

arp -d *
ipconfig /flushdns
nslookup headlamp.jihw.top
Test-NetConnection 192.168.3.230 -Port 443
Test-NetConnection headlamp.jihw.top -Port 443

如果 TcpTestSucceededTrue,就可以打开 https://headlamp.jihw.top 访问 Headlamp。

常用排查命令

查看 Headlamp 资源:

kubectl -n headlamp get pod,svc,ingress,certificate -o wide

查看 ingress-nginx 的 LoadBalancer IP:

kubectl -n ingress-nginx get svc ingress-nginx-controller -o wide

查看 MetalLB 是否宣告了 IP:

kubectl get servicel2status -A -o wide
kubectl -n metallb-system logs -l component=speaker --since=10m | grep -E '192.168.3.230|serviceAnnounced|serviceWithdrawn'

在集群内直接测试 Headlamp:

curl -kI https://192.168.3.230/ -H 'Host: headlamp.jihw.top'

重新生成登录 Token:

kubectl -n headlamp create token headlamp-admin --duration=24h

查看长期登录 Token:

kubectl -n headlamp get secret headlamp-admin-token \
  -o jsonpath='{.data.token}' | base64 -d

登录后提示没有权限

如果 Headlamp 页面突然提示没有权限访问,但 RBAC 没有改过,通常是浏览器里保存的旧 Token 过期了。kubectl create token 生成的是有有效期的 Token,不是永久 Token。

重新生成一个 24 小时有效的 Token:

kubectl -n headlamp create token headlamp-admin --duration=24h

确认这个 Token 有权限:

TOKEN=$(kubectl -n headlamp create token headlamp-admin --duration=24h)

kubectl auth can-i '*' '*' --all-namespaces --token="$TOKEN"
kubectl auth can-i list nodes.metrics.k8s.io --token="$TOKEN"

两个命令都应该输出:

yes

然后打开 Headlamp,退出当前登录,重新粘贴新 Token 登录。如果页面一直自动使用旧 Token,可以清理浏览器里 headlamp.jihw.top 的站点数据,或者用无痕窗口重新打开。 中文访问方式

https://headlamp.jihw.top/?lng=zh-tw