从探针到权限:一篇吃透 Kubernetes 健康检查、RBAC 认证授权与 Dashboard 可视化管理

从探针到权限:一篇吃透 Kubernetes 健康检查、RBAC 认证授权与 Dashboard 可视化管理

摘要 :容器进程在跑 ≠ 服务能用。本文用「反例演示 + 逐条命令验证」的方式,一次性讲透 Kubernetes 三大核心主题:Health Check 健康探针(liveness/readiness/startup × httpGet/exec/tcpSocket) 、认证与授权(X509 用户、Role/ClusterRole、ServiceAccount) 、Dashboard 可视化管理(Kuboard + Kubernetes Dashboard)。所有实验均可复现,附完整 YAML 与排错要点。

关键词:Kubernetes、健康检查、livenessProbe、readinessProbe、RBAC、ServiceAccount、X509 证书、Dashboard、Kuboard


一、开篇:为什么容器 Running 不代表服务可用

应用会因为各种原因变得 unhealthy:临时连接断开、配置错误、程序自身 Bug、死锁(进程还在,但再也处理不了请求)。

kubectl get pod 显示 Running,只说明容器主进程没退出,并不代表业务能正常响应。K8s 的判断逻辑是:

  • 进程退出 → 容器崩溃 → 重启(这是 K8s 默认就能做到的)
  • 进程还在但已经不干活 → K8s 默认无能为力 → 必须靠 探针(Probe)

kubelet通过探针周期性探测容器中的应用,决定什么时候重启容器 、什么时候把容器从 Service 后端摘掉。这就是本文的主角。


二、Health Check 健康检查

环境准备

bash 复制代码
root@master30:~# kubectl create ns health
root@master30:~# kubectl config set-context --current --namespace health

实验镜像说明 :本文统一使用内部仓库 hub.laoma.cloud/library/httpd,公网环境可替换为 httpd:2.4 或 nginx:alpine(注意 nginx 主页路径是 /usr/share/nginx/html/index.html)。


2.1 反例:没有探针会发生什么

先创建一个不带任何探针的 Pod,看看问题有多隐蔽:

bash 复制代码
# 创建一个普通 pod
root@master30:~# kubectl run web --image=hub.laoma.cloud/library/httpd --image-pull-policy=IfNotPresent
pod/web created

# 查看 Pod IP
root@master30:~# kubectl describe pod web | grep '^IP:'
IP:           10.224.73.16

# 正常访问,返回 "It works!"
root@master30:~# curl 10.224.73.16
<html><body><h1>It works!</h1></body></html>

现在手工制造故障:删掉主页文件,模拟"应用数据丢失 / 依赖资源不可用"。

bash 复制代码
# 删除主页文件
root@master30:~# kubectl exec web -- rm -f htdocs/index.html

# 再次访问,已经变成目录列表页(业务已损坏)
root@master30:~# curl 10.224.73.16
<!DOCTYPE HTML PUBLIC "-//W3C/DTD HTML 3.2 Final//EN">
<html>
 <head>
  <title>Index of /</title>
 </head>
 <body>
<h1>Index of /</h1>
<ul></ul>
</body></html>

关键观察点 :业务已经坏了,但 Pod 状态依然是 Running,RESTARTS 为 0,K8s 完全无感知。

bash 复制代码
root@master30:~# kubectl get pod
NAME   READY   STATUS    RESTARTS   AGE
web    1/1     Running   0          51s

结论:这就是"僵尸容器"------活着,但没用。生产环境中如果靠人工发现,往往已经是用户投诉之后了。

bash 复制代码
# 清理环境
root@master30:~# kubectl delete pod web --force

2.2 三类探针:Liveness / Readiness / Startup

探针 作用 失败后果 典型用途
livenessProbe 存活探针 判断容器是否处于 healthy 状态 kubelet 杀掉并重启容器(重建同名 Pod) 检测死锁、假死,自动"复活"
readinessProbe 就绪探针 判断容器是否可以对外提供服务 从 Service 的 Endpoints 中摘除该 Pod IP,不重启 控制流量入口,实现优雅上线
startupProbe 启动探针 判断容器内应用是否已完成初始化 失败则重启容器,同 liveness 保护慢启动容器(如加载大模型、预热缓存)

核心区别一句话记住:

  • liveness 决定"要不要重启你"
  • readiness 决定"要不要把流量给你"
  • startup 决定"现在还不许动我"

startupProbe 存在的意义 :如果配置了启动探针,那么在它成功之前,liveness 和 readiness 都不会执行。这避免了慢启动应用在还没初始化完成时就被 liveness 判死、反复重启(经典 CrashLoopBackOff 元凶)。

yaml 复制代码
# startupProbe 典型写法(本文后续实验不深入展开)
startupProbe:
  httpGet:
    path: /healthz
    port: 8080
  failureThreshold: 30      # 允许失败 30 次
  periodSeconds: 10         # 每 10 秒一次
  # 相当于给应用最多 30 × 10 = 300 秒的启动宽限期

2.3 四种检测方式

检测方式 原理 成功判定 适用场景
httpGet 对容器 IP 的指定端口 + 路径发起 HTTP GET 状态码 200 ≤ code < 400 Web 服务(最常用)
exec 在容器内执行指定命令 命令退出码为 0 无法开放端口、需检查文件/进程
tcpSocket 对容器 IP 的指定端口发起 TCP 连接 端口能连上即为健康(连接后立即关闭也算健康) 非 HTTP 服务:MySQL、Redis、自定义 TCP
grpc 调用 gRPC 健康检查接口 响应状态为 SERVING gRPC 服务(需实现 health-checking 协议)

四种方式可混用:一个容器可以同时配置 liveness(tcpSocket)+ readiness(httpGet)。


2.4 probe 通用参数速查表

参数 是否必选 说明 默认值 / 最小值
initialDelaySeconds 必选 容器启动后,等待多久才开始第一次探测 0
timeoutSeconds 必选 单次探测超时时间,超时即判失败 默认 1s,最小 1s
periodSeconds 可选 探测间隔(检查频率) 默认 10s,最小 1s
successThreshold 可选 连续成功多少次才算"健康" 默认 1,最小 1(liveness 必须为 1)
failureThreshold 可选 连续失败多少次才算"不健康" 默认 3,最小 1

排错口诀 :Pod 反复重启先看 liveness;Pod Running 但 READY 0/1 且 Service 访问不到,先看 readiness;Pod 一启动就被杀,把 initialDelaySeconds 调大或改用 startupProbe。


2.5 实战:httpGet 之 livenessProbe

场景 :主页文件被删除 → 探测 /index.html 返回 404 → 连续失败 3 次 → 容器重启 → 文件恢复(因为镜像里本来就有)。

bash 复制代码
root@master30:~# vim deploy-httpGet-liveness.yaml
yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: web
  name: web
spec:
  replicas: 1                          # 副本数量,启动1个pod
  selector:
    matchLabels:
      app: web                         # 匹配pod标签,关联模板
  template:
    metadata:
      labels:
        app: web                       # pod标签,和selector保持一致
    spec:
      containers:
      - image: hub.laoma.cloud/library/httpd   # 镜像地址,httpd网页服务
        imagePullPolicy: IfNotPresent          # 本地有镜像就不拉取,没有才拉
        name: httpd                            # 容器名称
        # ===== 添加 livenessProbe 存活探针 =====
        livenessProbe:
          failureThreshold: 3           # 连续失败3次,判定容器异常,重启Pod
          initialDelaySeconds: 5        # 容器启动后,延迟5s才开始第一次探测
          periodSeconds: 10             # 每隔10秒执行一次存活探测
          successThreshold: 1           # 失败后连续1次成功就恢复正常,存活探针固定为1
          timeoutSeconds: 5             # 单次探测请求超时时间,超过5s即判定失败
          httpGet:
            path: /index.html           # HTTP健康检查访问路径
            port: 80                    # httpd默认端口80
            scheme: HTTP                # 请求协议,HTTP/HTTPS

部署与验证:

bash 复制代码
# 创建应用
root@master30:~# kubectl apply -f deploy-httpGet-liveness.yaml
deployment.apps/web created

root@master30:~# kubectl get pod
NAME                   READY   STATUS    RESTARTS   AGE
web-85c6ff748f-qwszz   1/1     Running   0          12m

# 查看 Pod IP
root@master30:~# kubectl describe pod web-85c6ff748f-qwszz | grep '^IP:'
IP:           10.98.146.216

制造故障:

bash 复制代码
# 删除主页文件
root@master30:~# kubectl exec web-85c6ff748f-qwszz -- bash -c 'rm htdocs/index.html'

等待约 30~35 秒(5s 首次延迟 + 3 次 ×10s 间隔),观察 Pod:

bash 复制代码
root@master30:~# kubectl get pod
NAME                   READY   STATUS    RESTARTS   AGE
web-85c6ff748f-qwszz   1/1     Running   1          13m

RESTARTS 变成 1 ------ 容器被 kubelet 判定不健康并重启,镜像自带的主页文件随之恢复:

bash 复制代码
root@master30:~# curl 10.98.146.216
<html><body><h1>It works!</h1></body></html>

注意 :容器删除需要一点时间,由 terminationGracePeriodSeconds(默认 30s)控制。只有等旧容器删除、新容器创建完成后,探测才会继续。这也是为什么重启后不会立刻看到 RESTARTS 变化。

查看失败原因(排错利器):

bash 复制代码
root@master30:~# kubectl describe pod web-85c6ff748f-qwszz
...
Events:
  Warning  Unhealthy  kubelet  Liveness probe failed: HTTP probe failed with statuscode: 404
  Normal   Killing    kubelet  Container httpd failed liveness probe, will be restarted
bash 复制代码
# 清理环境
root@master30:~# kubectl delete deployments.apps web

2.6 实战:httpGet 之 readinessProbe

场景 :3 副本 + Service。删掉其中一个 Pod 的主页 → 该 Pod 被判定"未就绪" → 从 Endpoints 摘除 → 流量不再转发给它,且 Pod 不会被重启。

bash 复制代码
root@master30:~# vim deploy-httpGet-readiness.yaml
yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: web
  name: web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - image: hub.laoma.cloud/library/httpd
        imagePullPolicy: IfNotPresent
        name: httpd
        # ===== 添加 readinessProbe 部分 =====
        readinessProbe:
          failureThreshold: 3
          initialDelaySeconds: 5
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 5
          httpGet:
            path: /index.html
            port: 80
            scheme: HTTP

部署并暴露服务:

bash 复制代码
# 创建应用
root@master30:~# kubectl apply -f deploy-httpGet-readiness.yaml
deployment.apps/web created

root@master30:~# kubectl expose deployment web --port=80 --target-port=80
service/web exposed

root@master30:~# kubectl get svc
NAME   TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
web    ClusterIP   10.98.146.216   <none>        80/TCP    4m5s

root@master30:~# kubectl get pod
NAME                  READY   STATUS    RESTARTS   AGE
web-9479dc55c-6bpg7   1/1     Running   0          2m34s
web-9479dc55c-d2gbn   1/1     Running   0          2m34s
web-9479dc55c-hqh8q   1/1     Running   0          2m34s

给 3 个 Pod 写入不同主页,方便观察负载均衡效果:

bash 复制代码
# 准备 3 个 pod 主页文件
root@master30:~# for pod in $(kubectl get pods -o name | awk -F / '{print $2}'); do \
  kubectl exec $pod -- bash -c "echo $pod > htdocs/index.html"; \
done

通过 Service 访问,验证负载均衡:

bash 复制代码
# 用变量保存 ClusterIP,避免手抄 IP 出错
root@master30:~# SVC_IP=$(kubectl get svc web -o jsonpath='{.spec.clusterIP}')

root@master30:~# for i in {1..90}; do curl -s $SVC_IP; done | sort | uniq -c
     24 web-9479dc55c-6bpg7
     33 web-9479dc55c-d2gbn
     33 web-9479dc55c-hqh8q

# 查看 Service 后端 Endpoints
root@master30:~# kubectl get endpoints web
NAME   ENDPOINTS                                         AGE
web    10.224.73.15:80,10.224.73.34:80,10.224.73.35:80   21m

制造故障 :删掉 web-9479dc55c-6bpg7 的主页文件。

bash 复制代码
root@master30:~# kubectl exec -it web-9479dc55c-6bpg7 -- rm -f htdocs/index.html

观察 Endpoints ------ 故障 Pod 的 IP 已被摘除:

bash 复制代码
root@master30:~# kubectl get endpoints web
NAME   ENDPOINTS                         AGE
web    10.224.73.15:80,10.224.73.34:80   23m

再次访问 Service,流量不再打到故障 Pod:

bash 复制代码
root@master30:~# for i in {1..90}; do curl -s $SVC_IP; done | sort | uniq -c
     50 web-9479dc55c-d2gbn
     40 web-9479dc55c-hqh8q

观察 Pod 状态 :READY 变成 0/1,但 RESTARTS 依然是 0 ------ 这正是 readiness 与 liveness 的本质区别。

bash 复制代码
root@master30:~# kubectl get pod
NAME                  READY   STATUS    RESTARTS   AGE
web-9479dc55c-6bpg7   0/1     Running   0          8m49s
web-9479dc55c-d2gbn   1/1     Running   0          8m49s
web-9479dc55c-hqh8q   1/1     Running   0          8m49s

生产价值 :readiness 是"只摘流量、不杀容器",特别适用于依赖外部数据库/缓存的场景------DB 抖动时先摘流量自保,DB 恢复后自动回归,避免无谓重启导致的冷启动雪崩。

bash 复制代码
# 清理环境
root@master30:~# kubectl delete deployments.apps web
root@master30:~# kubectl delete svc web

2.7 实战:exec 检测

原理 :kubelet 在容器内执行指定命令,退出码为 0 判定成功,非 0 判定失败。

示例 1:检测容器自带文件
bash 复制代码
root@master30:~# vim deploy-exec-liveness.yaml
yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: web
  name: web
spec:
  replicas: 1
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - image: hub.laoma.cloud/library/httpd
        imagePullPolicy: IfNotPresent
        name: httpd
        # ===== 添加 livenessProbe 部分 =====
        livenessProbe:
          failureThreshold: 3
          initialDelaySeconds: 5
          periodSeconds: 5
          successThreshold: 1
          timeoutSeconds: 10
          exec:
            command:
            - cat
            - /usr/local/apache2/htdocs/index.html
bash 复制代码
# 创建应用
root@master30:~# kubectl apply -f deploy-exec-liveness.yaml
deployment.apps/web created

root@master30:~# kubectl get pods
NAME                  READY   STATUS    RESTARTS     AGE
web-8c9ff9b76-nm6s2   1/1     Running   1 (2s ago)   18s

# 删除主页文件
root@master30:~# kubectl exec web-8c9ff9b76-nm6s2 -- bash -c 'rm htdocs/index.html'

# 观察 pod 状态,RESTARTS 次数变为 1
root@master30:~# kubectl get pod
NAME   READY   STATUS    RESTARTS   AGE
web    1/1     Running   1          4m5s

注意路径 :httpd 官方镜像的工作目录是 /usr/local/apache2,kubectl exec 时相对路径 htdocs/index.html 等价于此目录下的 htdocs/index.html,但探针里必须写绝对路径。

示例 2:检测自定义健康标记文件(经典官方示例)

这个例子演示"应用自己控制健康状态":前 10 秒文件存在(健康),之后删除文件(不健康),容器被重启,重启后文件又被创建,如此循环。

bash 复制代码
# 生成 Pod 骨架
root@master30:~# kubectl run busybox --image=busybox --image-pull-policy=IfNotPresent \
  -o yaml --dry-run=client > busybox.yml

root@master30:~# vim deploy-exec-busybox.yml
yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: busybox
  name: busybox
spec:
  containers:
  - image: busybox
    imagePullPolicy: IfNotPresent
    name: busybox

    # ===== 添加 args 参数:模拟"健康 10 秒,然后不健康" =====
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 10; rm -rf /tmp/healthy; sleep 100

    # ===== 添加 livenessProbe 参数 =====
    livenessProbe:
      failureThreshold: 3
      initialDelaySeconds: 5
      periodSeconds: 5
      successThreshold: 1
      timeoutSeconds: 10
      exec:
        command:
        - ls
        - /tmp/healthy
  dnsPolicy: ClusterFirst
  restartPolicy: Always
bash 复制代码
root@master30:~# kubectl apply -f deploy-exec-busybox.yml
pod/busybox created

# 持续观察:RESTARTS 会不断增长
root@master30:~# kubectl get pod busybox -w
NAME      READY   STATUS    RESTARTS      AGE
busybox   1/1     Running   0             5s
busybox   1/1     Running   1 (5s ago)    35s
busybox   1/1     Running   2 (5s ago)    70s

时间线拆解:

时间 事件
0s 容器启动,创建 /tmp/healthy
5s 首次探测(initialDelaySeconds=5),文件存在 → 成功
10s 脚本 sleep 10 结束,删除 /tmp/healthy
15s / 20s / 25s 连续 3 次探测失败 → 达到 failureThreshold=3
~25s kubelet 杀死并重启容器 → RESTARTS +1,循环开始

2.8 实战:tcpSocket 检测

原理 :kubelet 尝试与容器指定端口建立 TCP 连接,能连上即健康(注意:对端连接建立后立即关闭,也算健康)。

bash 复制代码
root@master30:~# vim deploy-tcpSocket-liveness.yaml
yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: web
  name: web
spec:
  replicas: 1
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - image: hub.laoma.cloud/library/httpd
        imagePullPolicy: IfNotPresent
        name: httpd
        # ===== 添加 livenessProbe 部分 =====
        livenessProbe:
          failureThreshold: 3
          initialDelaySeconds: 5
          periodSeconds: 5
          successThreshold: 1
          timeoutSeconds: 10
          tcpSocket:
            port: 80
bash 复制代码
root@master30:~# kubectl apply -f deploy-tcpSocket-liveness.yaml
deployment.apps/web created

root@master30:~# kubectl get pod -w
NAME                   READY   STATUS    RESTARTS   AGE
web-6d4f8b9c7d-x2k9p   1/1     Running   0          12s

tcpSocket vs httpGet 怎么选?

  • tcpSocket 更轻量,只验证"端口在监听",适合 MySQL(3306)、Redis(6379)、SSH 等非 HTTP 服务;
  • httpGet 能验证"业务逻辑正常"(返回 200 而不是 500),信息量更大,是 Web 服务首选;
  • tcpSocket 无法发现"进程还在、端口还开着、但返回 500"这种假死状态。

2.9 生产场景:Scale Up 与滚动更新

场景 A:Scale Up(扩容)

多副本应用扩容时,新副本会被作为 backend 加入 Service 负载均衡。但应用从容器启动到真正能提供服务,通常需要一个准备阶段:加载缓存数据、连接数据库、预热 JVM/模型。

如果没有 readinessProbe ,容器一 Running 就被塞进 Endpoints → 请求打到"还没准备好"的 Pod → 用户看到 502/超时。

有了 readinessProbe:新副本只有探测通过才会被加入 Service,请求零损失。

场景 B:Rolling Update(滚动更新)

假设现有一个正常运行的多副本应用,现在要更新镜像版本:

  1. 正常情况下新副本需要 10 秒完成准备工作,在此之前无法响应业务请求;
  2. 但由于人为配置错误 (例如连不上后端数据库),新副本始终无法完成准备工作。

如果没有配置 Health Check 会怎样?

新副本本身没有异常退出,默认的"健康检查"机制(只看进程是否存活)会认为容器已经就绪,于是逐步用新副本替换现有副本。结果是:当所有旧副本都被替换后,整个应用无法处理任何请求,服务彻底中断。如果发生在生产系统上,后果非常严重。

如果正确配置了 Health Check?

新副本只有通过探测才会被添加到 Service;如果探测始终不通过,滚动更新会卡在新副本,现有旧副本不会被全部替换,业务仍然正常进行 。这正是 K8s 滚动更新 + maxUnavailable 能实现"零中断发布"的底层保障。

一句话总结 :Health Check 是滚动更新的安全阀。 没有它,一次错误的发布就是一次全站宕机。


环境清理

bash 复制代码
root@master30:~# kubectl delete ns health

三、Kubernetes 认证与授权

环境准备

bash 复制代码
root@master30:~# kubectl create ns auth
namespace/auth created

root@master30:~# kubectl config set-context --current --namespace auth
Context "kubernetes-admin@kubernetes" modified.

3.1 访问控制全流程

当用户使用 User 或 ServiceAccount 访问 Kubernetes API 时,每个请求都要经过多阶段访问控制才会被接受:

text 复制代码
                    ┌─────────────────────────────────────────┐
   kubectl / curl    │          kube-apiserver (6443)          │
         │           │                                         │
         ├──────────►│  ① 认证 Authentication                  │
         │           │     客户端证书 / Token / SA Token ...    │
         │           │        失败 → HTTP 401 Unauthorized     │
         │           ├─────────────────────────────────────────┤
         │           │  ② 鉴权 Authorization                   │
         │           │     RBAC / Node / ABAC / Webhook         │
         │           │        失败 → HTTP 403 Forbidden        │
         │           ├─────────────────────────────────────────┤
         │           │  ③ 准入控制 Admission Control            │
         │           │     Mutating → Validating                │
         │           │        (仅对写操作生效,可修改/拒绝)    │
         │           ├─────────────────────────────────────────┤
         │           │  ④ 持久化:写入 etcd                     │
         │           └─────────────────────────────────────────┘
         │
         └── 全程伴随:⑤ 审计 Audit(按时间顺序记录安全相关操作)

各阶段要点:

阶段 关键点
传输安全 API Server 默认在 6443 端口监听,受 TLS 保护(--secure-port 可改端口,--bind-address 可改监听地址)。客户端 ~/.kube/config 中需携带 CA 证书副本以信任连接
① 认证 依次尝试各个认证模块,只要一个成功 即通过;全部失败返回 401
② 鉴权 请求需携带「用户名 + 动作 + 资源对象」;任意一个模块批准即通过;全部拒绝返回 403 。默认拒绝
③ 准入控制 只对创建/修改/删除/连接 生效,读操作不触发;任一控制器拒绝即立即拒绝(无"投票"机制);可设置复杂默认值
⑤ 审计 记录集群中与安全相关的、按时间排序的操作序列

3.2 Kubernetes 中的两类用户

类别 说明
普通用户(User) K8s 不提供普通用户对象,由外部认证系统(X509 证书、LDAP、OIDC 等)提供。无法通过 API 添加用户
服务账号(ServiceAccount) 由 K8s API 管理的用户,绑定到特定 namespace,凭据挂载进 Pod,供集群内进程访问 API

核心认知 :K8s 认为「能提供由集群 CA 签名的合法证书的用户,就是通过认证的用户 」,并用证书中 subject 的 CN(Common Name) 字段(如 /CN=bob)作为用户名,用 O(Organization) 字段作为用户组。随后由 RBAC 决定该用户能做什么。

认证成功后,认证插件会为请求关联以下属性供鉴权使用:

  • 用户名 (username):如 kubernetes-admin、jane@example.com
  • 用户 ID(uid):比用户名更具一致性与唯一性
  • 用户组 (groups):如 system:masters、devops-team
  • 附加字段(extra):额外的键值映射

3.3 认证插件全家福

K8s 支持同时开启多个认证插件,任意一个通过即可;全部失败返回 401。

认证方式 启用方式 / 说明 备注
X509 客户证书 API Server 启动参数 --client-ca-file=SOMEFILE 默认使用,本文重点
静态令牌文件 --token-auth-file=SOMEFILE,CSV 格式 默认未启用
启动引导令牌 --enable-bootstrap-token-auth,以 Secret 存于 kube-system kubeadm 自动配置
服务账号令牌 自动启用,JWT 形式 默认使用,本文重点
Webhook 令牌 HTTP 回调验证持有者令牌 对接外部认证系统
身份认证代理 从请求头(如 X-Remote-User)识别用户 配合认证代理使用
basic-auth-file K8s 1.19 起已废弃 不再可用
bash 复制代码
# 查看 X509 客户端 CA 配置
root@master30:~# cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep -- --client-ca-file
    - --client-ca-file=/etc/kubernetes/pki/ca.crt

# 查看静态令牌文件(默认未启用,无输出)
root@master30:~# cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep -- --token-auth-file

# 查看启动引导令牌开关
root@master30:~# cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep bootstrap
    - --enable-bootstrap-token-auth=true

静态令牌文件格式(CSV,至少三列):

bash 复制代码
token,user,uid,"group1,group2,group3"
匿名请求

如果请求没有被任何已配置的认证方法拒绝 ,则被视为匿名请求,用户名 system:anonymous,用户组 system:unauthenticated。

  • 1.5.1~1.5.x:默认禁用
  • 1.6 及以后 :只要鉴权模式不是 AlwaysAllow,匿名访问默认启用 ;RBAC/ABAC 需要对 system:anonymous 或 system:unauthenticated 显式授权 ,旧的 user:* / group:* 规则不再包含匿名用户

实验现象 :配置了令牌认证且启用匿名访问的服务器上,提供非法令牌 → 401;不提供令牌 → 视为匿名请求。


3.4 实战:X509 证书创建用户

实验拓扑 :client 节点(用户 fb)生成密钥与证书请求 → 发给管理员 → master30 用集群 CA 签名 → 回传 → 客户端组装 kubeconfig → 管理员授权 → 验证。

第一步:客户端准备申请材料
bash 复制代码
# 客户端需安装与集群版本一致的 kubectl
root@client:~# apt install -y kubectl=1.30.2-1.1

# 创建私钥
root@client:~# openssl genrsa -out fb.key 2048

# 根据私钥创建证书请求(CSR)
root@client:~# openssl req -new -key fb.key -out fb.csr -subj '/CN=fb/O=kubernets'

subject 字段说明:

字段 含义
C Country,国家
ST STate,省份
L Location,城市
O Organization,组织/公司(对应 K8s 用户组)
OU Organization Unit,部门
CN Common Name,对应 K8s 用户名
emailAddress 联系人邮箱

完整示例:

bash 复制代码
root@client:~# openssl req -new -key servera.key -out servera.csr \
  -subj "/C=CHINA/ST=JS/L=NJ/O=LM/OU=DEVOPS/CN=servera.lab.example.com/emailAddress=fb@lab.example.com"

# 客户端将证书请求发给 kubernetes 管理员
root@client:~# scp fb.csr root@master30:
第二步:管理员签名,生成用户证书
bash 复制代码
# 使用 ca.crt 和 ca.key 签名fb.csr,得到 fb.crt 证书(有效期 1095 天)
root@master30:~# openssl x509 -req -in fb.csr \
  -CA /etc/kubernetes/pki/ca.crt \
  -CAkey /etc/kubernetes/pki/ca.key \
  -CAcreateserial -out fb.crt -days 1095

# 导出 kubeconfig 模版
root@master30:~# kubectl config view > config.tpl

# 将 kubeconfig 模板、用户证书和集群 CA 证书发给客户端
root@master30:~# scp config.tpl fb.crt /etc/kubernetes/pki/ca.crt root@client:~
第三步:客户端创建 kubeconfig

方法一(推荐):用 kubectl config 命令逐步生成

先编辑模版 config.tpl:

yaml 复制代码
apiVersion: v1
clusters:
- cluster:
    server: https://10.1.8.30:6443
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    namespace: default
    user: fb
  name: fb@kubernetes
current-context: fb@kubernetes
kind: Config
users:
- name: fb

然后依次设置 cluster / credentials / context:

bash 复制代码
root@client:~# mv config.tpl config

# 设置 cluster
root@client:~# kubectl config set-cluster kubernetes --kubeconfig=config \
  --certificate-authority=ca.crt --embed-certs
# --kubeconfig            指定 kubeconfig 文件
# --server                指定 kubernetes 服务器 API 地址
# --certificate-authority 指定 CA 证书
# --embed-certs           将 ca.crt 的内容内嵌到 config 文件中(不加则只记录路径)

# 设置 credentials
root@client:~# kubectl config set-credentials fb --kubeconfig=config \
  --client-key=fb.key --client-certificate=fb.crt --embed-certs
# --client-key          指定用户私钥
# --client-certificate  指定服务器为用户生成的证书

# 设置 context
root@client:~# kubectl config set-context fb --kubeconfig=config \
  --namespace=default --cluster=kubernetes --user=fb

管理员授权(RBAC 部分后文详述):

bash 复制代码
root@master30:~# kubectl create clusterrolebinding fb-admin \
  --clusterrole=cluster-admin --user=fb
clusterrolebinding.rbac.authorization.k8s.io/fb-admin created

验证结果:

bash 复制代码
root@client:~# kubectl get nodes --kubeconfig=config
NAME                  STATUS   ROLES           AGE   VERSION
master30.laoma.cloud   Ready    control-plane   40h   v1.30.2
worker31.laoma.cloud   Ready    <none>          40h   v1.30.2
worker32.laoma.cloud   Ready    <none>          40h   v1.30.2

想省事的话,把 config 放到 ~/.kube/config,之后就不用每次带 --kubeconfig 了。

方法二(不推荐):手工编辑 base64

bash 复制代码
# 获取 ca.crt 的 base64 编码,填入 certificate-authority-data
root@client:~# cat ca.crt | base64 | tr -d '\n'

# 获取 fb.key 的 base64 编码,填入 client-key-data
root@client:~# cat fb.key | base64 | tr -d '\n'

# 获取 fb.crt 的 base64 编码,填入 client-certificate-data
root@client:~# cat fb.crt | base64 | tr -d '\n'

对应 kubeconfig 结构:

yaml 复制代码
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: DATA+OMITTED
    server: https://10.1.8.30:6443
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: kubernetes-fb
  name: kubernetes-fb@kubernetes
current-context: kubernetes-fb@kubernetes
kind: Config
preferences: {}
users:
- name: kubernetes-fb
  user:
    client-certificate-data: REDACTED
    client-key-data: REDACTED
删除账户
bash 复制代码
# 删除权限(关键步骤)
root@master30:~# kubectl delete clusterrolebindings.rbac.authorization.k8s.io fb-admin

# 验证权限
root@client:~# kubectl get nodes
Error from server (Forbidden): nodes is forbidden: User "fb" cannot list resource "nodes" in API group "" at the cluster scope

重要理解 :删除 k8s 中的 CSR 资源后,客户端用户仍然可以访问集群。 因为用户认证由 X509 提供,与 K8s 无关------K8s 只认"证书是不是集群 CA 签的"。

要真正禁止某用户登录,必须从 X509 认证机制下手(例如通过 CA 吊销列表 CRL 将证书加入黑名单),而不是在 K8s 里删资源。


3.5 鉴权模式

bash 复制代码
root@master30:~# cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep mode
    - --authorization-mode=Node,RBAC

默认使用 RBAC 与 Node 两种模式。

模式 说明
Node 节点专用,根据调度到 kubelet 上运行的 Pod 为 kubelet 授予权限
RBAC 基于角色的访问控制,通过 rbac.authorization.k8s.io API 组动态配置策略(生产标准)
ABAC 基于属性的访问控制,策略文件静态,修改需重启 API Server
Webhook 通过 HTTP POST 回调外部服务做鉴权决策
AlwaysAllow 允许所有请求(仅测试用,极度危险)
AlwaysDeny 拒绝所有请求(不限制 admin 用户)

多个鉴权模块时的裁决规则:

  • 任一模块批准或拒绝 → 立即返回该决定,不再询问其他模块
  • 所有模块都"没意见" → 拒绝(默认拒绝),返回 403

实验:切换为 AlwaysAllow 验证认证与鉴权的分离

bash 复制代码
root@master30:~# vim /etc/kubernetes/manifests/kube-apiserver.yaml
spec:
  containers:
  - command:
    - kube-apiserver
    ...
    - --authorization-mode=AlwaysAllow     # 原为 Node,RBAC

修改保存后,K8s 会自动重启 kube-apiserver 静态 Pod,等待其 Running 后验证:

bash 复制代码
root@client:~# kubectl get nodes
NAME                   STATUS   ROLES           AGE   VERSION
master30.laoma.cloud   Ready    control-plane   12d   v1.30.2
worker31.laoma.cloud   Ready    <none>          12d   v1.30.2
worker32.laoma.cloud   Ready    <none>          12d   v1.30.2

结论 :fb 用户的权限是被删除了 (无 ClusterRoleBinding),但因为鉴权模式变成 AlwaysAllow,请求依然通过。这清楚证明:认证(你是谁)与鉴权(你能干什么)是两个独立阶段 。实验完成后请务必改回 Node,RBAC!


3.6 Role 与 ClusterRole 管理

K8s 将一组权限打包成角色(Role),再把角色**绑定(Binding)**给用户/组/SA,用户即继承该角色的权限。

角色分类
角色 绑定对象 生效范围
Role RoleBinding 只能在当前这一个 namespace生效
ClusterRole ClusterRoleBinding 全集群所有 namespace + 集群资源
ClusterRole RoleBinding 只在绑定的这一个 namespace 生效(复用 ClusterRole 规则,缩小范围)
内置 ClusterRole

K8s 预定义了若干 ClusterRole,常用四个:

ClusterRole 权限范围
view 几乎所有对象的 get、list、watch(只读)
edit view 权限 + 部分对象的 create、delete、deletecollection、patch、update
admin 大部分对象的全部权限(含 RBAC 管理)
cluster-admin 所有对象的所有权限(超级管理员)
bash 复制代码
root@master30:~# kubectl describe clusterroles admin
Name:         admin
Labels:       kubernetes.io/bootstrapping=rbac-defaults
Annotations:  rbac.authorization.k8s.io/autoupdate: true
PolicyRule:
  Resources                                       Non-Resource URLs  Resource Names  Verbs
  ---------                                       -----------------  --------------  -----
  rolebindings.rbac.authorization.k8s.io          []                 []              [create delete deletecollection get list patch update watch]
  roles.rbac.authorization.k8s.io                 []                 []              [create delete deletecollection get list patch update watch]
  configmaps                                      []                 []              [create delete deletecollection patch update get list watch]
  endpoints                                       []                 []              [create delete deletecollection patch update get list watch]
......

PolicyRule 字段释义:

  • Resources:资源类型,如 pods、secrets、configmaps
  • Resource Names:特定资源实例名(如指定某个 Secret 名称)
  • Non-Resource URLs :非资源 URL(如 /healthz),一般无需关注
  • Verbs :动作,包括 get、list、create、delete、update、patch、watch、exec 等

get 与 list 的区别(面试常考):

bash 复制代码
# 只有 list 权限:可以列出清单,但不能获取单个对象详情
root@client:~# kubectl get pod -n kube-system
NAME                                        READY   STATUS    RESTARTS   AGE
calico-kube-controllers-6dfcd885bf-lq4n5    1/1     Running   4          52d
...

root@client:~# kubectl get pod kube-proxy-8kp8w -n kube-system
Error from server (Forbidden): pods "kube-proxy-8kp8w" is forbidden: User "fb" cannot get resource "pods" in API group "" in the namespace "kube-system"
bash 复制代码
# 只有 get 权限:可以获取单个对象,但不能列出清单
root@client:~# kubectl get pod -n kube-system
Error from server (Forbidden): pods is forbidden: User "fb" cannot list resource "pods" in API group "" in the namespace "kube-system"

root@client:~# kubectl get pod kube-proxy-8kp8w -n kube-system
NAME               READY   STATUS    RESTARTS   AGE
kube-proxy-8kp8w   1/1     Running   4          52d
创建 Role
bash 复制代码
root@master30:~# kubectl create role -h
Create a role with single rule.

Usage:
  kubectl create role NAME --verb=verb --resource=resource.group/subresource
[--resource-name=resourcename] [--dry-run=server|client|none] [options]

示例 1:命名空间内所有 Pod 的 get、list、watch 权限

bash 复制代码
root@master30:~# kubectl create role pod-role --verb=get,list,watch --resource=pods -n default --dry-run=client -o yaml
yaml 复制代码
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  creationTimestamp: null
  name: pod-role
  namespace: default
rules:
- apiGroups:
  - ""
  resources:
  - pods
  verbs:
  - get
  - list
  - watch
bash 复制代码
root@master30:~# kubectl create role pod-role --verb=get,list,watch --resource=pods -n default
role.rbac.authorization.k8s.io/pod-role created

root@master30:~# kubectl get roles pod-role -n default
NAME       CREATED AT
pod-role   2021-08-24T10:24:36Z

root@master30:~# kubectl describe roles pod-role -n default
Name:         pod-role
Labels:       <none>
Annotations:  <none>
PolicyRule:
  Resources  Non-Resource URLs  Resource Names  Verbs
  ---------  -----------------  --------------  -----
  pods       []                 []              [get list watch]

示例 2:仅能访问特定名称的资源

bash 复制代码
# 权限规则:只允许 get 两个指定名字的 Pod:`readablepod` 和 `anotherpod`,同命名空间下别的 Pod 不能 get
root@master30:~# kubectl create role pod-role --verb=get --resource=pods \
  --resource-name=readablepod --resource-name=anotherpod

示例 3:针对 replicasets 授权

bash 复制代码
root@master30:~# kubectl create role foo --verb=get,list,watch --resource=replicasets
修改 Role
bash 复制代码
# 增加 create 权限
root@master30:~# kubectl edit roles -n default pod-role
......
rules:
- apiGroups:
  - ""
  resources:
  - pods
  verbs:
  - list
  - get
  - watch
  - create      # 新增
创建 ClusterRole
bash 复制代码
root@master30:~# kubectl create clusterrole -h
Usage:
  kubectl create clusterrole NAME --verb=verb --resource=resource.group
[--resource-name=resourcename] [--dry-run=server|client|none] [options]
bash 复制代码
# 示例1:所有 namespace 中 Pods 的 get、list、watch
root@master30:~# kubectl create clusterrole pod-role --verb=get,list,watch --resource=pods

# 示例2:仅限指定名称的 Pod
root@master30:~# kubectl create clusterrole pod-reader --verb=get --resource=pods \
  --resource-name=readablepod --resource-name=anotherpod

# 示例3:同时授权 pods 与 pods/status 子资源
root@master30:~# kubectl create clusterrole foo --verb=get,list,watch --resource=pods,pods/status

对应 YAML:

yaml 复制代码
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  creationTimestamp: null
  name: pod-role
rules:
- apiGroups:
  - ""
  resources:
  - pods
  verbs:
  - get
  - list
  - watch
bash 复制代码
root@master30:~# kubectl describe clusterrole pod-role
Name:         pod-role
Labels:       <none>
Annotations:  <none>
PolicyRule:
  Resources  Non-Resource URLs  Resource Names  Verbs
  ---------  -----------------  --------------  -----
  pods       []                 []              [get list watch]

3.7 apiGroups:最容易踩的坑

Role 的 rules.apiGroups 默认为空字符串 ""(即 core 组)。

资源类型 apiVersion apiGroups
Pod、Service、PersistentVolume、PersistentVolumeClaim、ConfigMap、Secret、Namespace、Node v1 ""
Deployment、DaemonSet、StatefulSet、ReplicaSet apps/v1 apps
Job batch/v1 batch
CronJob batch/v1 batch
Role、RoleBinding、ClusterRole、ClusterRoleBinding rbac.authorization.k8s.io/v1 rbac.authorization.k8s.io
NetworkPolicy、Ingress networking.k8s.io/v1 networking.k8s.io

查询任意资源的 apiVersion:

bash 复制代码
root@master30:~# kubectl explain deployment | grep VERSION
VERSION:  apps/v1

root@master30:~# kubectl explain networkpolicy | grep VERSION
VERSION:  networking.k8s.io/v1

# 或者
root@master30:~# kubectl api-resources | grep -i deploy
deployments   deploy   apps/v1   true   Deployment

❌ 错误示例 :apiGroups 写成 "",却想管 deployments ------ 权限不生效!

yaml 复制代码
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: deployments-role
  namespace: default
rules:
- apiGroups:
  - ""                 # ← 错误!应为 "apps"
  resources:
  - deployments
  verbs:
  - get
  - list
  - watch

✅ 正确示例 1:只读 deployments

yaml 复制代码
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: deployments-role
  namespace: default
rules:
- apiGroups:
  - "apps"
  resources:
  - deployments
  verbs:
  - get
  - list
  - watch

✅ 正确示例 2 :可 scale deployments(kubectl scale 需要 patch + deployments/scale 子资源)

yaml 复制代码
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: deployments-role
  namespace: default
rules:
- apiGroups:
  - "apps"
  resources:
  - deployments
  - deployments/scale      # 关键:子资源
  verbs:
  - get
  - list
  - watch
  - patch                  # 关键:scale 本质是 patch

✅ 正确示例 3:多组资源分别授权

yaml 复制代码
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: all-role
  namespace: default
rules:
- apiGroups:
  - ""
  resources:
  - pods
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - "apps"
  resources:
  - deployments
  - deployments/scale
  verbs:
  - get
  - list
  - watch
  - patch

3.8 绑定、回收与删除

RoleBinding

先 dry-run 看结构:

bash 复制代码
root@master30:~# kubectl create rolebinding default-pod-fb -n default \
  --role=pod-role --user=fb --dry-run=client -o yaml
yaml 复制代码
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  creationTimestamp: null
  name: default-pod-fb
  namespace: default
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: pod-role
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: User
  name: fb

正式创建并验证:

bash 复制代码
# 绑定 ns/default 中角色 pod-role 给 fb
root@master30:~# kubectl create rolebinding default-pod-fb -n default --role=pod-role --user=fb
rolebinding.rbac.authorization.k8s.io/default-pod-fb created

root@master30:~# kubectl get rolebindings -n default default-pod-fb
NAME               ROLE             AGE
default-pod-fb  Role/pod-role    2m15s

root@master30:~# kubectl describe rolebindings -n default default-pod-fb
Name:         default-pod-fb
Role:
  Kind:  Role
  Name:  pod-role
Subjects:
  Kind  Name   Namespace
  ----  ----   ---------
  User  fb

客户端验证:

bash 复制代码
root@client:~# kubectl get pod -n default
NAME   READY   STATUS    RESTARTS   AGE
web    1/1     Running   0          2m30s

root@client:~# kubectl get pod -n default -w
NAME   READY   STATUS    RESTARTS   AGE
web    1/1     Running   0          2m42s

⚠️ 重要提醒 :--user 与 --username 是两个完全不同的选项!

  • --user:要绑定角色的用户名(RBAC subject)
  • --username:用于 API Server basic 认证的用户名

绑错的话,RBAC 会把你当成另一个用户,权限自然不生效。
动态性 :角色绑定完成后,角色权限发生变化,用户获得的权限会跟着动态变化,无需重新绑定。

回收权限 / 删除角色
bash 复制代码
# 回收:删除绑定
root@master30:~# kubectl delete rolebindings default-pod-fb -n default
rolebinding.rbac.authorization.k8s.io "default-pod-fb" deleted

# 再次验证
root@client:~# kubectl get pod -n default
Error from server (Forbidden): pods is forbidden: User "fb" cannot list resource "pods" in API group "" in the namespace "default"

# 删除角色本身
root@master30:~# kubectl delete roles pod-role -n default
role.rbac.authorization.k8s.io "pod-role" deleted
ClusterRoleBinding
bash 复制代码
root@master30:~# kubectl create clusterrolebinding fb-pod-role \
  --clusterrole=pod-role --user=fb --dry-run=client -o yaml
yaml 复制代码
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  creationTimestamp: null
  name: fb-pod-role
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: pod-role
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: User
  name: fb
bash 复制代码
root@master30:~# kubectl create clusterrolebinding fb-pod-role --clusterrole=pod-role --user=fb
clusterrolebinding.rbac.authorization.k8s.io/fb-pod-role created

root@master30:~# kubectl describe clusterrolebinding fb-pod-role
Name:         fb-pod-role
Role:
  Kind:  ClusterRole
  Name:  pod-role
Subjects:
  Kind  Name   Namespace
  ----  ----   ---------
  User  fb

# client 节点使用 fb 用户测试权限(所有 namespace 的 Pod 都能看)
root@client:~# kubectl get pod -n kube-system
NAME                                        READY   STATUS    RESTARTS   AGE
calico-kube-controllers-6dfcd885bf-lq4n5    1/1     Running   6          53d
calico-node-44cf4                           1/1     Running   6          53d
......

root@client:~# kubectl get pod -n default
NAME   READY   STATUS    RESTARTS   AGE
web    1/1     Running   1          15h
bash 复制代码
# 回收
root@master30:~# kubectl delete clusterrolebinding fb-pod-role
# 删除 clusterrole
root@master30:~# kubectl delete clusterrole pod-rule
ClusterRole 的两种绑定模式(重要)
绑定方式 ClusterRole 生效范围
ClusterRoleBinding 全局生效(所有 namespace)
RoleBinding 仅在该 RoleBinding 所在 namespace 生效

这是用 ClusterRole 做"按命名空间授权"的标准做法:定义一份 ClusterRole,在各个 namespace 里用 RoleBinding 分别绑定,避免重复定义多份 Role。

实践 1:角色管理

bash 复制代码
# 1. 在 auth 命名空间,创建角色 pod-reader,针对 pod 具备 get、list、watch
kubectl create role pod-reader --verb=get,list,watch --resource=pods -n auth

# 2. 赋予 fb 用户 auth 命名空间角色 pod-reader
kubectl create rolebinding fb-pod-reader -n auth --role=pod-reader --user=fb

# 3. 管理员身份在 auth 命名空间创建 deployment
kubectl create deployment web --image=hub.laoma.cloud/library/httpd -n auth

# 4. 回收 fb 用户集群管理员角色 cluster-admin(如果存在)
kubectl delete clusterrolebinding fb-admin

# 5. fb 用户验证:查看 deployment 和 pod(deployment 应被拒绝,pod 可查看)
kubectl get pod -n auth
kubectl get deployments -n auth      # Forbidden

# 6. 清理资源
kubectl delete rolebinding fb-pod-reader -n auth
kubectl delete role pod-reader -n auth
kubectl delete deployment web -n auth

实践 2:使用现有集群角色(验证"限定命名空间无效")

bash 复制代码
# 1. 赋予 fb 用户集群角色 admin,并「试图」限定在 auth 命名空间
#    注意:这里用的是 clusterrolebinding(集群级绑定)
kubectl create clusterrolebinding fb-admin --clusterrole=admin --user=fb
# 即便你手动给这个 ClusterRoleBinding 加上 namespace: auth 字段,
# 它仍然是集群级对象,不会因此被限制在 auth 内。

# 2. fb 用户在 auth 命名空间:创建、查看和删除 deployment  → 成功
kubectl create deployment web1 --image=hub.laoma.cloud/library/httpd -n auth
kubectl get deployments -n auth
kubectl delete deployment web1 -n auth

# 3. fb 用户在 default 命名空间:创建、查看和删除 deployment  → 同样成功
kubectl create deployment web2 --image=hub.laoma.cloud/library/httpd -n default
kubectl get deployments -n default
kubectl delete deployment web2 -n default

# 4. 回收权限
kubectl delete clusterrolebinding fb-admin

结论(实验要点) :用 ClusterRoleBinding 绑定集群角色时,"限定某个命名空间"是没有意义的 ------ClusterRoleBinding 本身是集群级对象,不归属任何 namespace,权限仍然针对集群级别、所有命名空间生效。

若真想把权限限制在单个命名空间,正确做法是:用 ClusterRole + RoleBinding(如上文 3.8 表所示),而不是给 ClusterRoleBinding 加 namespace 字段。

生产上要特别小心 cluster-admin 的 ClusterRoleBinding ------ 一次绑定,全集群裸奔。

实践 3:自定义集群角色

bash 复制代码
# 1. 创建 clusterrole,名称 cluster-pod-deploy-reader,能够查看 pod 和 deployment
#    注意:deployments 属于 apps 组,必须写 deployments.apps
kubectl create clusterrole cluster-pod-deploy-reader \
  --verb=get,list,watch --resource=pods,deployments.apps

# 验证生成的规则
kubectl describe clusterrole cluster-pod-deploy-reader

# 2. 赋予 fb 用户(ClusterRoleBinding 全局生效)
kubectl create clusterrolebinding fb-pod-deploy-reader \
  --clusterrole=cluster-pod-deploy-reader --user=fb

# 3. fb 用户在 auth 命名空间查看 pod、deployment       → 成功
# 4. fb 用户在 kube-system 命名空间查看 pod、deployment → 成功
kubectl get pod,deploy -n auth
kubectl get pod,deploy -n kube-system

# 5. 删除集群角色绑定和集群角色
kubectl delete clusterrolebinding fb-pod-deploy-reader
kubectl delete clusterrole cluster-pod-deploy-reader

3.9 ServiceAccount 服务账户

概述

ServiceAccount(SA) 是 Pod 中进程访问 K8s API 的身份。给 SA 授予角色,使用该 SA 运行的 Pod 中的进程就具备相应权限。

每个 namespace 都有一个名为 default 的 SA,未显式指定时 Pod 都以它运行:

bash 复制代码
root@master30:~# kubectl get sa
NAME      SECRETS   AGE
default   1         53d

# 创建一个 pod,验证 Service Account 信息
root@master30:~# kubectl run web --image=hub.laoma.cloud/library/httpd --image-pull-policy=IfNotPresent
root@master30:~# kubectl get pod web -o yaml | grep serviceAccount
  serviceAccount: default
  serviceAccountName: default
      - serviceAccountToken:
服务账号 vs 用户账号
类别 用户账号(User) 服务账号(ServiceAccount)
使用主体 针对人 针对 Pod,Pod 中的应用通过 SA 访问集群
范围 集群范围,名称必须唯一 命名空间范围,不同 ns 可有同名 SA
轻量程度 重量级,通过认证模块配置 轻量级,按需创建,便于遵循最小权限原则
服务账号令牌卷机制(K8s 1.24+ 重点)

Pod 调度到节点、kubelet 启动容器前:

  1. 根据 spec.serviceAccountName 在当前命名空间查找 SA;
  2. 获取该 SA 对应的令牌 ------ 1.24+ 默认不再自动创建永久 Secret,改用 Bound Service Account Token(短期轮换令牌);
  3. 将 token、ca.crt、namespace 以 投射卷(Projected Volume) 形式自动挂载到容器内默认路径:
    /var/run/secrets/kubernetes.io/serviceaccount/
bash 复制代码
root@master30:~# kubectl get pod web -o yaml
yaml 复制代码
...
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: kube-api-access-sjffr
      readOnly: true
...
  volumes:
  - name: kube-api-access-sjffr
    projected:
      sources:
        - serviceAccountToken:
            path: token            # 必须与应用所预期的路径匹配
        - configMap:
            items:
              - key: ca.crt
                path: ca.crt
            name: kube-root-ca.crt
        - downwardAPI:
            items:
              - fieldRef:
                  apiVersion: v1
                  fieldPath: metadata.namespace
                path: namespace

三个数据源详解:

数据源 作用
serviceAccountToken kubelet 通过 TokenRequest API 获取的有时间限制的令牌 。Pod 删除或生命周期(默认 1 小时)结束后过期;绑定到特定 Pod,audience 与 apiserver 匹配。取代了旧的"不过期的 Secret 令牌"机制
configMap CA 证书数据,Pod 用它验证自己连的是真正的 apiserver,而非中间人
downwardAPI 将 Pod 所在 namespace 名称暴露给容器内应用

容器内验证:

bash 复制代码
root@master30:~# kubectl exec -it web -- bash
root@web:/usr/local/apache2# ls /var/run/secrets/kubernetes.io/serviceaccount
ca.crt  namespace  token

root@web:/usr/local/apache2# cat /var/run/secrets/kubernetes.io/serviceaccount/token
eyJhbGciOiJSUzI1NiIsI......

root@web:/usr/local/apache2# cat /var/run/secrets/kubernetes.io/serviceaccount/namespace
auth

Pod 内应用(client-go / kubectl)访问 apiserver 的鉴权流程:

text 复制代码
1. 读取容器内 /var/run/secrets/kubernetes.io/serviceaccount/token
2. HTTP 请求头带上:Authorization: Bearer <token>
3. 请求发送给 apiserver
4. apiserver 验证 Token 合法性,解析出对应的 ServiceAccount(namespace + SA 名称)
5. apiserver 查找所有绑定到此 SA 的 RoleBinding / ClusterRoleBinding
6. 根据 RBAC 规则判断:是否允许本次操作
7. 返回 允许 / 拒绝
SA 管理
bash 复制代码
# 创建 SA
root@master30:~# kubectl create sa sa1
serviceaccount/sa1 created

root@master30:~# kubectl get sa sa1
NAME   SECRETS   AGE
sa1    1         9s

# 授权:注意 --serviceaccount 格式为 namespace:ServiceAccountName
root@master30:~# kubectl create clusterrolebinding auth-sa1-cluster-admin \
  --clusterrole=cluster-admin --serviceaccount=auth:sa1
clusterrolebinding.rbac.authorization.k8s.io/auth-sa1-cluster-admin created

Pod 使用指定 SA:

bash 复制代码
root@master30:~# kubectl delete pod web
root@master30:~# kubectl run web --image=hub.laoma.cloud/library/httpd \
  --image-pull-policy=IfNotPresent --dry-run=client -o yaml > web.yaml
root@master30:~# vim web.yaml
yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: web
  name: web
spec:
  containers:
  - image: httpd
    imagePullPolicy: IfNotPresent
    name: web
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
  # ===== 添加以下任一记录(推荐写 serviceAccountName)=====
  serviceAccount: sa1
  serviceAccountName: sa1
status: {}
bash 复制代码
root@master30:~# kubectl apply -f web.yaml
pod/web created

root@master30:~# kubectl get pod web -o yaml
......
spec:
......
  serviceAccount: sa1
  serviceAccountName: sa1

⚠️ 重要安全提醒 :虽然 pod/web 中的 httpd 进程现在持有 clusterrole/cluster-admin 权限,但它只是提供 httpd 服务,不会主动做别的操作。然而,一旦该容器被攻破,攻击者就能直接用挂载的 token 接管整个集群。

生产原则:永远遵循最小权限,不要图省事给 SA 绑 cluster-admin。

bash 复制代码
# 回收权限
root@master30:~# kubectl delete clusterrolebindings auth-sa1-cluster-admin
clusterrolebinding.rbac.authorization.k8s.io "auth-sa1-cluster-admin" deleted

典型使用案例:集群 Dashboard(如 Kubernetes Dashboard、Kuboard)中的 Pod 使用服务账户来管理集群。

3.10 手动管理服务账号 Token

  • v1.22 之前 :自动创建令牌 Secret,并挂载到运行的 Pod 中(永久有效,安全风险高)
  • v1.28 及近期版本 :使用 TokenRequest API 直接获取 API 凭据,并通过投射卷挂载。令牌具有绑定的生命周期,Pod 删除时自动失效

创建临时令牌(推荐):

bash 复制代码
root@master30:~# kubectl create sa sa1
root@master30:~# kubectl create token sa1
eyJhbGciOiJSUzI1NiIsImt......

# 可指定有效期
root@master30:~# kubectl create token sa1 --duration=3600s

注意 :服务账号属性中看不到关联的 token(这是设计使然):

bash 复制代码
root@master30:~# kubectl get sa sa1 -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  creationTimestamp: "2023-11-01T04:05:03Z"
  name: sa1
  namespace: fb
  resourceVersion: "11548"
  uid: 4944f75b-43ae-43fc-abfd-f2d20b7375f1

创建永久令牌(不推荐,仅兼容旧场景):

bash 复制代码
root@master30:~# vim sa1-token.yaml
yaml 复制代码
apiVersion: v1
kind: Secret
metadata:
  name: sa1-secret
  annotations:
    kubernetes.io/service-account.name: sa1
type: kubernetes.io/service-account-token
bash 复制代码
root@master30:~# kubectl apply -f sa1-token.yaml
secret/sa1-secret created

# 查看 token
root@master30:~# kubectl get secret sa1-secret -o jsonpath='{.data.token}' | base64 -d

自动清理:删除 SA 时,K8s 控制面会自动清理与之关联的长期令牌 Secret。

bash 复制代码
root@master30:~# kubectl delete sa sa1
serviceaccount "sa1" deleted

root@master30:~# kubectl get secrets
No resources found in fb namespace.

3.11 思考:K8s 为何不做用户管理?

问题 :Kubernetes 采用 RBAC 模型管理用户(User)和用户组(Group)权限,但集群中找不到真正的用户和用户组信息 ------甚至没有 User、Group 这两种 Resource。通过 API Server 根本无法列出可信的 User 列表与 Group 列表。换句话说,K8s 并没有提供用户管理和身份认证功能(ServiceAccount 除外),所有用户信息都依赖外部用户管理系统存储。

解答 :这非常符合 UNIX 设计哲学 ------ Do One Thing and Do It Well(做一件事,并把它做好)。

K8s 只专注于应用编排,其他功能一律提供接口集成。 除了认证和鉴权,网络(CNI)、存储(CSI)、运行时(CRI)都是同样的思路。

好处显而易见:

  1. 用户账户信息与 K8s 集群松耦合;
  2. 便于集成企业已有的身份认证系统:AD、LDAP、Keycloak、OIDC 等;
  3. K8s 核心保持精简,不被"用户管理"这类非核心需求拖累。

环境清理

bash 复制代码
root@master30:~# kubectl delete ns auth

四、Dashboard 可视化管理

4.1 Kuboard

简介

Kuboard 是一款专为 Kubernetes 设计的免费管理界面,兼容 K8s 1.13 及以上版本。与官方 Dashboard 相比,主要区别在于:

  • 从微服务参考架构视角组织界面,更贴近业务分层;
  • 无需手工编写 YAML,大幅降低 K8s 使用门槛;
  • 支持导出/导入整个微服务架构的部署信息;
  • 提供内建监控套件、日志套件(成熟度较高)。
核心特点
特性 说明
多种认证方式 内建用户库、GitLab/GitHub 单点登录、LDAP 用户库,避免分发 SA Token 的麻烦
多集群管理 多集群导入 + 按集群/命名空间分配权限
微服务分层展示 按经典微服务分层展示工作负载,可自定义命名空间布局
工作负载直观展示 Deployment 历史版本、Pod 列表、事件、容器信息同屏呈现
图形化编辑 支持 Node、Deployment、StatefulSet、DaemonSet、Secret、ConfigMap、Service、Ingress、PVC、StorageClass、LimitRange、ResourceQuota、SA、Role、CRD 等
存储类型支持 对接 NFS、CephFS,支持扩容与快照
丰富互操作 Top Nodes/Pods、容器日志与终端、容器文件浏览器(上传/下载)、KuboardProxy
套件扩展 资源监控(Prometheus + Grafana)、日志聚合(Loki + Promtail)、存储卷浏览器
告警配置 邮件/微信告警、告警路由、告警规则
操作审计 审计界面与 API 操作,支持自定义审计规则

在线演示 :https://demo.kuboard.cn (用户 demo,密码 demo123,只读权限)

版本选择
版本 说明
Kuboard v2.x 支持单集群管理
Kuboard v3.x 支持多集群管理,支持 amd64 与 arm64(armv8) 架构

本文实验环境 K8s 版本 1.32.0,选用 Kuboard v3.x。

安装:以独立容器方式运行(推荐)

推荐理由:结构更清晰(Kuboard 作为多集群管理界面应独立于任何集群之外)、认证方式独立、排错更简单。

bash 复制代码
# 创建数据目录
root@master30:~# mkdir /kuboard-data

# 创建容器(10.1.8.30 为物理机 IP)
root@master30:~# nerdctl run -d --name=kuboard \
  -p 80:80/tcp \
  -p 10081:10081/tcp \
  -e KUBOARD_ENDPOINT="http://10.1.8.30:80" \
  -e KUBOARD_AGENT_SERVER_TCP_PORT="10081" \
  -v /kuboard-data:/data \
  eipwork/kuboard:v3

若使用 Docker,将 nerdctl 替换为 docker 即可。

卸载:

bash 复制代码
root@master30:~# nerdctl container rm kuboard --force
root@master30:~# rm -fr /kuboard-data
安装:以 static pod 方式运行(自学)
bash 复制代码
# 在 master 节点上执行
root@master30:~# curl -fsSL https://addons.kuboard.cn/kuboard/kuboard-static-pod.sh -o kuboard.sh
root@master30:~# sh kuboard.sh
current ip address is 10.1.8.30
create file /root/kuboard-sa.yaml
...
restart kubelet

# 验证
root@master30:~# kubectl get all -n kuboard
NAME                                READY   STATUS    RESTARTS   AGE
pod/kuboard-v3-laoma30.redhat.fun   1/1     Running   0          45s

卸载:

bash 复制代码
root@master30:~# rm -f /etc/kubernetes/manifests/kuboard.yaml
root@master30:~# rm -fr /usr/share/kuboard
root@master30:~# kubectl delete ns kuboard
访问与导入集群

浏览器访问 http://<your-host-ip>:80

  • 用户名:admin
  • 密码:Kuboard123

导入集群步骤:

  1. 点击「添加集群」;
  2. 凭据方式选择 .kubeconfig;
  3. 将控制节点 ~/.kube/config 的内容复制到文本框中导入;
  4. 选择访问集群时使用的身份:ServiceAccount kuboard-admin。

4.2 Kubernetes Dashboard

项目地址:https://github.com/kubernetes/dashboard/

官方 Web UI,可部署容器化应用、诊断应用问题、管理集群资源、查看运行中的应用程序、创建修改资源、展示集群错误、伸缩 Deployment、执行滚动更新、重启 Pod 等。

部署
bash 复制代码
# 方式一:下载官方资源清单
root@master30:~# wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.6.1/aio/deploy/recommended.yaml

# 查看镜像(国内拉取困难)
root@master30:~# grep image: recommended.yaml
          image: kubernetesui/dashboard:v2.6.1
          image: kubernetesui/metrics-scraper:v1.0.8

# 方式二:使用内网镜像仓库版本(实验环境推荐)
root@master30:~# wget -O recommended.yaml \
  http://192.168.50.200/class/course-materials/softwares/stage03/kubernetes-dashboard-recommended-v2.6.1.yaml

root@master30:~# grep image: recommended.yaml
          image: hub.laoma.cloud/kubernetesui/dashboard:v2.6.1
          image: hub.laoma.cloud/kubernetesui/metrics-scraper:v1.0.8

# 执行部署
root@master30:~# kubectl apply -f recommended.yaml
验证
bash 复制代码
root@master30:~# kubens
default
kube-node-lease
kube-public
kube-system
kubernetes-dashboard

root@master30:~# kubens kubernetes-dashboard

root@master30:~# kubectl get deployments.apps
NAME                        READY   UP-TO-DATE   AVAILABLE   AGE
dashboard-metrics-scraper   1/1     1            1           2m25s
kubernetes-dashboard        1/1     1            1           2m25s

root@master30:~# kubectl get service
NAME                                   TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
kubernetes-dashboard-metrics-scraper   ClusterIP   10.98.116.192   <none>        8000/TCP   3m54s
kubernetes-dashboard-web               ClusterIP   10.107.63.216   <none>        8000/TCP   3m54s

root@master30:~# kubectl get serviceaccounts
NAME                   SECRETS   AGE
default                1         23h
kubernetes-dashboard   1         23h

分析默认 RBAC 配置(这是理解 Dashboard 权限的关键):

bash 复制代码
# 创建了 clusterrolebindings/kubernetes-dashboard
root@master30:~# kubectl get clusterrolebindings.rbac.authorization.k8s.io | grep dashboard
kubernetes-dashboard   ClusterRole/kubernetes-dashboard   23h

# sa/kubernetes-dashboard 绑定了 ClusterRole/kubernetes-dashboard
root@master30:~# kubectl describe clusterrolebindings.rbac.authorization.k8s.io kubernetes-dashboard
Name:         kubernetes-dashboard
Role:
  Kind:  ClusterRole
  Name:  kubernetes-dashboard
Subjects:
  Kind            Name                  Namespace
  ----            ----                  ---------
  ServiceAccount  kubernetes-dashboard  kubernetes-dashboard

# ClusterRole/kubernetes-dashboard 权限极小!
root@master30:~# kubectl describe clusterroles kubernetes-dashboard
Name:         kubernetes-dashboard
PolicyRule:
  Resources             Non-Resource URLs  Resource Names  Verbs
  ---------             -----------------  --------------  -----
  nodes.metrics.k8s.io  []                 []              [get list watch]
  pods.metrics.k8s.io   []                 []              [get list watch]

# deployments.apps/kubernetes-dashboard 以 sa/kubernetes-dashboard 身份运行
root@master30:~# kubectl get deployments.apps kubernetes-dashboard -o yaml | grep ' serviceAccount'
      serviceAccount: kubernetes-dashboard
      serviceAccountName: kubernetes-dashboard

结论 :默认部署的 Dashboard 只能看 metrics,什么都干不了。这是官方有意的安全设计(最小权限)。

修改 Service 为 NodePort
bash 复制代码
root@master30:~# kubectl edit svc kubernetes-dashboard
spec:
......
  type: NodePort      # 由 ClusterIP 改为 NodePort

root@master30:~# kubectl get svc kubernetes-dashboard
NAME                   TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)         AGE
kubernetes-dashboard   NodePort   10.111.32.116   <none>        443:30518/TCP   4m45s

备选方案:使用 Ingress(需开启 SSL 透传)

yaml 复制代码
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-dashboard
  namespace: kubernetes-dashboard
  annotations:
    # 关键:透传 TLS,Ingress 不解密
    nginx.ingress.kubernetes.io/ssl-passthrough: "true"
    nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
  ingressClassName: nginx
  rules:
    - host: dashboard.laoma.cloud
      http:
        paths:
        - path: /
          pathType: Prefix
          backend:
            service:
              name: kubernetes-dashboard
              port:
                number: 443

客户端将 dashboard.laoma.cloud 解析到 Ingress 服务地址(实验环境 10.1.8.40),即可访问 https://dashboard.laoma.cloud。

访问 Dashboard(新版本:只支持 Bearer Token)

浏览器访问 https://10.1.8.30:30518(忽略证书告警)。

新版 Dashboard 只支持 Bearer Token 登录。先看看默认账号有多小权限:

bash 复制代码
# Kubernetes Dashboard 以 deployment 形式运行
root@master30:~# kubectl get deployments.apps
NAME                        READY   UP-TO-DATE   AVAILABLE   AGE
dashboard-metrics-scraper   1/1     1            1           146m
kubernetes-dashboard        1/1     1            1           146m

# 以 serviceAccount: kubernetes-dashboard 身份运行
root@master30:~# kubectl get deployments.apps kubernetes-dashboard -o yaml | grep serviceAc
      serviceAccount: kubernetes-dashboard
      serviceAccountName: kubernetes-dashboard

# sa/kubernetes-dashboard 绑定了 Role/kubernetes-dashboard
root@master30:~# kubectl get rolebindings.rbac.authorization.k8s.io
NAME                   ROLE                        AGE
kubernetes-dashboard   Role/kubernetes-dashboard   147m

# Role/kubernetes-dashboard 仅具备这些能力(只够 Dashboard 自身运行)
root@master30:~# kubectl describe role kubernetes-dashboard
Name:         kubernetes-dashboard
PolicyRule:
  Resources       Non-Resource URLs  Resource Names                     Verbs
  ---------       -----------------  --------------                     -----
  secrets         []                 [kubernetes-dashboard-certs]       [get update delete]
  secrets         []                 [kubernetes-dashboard-csrf]        [get update delete]
  secrets         []                 [kubernetes-dashboard-key-holder]  [get update delete]
  configmaps      []                 [kubernetes-dashboard-settings]    [get update]
  services/proxy  []                 [dashboard-metrics-scraper]        [get]
  services/proxy  []                 [heapster]                         [get]
  services        []                 [dashboard-metrics-scraper]        [proxy]
  services        []                 [heapster]                         [proxy]

创建登录 Token:

bash 复制代码
root@master30:~# kubectl -n kubernetes-dashboard create token kubernetes-dashboard
eyJhbGciOiJSUzI1NiIsImtpZCI6Im9jekNIUUFRZUpKYzA3VXdmRHJvT3ZhaU1fMFNxVkh6NEJZa2lyUnhaaXcifQ...

用这个 Token 登录后,你会发现看不到 Pods ------ 因为权限只有 metrics。

授予集群管理员权限(实验环境做法):

bash 复制代码
root@master30:~# kubectl create clusterrolebinding kubernetes-dashboard-cluster-admin \
  --clusterrole cluster-admin \
  --serviceaccount kubernetes-dashboard:kubernetes-dashboard
clusterrolebinding.rbac.authorization.k8s.io/kubernetes-dashboard-cluster-admin created

重新创建 Token 登录后,即可看到全部资源。

⚠️ 两点提醒:

  1. Token 默认有效期 1 小时 (kubectl create token 默认时长),过期需重新生成。可用 --duration 指定,如 kubectl -n kubernetes-dashboard create token kubernetes-dashboard --duration=8760h。
  2. 生产环境不要 直接绑 cluster-admin,应创建只读或受限 ClusterRole,按需授权。
旧版本获取 Token

旧版本默认已为 sa/kubernetes-dashboard 创建了长期 Secret:

bash 复制代码
# 查看 SA 关联的 token
root@master30:~# kubectl describe sa kubernetes-dashboard
Name:                kubernetes-dashboard
Namespace:           kubernetes-dashboard
Mountable secrets:   kubernetes-dashboard-token-hm26j
Tokens:              kubernetes-dashboard-token-hm26j

# 获取 token 内容
root@master30:~# kubectl describe secrets kubernetes-dashboard-token-hm26j
Name:         kubernetes-dashboard-token-hm26j
Namespace:    kubernetes-dashboard
Annotations:  kubernetes.io/service-account.name: kubernetes-dashboard
              kubernetes.io/service-account.uid: d4b7d7ec-a76b-46c5-9dc5-629217cd07ba

Type:  kubernetes.io/service-account-token

Data
====
token:      eyJhbGciOiJSUzI1NiIsImtpZCI6IkNVVmdmMGNUckVGZFFPUVJ0dTkxZGtEc2hoOTlGZ2ktWjI2RmtnRWI5dFEifQ...
ca.crt:     1066 bytes
namespace:  20 bytes

# 一条命令直接取出
root@master30:~# kubectl -n kubernetes-dashboard get secret kubernetes-dashboard-token-hm26j \
  -o jsonpath='{.data.token}' | base64 -d

五、面试速查清单

健康检查

问题 答案
liveness 失败会怎样? 重启容器(Pod 不变,容器重建)
readiness 失败会怎样? 从 Endpoints 摘除 ,不重启
startupProbe 的作用? 保护慢启动容器,成功前 liveness/readiness 不执行
httpGet 成功状态码范围? 200 ≤ code < 400
exec 成功条件? 命令退出码为 0
tcpSocket 成功条件? 端口能建立连接(立即关闭也算健康)
Pod Running 但 READY 0/1? readiness 未通过,检查探针路径/端口
Pod 反复重启? liveness 未通过,检查 initialDelaySeconds 是否太短
滚动更新导致全站不可用? 缺 Health Check,新副本未就绪就被加入 Service

认证与授权

问题 答案
认证失败 / 鉴权失败的状态码? 401 / 403
三阶段顺序? 认证 → 鉴权 → 准入控制(读操作不触发准入)
K8s 有 User 对象吗? 没有,普通用户由外部系统提供,靠 X509 证书 CN 识别
Role 与 ClusterRole 区别? 前者 namespace 级,后者集群级
ClusterRole + RoleBinding 效果? 仅在该 namespace 生效
ClusterRole + ClusterRoleBinding 效果? 全集群生效
写 Deployment 规则时 apiGroups 填什么? apps (填 "" 不生效)
--user 与 --username 区别? 前者是 RBAC 绑定用户,后者是 basic 认证用户名
SA Token 挂载路径? /var/run/secrets/kubernetes.io/serviceaccount/
K8s 1.24+ SA Token 变化? 不再自动创建永久 Secret,改用有时限的 Bound Token
创建临时 Token 命令? kubectl create token <sa>
为什么 K8s 不做用户管理? Do One Thing and Do It Well,便于集成 AD/LDAP/OIDC

Dashboard

问题 答案
新版 Dashboard 登录方式? 仅 Bearer Token
默认 Dashboard 权限为何看不到 Pod? 只绑了 ClusterRole/kubernetes-dashboard(仅 metrics 权限)
让它能看全集群的命令? kubectl create clusterrolebinding ... --clusterrole cluster-admin --serviceaccount kubernetes-dashboard:kubernetes-dashboard
Kuboard v3 默认账密? admin / Kuboard123

六、总结

本文覆盖了 K8s 运维中最常出问题、也最能体现专业度的三个主题:

  1. 健康检查 ------ 让集群具备"自愈"与"优雅上下线"能力,是滚动更新零中断的基石;
  2. 认证授权 ------ 理解"认证管你是谁、鉴权管你能干什么",掌握 X509 + RBAC 的完整链路;
  3. 可视化管理 ------ Kuboard 与 Dashboard 双方案,快速上手日常运维。

三条生产建议:

  • liveness 探针要轻量且保守 (不要因为依赖抖动就重启容器),readiness 探针可以激进一些;
  • RBAC 永远遵循最小权限原则 ,拒绝图省事的 cluster-admin;
  • SA Token 优先使用短期 TokenRequest,避免创建永久 Secret。
相关推荐
wefg12 小时前
【Docker】基本指令
运维·docker·容器
mengge.cloud2 小时前
10-28-Kubernetes健康检查认证授权与Dashboard
云原生·容器·kubernetes
做运维的阿瑞3 小时前
EKS 可销毁环境:集群创建、应用部署到账单归零
云原生·云计算·terraform
每天一道题3 小时前
微服务为什么会变慢?从 QPS、P99 到 Trace 与性能瓶颈
微服务·云原生·架构
牛马工作号4 小时前
Copyparty 轻量文件共享:Docker Compose 部署、权限与多协议使用全指南
运维·docker·容器
oioihoii5 小时前
Windows部署MySpeed自建测速服务:Docker运行后直接测速,再配置固定公网地址远程查看
运维·docker·容器
时间售卖铺6 小时前
MySQL事务ACID与隔离级别深度解析
运维·数据库·mysql·云原生
SatanII6 小时前
Kubernetes 认证与授权详解:守护集群API访问安全
安全·贪心算法·kubernetes
每天一道题6 小时前
微服务为什么会发生级联故障?从 Timeout、Retry 到熔断、限流与 Backpressure
微服务·云原生·架构