K8S 搭建指南(二)

Posted by Jason Lee on 2026-07-08

前言

在本搭建指南的第一部分中,我们从零开始,详细介绍了两种主流的 Kubernetes 集群搭建方法。首先,我们探讨了如何使用 kubeadm 工具快速部署一个功能完备的集群,
这个过程涵盖了从环境准备、容器运行时安装到集群初始化的每一个步骤。随后,我们深入到手动二进制部署的细节,构建了一个高可用的生产级集群。
这包括:手动签发所有 TLS 证书,搭建高可用 etcd 集群,配置 keepalivedhaproxy 实现 API Server 的负载均衡,
并逐一部署了 kube-apiserverkube-controller-managerkube-schedulerkubeletkube-proxy 等核心组件。
最后,我们成功安装了 Calico CNI 插件和 CoreDNS 服务,完成了一个健壮的底层平台搭建。
在第一部分完成集群基础建设之后,本篇(第二部分)将聚焦于集群的上层应用和管理。我们将从安装 Kubernetes Dashboard 开始,为您提供一个直观的 Web UI 来管理集群资源。
本文也已经假定你熟悉 k8s的各种资源,概念和含义。

下面来回顾以下机器的分配:


配置hosts文件

1
2
3
4
5
6
7
8
9
10
192.168.3.120  k8smaster  (VIP)
192.168.3.121 k8snode1 k8smaster1 etcd1
192.168.3.122 k8snode2 k8smaster2 etcd2
192.168.3.123 k8snode3 k8smaster3 etcd3

192.168.3.124 k8snode4 k8worker1
192.168.3.125 k8snode5 k8worker2
192.168.3.126 k8snode6 k8worker3
192.168.3.127 k8snode7 k8worker4
...

搭建 dashboard

dashboard 对比

工具 类型 核心优势 主要缺点 适合场景
Kubernetes Dashboard Web UI 官方支持,基础功能全面 权限配置复杂,功能单一 快速搭建一个基础的、用于资源增删改查的 Web 界面。
Lens 桌面客户端 功能强大的一站式 IDE,多集群管理体验好 资源消耗大,需要本地安装,商业模式有争议 开发者和管理员日常工作的主力工具,尤其适合管理多个集群。
K9s 终端 TUI 极致轻量和高效,键盘驱动 学习曲线陡峭,无图形化界面 追求效率的命令行重度用户,用于快速排障和日常巡检。
Grafana 监控仪表盘 无与伦比的监控数据可视化能力 部署复杂,非资源管理工具 深入的性能分析、故障定位和集群健康状况的长期监控。 您可以根据团队的需求和技术偏好来选择最适合的工具,在实际工作中,很多开发者会组合使用它们,例如:•使用 K9s 进行快速的问题排查和日常操作。•使用 Lens 进行复杂的资源管理和多集群切换。•使用 Grafana 监控集群的整体性能和趋势。•为团队提供官方 Dashboard 作为一个基础的、无需本地安装的访问入口。

下面我的搭建 dashboard 用于管理k8s 集群

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
apiVersion: v1
kind: Namespace
metadata:
name: kubernetes-dashboard

---

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

---

kind: Service
apiVersion: v1
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard
namespace: kubernetes-dashboard
spec:
type: NodePort # 必须改为 NodePort
ports:
- port: 443
targetPort: 8443
nodePort: 32443 # 建议手动指定一个固定端口,方便后面 HAProxy 配置
selector:
k8s-app: kubernetes-dashboard

---

apiVersion: v1
kind: Secret
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard-certs
namespace: kubernetes-dashboard
type: Opaque

---

apiVersion: v1
kind: Secret
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard-csrf
namespace: kubernetes-dashboard
type: Opaque
data:
csrf: ""

---

apiVersion: v1
kind: Secret
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard-key-holder
namespace: kubernetes-dashboard
type: Opaque

---

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

---

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.
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["kubernetes-dashboard-key-holder", "kubernetes-dashboard-certs", "kubernetes-dashboard-csrf"]
verbs: ["get", "update", "delete"]
# 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.
- apiGroups: [""]
resources: ["services"]
resourceNames: ["heapster", "dashboard-metrics-scraper"]
verbs: ["proxy"]
- apiGroups: [""]
resources: ["services/proxy"]
resourceNames: ["heapster", "http:heapster:", "https:heapster:", "dashboard-metrics-scraper", "http:dashboard-metrics-scraper"]
verbs: ["get"]

---

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard
rules:
# Allow Metrics Scraper to get metrics from the Metrics server
- apiGroups: ["metrics.k8s.io"]
resources: ["pods", "nodes"]
verbs: ["get", "list", "watch"]

---

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

---

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: kubernetes-dashboard
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: kubernetes-dashboard
subjects:
- kind: ServiceAccount
name: kubernetes-dashboard
namespace: kubernetes-dashboard

---

kind: Deployment
apiVersion: apps/v1
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard
namespace: kubernetes-dashboard
spec:
replicas: 3
revisionHistoryLimit: 10
selector:
matchLabels:
k8s-app: kubernetes-dashboard
template:
metadata:
labels:
k8s-app: kubernetes-dashboard
spec:
# 1. 添加容忍度,允许 Pod 运行在有污点的 Master 节点上
tolerations:
- key: "node-role.kubernetes.io/master"
operator: "Exists"
effect: "NoSchedule"
- key: "node-role.kubernetes.io/control-plane"
operator: "Exists"
effect: "NoSchedule"
# 2. 添加亲和性,强制 Pod 调度到这三台 Master 节点
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- k8snode1 # 替换为你实际的 Master1 主机名
- k8snode2 # 替换为你实际的 Master2 主机名
- k8snode3 # 替换为你实际的 Master3 主机名
securityContext:
seccompProfile:
type: RuntimeDefault
containers:
- name: kubernetes-dashboard
image: kubernetesui/dashboard:v2.7.0
imagePullPolicy: Always
ports:
- containerPort: 8443
protocol: TCP
args:
- --auto-generate-certificates
- --namespace=kubernetes-dashboard
# Uncomment the following line to manually specify Kubernetes API server Host
# If not specified, Dashboard will attempt to auto discover the API server and connect
# to it. Uncomment only if the default does not work.
# - --apiserver-host=http://my-address:port
volumeMounts:
- name: kubernetes-dashboard-certs
mountPath: /certs
# Create on-disk volume to store exec logs
- mountPath: /tmp
name: tmp-volume
livenessProbe:
httpGet:
scheme: HTTPS
path: /
port: 8443
initialDelaySeconds: 30
timeoutSeconds: 30
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsUser: 1001
runAsGroup: 2001
volumes:
- name: kubernetes-dashboard-certs
secret:
secretName: kubernetes-dashboard-certs
- name: tmp-volume
emptyDir: {}
serviceAccountName: kubernetes-dashboard
nodeSelector:
"kubernetes.io/os": linux
# Comment the following tolerations if Dashboard must not be deployed on master
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule

---

kind: Service
apiVersion: v1
metadata:
labels:
k8s-app: dashboard-metrics-scraper
name: dashboard-metrics-scraper
namespace: kubernetes-dashboard
spec:
ports:
- port: 8000
targetPort: 8000
selector:
k8s-app: dashboard-metrics-scraper



---

kind: Deployment
apiVersion: apps/v1
metadata:
labels:
k8s-app: dashboard-metrics-scraper
name: dashboard-metrics-scraper
namespace: kubernetes-dashboard
spec:
replicas: 3
revisionHistoryLimit: 10
selector:
matchLabels:
k8s-app: dashboard-metrics-scraper
template:
metadata:
labels:
k8s-app: dashboard-metrics-scraper
spec:
# 1. 添加容忍度,允许 Pod 运行在有污点的 Master 节点上
tolerations:
- key: "node-role.kubernetes.io/master"
operator: "Exists"
effect: "NoSchedule"
- key: "node-role.kubernetes.io/control-plane"
operator: "Exists"
effect: "NoSchedule"

# 2. 添加亲和性,强制 Pod 调度到这三台 Master 节点
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- k8snode1 # 替换为你实际的 Master1 主机名
- k8snode2 # 替换为你实际的 Master2 主机名
- k8snode3 # 替换为你实际的 Master3 主机名
securityContext:
seccompProfile:
type: RuntimeDefault
containers:
- name: dashboard-metrics-scraper
image: kubernetesui/metrics-scraper:v1.0.8
ports:
- containerPort: 8000
protocol: TCP
livenessProbe:
httpGet:
scheme: HTTP
path: /
port: 8000
initialDelaySeconds: 30
timeoutSeconds: 30
volumeMounts:
- mountPath: /tmp
name: tmp-volume
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsUser: 1001
runAsGroup: 2001
serviceAccountName: kubernetes-dashboard
nodeSelector:
"kubernetes.io/os": linux
# Comment the following tolerations if Dashboard must not be deployed on master
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule
volumes:
- name: tmp-volume
emptyDir: {}

部署如下

1
2
3

kubectl apply -f dashborad.yaml


我们在 master 节点执行 kubectl get pods -A -o wide 得到以下数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
kube-system calico-kube-controllers-c57dfffd6-9sbjs 1/1 Running 1 (178m ago) 62d 10.244.98.192 k8snode3 <none> <none>
kube-system calico-node-52fnv 1/1 Running 0 62d 192.168.3.123 k8snode3 <none> <none>
kube-system calico-node-5ndpq 1/1 Running 1 (59d ago) 62d 192.168.3.121 k8snode1 <none> <none>
kube-system calico-node-77bst 1/1 Running 0 62d 192.168.3.125 k8snode5 <none> <none>
kube-system calico-node-8wpgl 1/1 Running 0 62d 192.168.3.122 k8snode2 <none> <none>
kube-system calico-node-bgk6r 1/1 Running 0 62d 192.168.3.124 k8snode4 <none> <none>
kube-system coredns-6dcb4b89d5-2crlk 1/1 Running 0 60d 10.244.249.3 k8snode1 <none> <none>
kube-system coredns-6dcb4b89d5-c2cb9 1/1 Running 7 (61d ago) 61d 10.244.98.194 k8snode3 <none> <none>
kube-system coredns-6dcb4b89d5-vvpp5 1/1 Running 7 (61d ago) 61d 10.244.185.193 k8snode2 <none> <none>
kubernetes-dashboard dashboard-metrics-scraper-59d6dd7d8-7vshq 1/1 Running 0 57d 10.244.185.198 k8snode2 <none> <none>
kubernetes-dashboard dashboard-metrics-scraper-59d6dd7d8-g6v9k 1/1 Running 0 57d 10.244.185.197 k8snode2 <none> <none>
kubernetes-dashboard dashboard-metrics-scraper-59d6dd7d8-t6cw6 1/1 Running 0 57d 10.244.98.197 k8snode3 <none> <none>
kubernetes-dashboard kubernetes-dashboard-955db9b78-5pz4z 1/1 Running 0 57d 10.244.185.196 k8snode2 <none> <none>
kubernetes-dashboard kubernetes-dashboard-955db9b78-j2pmw 1/1 Running 0 57d 10.244.185.199 k8snode2 <none> <none>
kubernetes-dashboard kubernetes-dashboard-955db9b78-krlpf 1/1 Running 0 57d 10.244.98.198 k8snode3 <none> <none>

可以看到,在kubernetes-dashboard 中创建 dashboard-metrics-scraper 和 kubernetes-dashboard 两个类型的组件,每个组件为 3 pod。
对于 deployment 和 service 我们可以指定 namespace 的空间

1
2
3
4
5
6
7
8
9
[root@k8snode1 ~]# kubectl get deployments  --namespace=kubernetes-dashboard -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
dashboard-metrics-scraper 3/3 3 3 57d dashboard-metrics-scraper kubernetesui/metrics-scraper:v1.0.8 k8s-app=dashboard-metrics-scraper
kubernetes-dashboard 3/3 3 3 57d kubernetes-dashboard kubernetesui/dashboard:v2.7.0 k8s-app=kubernetes-dashboard```

[root@k8snode1 ~]# kubectl get svc --namespace=kubernetes-dashboard -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
dashboard-metrics-scraper ClusterIP 10.96.144.232 <none> 8000/TCP 57d k8s-app=dashboard-metrics-scraper
kubernetes-dashboard NodePort 10.96.152.197 <none> 443:32443/TCP 57d k8s-app=kubernetes-dashboard

dashboard-metrics-scraper, kubernetes-dashboard 的功能如下

  1. kubernetes-dashboard:核心前端界面这是 Kubernetes 官方提供的 Web 用户界面,是您与集群进行可视化交互的核心入口。可以将其视为 kubectl 命令行的图形化版本。•
    • 核心功能:•资源可视化:以图形化方式展示集群中的各种资源,如节点(Nodes)、命名空间(Namespaces)、工作负载(Deployments, Pods, StatefulSets 等)、服务(Services)、存储(PersistentVolumeClaims)和配置(ConfigMaps, Secrets)等。•
    • 资源管理:允许您通过浏览器直接创建、编辑、更新和删除资源。例如,您可以上传 YAML 文件来部署应用,或者在 UI 上直接修改 Deployment 的副本数来实现扩缩容。•
    • 基本监控与排障:提供对 Pod 日志的实时查看功能,可以进入 Pod 的 Shell 执行命令,并展示资源的基本状态和事件,帮助快速定位问题。在下面的配置中,它被部署为一个 Deployment,拥有3个副本以确保高可用,并通过一个 NodePort 类型的 Service 将其 8443 端口映射到节点的 32443 端口,从而允许从集群外部访问。
  2. dashboard-metrics-scraper:指标数据抓取器可以将其理解为 Dashboard 的一个辅助组件或“数据探针”。它本身不提供用户界面,而是默默地在后台工作,为 kubernetes-dashboard 提供关键的性能指标数据。•
    • 核心功能:•指标采集:它的唯一职责是从 Kubernetes 的核心监控管道——指标服务器(Metrics Server)——中抓取各个 Pod 和节点的资源使用情况,主要是 CPU 和内存的实时数据。•
    • 数据供给:它将采集到的数据通过一个内部 API 暴露给 kubernetes-dashboard 前端。Dashboard UI 在展示 Pod 列表或节点列表时,会调用 metrics-scraper 的接口来获取并展示那些迷你的 CPU 和内存使用图表(Sparkline charts)。简单来说:如果没有 metrics-scraper,你的 Dashboard 只能看到资源“长什么样”(例如,Pod 的名字和状态),但无法看到它“活得怎么样”(例如,它消耗了多少 CPU 和内存)。那些直观的性能曲线将无法显示。

总结一下:kubernetes-dashboard 是提供操作界面的“大脑”和“躯干”,而 dashboard-metrics-scraper 则是为其提供实时性能数据的“眼睛”。两者结合,才能提供一个功能完整的可视化体验。

NameSpace 和 ServiceAccount

  1. Namespace (命名空间):集群中的逻辑隔离区您可以把 Namespace 想象成在一台计算机上创建的不同文件夹,或者在一栋办公楼里划分出的不同独立办公室。
    • 核心作用:
      • 1.资源隔离与组织:Namespace 将一个物理的 Kubernetes 集群划分为多个逻辑上的“虚拟集群”。这使得不同的团队、项目或环境(如“开发环境”、“测试环境”、“生产环境”)可以共享同一个 K8s 集群,但各自的资源(如 Pods, Deployments, Services)却互不干扰,仿佛在自己的独立空间里。
      • 2.命名范围:在不同的 Namespace 中,你可以拥有同名的资源。例如,namespace-a 里的 Service my-db 和 namespace-b 里的 Service my-db 是两个完全不同的资源,避免了命名冲突。
      • 3.访问控制:Namespace 是实现基于角色的访问控制(RBAC)的基本单位。你可以精细地为某个用户或用户组设置权限,例如“A 团队的用户只能访问 team-a 命名空间内的所有资源”。

在我们的配置中: 我们创建了一个名为 kubernetes-dashboard 的 Namespace 。这样做的好处是,所有与 Dashboard 相关的组件(包括它的 Deployment、Service、Secret 等)都会被整齐地放在这个专属的“文件夹”里,与您业务应用完全隔离,便于管理和后续的清理(例如,不再需要时,只需一条命令 kubectl delete namespace kubernetes-dashboard 即可删除所有相关资源)。

  1. ServiceAccount (服务账户):Pod 的身份标识如果说普通的用户账户(UserAccount)是给人类(比如管理员您)登录集群使用的,那么 ServiceAccount 就是专门给程序(运行在 Pod 里的进程)使用的“身份证”。•
    • 核心作用:
      • 提供身份:ServiceAccount 为 Pod 中的进程提供了一个可以在集群内部被识别的身份。当 Pod 需要与 Kubernetes API 服务器进行通信时(例如,查询其他 Pod 的信息),它就会亮出这个“身份证”来证明自己是谁。
      • 权限认证:单有身份是不够的,还需要有权限。ServiceAccount 本身不包含任何权限,但它是权限绑定的目标。通过创建 RoleBinding 或 ClusterRoleBinding,你可以将特定的权限(定义在 Role 或 ClusterRole 中)授予某个 ServiceAccount。这样,使用了该 ServiceAccount 的 Pod 就拥有了相应的操作权限。
        在我们的配置中: 我们创建了一个名为 kubernetes-dashboard 的 ServiceAccount 。这个服务账户会被 Dashboard 的 Pod 使用。后续的 RoleBinding 和 ClusterRoleBinding 配置,就是将“读取集群节点和 Pod 指标”以及“管理自身配置”等权限,精确地授予给了这个 kubernetes-dashboard 的“身份”。这样,Dashboard 程序才能合法地从 API Server 获取信息并在前端页面上展示出来。

Role 和 RoleBinding

K8s 的授权方式为RBAC(基于角色的访问控制)的四个关键组件:Role、ClusterRole、RoleBinding 和 ClusterRoleBinding。 RBAC 的核心逻辑可以概括为一句话:“把 (Bind) [什么权限] 授予 (to) [谁]”。•

  • [什么权限]:这就是 Role 和 ClusterRole 定义的内容。它们是一份“权限清单”。
  • [谁]:这就是主体(Subject),通常是 User(用户)、Group(用户组)或 ServiceAccount(服务账户)。
  • 把…授予… (Bind):这个“授权”的动作,就是 RoleBinding 和 ClusterRoleBinding 完成的工作。

Role:命名空间内的权限清单

功能:Role 定义了一系列在特定命名空间内允许执行的操作。它只对它所在的那个命名空间生效。•在配置中

1
2
3
4
5
6
7
8
9
10
11
12
13
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard
namespace: kubernetes-dashboard # <-- 关键点:指定了命名空间
rules:
- apiGroups: [""]
resources: ["secrets", "configmaps"]
resourceNames: ["kubernetes-dashboard-key-holder", "kubernetes-dashboard-certs", "kubernetes-dashboard-csrf", "kubernetes-dashboard-settings"]
verbs: ["get", "update", "delete"]
# ... 其他规则

这个 Role 定义了这样一条规则:

  • “在 kubernetes-dashboard 这个命名空间内,允许对名为 kubernetes-dashboard-key-holder 等特定的 secrets 和 configmaps 资源进行 get(获取)、update(更新)和 delete(删除)操作”。
  • 这个权限是有范围限制的。拥有这个 Role 的用户,无法操作其他命名空间(比如 default)里的 secrets。

ClusterRole:整个集群的权限清单

功能:ClusterRole 定义了一系列在整个集群范围内允许执行的操作。它不受命名空间限制。它有两种主要用途:

  • 1.授权操作集群级别的资源(例如 Nodes、PersistentVolumes,这些资源不属于任何命名空间)。
  • 2.授权操作所有命名空间中的同类资源(例如,允许“查看所有命名空间中的 Pods”)。•在配置中:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    kind: ClusterRole
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
    labels:
    k8s-app: kubernetes-dashboard
    name: kubernetes-dashboard # <-- 注意:这里没有 namespace 字段
    rules:
    - apiGroups: ["metrics.k8s.io"]
    resources: ["pods", "nodes"]
    verbs: ["get", "list", "watch"]

    这个 ClusterRole 定义了这样一条规则:“在整个集群的任何地方,允许对 metrics.k8s.io API 组下的 pods 和 nodes 资源进行 get(获取)、list(列出)和 watch(监听)操作”。
    这个权限是全局的。这是 dashboard-metrics-scraper 需要的,因为它必须能够从集群的所有节点和 Pod 中抓取监控指标,而不能只局限于某一个命名空间。

RoleBinding:在命名空间内进行授权

功能:RoleBinding 的作用就是执行“授权”这个动作。它把一个 Role 所定义的权限,授予给一个或多个主体(ServiceAccount 等),但这个授权行为仅在 RoleBinding 所在的命名空间内有效。•在配置中:YAML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
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


解释: 这部分配置的意思是:“在 kubernetes-dashboard 命名空间内,将我们上面定义的那个名为 kubernetes-dashboard 的 Role(权限清单),授予给 kubernetes-dashboard 这个 ServiceAccount”。

ClusterRoleBinding:在整个集群进行授权

功能:ClusterRoleBinding 同样是执行“授权”动作,但它授予的是全局范围的权限。它将一个 ClusterRole 授予给一个或多个主体,使其在整个集群都拥有该权限。

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: kubernetes-dashboard # <-- 注意:这里没有 namespace 字段
roleRef: # <-- [什么权限]
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: kubernetes-dashboard
subjects: # <-- [谁]
- kind: ServiceAccount
name: kubernetes-dashboard
namespace: kubernetes-dashboard

解释: 这部分配置的意思是:“在整个集群范围内,将我们上面定义的那个名为 kubernetes-dashboard 的 ClusterRole(全局权限清单),授予给 kubernetes-dashboard 这个 ServiceAccount”。正是因为有了这个 ClusterRoleBinding,Dashboard 的 Pod(通过其 ServiceAccount)才获得了在所有命名空间中查看节点和 Pod 指标的能力。

查看内置的 Role

  • 查看 ServiceAccount (服务账户)这些命令用于查看和检查赋予 Pod 身份的 ServiceAccount。Shell Script# 1. 列出 ‘kubernetes-dashboard’ 命名空间中所有的 ServiceAccount
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    # 你应该能看到我们创建的 'kubernetes-dashboard' 这个 ServiceAccount
    kubectl get serviceaccount -n kubernetes-dashboard

    # 2. 获取 'kubernetes-dashboard' ServiceAccount 的详细信息
    # 这个命令会显示它的基本信息,但不会显示权限
    kubectl describe serviceaccount kubernetes-dashboard -n kubernetes-dashboard

    # 3. (可选) 列出集群中所有命名空间下的所有 ServiceAccount
    kubectl get serviceaccount --all-namespaces
  • 查看 Role 和 RoleBinding (命名空间级别)这些命令用于检查在特定命名空间内定义的权限和授权关系。Shell Script# 1. 列出 ‘kubernetes-dashboard’ 命名空间中所有的 Role
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    # 你应该能看到我们创建的 'kubernetes-dashboard' 这个 Role
    kubectl get role -n kubernetes-dashboard

    # 2. 查看 'kubernetes-dashboard' Role 的详细权限规则
    # 这是核心!它会清楚地告诉你这个 Role 到底“能做什么”
    kubectl describe role kubernetes-dashboard -n kubernetes-dashboard

    # 3. 列出 'kubernetes-dashboard' 命名空间中所有的 RoleBinding
    # 你应该能看到我们创建的 'kubernetes-dashboard' 这个 RoleBinding
    kubectl get rolebinding -n kubernetes-dashboard

    # 4. 查看 'kubernetes-dashboard' RoleBinding 的详细信息
    # 这个命令会告诉你“谁”被授予了“什么权限”,即 Subjects 和 roleRef
    kubectl describe rolebinding kubernetes-dashboard -n kubernetes-dashboard

查看 ClusterRole 和 ClusterRoleBinding (集群级别)这些命令用于检查整个集群范围的权限和授权关系。Shell Script# 1. 列出集群中所有的 ClusterRole (因为列表很长,用 grep 过滤)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 查找与 dashboard 相关的全局权限
kubectl get clusterrole | grep dashboard
#system: 开头内置系统组件专用
#system:controller: 开头内置控制器专用
#cluster-admin / admin / edit / view
#内置用户角色其他自定义名字自己创建的


# 2. 查看 'kubernetes-dashboard' ClusterRole 的详细权限规则
# 这会显示它拥有的全局权限,比如访问 metrics.k8s.io
kubectl describe clusterrole kubernetes-dashboard

# 3. 列出集群中所有的 ClusterRoleBinding (同样用 grep 过滤)
# 查找与 dashboard 相关的全局绑定
kubectl get clusterrolebinding | grep dashboard

# 4. 查看 'kubernetes-dashboard' ClusterRoleBinding 的详细信息
# 这会显示谁(哪个 ServiceAccount)被授予了上述的全局权限
kubectl describe clusterrolebinding kubernetes-dashboard

# 5. 查看内置 ClusterRole(有 rbac-defaults 标签的)
kubectl get clusterrole -l kubernetes.io/bootstrapping=rbac-defaults

# 6. 查看自定义 ClusterRole(没有这个标签的)
kubectl get clusterrole -l '!kubernetes.io/bootstrapping'

  • 权限验证与检查 (auth can-i)这是最强大、最直接的验证工具。它能准确地回答“某某某到底能不能做某某事?”Shell Script# 1. 检查当前用户(你自己)是否可以在 ‘default’ 命名空间中列出 Pods
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

    kubectl auth can-i list pods -n default

    # 2. 检查 'kubernetes-dashboard' ServiceAccount 是否可以获取它自己的命名空间中的 secrets
    # 这是验证我们 Role 和 RoleBinding 是否生效的关键
    # --as 指定了要模拟的用户身份
    kubectl auth can-i get secrets -n kubernetes-dashboard --as=system:serviceaccount:kubernetes-dashboard:kubernetes-dashboard

    # 3. 检查 'kubernetes-dashboard' ServiceAccount 是否可以更新它自己的 ConfigMap
    kubectl auth can-i update configmaps -n kubernetes-dashboard --as=system:serviceaccount:kubernetes-dashboard:kubernetes-dashboard

    # 4. 检查 'kubernetes-dashboard' ServiceAccount 是否可以列出集群中的所有节点 (nodes)
    # 这是验证我们 ClusterRole 和 ClusterRoleBinding 是否生效的关键
    # 注意:因为 nodes 是集群资源,所以不需要 -n 参数
    kubectl auth can-i list nodes --as=system:serviceaccount:kubernetes-dashboard:kubernetes-dashboard

    # 5. 检查 'kubernetes-dashboard' ServiceAccount 是否可以列出 'default' 命名空间中的 Pod 指标
    # 这里的 'pods' 属于 'metrics.k8s.io' API 组
    kubectl auth can-i list pods.metrics.k8s.io -n default --as=system:serviceaccount:kubernetes-dashboard:kubernetes-dashboard

    auth can-i 命令的返回结果非常直观:
  • 如果返回 yes,代表有权限。
    +如果返回 no,代表没有权限。
  • 通过运行以上这些命令,特别是第 4 部分的 auth can-i,您就可以非常精确地验证您在 YAML 文件中定义的每一条 RBAC 规则是否都已在集群中正确生效。

创建自己的 namespace

下面我们要创建自己的 namespace 和 service account

创建 namespace

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
kubectl create namespace zhipeng-space

cat > zhipeng1.yaml << 'EOF'
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: zhipeng1
namespace: zhipeng-space
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: zhipeng1-admin
namespace: zhipeng-space
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: admin # zhipeng-space 内所有权限
subjects:
- kind: ServiceAccount
name: zhipeng1
namespace: zhipeng-space
EOF
kubectl apply -f zhipeng1.yaml

创建超级用户

由于我们需要使用 ui-admin 来操作集群,因此我们需要一个超级用户 ui-amdin 来管理集群,因此我们需要班定 cluster-admin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 创建超级用户 
cat > ui-admin.yaml << 'EOF'
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: ui-admin
namespace: kubernetes-dashboard
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: ui-admin
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: ui-admin
namespace: kubernetes-dashboard
EOF

kubectl apply -f ui-admin.yaml

token 登录dashboard

1
2
3
4
5
6
7
kubectl -n kubernetes-dashboard create token ui-admin
# 创建好后,我们来看地址, kubernetes-dashboard
[root@k8snode1 ~]# kubectl get svc --namespace=kubernetes-dashboard
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
dashboard-metrics-scraper ClusterIP 10.96.144.232 <none> 8000/TCP 57d
kubernetes-dashboard NodePort 10.96.152.197 <none> 443:32443/TCP 57d

我们配置是 nodePort 因此我们需要 存在 kubernetes-dashboard node ip + 32443 来访问kubernetes-dashboard


我们使用 token 登录

1
2
3

# create token for dashboard login
kubectl -n kubernetes-dashboard create token ui-admin

之后使用 打印出来的 token 来登录即可

配置 kubectl

早在第一章节,我们已经在 配置合了 kubectl 这个是负责和 api-server 交互的,我们使用了内嵌证书的方式进行,内嵌证书在创建的时候已经将用户名写进证书体里,因此我们可以通过 kubectl 对 api 进行操作
现在我们在 k8snode1中执行

1
2
3
[root@k8snode1 .kube]# kubectl config get-contexts  --kubeconfig=/root/.kube/config
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
* kubernetes kubernetes kubernetes

可以看到我们现在 kubectl config 使用的是 kubernetes,在上一个章节中,我们使用了以下命令来生成证书

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 1. 生成私钥
openssl genrsa -out admin.key 2048
# 1. 生成 CSR (注意:O=system:masters 是获得集群管理权限的关键)
# 2. 生成 CSR (注意:CN=system:kubernetes)
openssl req -new -key admin.key \
-out admin.csr \
-subj "/C=CN/ST=Beijing/L=Beijing/O=system:masters/CN=kubernetes"

... #省略签发证书步骤

# 使用以下来配置 kubectl 的上下文
kubectl config set-context kubernetes \
--cluster=kubernetes \
--user=kubernetes \
--kubeconfig=/root/.kube/config

这里我们使用了system:masters 组别,因为一个名为 cluster-admin 的 ClusterRole 永久地绑定到了一个名为 system:masters 的用户组上。这是有 k8s集群隐式完成的
我们可以看一下所有用户组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 运行这个命令,它会列出所有被 ClusterRoleBinding 使用的用户组
kubectl get clusterrolebindings -o jsonpath='{.items[*].subjects[?(@.kind=="Group")].name}' | tr " " "\n" | sort | uniq
system:authenticated
system:masters
system:monitoring
system:serviceaccounts
system:unauthenticated

[root@k8snode1 .kube]# kubectl describe clusterrolebinding cluster-admin
Name: cluster-admin
Labels: kubernetes.io/bootstrapping=rbac-defaults
Annotations: rbac.authorization.kubernetes.io/autoupdate: true
Role:
Kind: ClusterRole
Name: cluster-admin
Subjects:
Kind Name Namespace
---- ---- ---------
Group system:masters

特别说明的是,这里的组并不是k8s集群中的实体概念,对于 k8s 来说在赋权的时候可以将某个权限赋予某个人或者某个组。对于技术上来说,用户是一个 k8s实体存在的东西
而组别并不存在,也就无法查询组下究竟有多少人,组可以理解为一个用户的 tag,那么我们在服务端不会维护这个 tag实际依赖关系
那么在生成用户凭凭证的时候 可以设置 O=(证书方式) 也可以是 jwt 的 group 方式来给用户打 “tag”, 这样以来就可以实现灵活分配用户权限。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@k8snode1 certs]# cat /etc/kubernetes/cfg/kubelet.kubeconfig
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ...
server: https://k8smaster:9443
name: default-cluster
contexts:
- context:
cluster: default-cluster
namespace: default
user: default-auth
name: default-context
current-context: default-context
kind: Config
users:
- name: default-auth
user:
client-certificate: /etc/kubernetes/certs/kubelet-client-current.pem
client-key: /etc/kubernetes/certs/kubelet-client-current.pem
[root@k8snode1 certs]# openssl x509 -in /etc/kubernetes/certs/kubelet-client-current.pem -noout -subject
subject=O=system:nodes, CN=system:node:k8snode1
[root@k8snode1 certs]# openssl x509 -in kube-apiserver.crt -noout -subject
subject=CN=kubernetes

从上面的内容当中,我们是使用了 kubelet-client-current.pem 为 kebuctl 的身份,而kubelet-client-current.pem
步骤 | 技术术语 | 您的操作 | Kubelet 的行为 |
| :—- | :—- | :—- | :—- |
| 1 | 使用 Bootstrap Token | 启动 kubelet 服务 | 读取 bootstrap.kubeconfig,用临时 Token 连接 API Server。 |
| 2 | 提交 CSR | (无) | 自动生成密钥对,并向 API Server 提交一个证书签名请求。 |
| 3 | 批准 CSR | kubectl certificate approve … | 等待 CSR 的状态变为 Approved。 |
| 4 | 签发证书 | (无) | kube-controller-manager 自动用 CA 签署证书。 |
| 5 | 获取并使用新证书 | (无) | 下载新证书,保存到本地,并创建 kubelet-client-current.pem 链接。从此改用新证书进行所有通信。 |

所以,您没有创建过 kubelet-client-current.pem 的记忆是完全正确的。它是 Kubelet 在您批准了它的“入职申请”后,自己“领取”并开始使用的正式身份证明。
这个自动化过程是 Kubernetes 安全性和可扩展性的基石。

了解上述背景之后,我们就可以着手创建 kubectl 的上线问,让我们可以在非 master 机器上操作我们自己的空间,注意,这里是非 master 节点!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# 1. 创建永久 Secret 并绑定到 zhipeng1
```bash
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
name: zhipeng1-perm-token
namespace: zhipeng-space
annotations:
kubernetes.io/service-account.name: "zhipeng1"
type: kubernetes.io/service-account-token
EOF

# 2. 提取 Token 字符串到变量中
PERM_TOKEN=$(kubectl -n zhipeng-space get secret zhipeng1-perm-token -o jsonpath={.data.token} | base64 -d)
# 验证 Token 是否获取成功
echo $PERM_TOKEN


KUBE_CONFIG="zhipeng-worker.kubeconfig"
SERVER_URL="https://k8smaster:9443" # 请确保这是 node4 能访问到的 Master 地址

# 1. 设置集群信息(复用现有的 CA 证书以确保 HTTPS 通信安全)
kubectl config set-cluster kubernetes \
--certificate-authority=/etc/kubernetes/ssl/ca.crt \
--embed-certs=true \
--server=${SERVER_URL} \
--kubeconfig=${KUBE_CONFIG}

# 2. 设置用户凭据(使用刚才生成的永久 Token)
kubectl config set-credentials zhipeng1-user \
--token=${PERM_TOKEN} \
--kubeconfig=${KUBE_CONFIG}

# 3. 设置上下文,并直接默认到 zhipeng-space 命名空间
kubectl config set-context zhipeng-context \
--cluster=kubernetes \
--user=zhipeng1-user \
--namespace=zhipeng-space \
--kubeconfig=${KUBE_CONFIG}

# 4. 激活上下文
kubectl config use-context zhipeng-context --kubeconfig=${KUBE_CONFIG}

scp zhipeng-worker.kubeconfig root@k8snode4: /root/.kube/ #将 zhipeng-worker.kubeconfig 重命名为 config



[root@k8snode4 ~]# kubectl config current-context
zhipeng-context

# 这样一来,-A 获取全部的 pods 这个参数就失效了,因为你没有权限
[root@k8snode4 ~]# kubectl get pods -A -o wide
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:zhipeng-space:zhipeng1" cannot list resource "pods" in API group "" at the cluster scope

部署一个应用到 自己的空间

下面我们部署一个 nginx 到 自己的空间,注意这里需要在 非 master 节点执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-zhipeng
# namespace 可选,因为您的 node4 config 已默认指向 zhipeng-space
# namespace: zhipeng-space
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
# 使用 zhipeng1 身份运行
serviceAccountName: zhipeng1
nodeSelector:
# type: worker 因为我们给 node45 打的是一下的标签,且没有 value 所以必须这么搞才能调度到 work 上
# 修改这里:使用系统自带的标签名,后面留空字符串
node-role.kubernetes.io/worker: ""
containers:
- name: nginx
image: nginx:1.21.6
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
namespace: zhipeng-space
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
够用够用 targetPort: 80
nodePort: 30080
type:够用 NodePort

部署我够用们第一个应用

以 zhipeng 的身份 deploy nginx-service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-zhipeng
# namespace 可选,因为您的 node4 config 已默认指向 zhipeng-space
# namespace: zhipeng-space
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
# 使用 zhipeng1 身份运行
serviceAccountName: zhipeng1

nodeSelector:
# type: worker 因为我们给 node45 打的是一下的标签,且没有 value 所以必须这么搞才能调度到 work 上
# 修改这里:使用系统自带的标签名,后面留空字符串
node-role.kubernetes.io/worker: ""
containers:
- name: nginx
image: nginx:1.21.6
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
namespace: zhipeng-space
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
nodePort: 30080
type: NodePort




[root@k8snode4 yaml]# kubectl get deployment
[root@k8snode4 yaml]# kubectl get deployment -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
nginx-zhipeng 1/2 2 1 29s nginx nginx:1.21.6 app=nginx


[root@k8snode4 yaml]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-service NodePort 10.96.94.231 <none> 80:30080/TCP 66s
springboot-svc ClusterIP 10.96.105.20 <none> 80/TCP 58d

部署好之后,我们检查一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
[root@k8snode4 yaml]# curl http://10.96.94.231
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

# 使用 node 的 ip 进行访问
[root@k8snode4 yaml]# curl http://k8snode4:30080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
[root@k8snode4 yaml]#

我们在检验一下,是否是一 zhipeng1 的身份运行

1
2
3
4
5
6
7
8
9
10
11
# 找其中一个 Pod 的名字替换掉下面
kubectl exec -it <Pod名字> -n zhipeng-space -- cat /var/run/secrets/kubernetes.io/serviceaccount/namespace
检查重点:输出应该是 zhipeng-space。

深度验证(查看具体 ServiceAccount):

Bash
kubectl get pod <Pod名字> -n zhipeng-space -o jsonpath='{.spec.serviceAccountName}'
输出必须是 zhipeng1。




支付宝打赏 微信打赏

赞赏一下