Kubernetes Dashboard 是一个 Web UI 的集群管理工具。项目主页在这里

首先根据它的主页上 README 里面的内容直接 kubectl apply -f recommended.yaml,这样集群中就会创建并运行 dashboard 的 POD。

接下来的问题是如何从外界访问到这个 UI。

我的 k8s 集群环境是一台物理主机上的三台虚拟机,每个虚拟机都是 Headless 启动,也就是说纯命令行没有桌面环境,无法打开浏览器,因此项目主页上说的 kubectl proxy 访问 http://localhost:8001 的方式不适用。

我希望最终能从物理主机上访问到这个 WebUI。

使用 NodePort

我们查看一下 kubernetes-dashboard 使用 recommended.yaml 部署之后 service 的类型是 ClusterIP

$ kubectl get svc --all-namespaces
NAMESPACE              NAME                        TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                  AGE
default                kubernetes                  ClusterIP   10.96.0.1       <none>        443/TCP                  17d
default                redis-nodeport              NodePort    10.96.39.42     <none>        6379:31250/TCP           15d
kube-system            kube-dns                    ClusterIP   10.96.0.10      <none>        53/UDP,53/TCP,9153/TCP   17d
kubernetes-dashboard   dashboard-metrics-scraper   ClusterIP   10.96.160.248   <none>        8000/TCP                 6m44s
kubernetes-dashboard   kubernetes-dashboard        ClusterIP   10.96.132.59    <none>        443/TCP                  6m45s

ClusterIP 是在 k8s 集群内部可以访问的 IP,在集群之外(例如我的物理主机)是无法 ping 通的,因此改成 NodePort 类型。

运行 edit 命令

$ kubectl edit svc kubernetes-dashboard -n kubernetes-dashboard

把 ClusterIP 改成 NodePort,保存并退出。

apiVersion: v1
kind: Service
metadata:
  ...

  labels:
    k8s-app: kubernetes-dashboard
  name: kubernetes-dashboard
  namespace: kubernetes-dashboard
  resourceVersion: "362415"
spec:
  clusterIP: 10.96.132.59
  externalTrafficPolicy: Cluster
  ports:
  - nodePort: 32410
    port: 443 
    protocol: TCP 
    targetPort: 8443
  selector:
    k8s-app: kubernetes-dashboard
  sessionAffinity: None
  type: NodePort        // 原来是 ClusterIP
status:
  loadBalancer: {}

之后我们再看 service 的信息。

kubectl get svc --all-namespaces
NAMESPACE              NAME                        TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                  AGE
default                kubernetes                  ClusterIP   10.96.0.1       <none>        443/TCP                  17d
default                redis-nodeport              NodePort    10.96.39.42     <none>        6379:31250/TCP           15d
kube-system            kube-dns                    ClusterIP   10.96.0.10      <none>        53/UDP,53/TCP,9153/TCP   17d
kubernetes-dashboard   dashboard-metrics-scraper   ClusterIP   10.96.160.248   <none>        8000/TCP                 14m
kubernetes-dashboard   kubernetes-dashboard        NodePort    10.96.132.59    <none>        443:32410/TCP            14m

可见 dashboard 的类型变成了 NodePort 并且 PORT 中多了一个 443:32410, 意思是 k8s 把集群内部的 443 端口映射到 32410 给集群外部访问。

这时,这个 service 在集群内部还是可以通过 10.96.132.59 端口 443 访问,对应到外部,通过访问集群中的任何一个节点 IP 加端口 32410 即可。

于是通过 https://192.168.56.10:32410/ 可以看到下面的界面

dashboard

创建 admin role

可以看到 dashboard 让你选择一种方式登录,这里我选择 token 的方式。

用下面这个 yaml 格式配置文件,创建 admin用户并赋予他管理员权限。

kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: admin
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
roleRef:
  kind: ClusterRole
  name: cluster-admin
  apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
  name: admin
  namespace: kube-system
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin
  namespace: kube-system
  labels:
    kubernetes.io/cluster-service: "true"
    addonmanager.kubernetes.io/mode: Reconcile

这种认证方式本质实际上是通过 Service Account 的身份认证加上 Bearer token 请求 API server 的方式,参考 Kubernetes 中的认证

获取 token


$ kubectl -n kube-system get secret|grep admin-token
admin-token-25s5j                                kubernetes.io/service-account-token   3      11s

$ kubectl -n kube-system describe secret admin-token-25s5j
Name:         admin-token-25s5j
Namespace:    kube-system
...
ca.crt:     1025 bytes
namespace:  11 bytes
token:      eyJhbG.....

最后把 token 复制到浏览器里就可以登录了。

Role-based Access Control

既然上面提到了 role 和 service account 的概念,那么再多说几句 RBAC。 所谓 RBAC 是 k8s 中管理账户和权限的概念,是 Role-based Access Control 的缩写。 它的基本概念是 Role 和 RoleBinding。

Role是一些 permission 的集合;而 RoleBinding 则是将 Role 授权给某些 User、某些 Group 或某些 ServiceAccount,他们的作用域都是在特定的 Namespace。

此外,k8s 还有一个特殊的 ClusterRole 和 ClusterRoleBinding,它的作用域是整个集群。

以 dashboard 官网上下载的 recommended.yaml 为例,看看这三者的关系。

首先是创建一个 account, ServiceAccount

apiVersion: v1
kind: ServiceAccount
metadata:
  labels:
    k8s-app: kubernetes-dashboard
  name: kubernetes-dashboard
  namespace: kubernetes-dashboard

然后是 Role,定义了这个 role 的权限。

kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  labels:
    k8s-app: kubernetes-dashboard
  name: kubernetes-dashboard
  namespace: kubernetes-dashboard
rules:
  # Allow Dashboard to get, update and delete Dashboard exclusive secrets.
    # Allow Dashboard to get and update 'kubernetes-dashboard-settings' config map.
  - apiGroups: [""]
    resources: ["configmaps"]
    resourceNames: ["kubernetes-dashboard-settings"]
    verbs: ["get", "update"]
    # Allow Dashboard to get metrics.

最后是把 account 绑定到 role 上。

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  labels:
    k8s-app: kubernetes-dashboard
  name: kubernetes-dashboard
  namespace: kubernetes-dashboard
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: kubernetes-dashboard
subjects:
  - kind: ServiceAccount
    name: kubernetes-dashboard
    namespace: kubernetes-dashboard

所以上面 3 个定义做了这样一件事:在 kubernetes-dashboard 的名空间创建名为 kubernetes-dashboard 的账户,和名为 kubernetes-dashboard 的 role,最后把它们绑定在一起。

按照 recommended.yaml 的内容,似乎不需要上面的 “创建 admin role” 这一步骤了,我是根据网上教程加了 admin 这一步。现在回想起来,可能默认的只是在 dashboard 的名空间权限不够,admin role 则是 ClusterRole 的权限。 到底是不是这样以后再说吧,下一步要先把玩一下 kubeflow 。

(完)

参考资料