Kubernetes Metric Server

Metrics Server 是 Kubernetes 集群中负责采集核心资源监控数据的组件。它是实现 Horizontal Pod Autoscaler (HPA) 等自动扩缩容功能的关键前提。

学习参考:Metric Server

环境准备

root@master30~ 10:15:12# kubectl create ns metric
root@master30~ 10:25:19# kubens metric

Metrics-Server 概述

我们在使用 Kubernetes 中过程中面临的问题:

  • 如何监控 node 计算资源使用情况?

  • 如何监控 pod 计算资源使用情况?

  • 如何根据 pod 计算资源使用情况,自动扩展?

我们可以使用 Metrics-Server监控:

  1. node 和 pod 计算资源使用情况。
  2. Metrics Server 是 Kubernetes 内置自动缩放管道的可扩展、高效的容器资源指标来源。
  3. Metrics Server 通过 Kubelet 收集资源指标,并通过 Metrics API 将它们公开在 Kubernetes apiserver 中,供 Horizontal Pod Autoscaler 和 Vertical Pod Autoscaler 使用。
  4. Metrics API 还可以通过 kubectl top 访问,从而更轻松地调试自动缩放管道。

Metrics Server 不适用于非自动缩放目的。 请勿将其用于将指标转发到监控解决方案,或作为监控解决方案指标的来源。 在这种情况下,请直接从 Kubelet /metrics/resource 端点收集指标。

指标服务器提供:

  • 适用于大多数集群的单一部署(请参阅要求)。
  • 快速自动缩放,每 15 秒收集一次指标。
  • 资源效率,集群中每个节点使用 1 mili 核心 CPU 和 2 MB 内存。
  • 可扩展支持多达 5,000 个节点集群。

Metrics-Server 部署

项目地址 kubernetes-sigs/metrics-server

# 下载 Metrics-Server
root@master30~ 10:25:25# wget https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.7.1/components.yaml

# 修改 Metrics-Server,不校验tls
root@master30~ 10:27:28# sed -i '/metric-resolution/a\        - --kubelet-insecure-tls' components.yaml

# 下载镜像
root@master30~ 10:27:47# grep image: components.yaml
        image: registry.k8s.io/metrics-server/metrics-server:v0.7.1

# 部署 Metrics-Server
root@master30~ 10:28:07# kubectl apply -f components.yaml
serviceaccount/metrics-server created
clusterrole.rbac.authorization.k8s.io/system:aggregated-metrics-reader created
clusterrole.rbac.authorization.k8s.io/system:metrics-server created
rolebinding.rbac.authorization.k8s.io/metrics-server-auth-reader created
clusterrolebinding.rbac.authorization.k8s.io/metrics-server:system:auth-delegator created
clusterrolebinding.rbac.authorization.k8s.io/system:metrics-server created
service/metrics-server created
deployment.apps/metrics-server created
apiservice.apiregistration.k8s.io/v1beta1.metrics.k8s.io created
# 查看 Metrics-Server 状态
root@master30~ 10:30:05# kubectl get pods -n kube-system | grep metrics
metrics-server-d994c478f-c9747               1/1     Running   0              68s

Metrics-Server 使用

查看node状态

root@master30~ 10:30:11# kubectl top node
NAME                 CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
master30.hgq.cloud   186m         9%     1921Mi          50%
worker31.hgq.cloud   78m          3%     1096Mi          29%
worker32.hgq.cloud   74m          3%     913Mi           24%

查看 pod 状态

root@master30~ 10:30:38# kubectl top pods -n kube-system
NAME                                         CPU(cores)   MEMORY(bytes)
calico-kube-controllers-585df69d45-jvw2t     5m           85Mi
calico-node-6hhpc                            61m          169Mi
calico-node-csnj7                            46m          170Mi
calico-node-p6bgb                            40m          169Mi
coredns-7db6d8ff4d-dbjhb                     1m           35Mi
coredns-7db6d8ff4d-h5vl2                     2m           38Mi
etcd-master30.hgq.cloud                      23m          96Mi
kube-apiserver-master30.hgq.cloud            59m          287Mi
kube-controller-manager-master30.hgq.cloud   16m          133Mi
kube-proxy-9gqs7                             1m           75Mi
kube-proxy-qvg5m                             18m          74Mi
kube-proxy-whsjf                             9m           74Mi
kube-scheduler-master30.hgq.cloud            4m           69Mi
metrics-server-d994c478f-c9747               2m           16Mi

单位:1cpu=1000m

扩容和减容控制

扩容和缩容时间参数

1. 指标采样周期
  • --horizontal-pod-autoscaler-sync-period duration
  • 默认:15s,HPA 每 15 秒拉取一次 metrics-server 指标。
2. 启动就绪窗口期
  • --horizontal-pod-autoscaler-initial-readiness-delay duration
  • 默认:30s,Pod 刚启动前 30 秒,视为 “启动中”,不参与 HPA 计算
3. 缩容稳定窗口期
  • --horizontal-pod-autoscaler-downscale-stabilization duration
  • 默认:5m0s(5 分钟),缩容冷却时间。

持续高负载 → 扩容

  1. 15s 采集一次 CPU/内存指标
  2. Pod 刚启动前 30 秒,视为 “启动中”,不参与 HPA 计算。
  3. 一次扩容动作完成后,再次等待 45s 冷却 才能下一次扩容

👉 结论:K8s 1.30 扩容最小间隔 = 45s

持续低负载 → 缩容

  1. 同样 15s 采样
  2. 指标长期低于阈值
  3. 需要满足 300s(5分钟)稳定低位 才会触发缩容
  4. 每次缩容后,再次锁定 5 分钟

设置扩容和减容时间参数

扩容和减容pod数量由kube-controller-manager管理,如需调快/调慢,直接修改 controller-manager 启动参数即可。

扩容和缩容pod是有冷却时间的,目的是防止流量抖动、瞬间峰值导致频繁炸裂扩容。

为了演示扩容和缩容效果,这里设置扩容冷却时间为30s(10+20),缩容冷却时间为60秒。

root@master30~ 10:31:05# vim /etc/kubernetes/manifests/kube-controller-manager.yaml
spec:
  containers:
  - command:
    - kube-controller-manager
    - --allocate-node-cidrs=true
    # ... 其他原有参数 ...

    # ===== 以下为 HPA 控制器调优参数 =====

    # 1. HPA 控制器评估指标并执行扩缩容操作的周期(默认 15s)
    #    设为 10s 可更快响应负载变化,但会增加 metrics-server 压力。
    - --horizontal-pod-autoscaler-sync-period=10s

    # 2. Pod 启动后,HPA 等待多久才将其“未就绪”状态纳入考虑(默认 30s)
    #    设为 20s 可以给新 Pod 更充裕的启动时间,避免启动瞬间被误判为不健康而触发缩容。
    - --horizontal-pod-autoscaler-initial-readiness-delay=20s

    # 3. 缩容稳定窗口,HPA 会参考过去这段时间内的所有推荐副本数,并取最大值(默认 300s)
    #    设为 60s 意味着缩容更积极,能更快回收闲置资源,但需确保应用能承受较快的 Pod 缩减。
    - --horizontal-pod-autoscaler-downscale-stabilization=60s

保存退出,静态 Pod 会自动重启生效

Horizontal Pod Autoscaler

学习参考:hpa

HPA 作用

控制器将从一系列API(metrics.k8s.iocustom.metrics.k8s.ioexternal.metrics.k8s.io)中获取度量值。 metrics.k8s.io API 通常由Metrics Server提供。根据 HPA 中定义的指标动态实现控制器中pod伸缩,支持replication controller、deployment和replicaset。

适用场景

  • 流量波动大的无状态服务(nginx、api、网关、微服务)
  • 需要提高并发削峰填谷
  • Deployment/StatefulSet 都支持

核心特点

  • 不修改 Pod 配置,只改副本数
  • 实时、秒级扩缩
  • 最常用、最稳定

支持指标

  • CPU 使用率
  • 内存使用率
  • 自定义指标(Prometheus 对接)
  • QPS、延迟等

基于 CPU 使用率伸缩

准备资源
root@master30~ 10:32:14# kubectl create deployment web --image=docker.io/library/nginx
root@master30~ 10:32:59# kubectl get pods
NAME                   READY   STATUS    RESTARTS   AGE
web-68b95c775c-74zjf   1/1     Running   0          17s
创建 hpa
Usage:  kubectl autoscale (-f FILENAME | TYPE NAME | TYPE/NAME) [--min=MINPODS]--max=MAXPODS [--cpu-percent=CPU] [options]

#用于为名为 web 的 Deployment 创建一个 HorizontalPodAutoscaler (HPA) 资源
root@master30~ 10:33:13# kubectl autoscale deployment web --max=5 --min=2 --cpu-percent=80
root@master30~ 10:33:55# kubectl get hpa web -o yaml | tee hpa-cpu.yaml
# 省略部分不重要配置
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web
spec:
  maxReplicas: 5
  metrics:
  - resource:
      name: cpu
      target:
        averageUtilization: 80
        type: Utilization
    type: Resource
  minReplicas: 2
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web
# 稍等片刻,会创建一个新的pod
root@master30~ 10:34:25# kubectl get pods
NAME                   READY   STATUS    RESTARTS   AGE
web-68b95c775c-74zjf   1/1     Running   0          111s
web-68b95c775c-zgktr   1/1     Running   0          42s

root@master30~ 10:34:47# kubectl get hpa
NAME   REFERENCE        TARGETS              MINPODS   MAXPODS   REPLICAS   AGE
web    Deployment/web   cpu: <unknown>/80%   2         5         2          58s
# CPU目标为unknown

要想看到 HPA 的 TARGETS 值必须满足2个条件:

  1. 安装 metric server。
  2. 为 pod 设定资源限制。
root@master30~ 10:34:53# kubectl edit deployments.apps web
# 配置资源上限
# 修改spec.template.spec.containers.[N].resources属性,添加limit属性,如下:
        resources:
          limits: 
            cpu: 100m
            memory: 200Mi

# 再次查看hpa
root@master30~ 10:36:54# kubectl get hpa
NAME   REFERENCE        TARGETS              MINPODS   MAXPODS   REPLICAS   AGE
web    Deployment/web   cpu: <unknown>/80%   2         5         2          3m13s

root@master30~ 10:37:08# kubectl expose deployment web --port=80 --target-port=80
service/web exposed
root@master30~ 10:37:35# kubectl get svc
NAME   TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
web    ClusterIP   10.103.61.217   <none>        80/TCP    4s
压力测试
# 打开一个监控窗口
root@master30~ 10:37:57# while true
do
  clear;
  kubectl get hpa;echo
  kubectl get pod;echo
  kubectl top pods
  sleep 1
done


# 上 CPU 压力
root@master30~ 10:37:39# apt install -y apache2-utils
root@master30~ 10:39:10# while true ;do ab -n 300000 -c 100 http://10.103.61.217/;sleep 1;done
#  -n 300000,总请求数
#  -c 100,每次并发数

最终扩容达到预期:

NAME   REFERENCE        TARGETS        MINPODS   MAXPODS   REPLICAS   AGE
web    Deployment/web   cpu: 92%/80%   2         5         5          8m43s

NAME                   READY   STATUS    RESTARTS   AGE
web-6dcdc45c94-djwfv   1/1     Running   0          112s
web-6dcdc45c94-spz57   1/1     Running   0          82s
web-6dcdc45c94-w6trt   1/1     Running   0          5m40s
web-6dcdc45c94-z5dlj   1/1     Running   0          5m44s
web-6dcdc45c94-zhf6h   1/1     Running   0          42s

NAME                   CPU(cores)   MEMORY(bytes)
web-6dcdc45c94-djwfv   94m          3Mi
web-6dcdc45c94-spz57   96m          3Mi
web-6dcdc45c94-w6trt   95m          3Mi
web-6dcdc45c94-z5dlj   93m          3Mi
web-6dcdc45c94-zhf6h   83m          3Mi

观察过程:

  1. 随着pod CPU使用率上升,自动扩展pod数量。

  2. 正常情况,30秒左右创建一个新pod,

  3. 即使pod CPU使用率超过阈值,但pod数量不会超过5。

  4. 停止压力测试,负载降低后,pod数量立刻减少为2。

清理资源
# 删除 hpa
root@master30~ 11:17:20# kubectl delete hpa web

# 删除 deployment
root@master30~ 11:17:02# kubectl delete deployments.apps web

# 保留 svc,后续使用

基于 Mem 使用率伸缩

我们仍然以nginx应用实践。想要Nginx 内存涨,要访问会占用内存的页面。最简单方法:让 Nginx 返回一个超大响应体

准备资源
# worker节点创建big.img
root@worker31~ 11:17:50# mkdir /www

#在 /www 目录下生成一个200MB 的空白填充文件 big.img
root@worker31~ 11:17:56# dd if=/dev/zero of=/www/big.img bs=1M count=200

root@worker32~ 11:18:43# mkdir /www
root@worker32~ 11:18:47# dd if=/dev/zero of=/www/big.img bs=1M count=200

root@master30~/metrics 11:19:28# vim deployment-web.yaml

分析dd if=/dev/zero of=/www/big.img bs=1M count=200

  • dd:磁盘复制工具,用于读写、生成、转换块数据。
  • if=/dev/zero
    • if = input file 输入源
    • /dev/zero 系统虚拟设备,源源不断输出二进制 0,不占用磁盘读取,速度极快。
  • of=/www/big.img
    • ``of= output file 输出文件,把生成的数据写入/www/big.img`。
  • bs=1M
    • block size 块大小,每次读写 1MB 数据。
  • count=200
    • 一共读写 200 个块。
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 1
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
        volumeMounts:
        - name: big-file
          mountPath: /usr/share/nginx/html
        resources:
          limits:
            cpu: 100m
            memory: 200Mi
      volumes:
      - name: big-file
      	#使用的是hostpath类型,故需要在节点主机上先布置相应的资源
        hostPath:
          path: /www
root@master30~/metrics 11:19:47# kubectl apply -f deployment-web.yaml
创建 hpa
root@master30~/metrics 11:20:58# vim hpa-mem.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web
spec:
  maxReplicas: 5
  metrics:
  - resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 60
    type: Resource
  minReplicas: 2
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web
root@master30~/metrics 11:21:24# kubectl apply -f hpa-mem.yaml
root@master30~/metrics 11:21:34# kubectl get hpa
NAME   REFERENCE        TARGETS                 MINPODS   MAXPODS   REPLICAS   AGE
web    Deployment/web   memory: <unknown>/60%   2         5         0          3s
# 目标值为空unknown

# 稍等片刻,创建一个新的pod
root@master30~/metrics 11:22:57# kubectl get pods
NAME                  READY   STATUS    RESTARTS   AGE
web-88cff78bb-86n5g   1/1     Running   0          2m17s
web-88cff78bb-nvggs   1/1     Running   0          78s


# 稍等一会,再次查看
root@master30~/metrics 11:23:02# kubectl get hpa
NAME   REFERENCE        TARGETS          MINPODS   MAXPODS   REPLICAS   AGE
web    Deployment/web   memory: 1%/60%   2         5         2          3m1s
压力测试
# 打开一个监控窗口
root@master30:~# watch -n 4 'kubectl get hpa;echo;kubectl top pods'

# 上 MEM 压力
root@master30:~# while true ;do ab -n 300000 -c 100 http://10.103.61.217/big.img;sleep 1;done

此时负载上来了,并新建了一个Pod

Every 3.0s: kubectl get hpa;echo;kubectl...  master30.hgq.cloud: Wed Jul  1 11:27:43 2026

NAME   REFERENCE        TARGETS           MINPODS   MAXPODS   REPLICAS   AGE
web    Deployment/web   memory: 56%/60%   2         5         3          6m10s

NAME                  CPU(cores)   MEMORY(bytes)
web-88cff78bb-86n5g   17m          169Mi
web-88cff78bb-hb5j9   0m           3Mi
web-88cff78bb-nvggs   20m          174Mi

最终达到预设Pod数量上限

Every 3.0s: kubectl get hpa;echo;kubectl top pods                     master30.hgq.cloud: Sun Apr 19 11:17:46 2026

NAME   REFERENCE        TARGETS           MINPODS   MAXPODS   REPLICAS   AGE
web    Deployment/web   memory: 58%/60%   2         5         5          16m

NAME                  CPU(cores)   MEMORY(bytes)
web-88cff78bb-2b7vh   15m          174Mi
web-88cff78bb-4hcm5   5m           3Mi
web-88cff78bb-76pg2   15m          171Mi
web-88cff78bb-ws6t4   1m           3Mi  
web-88cff78bb-t4q2p   100m         3Mi

观察过程:

  1. 随着pod MEM使用率上升,自动扩展pod数量。
  2. 即使pod MEM使用率超过阈值,但pod数量不会超过5。
清理资源
root@master30~/metrics 11:51:12# kubectl delete hpa web
root@master30~/metrics 11:51:21# kubectl delete deployments.apps web
# 删除 svc
root@master30~/metrics 11:51:29# kubectl delete svc web

Vertical Pod Autoscaler

VPA 作用

控制器将从一系列API(metrics.k8s.iocustom.metrics.k8s.ioexternal.metrics.k8s.io)中获取度量值,metrics.k8s.io API 通常由Metrics Server提供。根据 VPA 中定义的指标动态调整 Pod 的 requests 和 limits

VPA 适用场景

  • 资源配置不合理的服务
  • 长期运行、流量稳定的服务
  • Java、Go 等内存占用固定的应用
  • 不适合频繁扩缩的服务

VPA 核心特点

  • 不改变 Pod 数量
  • 自动给 Pod 推荐 / 设置 CPU / 内存
  • 生效需要重启 Pod(默认)
  • 比 HPA 少用,因为有侵入性

HPA 与 VPA 对比⭐️

项目 HPA 横向扩缩容 VPA 纵向扩缩容
全称 Horizontal Vertical
扩缩方式 增加 / 减少 Pod 数量 调整 CPU / 内存 request/limit
是否重启 Pod ❌ 不重启 ✅ 一般需要重启
适合负载 流量波动大 资源配置不合理
生效速度
稳定性
生产使用 非常普遍 较少
能否一起开 不能同时开(会冲突) -
支持资源 CPU、内存、自定义 CPU、内存

Quota and Limits

学习参考:

资源 管控对象 核心能力
LimitRange 单个容器 / 单个 Pod / 单个 PVC 单实例资源上下限、自动填充默认 requests/limits
ResourceQuota 整个命名空间所有资源总和 限制总 CPU、总内存、Pod、PVC、Service、Secret 总数上限

环境准备

  1. 创建一个独立的名字空间quota,并切换到该ns

    root@master30~/quota 13:41:08# kubectl create ns quota
    root@master30~/quota 13:41:25# kubens quota
    
  2. 提前部署好 Metric Server

ResourceQuota

ResourceQuota命名空间级全局限制,约束当前 namespace 下所有 Pod 的资源请求总和

资源配额的工作方式如下:

  • 不同的团队在不同的命名空间下工作。这可以通过 RBAC 强制执行。

  • 集群管理员可以为每个命名空间创建一个或多个 ResourceQuota 对象。

  • 当用户在命名空间下创建资源(如 Pod、Service 等)时,Kubernetes 的配额系统会跟踪集群的资源使用情况, 以确保使用的资源用量不超过 ResourceQuota 中定义的硬性资源限额。

  • 如果资源创建或者更新请求违反了配额约束,那么该请求会报错(HTTP 403 FORBIDDEN), 并在消息中给出有可能违反的约束。

  • 如果命名空间下的计算资源 (如 cpumemory)的配额被启用, 则用户必须为这些资源设定请求值(request)和约束值(limit),否则配额系统将拒绝 Pod 的创建。

    提示: 可使用 LimitRanger 准入控制器来为没有设置计算资源需求的 Pod 设置默认值。

启用资源配额

Kubernetes 默认启用了资源配额功能 。 当 API 服务器 的命令行标志 --enable-admission-plugins= 中包含 ResourceQuota 时, 资源配额会被启用。

当命名空间中存在一个 ResourceQuota 对象时,对于该命名空间而言,资源配额就是开启的。

配额类型

Kubernetes可以限制两种类型资源:

  • 对象数量:Kubernetes 资源数量,例如pods,services等。

    实施资源数量配额可以提高kubernetes稳定性,避免Etcd数据库无限增长,还可以避免占用node中其他功能资源(例如ip地址服务)。

  • 计算资源:物理或者虚拟资源容量,例如 CPU,memory 和存储容量。

    实施计算资源配额可以避免消耗 kubernetes 集群中单个node所有计算资源,避免单个namespace中应用消耗所有集群资源,导致其他namespace中应用无法正常运行。

**kubernetes 通过 ResourceQuota 类型资源实施配额。**一个namespace可以包含多个ResourceQuota对象,这些限制是累加的,一般情况,多个ResourceQuota对象不会限定同一个资源。

  • 对象数量:

    • persistentvolumeclaims
    • services
    • secrets
    • configmaps
    • replicationcontrollers
    • deployments.apps
    • replicasets.apps
    • statefulsets.apps
    • jobs.batch
    • cronjobs.batch
  • 计算资源

    资源名称 描述
    limits.cpu 在所有处于非终止状态的 Pod 中,CPU 限制的总和不能超过此值。
    limits.memory 在所有处于非终止状态的 Pod 中,内存限制的总和不能超过这个值。
    requests.cpu 在所有处于非终止状态的 Pod 中,CPU 请求的总和不能超过此值。
    requests.memory 在所有处于非终止状态的 Pod 中,内存请求的总和不能超过这个值。
    requests.storage 在所有持久卷声明中,存储请求的总和不能超过此值。
    cpu requests.cpu一样
    memory requests.memory一样

单位说明:

  • CPU1 cpu 等于1000 m,默认单位是 cpu核心数量。
  • memory:支持两种格式。
    • Ki | Mi | Gi | Ti | Pi | Ei,进制是1024,例如1024 = 1Ki
    • k | M | G | T | P | E,进制是1000,例如1000 = 1k
    • 默认单位是 G,例如1.5,代表1500M。

配额管理

重要说明: 如果项目级别配额限定了 requestlimit,那么创建pod的时候必须指定 requestlimit

创建 ResourceQuota 对象

root@master30~/quota 13:41:34# kubectl create quota myquota --hard=pods=2,services=3,secrets=5,persistentvolumeclaims=10

root@master30~/quota 14:02:43# kubectl get quota
NAME      AGE   REQUEST                                                                LIMIT
myquota   8s    persistentvolumeclaims: 0/10, pods: 0/2, secrets: 0/5, services: 0/3


root@master30~/quota 14:03:06# kubectl describe quota myquota
Name:                   myquota
Namespace:              quota
Resource                Used  Hard
--------                ----  ----
persistentvolumeclaims  0     10
pods                    0     2
secrets                 0     5
services                0     3

通过 yaml 文件创建

apiVersion: v1
kind: ResourceQuota
metadata:
  name: myquota
spec:
  hard:
    persistentvolumeclaims: "10"
    pods: "2"
    secrets: "5"
    services: "3"

测试配额

root@master30~/quota 14:03:32# kubectl create deployment web --image=docker.io/library/nginx --replicas=3
deployment.apps/web created
root@master30~/quota 14:04:18# kubectl get all
NAME                       READY   STATUS    RESTARTS   AGE
pod/web-68b95c775c-lhlvp   1/1     Running   0          4s
pod/web-68b95c775c-r5kq2   1/1     Running   0          4s

NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/web   2/3     2            2           4s

NAME                             DESIRED   CURRENT   READY   AGE
replicaset.apps/web-68b95c775c   3         2         2       4s       40s

root@master30~/quota 14:05:01# kubectl describe rs | tail -1
  Warning  FailedCreate      61s (x7 over 67s)  replicaset-controller  (combined from similar events): Error creating: pods "web-68b95c775c-f6j8c" is forbidden: exceeded quota: myquota, requested: pods=1, used: pods=2, limited: pods=2
# 超过配额,创建失败

# 修改配额 pod数量为10
# patch:局部更新资源,只修改指定字段
# -p:后面跟JSON格式补丁字符串
root@master30~/quota 14:06:52# kubectl patch resourcequotas myquota -p '{"spec":{"hard":{"pods":10}}}'

#或者使用edit方式修改quota
root@master30~/quota 14:10:21# kubectl edit quota myquota

# 此时重新扩展
root@master30~/quota 14:12:29# kubectl scale deployment web --replicas 3

# 再次验证pod数量
root@master30~/quota 14:12:38# kubectl get pods
NAME                   READY   STATUS    RESTARTS   AGE
web-68b95c775c-lhlvp   1/1     Running   0          9m52s
web-68b95c775c-lpfnf   1/1     Running   0          103s
web-68b95c775c-r5kq2   1/1     Running   0          9m52s


# 清理环境
root@master30~/quota 14:14:10# kubectl delete deployments.apps web
root@master30~/quota 14:14:24# kubectl delete quota myquota

思考: 如果一个用户可以管理多个 namespace,能否限定该用户配额呢?

Request 和 Limits

如果命名空间下的计算资源 (如 cpumemory)的配额被启用, 则用户必须为这些资源设定请求值(request)和约束值(limit),否则配额系统将拒绝 Pod 的创建。 ⭐️

pod.containers.resources 定义包含两部分:

  • requests,指明pod运行需要的最少计算资源,调度器查找具有充足计算资源的nodes。

  • limits,指明pod运行可以获得节点最多计算资源,用于阻止pod占用node太多计算资源。node使用Linux内核功能cgroup,限制pod资源使用。

测试-不指定计算资源

配额示例

root@master30~/quota 14:18:26# vim resourcequota.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: myquota
spec:
  hard:
    persistentvolumeclaims: "10"
    pods: "2"
    secrets: "5"
    services: "3"
    requests.cpu: 1000m
    requests.memory: "2048Mi"
    limits.cpu: 1000m
    limits.memory: "2048Mi"

requests.cpu / requests.memory:统计当前命名空间下所有运行中 Pod 内所有容器的 requests 总和,不能超过设定值;新建资源时累加超限直接拒绝创建。

limits.cpu / limits.memory:统计当前命名空间下所有运行中 Pod 内所有容器的 limits 总和,同样超限直接拦截 Pod 创建。

YAML 里不写 metadata.namespace 字段时,执行 kubectl apply 会把该 ResourceQuota 创建在当前上下文的命名空间,配额只约束这个命名空间

root@master30~/quota 14:18:34# kubectl apply -f resourcequota.yaml

pod 示例

root@master30~/quota 14:18:40# vim pod-without-quota.yaml
apiVersion: v1
kind: Pod
metadata:
  name: web
  labels:
    app: web
spec:
  containers:
  - name: web
    image: httpd
    imagePullPolicy: IfNotPresent
    ports:
    - name: web
      containerPort: 80
      protocol: TCP
root@master30~/quota 14:18:50# kubectl apply -f pod-without-quota.yaml
Error from server (Forbidden): error when creating "pod-without-quota.yaml": pods "web" is forbidden: failed quota: myquota: must specify limits.cpu for: web; limits.memory for: web; requests.cpu for: web; requests.memory for: web

# 清理环境
root@master30~/quota 14:18:54# kubectl delete quota myquota

分析:⭐️

核心规则

只要当前命名空间的 ResourceQuota 的 spec.hard 里定义了以下任意一项:

  • cpu / memory(容器总资源配额)
  • limits.cpu / limits.memory
  • requests.cpu / requests.memory

准入控制器会强制校验:该命名空间下所有容器必须同时声明 requests.cpu、requests.memory、limits.cpu、limits.memory,缺任意一个字段,Pod/Deployment 创建直接报错 Forbidden。

测试-Request

配额示例

root@master30~/quota 14:19:32# vim resourcequota.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: myquota
spec:
  hard:
    requests.cpu: 1000m
    requests.memory: "2048Mi"
root@master30~/quota 14:20:13# kubectl apply -f resourcequota.yaml

pod 示例1:申请资源超上限

root@master30~/quota 14:36:17# vim pod-request-1.yaml
apiVersion: v1
kind: Pod
metadata:
  name: web
  labels:
    app: web
spec:
  containers:
  - name: web
    image: docker.io/library/httpd
    imagePullPolicy: IfNotPresent
    resources:
      requests:
        cpu: 2000m
        memory: 4096Mi
    ports:
    - name: web
      containerPort: 80
      protocol: TCP
root@master30~/quota 14:36:38# kubectl apply -f pod-request-1.yaml
Error from server (Forbidden): error when creating "pod-request-1.yaml": pods "web" is forbidden: exceeded quota: myquota, requested: requests.cpu=2,requests.memory=4Gi, used: requests.cpu=0,requests.memory=0, limited: requests.cpu=1,requests.memory=2Gi

pod 示例2:申请资源未超上限

root@master30~/quota 14:36:49# vim pod-request-2.yaml
apiVersion: v1
kind: Pod
metadata:
  name: web
  labels:
    app: web
spec:
  containers:
  - name: web
    image: docker.io/library/httpd
    imagePullPolicy: IfNotPresent
    resources:
      requests:
        cpu: 200m
        memory: 1024Mi
    ports:
    - name: web
      containerPort: 80
      protocol: TCP
root@master30~/quota 14:39:23# kubectl apply -f pod-request-2.yaml
root@master30~/quota 14:39:50# kubectl get pods
NAME   READY   STATUS    RESTARTS   AGE
web    1/1     Running   0          5s

# 验证使用情况
root@master30~/quota 14:39:55# kubectl describe quota myquota
Name:            myquota
Namespace:       quota
Resource         Used  Hard
--------         ----  ----
requests.cpu     200m  1
requests.memory  1Gi   2G

# 删除pod和quota
root@master30~/quota 14:41:52# kubectl delete pod web
root@master30~/quota 14:42:17# kubectl delete quota myquota

测试-Limits

压力测试镜像

可以直接使用镜像 docker.io/progrium/stress 进行压力测试,该镜像中运行stress命令。

Usage: stress [OPTION [ARG]] ...
 -?, --help         show this help statement
     --version      show version statement
 -v, --verbose      be verbose
 -q, --quiet        be quiet
 -n, --dry-run      show what would have been done
 -t, --timeout N    timeout after N seconds
     --backoff N    wait factor of N microseconds before work starts
 -c, --cpu N        spawn N workers spinning on sqrt()
 -i, --io N         spawn N workers spinning on sync()
 -m, --vm N         spawn N workers spinning on malloc()/free()
     --vm-bytes B   malloc B bytes per vm worker (default is 256MB)
     --vm-stride B  touch a byte every B bytes (default is 4096)
     --vm-hang N    sleep N secs before free (default none, 0 is inf)
     --vm-keep      redirty memory instead of freeing and reallocating
 -d, --hdd N        spawn N workers spinning on write()/unlink()
     --hdd-bytes B  write B bytes per hdd worker (default is 1GB)

Example: stress --cpu 8 --io 4 --vm 2 --vm-bytes 128M --timeout 10s

Note: Numbers may be suffixed with s,m,h,d,y (time) or B,K,M,G (size).

常用选项:

  • -c, --cpu N spawn N workers spinning on sqrt()

  • -m, --vm N spawn N workers spinning on malloc()/free()

    ​ --vm-bytes B malloc B bytes per vm worker (default is 256MB)

  • -d, --hdd N spawn N workers spinning on write()/unlink()
    –hdd-bytes B write B bytes per hdd worker (default is 1GB)

示例:

# 压力测试内存
$ docker run --name stress docker.io/progrium/stress -m 1 --vm-bytes 512M

# 压力测试CPU
$ docker run --name stress docker.io/progrium/stress -c 1

# 压力测试IO
$ docker run --name stress docker.io/progrium/stress –d 1 --hdd-bytes 3G

对于 pod:

apiVersion: v1
kind: Pod
metadata:
  name: stress
spec:
  containers:
  - name: stress
    image: docker.io/progrium/stress
    imagePullPolicy: IfNotPresent
    command: ['sh','-c','sleep 3600']
    # 或者不用command,而是使用args作为参数传递给镜像的Entrypoint。
    #args: ['-m','1','--vm-bytes','512M']
    #args: ['-c','1']
    #args: ['-d','1','--hdd-bytes','3G']
配额示例
root@master30~/quota 14:43:49# vim resourcequota.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: myquota
spec:
  hard:
    limits.cpu: 1000m
    limits.memory: "2048Mi"
root@master30~/quota 14:44:08# kubectl apply -f resourcequota.yaml
测试 CPU 资源

pod示例:

root@master30~/quota 14:44:14# vim pod-limit-cpu.yaml
apiVersion: v1
kind: Pod
metadata:
  name: stress
spec:
  containers:
  - name: stress
    image: docker.io/progrium/stress
    imagePullPolicy: IfNotPresent
    args: ['-c','1']
    resources:
      limits:
        cpu: 200m
        memory: "256Mi"
root@master30~/quota 14:44:52# kubectl apply -f pod-limit-cpu.yaml

打开一个终端监控

kubectl top 命令需要提前部署Metrics-Server

root@master30~/quota 14:51:00# kubectl top pods
NAME     CPU(cores)   MEMORY(bytes)
stress   201m         0Mi

**可以发现:**CPU 使用率维持在 200m 左右。

# 删除 pod
root@master30~/quota 14:58:14# kubectl delete pod stress --force
测试 MEMORY 资源

pod示例:

root@master30~/quota 14:58:39# vim pod-limit-memory.yaml
apiVersion: v1
kind: Pod
metadata:
  name: stress
spec:
  containers:
  - name: stress
    image: docker.io/progrium/stress
    imagePullPolicy: IfNotPresent
    args: ['-m','1','--vm-bytes','512M']
    resources:
      limits:
        cpu: 200m
        memory: "256Mi"
root@master30~/quota 15:17:23# kubectl apply -f pod-limit-memory.yaml

打开一个终端监控

root@master30~/quota 15:17:34# kubectl get pods -w
NAME     READY   STATUS    RESTARTS   AGE
stress   1/1     Running   0          13s
stress   0/1     OOMKilled   0          13s
stress   1/1     Running     1 (2s ago)   14s
stress   0/1     OOMKilled   1 (3s ago)   15s
stress   0/1     CrashLoopBackOff   1 (15s ago)   30s

可以发现: Pod 状态为 OOMKilled,并进行restart。

# 删除 pod he 
root@master30~/quota 15:19:43# kubectl delete pod stress --force
root@master30~/quota 15:20:45# kubectl delete resourcequotas myquota
总结
  1. 计算资源的 limits 总和是否会超过节点上资源总和?

    答案:可能会。 假设 node可用MEMORY为1G。

    每个pod内存 requests是256M,limit是512M。创建5个pod,pod实际占用内存也为256M(有可能小于256M)。

    node上大概可以创建4个pod,而此时的limits总和是2G。

  2. 当计算资源的limits总和超过节点上资源总和时,kubernetes如何处理?

    • 对于 cpu,kubernetes 认为 cpu 是可被压缩的资源,在应用达到limits时,减少该容器的调度时间,并不会杀死应用。
    • 对于 memory,kubernetes 认为 memory 是无法被压缩的资源,此时k8s会杀死占用资源超过其request的应用(1.9版本之后的版本)。首当其冲的是没有指定request的container,然后是使用资源超过其request更多的container。同等情况下优先级更低的container更容易被杀死。

LimitRange

kubernetes创建pod时,默认不指定资源请求和限制。如果namespace设置了配额,那么创建不指定资源请求和资源限制的pod是不允许的。为了在设定配额的namespace中使用pod,namespace还需要为pod资源请求设定默认范围

LimitRange 资源,也称为limits,定义了单个pod的资源请求和资源限制default、minimum、maximum值。pod的资源请求是其中所有容器请求的总和。

LimitRange 资源用于限定特定 namespace。

namespace设定了LimitRange,创建资源规则:

  • 如果项目中请求一个未提供计算资源的对象,那么此时namespace将使用limit范围default值创建该对象。
  • 如果项目中请求一个计算资源的对象,请求的资源小于limit最小值,那么该资源**无法创建**。
  • 如果项目中请求一个计算资源的对象,请求的资源大于limit最大值,那么该资源**无法创建**。

LimitRange 示例

root@master30~ 17:11:09# vim limits.yaml
apiVersion: v1
kind: LimitRange
metadata:
  name: mylimit
spec:
  limits:
    - type: Container
      max:
        memory: 1024Mi
        #`1`等价于1000m
        cpu: 1
      min:
        memory: 128Mi
        cpu: 100m
      default:
        memory: 512Mi
        cpu: 500m
      defaultRequest:
        memory: 256Mi
        cpu: 200m

说明:

  • name:只能使用小写字母,数字, ‘-’ 和 ‘.’,而且只能是数字或字母开头和结尾。
  • default:即该namespace配置resourceQuota时,创建container的默认limit上限
  • defaultRequest:即该namespace配置resourceQuota时,创建container的默认request上限
  • max:即该namespace下创建container的资源最大值
  • min:即该namespace下创建container的资源最小值

其中: min <= defaultRequest <= default <= max

root@master30~ 17:13:15# kubectl apply -f limits.yaml
root@master30~ 17:14:36# kubectl get limitranges
NAME      CREATED AT
mylimit   2026-07-02T09:14:36Z


root@master30~ 17:15:03# kubectl describe limitranges mylimit
Name:       mylimit
Namespace:  quota
Type        Resource  Min    Max  Default Request  Default Limit  Max Limit/Request Ratio
----        --------  ---    ---  ---------------  -------------  -----------------------
Container   cpu       100m   1    200m             500m           -
Container   memory    128Mi  1Gi  256Mi            512Mi          -

未指定 resources

示例1

root@master30~ 17:15:33# vim pod-without-limits.yaml
apiVersion: v1
kind: Pod
metadata:
  name: stress
spec:
  containers:
  - name: stress
    image: docker.io/progrium/stress
    imagePullPolicy: IfNotPresent
    args: ['-c','1']

**结论:**创建出来的pod的resources 与 limitranage 指定的相关默认值一致。

root@master30~ 18:41:50# kubectl apply -f pod-without-limits.yaml
root@master30~ 18:43:40# kubectl top pods
NAME     CPU(cores)   MEMORY(bytes)   
stress   501m         0Mi

root@master30~ 18:44:17# kubectl get pod stress -o yaml
......
spec:
  containers:
    image: docker.io/progrium/stress
    imagePullPolicy: IfNotPresent
    name: stress
    resources:
      limits:
        cpu: 500m
        memory: 512Mi
      requests:
        cpu: 200m
        memory: 256Mi
......

只指定 limit 值

示例 2-1:limit 值大于 max 值
apiVersion: v1
kind: Pod
metadata:
  name: web
spec:
  containers:
  - name: web
    image: docker.io/library/nginx
    resources:
      limits:
        cpu: 1.1
        memory: 1100Mi
root@master30~ 18:47:01# kubectl apply -f limit.yaml
Error from server (Forbidden): error when creating "limit.yaml": pods "web" is forbidden: [maximum memory usage per Container is 1Gi, but limit is 1100Mi, maximum cpu usage per Container is 1, but limit is 1100m]
示例 2-2:limit 值小于 min 值
apiVersion: v1
kind: Pod
metadata:
  name: web
spec:
  containers:
  - name: web
    image: docker.io/library/nginx
    resources:
      limits:
        cpu: 60m
        memory: 60Mi
root@master30~ 18:48:15# kubectl apply -f limit.yaml
Error from server (Forbidden): error when creating "limit.yaml": pods "web" is forbidden: [minimum cpu usage per Container is 100m, but request is 60m, minimum memory usage per Container is 128Mi, but request is 60Mi]
示例 2-3:min 值< limit 值< max 值
apiVersion: v1
kind: Pod
metadata:
  name: web
spec:
  containers:
  - name: web
    image: docker.io/library/nginx
    resources:
      limits:
        cpu: 600m
        memory: 600Mi
root@master30~ 18:48:54# kubectl apply -f limit.yaml
root@master30~ 18:48:57# kubectl get pods web -o yaml
......
spec:
  containers:
  - image: docker.io/library/nginx
    imagePullPolicy: Always
    name: web
    resources:
      limits:
        cpu: 600m
        memory: 600Mi
      requests:
        cpu: 600m
        memory: 600Mi
......

只指定 requests

示例 3-1:requests 大于 max 值
apiVersion: v1
kind: Pod
metadata:
  name: web
spec:
  containers:
  - name: web
    image: docker.io/library/nginx
    resources:
      requests:
        cpu: 1600m
        memory: 600Mi
root@master30~ 18:51:08# kubectl apply -f request.yaml
The Pod "web" is invalid:
* spec.containers[0].resources.requests: Invalid value: "1600m": must be less than or equal to cpu limit of 500m
* spec.containers[0].resources.requests: Invalid value: "600Mi": must be less than or equal to memory limit of 512Mi
示例 3-2:requests 小于 min 值
apiVersion: v1
kind: Pod
metadata:
  name: web
spec:
  containers:
  - name: web
    image: docker.io/library/nginx
    resources:
      requests:
        cpu: 60m
        memory: 60Mi
root@master30~ 18:59:49# kubectl apply -f request.yaml
Error from server (Forbidden): error when creating "request.yaml": pods "web" is forbidden: [minimum cpu usage per Container is 100m, but request is 60m, minimum memory usage per Container is 128Mi, but request is 60Mi]
示例 3-3:min 值< request 值< max 值
apiVersion: v1
kind: Pod
metadata:
  name: web
spec:
  containers:
  - name: web
    image: docker.io/library/nginx
    resources:
      requests:
        cpu: 400m
        memory: 400Mi
root@master30~ 19:00:35# kubectl get pods web -o yaml
......
spec:
  containers:
  - image: docker.io/library/nginx
    imagePullPolicy: Always
    name: web
    resources:
      limits:
        cpu: 500m
        memory: 512Mi
      requests:
        cpu: 400m
        memory: 400Mi
......

总结

  • 创建的容器limits值必须满足条件:min值<指定的limit值<max值

  • 当只指定limits值时,requests值与limits值保持一致,而不是default request。⭐️

  • 创建的容器requests值必须满足条件:min值<requests值<limits值

  • 当只指定requests值时,limits值与default值保持一致。:⭐️

限定资源类型

LimitRange 资源可以限定如下资源:

Type Resource Name Description
container cpu、memory 限定容器 cpu、memroy
Pod cpu、memory 限定 Pod 中所有容器cpu、memroy的总和
PVC storage 限定PVC申请的存储空间大小

LimitRange for PVC 示例:

apiVersion: v1
kind: LimitRange
metadata:
  name: storagelimits
spec:
  limits:
  - type: PersistentVolumeClaim
    max:
      storage: 2Gi
    min:
      storage: 1Gi

Kubernetes Health Check

参考:配置存活、就绪和启动探针

环境准备

root@master30~ 19:03:53# kubectl create ns health
root@master30~ 19:05:18# kubens health

Health Check

应用可能会因为各种问题,变的 unhealthy,例如临时连接断开,配置错误,应用本身错误。

kubelet 使用 probes(探针),周期性地监控容器中应用是否为healthy状态,进一步决定什么时候要重启容器。 例如,当存活探针可以探测到应用死锁(应用在运行,但是无法继续执行后面的步骤)情况,进而重启pod,有助于提高应用的可用性,即使其中存在缺陷。

没有探测的情况,例如:

# 创建一个普通 pod
root@master30~ 19:06:32# kubectl run web --image=docker.io/library/httpd --image-pull-policy=IfNotPresent

root@master30~ 19:08:24# kubectl get pods -o wide | awk '{print$6}'                      IP
10.224.128.55

root@master30~ 19:08:31# curl 10.224.128.55
<html><body><h1>It works!</h1></body></html>

# 删除主页文件,即使pod中应用数据丢失,pod状态依然为Running
root@master30~ 19:08:52# kubectl exec web -- rm -f htdocs/index.html

# 查看主页内容
root@master30~ 19:09:22# curl 10.224.128.55
<!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>

root@master30~ 19:09:29# kubectl get pods
NAME   READY   STATUS    RESTARTS   AGE
web    1/1     Running   0          2m38s

Probe Type

kubelet 使用启动探针来了解应用容器何时启动。 如果配置了这类探针,存活探针和就绪探针成功之前不会重启,确保这些探针不会影响应用的启动。 启动探针可以用于对慢启动容器进行存活性检测,避免它们在启动运行之前就被杀掉。

  • LivenessProbe(存活探针):用于确定pod中应用是否处于healthy状态。如果liveness probe检测的状态为unhealthy,则控制器将重启创建一个同名的pod

  • ReadinessProbe(就绪探针):判断容器是否完成初始化、能正常对外提供服务。

    • 未就绪:Pod 状态为 NotReady,Service 不会把流量转发给它;
    • 就绪后:加入 Service 后端端点,接收流量。
    • 如果返回失败状态,则**服务将从endpoints 中删除容器ip地址。**即使容器处于运行状态,也不接受代理发过来的请求。
  • StartupProbe(启动探针):用于确定pod是否成功初始化。 如果指定,则在成功完成之前不会执行其他探测。如果此探测失败,Pod 将重新启动,就像 livenessProbe 失败一样。 这可用于在 Pod 生命周期开始时提供不同的探测参数,此时加载数据或预热缓存可能需要比稳态操作期间更长的时间。 这无法更新。

Checking Methods

探针检查容器有四种不同的方法:

  • httpGet,对容器的 IP 地址上指定端口和路径执行 HTTP GET 请求。如果响应的状态码大于等于 200 且小于 400,则诊断被认为是成功的。
  • exec,在容器内执行指定命令。如果命令退出时返回码为 0,则认为诊断成功。
  • tcpSocket,对容器的 IP 地址上的指定端口执行 TCP 检查。如果端口打开,则诊断被认为是成功的。 如果远程系统(容器)在打开连接后立即将其关闭,这算作是健康的。
  • grpc,使用 gRPC 执行一个远程过程调用。 目标应该实现 gRPC 健康检查。 如果响应的状态是 “SERVING”,则认为诊断成功。

HTTP Checks-httpGet

当使用HTTP Checks,控制器使用webhoook判定容器健康情况。如果HTTP的响应码在200-399之间,判定check成功。适应范围:可以返回HTTP状态码应用。

livenessProbe
root@master30~ 19:10:16# vim deploy-httpGet-liveness.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: docker.io/library/httpd
        imagePullPolicy: IfNotPresent
        name: httpd
        # 添加livenessProbe部分
        livenessProbe:
          #连续探测失败 3 次,判定容器不健康,触发重启容器
          failureThreshold: 3
          #容器启动后,等待 5 秒才第一次执行探针
          initialDelaySeconds: 5
          #探针执行间隔,每 5 秒探测一次健康状态
          periodSeconds: 5
          #探测失败后,连续成功多少次才标记为健康
          successThreshold: 1
          #单次探测超时时间:发请求后 10 秒内没返回结果 → 判定探测失败
          timeoutSeconds: 10
          #使用的探测方法
          httpGet:
            path: /index.html
            # port填写时间web端口
            port: 80
            # scheme指定协议,HTTP或者HTTPS
            scheme: HTTP

probe选项说明

  • initialDelaySeconds:必选。容器启动后多长时间,probe开始生效。
  • timeoutSeconds:必选。probe需要多长时间完成。如果超过该值,控制器判定probe失败。默认值1s,最小值是1秒。
  • periodSeconds:可选。检查频率。默认值10s,最小值是1秒。
  • successThreshold:可选,连续成功最少次数后判定probe成功。默认值1,最小值是1。
  • failureThreshold:可选。连续失败最少次数后判定probe失败。默认值3,最小值是1。
root@master30~ 19:12:38# kubectl apply -f deploy-httpGet-liveness.yaml
root@master30~ 19:22:49# kubectl get pods
NAME                  READY   STATUS    RESTARTS   AGE
web-d746d549f-smwl2   1/1     Running   0          12s
root@master30~ 19:23:02# kubectl get pods -o wide | awk '{print $6}'
IP
10.224.128.54

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

# 观察pod状态,RESTARTS次数变位1,再次访问
root@master30~ 19:24:24# kubectl get pod
NAME                  READY   STATUS    RESTARTS      AGE
web-d746d549f-smwl2   1/1     Running   1 (11s ago)   102s

root@master30~ 19:24:32# curl 10.224.128.54
<html><body><h1>It works!</h1></body></html>
readinessProbe
root@master30~ 19:26:55# vim deploy-httpGet-readiness.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: docker.io/library/httpd
        imagePullPolicy: IfNotPresent
        name: httpd
        # 添加readinessProbe部分
        readinessProbe:
          failureThreshold: 3
          initialDelaySeconds: 5
          periodSeconds: 5
          successThreshold: 1
          timeoutSeconds: 10
          httpGet:
            path: /index.html
            port: 80
            scheme: HTTP
# 创建应用
root@master30~ 19:27:18# kubectl apply -f deploy-httpGet-readiness.yaml
root@master30~ 19:28:10# kubectl expose deployment web --port=80 --target-port=80
root@master30~ 19:28:42# kubectl get svc
NAME   TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
web    ClusterIP   10.98.221.134   <none>        80/TCP    3s

root@master30~ 19:28:45# kubectl get pods
NAME                   READY   STATUS    RESTARTS   AGE
web-679db8c59c-bclh2   1/1     Running   0          85s
web-679db8c59c-m229r   1/1     Running   0          85s
web-679db8c59c-z27jt   1/1     Running   0          85s


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

root@master30~ 19:31:20# for i in {1..90};do curl -s 10.98.221.134;done|sort |uniq -c
     30 web-679db8c59c-bclh2
     30 web-679db8c59c-m229r
     30 web-679db8c59c-z27jt


root@master30~ 19:31:44# kubectl get endpoints web
NAME   ENDPOINTS                                            AGE
web    10.224.128.56:80,10.224.128.60:80,10.224.96.215:80   3m19s


# 删除其中一个Pod的主页文件
root@master30~ 19:34:20# kubectl exec web-679db8c59c-bclh2 -- rm -rf htdocs/index.html

# web 服务的后端没有pod的ip
root@master30~ 19:35:09# kubectl get endpoints web
NAME   ENDPOINTS                           AGE
web    10.224.128.60:80,10.224.96.215:80   6m40s


# 访问svc,后端无法看到被删除文件的Pod
root@master30~ 19:35:22# for i in {1..90};do curl -s 10.98.221.134;done|sort |uniq -c
     45 web-679db8c59c-m229r
     45 web-679db8c59c-z27jt

# 观察web1状态,READY为0,RESTARTS数量为0
root@master30~ 19:36:28# kubectl get pods
NAME                   READY   STATUS    RESTARTS   AGE
web-679db8c59c-bclh2   0/1     Running   0          9m1s
web-679db8c59c-m229r   1/1     Running   0          9m1s
web-679db8c59c-z27jt   1/1     Running   0          9m1s

# 清理环境
root@master30~ 19:36:41# kubectl delete deployments.apps web

Execution Checks-exec

当使用容器执行检测,kubelet代理将在容器内执行命令。返回值是0,代表check成功。

示例:检测容器自带文件

root@master30~ 19:37:57# vim deploy-exec-liveness.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: docker.io/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
# 创建应用
root@master30~ 19:38:07# kubectl apply -f deploy-exec-liveness.yaml
root@master30~ 19:38:57# kubectl get pods
NAME                   READY   STATUS    RESTARTS   AGE
web-6dff956c58-ml24l   1/1     Running   0          7s

# 删除主页文件
root@master30~ 19:39:04# kubectl exec web-6dff956c58-ml24l -- rm -rf htdocs/index.html

# 观察pod状态,RESTARTS次数变位1
root@master30~ 19:39:37# kubectl get pods
NAME                   READY   STATUS    RESTARTS      AGE
web-6dff956c58-ml24l   1/1     Running   1 (23s ago)   66s

TCP Socket Checks-tcpSocket

当使用TCP socket checks,kubelet代理尝试打开容器socket。如果check可以建立连接,判定check成功。

示例:liveness probe使用TCP Socket check

root@master30~ 19:46:14# vim deploy-tcpSocket-liveness.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: docker.io/library/httpd
        imagePullPolicy: IfNotPresent
        name: httpd
        # 添加livenessProbe部分
        livenessProbe:
          failureThreshold: 3
          initialDelaySeconds: 5
          periodSeconds: 5
          successThreshold: 1
          timeoutSeconds: 10
          tcpSocket:
            port: 80

Health Check Case

Health Check 在 Scale Up 中的应用

对于多副本应用, 当执行Scale Up操作时, 新副本会作为backend被添加到Service的负载均衡中, 与已有副本一起处理客户的请求。考虑到应用启动通常都需要一个准备阶段, 比如加载缓存数据、 连接数据库等, 从容器启动到真正能够提供服务是需要一段时间的。 我们可以通过Readiness探测判断容器是否就绪, 避免将请求发送到还没有准备好的backend。

Health Check 在滚动更新中的应用

Health Check另一个重要的应用场景是Rolling Update。 试想一下, 现有一个正常运行的多副本应用, 接下来对应用进行更新(比如使用更高版本的image) , Kubernetes会启动新副本, 然后发生了如下事件:

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

如果没有配置Health Check, 会出现怎样的情况?

因为新副本本身没有异常退出, 默认的Health Check机制会认为容器已经就绪, 进而会逐步用新副本替换现有副本, 其结果就是: 当所有旧副本都被替换后, 整个应用将无法处理请求, 无法对外提供服务。 如果这是发生在重要的生产系统上, 后果会非常严重。

如果正确配置了Health Check, 新副本只有通过了探测才会被添加到Service; 如果没有通过探测, 现有副本不会被全部替换, 业务仍然正常进行。

Kubernetes 认证和授权

学习参考:API 访问控制

环境准备

root@master30~ 10:16:51# kubectl create ns auth
root@master30~ 10:33:15# kubens auth

Kubernetes API 访问控制

学习参考:Kubernetes API 访问控制

当用户使用User服务账号访问Kubernetes API时,每个请求都会经过多阶段的访问控制之后才会被接受,包括身份认证、鉴权以及准入控制(Admission Control)。

如下图所示:
在这里插入图片描述

传输安全

默认情况下,Kubernetes API 服务器在第一个非 localhost 网络接口的 6443 端口上进行监听, 受 TLS 保护。在一个典型的 Kubernetes 生产集群中,API 使用 443 端口。 该端口可以通过 --secure-port 进行变更,监听 IP 地址可以通过 --bind-address 标志进行变更。

客户端向 Kubernetes API 服务器发起请求时,**API 服务器出示证书。**该证书可以使用私有证书颁发机构(CA)签名,也可以基于链接到公认的 CA 的公钥基础架构签名。 该证书和相应的私钥可以通过使用 --tls-cert-file--tls-private-key-file 标志进行设置。

如果你的集群使用私有证书颁发机构,你需要在客户端的 ~/.kube/config 文件中提供该 CA 证书的副本, 以便你可以信任该连接并确认该连接没有被拦截。

身份认证

如上图步骤 所示:Kubernetes 操作前首先进行身份认证(Authentication)。

Kubernetes 使用认证模块进行认证,认证模块包含客户端证书密码普通令牌、引导令牌和 JSON Web 令牌(JWT,用于服务账号)等。如果 Kubernetes 指定多个认证模块,服务器依次尝试每个验证模块,直到其中一个成功。

  • 如果请求认证通过,下一步对该用户名进行鉴权。
  • 如果请求认证不通过,服务器将以 HTTP 状态码 401 拒绝该请求。

鉴权

如上图的步骤 所示,将请求验证为来自特定的用户后,请求必须被鉴权(Authorization)。请求必须包含请求者的用户名、请求的行为以及受该操作影响的对象。 如果现有策略声明用户有权完成请求的操作,那么该请求被鉴权通过。

示例: 以下策略,Bob 只能在 projectCaribou 名称空间中读取 Pod。

{
    "apiVersion": "abac.authorization.kubernetes.io/v1beta1",
    "kind": "Policy",
    "spec": {
        "user": "bob",
        "namespace": "projectCaribou",
        "resource": "pods",
        "readonly": true
    }
}
  • 如果 Bob 执行以下请求:读取 projectCaribou 名称空间中的对象清单,其鉴权请求将被允许。
{
  "apiVersion": "authorization.k8s.io/v1beta1",
  "kind": "SubjectAccessReview",
  "spec": {
    "resourceAttributes": {
      "namespace": "projectCaribou",
      "verb": "get",
      "group": "unicorn.example.org",
      "resource": "pods"
    }
  }
}
  • 如果 Bob 在 projectCaribou 名字空间中请求写(createupdate)对象或在其它名字空间中请求读取(get)对象,其鉴权请求会被拒绝。

**注意:**Kubernetes 鉴权要求使用公共 REST 属性与现有的组织范围或云提供商范围的访问控制系统进行交互。 使用 REST 格式很重要,因为这些控制系统可能会与 Kubernetes API 之外的 API 交互。

Kubernetes 支持多种鉴权模块,例如 ABAC 模式、RBAC 模式和 Webhook 模式等。 管理员创建集群时,他们配置应在 API 服务器中使用的鉴权模块。 如果配置了多个鉴权模块,则 Kubernetes 会检查每个模块,任意一个模块鉴权该请求,请求即可继续; 如果所有模块拒绝了该请求,请求将会被拒绝(HTTP 状态码 403)。

准入控制

这一操作如上图的步骤 所示。

  • 准入控制器对创建、修改、删除或(通过代理)连接对象的请求进行操作。 当有多个准入控制器被配置时,服务器将依次调用它们。准入控制器不会对仅读取对象的请求起作用。准入控制模块是可以修改或拒绝请求的软件模块。 除鉴权模块可用的属性外,准入控制模块还可以访问正在创建或修改的对象的内容。

  • 与身份认证和鉴权模块不同,如果任何准入控制器模块拒绝某请求,则该请求将立即被拒绝。除了拒绝对象之外,准入控制器还可以为字段设置复杂的默认值。

  • 请求通过所有准入控制器后,将使用检验例程检查对应的 API 对象,然后将其写入对象存储(如步骤 4 所示)。

审计

Kubernetes 审计提供了一套与安全相关的、按时间顺序排列的记录,其中记录了集群中的操作序列。 集群对用户、使用 Kubernetes API 的应用程序以及控制平面本身产生的活动进行审计。

认证管理

参考:认证

Kubernetes 中的用户

Kubernetes 集群有两类用户:

  • 普通用户,Kubernetes 中普通用户不是由 kubernetes 直接提供,而是身份认证插件提供,Kubernetes 并不包含用来代表普通用户账号的对象。 普通用户的信息无法通过 API 调用添加到集群中,Kubernetes 认为:能够提供由集群的证书机构签名的合法证书的用户是通过身份认证的用户。 基于这样的机制,Kubernetes 使用证书中的 ‘subject’ 的通用名称(Common Name)字段 (例如,“/CN=bob”)来确定用户名。 接下来,基于角色访问控制(RBAC)子系统会确定用户是否有权针对某资源执行特定的操作。
  • 服务账号,Kubernetes 中服务账号是 Kubernetes API 所管理的用户。它们被绑定到特定的名字空间, 或者由 API 服务器自动创建,或者通过 API 调用创建。服务账号与一组以 Secret 保存的凭据相关,这些凭据会被挂载到 Pod 中,从而允许集群内的进程访问 Kubernetes API。

二者异同点

  1. 核心相同点(授权机制完全一致)
  • 权限来源相同:无论是 SA 还是 User,本身都是“空身份”,不携带任何权限。它们的权限都 100% 由绑定的 Role/ClusterRole 决定
  • 绑定逻辑相同:都是通过 RoleBinding / ClusterRoleBinding 将 Role/ClusterRole 与主体(Subject)绑定。YAML 写法几乎一样,只是 subjects.kind 字段不同。
  1. 核心区别(决定它们本质不同的关键)

    对比维度 ServiceAccount (SA) User Account (User)
    服务对象 非人类实体(Pod、应用程序、自动化进程)。 人类实体(管理员、开发者、运维人员)。
    在 K8s 中的存在形式 是 K8s 内置的 API 资源对象。 可通过 kubectl get sa 查看,真实存储在 Etcd 中。 不是 K8s 的 API 资源对象。 执行 kubectl get user 会报错,它在集群内只是一个字符串名称
    作用范围(命名空间) 命名空间(Namespace)级别。 不同命名空间可以有同名的 SA。 集群全局(Global)级别。 用户名在整个集群内必须唯一。
    身份验证(认证)方式 K8s API Server 自动管理。 通过挂载 Pod 中的加密 Token(或投射卷)进行认证。 外部系统管理。 依赖客户端证书(CN 字段)、OIDC 或 LDAP 等外部认证。
    RBAC 绑定时的写法 绑定 YAML 中必须指定: kind: ServiceAccount + 所在的 namespace 绑定 YAML 中只需指定: kind: User + 用户名(无需 namespace)。

每个 API 请求必须包含一个用户:普通用户相关或者服务账号,客户端也可以发起匿名请求。这意味着集群内外的每个进程在向 API 服务器发起请求时都必须通过身份认证,否则会被视作匿名用户。

身份认证策略

Kubernetes 通过身份认证插件认证 API 请求的身份。HTTP 请求发给 API 服务器时,插件会将以下属性关联到请求本身:

  • 用户名:用来辩识最终用户的字符串。常见的值可以是 kube-adminjane@example.com
  • 用户 ID:用来辩识最终用户的字符串,旨在比用户名有更好的一致性和唯一性。
  • 用户组:取值为一组字符串,其中各个字符串用来标明用户是某个命名的用户逻辑集合的成员。 常见的值可能是 system:masters 或者 devops-team 等。
  • 附加字段:一组额外的键-值映射,键是字符串,值是一组字符串; 用来保存一些鉴权组件可能觉得有用的额外信息。

所有(属性)值对于身份认证系统而言都是不透明的, 只有被鉴权组件解释过之后才有意义。

可以同时启用多种身份认证方法,通常至少使用两种方法:

  • 针对服务账号使用服务账号令牌。
  • 至少另外一种方法对用户的身份进行认证,例如X509客户证书

Kubernetes 默认使用的是服务账号和X509客户证书

认证插件

Kubernetes支持同时开启多个认证插件,只要有一个认证通过即可。如果认证成功,则用户的username会被传入鉴权模块做进一步鉴权验证;而对于认证失败的请求则返回HTTP 401。

常用认证插件:

  • X509 客户证书

    • 客户端用户与Kubernetes交互时候,使用的认证凭据文件.kube/config就是使用X509证书。

    • API Server启动时配置 –client-ca-file=SOMEFILE 参数。

      root@master30~ 13:35:12# cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep -- --client-ca-file
          - --client-ca-file=/etc/kubernetes/pki/ca.crt
      
  • 静态令牌文件

    • API Server启动时配置 –token-auth-file=SOMEFILE 参数。

      cat /etc/kubernetes/manifests/kube-apiserver.yaml |\
      grep -- --token-auth-file
      
    • 文件为 csv格式,每行至少包括三列 token,username,user id。第四列为可选group 名项。如果有多个group名,列必须用””双引号包含其中,例如:

      token,user,uid,"group1,group2,group3"
      
    • 默认未启用。

  • 启动引导令牌

    • 为了支持平滑地启动引导新的集群,Kubernetes 包含了一种动态管理的持有者令牌类型, 称作 启动引导令牌(Bootstrap Token)。 这些令牌以 Secret 的形式保存在 kube-system 名字空间中,可以被动态管理和创建。 使用 kubeadm 引导和管理集群时,kubeadm 会自动完成这些设置。

    • 你必须在 API 服务器上设置 --enable-bootstrap-token-auth 标志来启用基于启动引导令牌的身份认证组件。

      root@master30~ 13:38:30# cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep bootstrap
          - --enable-bootstrap-token-auth=true
      
    • 请参阅启动引导令牌, 以了解关于启动引导令牌身份认证组件详细信息。

  • 服务账号令牌

    • 服务账号(Service Account)是一种自动被启用的用户认证机制,使用经过签名的持有者令牌来验证请求。

    • 服务账号通常由 API 服务器自动创建并通过 ServiceAccount 准入控制器关联到集群中运行的 Pod 上。 持有者令牌会挂载到 Pod 中可预知的位置,允许集群内进程与 API 服务器通信。

    • 服务账号也可以使用 Pod 规约的 serviceAccountName 字段显式地关联到 Pod 上。

  • Webhook 令牌身份认证

    Webhook 身份认证是一种用来验证持有者令牌的回调机制。

  • 身份认证代理

    API 服务器可以配置成从请求的头部字段值(如 X-Remote-User)中辩识用户。 这一设计是用来与某身份认证代理一起使用 API 服务器,代理负责设置请求的头部字段值。

  • basic-auth-file认证

    在kubernetes 1.19及之后的版本中,kubernetes放弃了 basic-auth-file 认证方式。

匿名请求

如果请求没有被已配置的身份认证方法拒绝, 则被视作匿名请求(Anonymous Requests)。这类请求获得用户名 system:anonymous 和对应的用户组 system:unauthenticated

  • 在 1.5.1-1.5.x 版本中,匿名访问默认情况下是被禁用的,可以通过为 API 服务器设定 --anonymous-auth=true 来启用。

  • 在 1.6 及之后版本中,如果所使用的鉴权模式不是 AlwaysAllow,则匿名访问默认是被启用的。 从 1.6 版本开始,ABAC 和 RBAC 鉴权模块要求对 system:anonymous 用户或者 system:unauthenticated 用户组执行显式的权限判定,所以之前的为用户 * 或用户组 * 赋予访问权限的策略规则都不再包含匿名用户。

**例如,**在一个配置了令牌身份认证且启用了匿名访问的服务器上,如果请求提供了非法的持有者令牌, 则会返回 401 Unauthorized 错误。如果请求没有提供持有者令牌,则被视为匿名请求。

创建账户

以下探讨使用 **X509客户证书 **插件管理用户。

客户端准备

客户端要想访问集群,必须安装与集群版本一致的kubectl工具。

root@master30~ 10:03:46# apt install -y kubectl=1.30.2-00

准备申请材料

# 创建私钥
root@client~ 10:04:43# openssl genrsa -out hgq.key 2048

# 根据私钥,创建请求证书
root@client~ 10:06:49# openssl req -new -key hgq.key -out hgq.csr -subj '/CN=hgq/O=kubernets'
# 参数说明:
## C,Country,代表国家
## ST,STate,代表省份
## L,Location,代表城市
## O,Organization,代表组织,公司
## OU,Organization Unit,代表部门
## CN,Common Name,代表服务器域名
## emailAddress,代表联系人邮箱地址。

# 其他示例:
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=hgq@lab.example.com"

# 客户端将自己请求证书发给kubernetes管理员
root@client~ 10:06:54# scp hgq.csr root@10.1.8.30:
创建用户凭据

管理员使用以下资源文件创建用户的kubeconfig。

# 使用ca.crt和ca.key签名hgq.csr,得到hgq.crt证书
root@master30~ 10:07:53# openssl x509 -req -in hgq.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out hgq.crt -days 1095

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

# 将kubeconfig模板、hgq.crt和kubernetes的ca证书发给客户端
root@master30~ 10:08:17# scp config.tpl hgq.crt /etc/kubernetes/pki/ca.crt root@10.1.8.10:~
创建 kubeconfig

kubeconfig创建方法:

使用kubectl config命令创建。

修改模版config.tpl,结果如下:

apiVersion: v1
clusters:
- cluster:
    server: https://10.1.8.30:6443
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    namespace: default
    user: hgq
  name: hgq@kubernetes
current-context: hgq@kubernetes
kind: Config
users:
- name: hgq
# 设置 cluster
root@client~ 10:09:06# kubectl config set-cluster kubernetes --kubeconfig=config --certificate-authority=ca.crt --embed-certs

# --kubeconfig指定kubeconfig文件
# --server指定kubernetes服务器认证地址
# --certificate-authority选项指定ca证书
# --embed-certs选项作用是将ca.crt的内容添加到config文件中,
# 如果没有该选项,则添加 --certificate-authority 选项指定的路径,也就是ca.crt

# 设置credentials
root@client~ 10:09:17# kubectl config set-credentials hgq --kubeconfig=config --client-key=hgq.key --client-certificate=hgq.crt --embed-certs

# --client-key 指定用户私钥
# --client-certificate 指定服务器为用户生成的证书

# 设置context
root@client:~# kubectl config set-context hgq --kubeconfig=config --namespace=default --cluster=kubernetes --user=hgq
# --namespace指定Namespace
# --cluster指定集群
# --user指定用户

# 授权用户hgq集群管理员角色,后续详细讲解角色管理
root@master30~ 10:10:26# kubectl create clusterrolebinding hgq-admin --clusterrole=cluster-admin --user=hgq

# 验证结果
root@client~ 10:19:04# kubectl get nodes --kubeconfig=config
NAME                 STATUS   ROLES           AGE   VERSION
master30.hgq.cloud   Ready    control-plane   8d    v1.30.2
worker31.hgq.cloud   Ready    <none>          8d    v1.30.2
worker32.hgq.cloud   Ready    <none>          8d    v1.30.2

删除账户

# 通过删除证书进行删除账户即可,为了后续操作方便,该账户不删除

# k8s删除用户csr资源后,客户端用户仍可以访问集群。
# 解释:用户认证功能由x509提供,与k8s无关。
# 如果不予许用户登录,应该从x509认证机制方面下手,例如ca.crt将相应客户端csr加入黑名单。

# 删除权限
root@master30:~# kubectl delete clusterrolebindings.rbac.authorization.k8s.io hgq-admin

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

授权管理

参考:鉴权使用 RBAC 鉴权

鉴权概述

Kubernetes API 服务器对 API 请求进行鉴权。 它根据所有策略评估所有请求属性来决定允许或拒绝请求。 一个 API 请求的所有部分都必须被某些策略允许才能继续。 这意味着默认情况下拒绝权限。

当系统配置了多个鉴权模块时,Kubernetes 将按顺序使用每个模块。

  • 如果任何鉴权模块批准或拒绝请求,则立即返回该决定,并且不会与其他鉴权模块协商。
  • 如果所有模块对请求没有意见,则拒绝该请求。 被拒绝响应返回 HTTP 状态代码 403。

鉴权模式⭐️

鉴权模式定义认证成功的用户对集群的操作权限,由kube-apiserver配置文件指定:

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

这里使用RBACNode模式。

支持的模式:

  • Node是节点专用的鉴权模式根据调度到 kubelet 上运行的 Pod 为 kubelet 授予权限。 要了解有关使用节点鉴权模式的更多信息,请参阅节点鉴权

  • ABAC基于属性的访问控制(ABAC)定义了一种访问控制范型,通过使用将属性组合在一起的策略, 将访问权限授予用户。策略可以使用任何类型的属性(用户属性、资源属性、对象,环境属性等)。 要了解有关使用 ABAC 模式的更多信息,请参阅 ABAC 模式

  • RBAC基于角色的访问控制(RBAC) 是一种基于企业内个人用户的角色来管理对计算机或网络资源的访问的方法。要了解有关使用 RBAC 模式的更多信息,请参阅 RBAC 模式

    • 被启用之后,RBAC(基于角色的访问控制)使用 rbac.authorization.k8s.io API 组来驱动鉴权决策,从而允许管理员通过 Kubernetes API 动态配置权限策略。
    • 要启用 RBAC,请使用 --authorization-mode = RBAC 启动 API 服务器。
  • Webhook —— WebHook 是一个 HTTP 回调:发生某些事情时调用的 HTTP POST; 通过 HTTP POST 进行简单的事件通知。 实现 WebHook 的 Web 应用程序会在发生某些事情时将消息发布到 URL。 要了解有关使用 Webhook 模式的更多信息,请参阅 Webhook 模式

  • AlwaysAllow允许用户所有请求。

  • AlwaysDeny拒绝用户所有请求,不管用户是否具有权限,但不限制admin用户。

role 管理

kubernetes 方便管理权限,将一组特定权限赋予角色,然后将角色赋予用户,那么用户将继承该角色具有的权限。

角色分类

Role / ClusterRole 是纯权限模板,只定义「能做什么、对哪些资源操作」,本身不带任何身份信息。

  • rolenamespace 角色==,限定用户访问特定namespace==。role绑定给用户,称之为rolebinding

  • clusterrolecluster 角色,可以管理集群==,包括所有namespace中资源==。clusterrole绑定给用户,称之为clusterrolebinding

权限由kubernetes系统预定义的,clusterroles/admin中包涵系统中全部权限列表。

root@master30:~# kubectl describe clusterroles admin
Name:         admin
Labels:       kubernetes.io/bootstrapping=rbac-defaults
Annotations:  rbac.authorization.kubernetes.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]
......

输出说明:

  • Resources代表系统中资源类型,例如Secret,Configmap等。

  • Resource Names:代表特定资源。如果Resources是Secret,那么这里就指特定Secret。

  • Non-Resource URLs: 被称为非资源URL或虚拟URL对象,是k8s中所需要的特殊动作(不需要关注)。

  • Verbs代表针对资源执行的动作,包括操作get、list、create、delete、update、edit、watch、exec。

  • get,用于获得特定资源信息,例如,针对pod,可以执行GET /api/v1/namespaces/{namespace}/pods/{podname}

root@client:~# kubectl get pod -n kube-system 
Error from server (Forbidden): pods is forbidden: User "hgq" 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
  • list,用户查看某一类型资源清单,例如针对pod,可以执行GET /api/v1/namespaces/{namespace}/pods
root@client~ 10:46:47# kubectl get pods -n kube-system --kubeconfig=config
NAME                                         READY   STATUS    RESTARTS       AGE
calico-kube-controllers-585df69d45-jvw2t     1/1     Running   14 (52m ago)   8d
calico-node-6hhpc                            1/1     Running   12 (52m ago)   8d
calico-node-csnj7                            1/1     Running   14 (48m ago)   8d
calico-node-p6bgb                            1/1     Running   14 (48m ago)   8d
coredns-7db6d8ff4d-dbjhb                     1/1     Running   13 (52m ago)   8d
coredns-7db6d8ff4d-h5vl2                     1/1     Running   13 (52m ago)   8d
etcd-master30.hgq.cloud                      1/1     Running   17 (52m ago)   8d

    
    root@client:~# kubectl get pod kube-proxy-8kp8w -n kube-system 
    Error from server (Forbidden): pods "kube-proxy-8kp8w" is forbidden: User "hgq" cannot get resource "pods" in API group "" in the namespace "kube-system"

更多API信息参考:https://kubernetes.io/docs/reference/kubernetes-api/

创建 role
root@master30~ 10:46:31# 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:可以对项目中所有pods执行get、list、watch操作

root@master30~ 10:51:05# kubectl create role pod-role --verb=get,list,watch --resource=pods -n default --dry-run=client -o 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
root@master30~ 10:52:47# kubectl create role pod-role --verb=get,list,watch --resource=pods -n default

root@master30~ 10:54:39# kubectl get roles pod-role -n default
NAME       CREATED AT
pod-role   2026-07-02T02:54:16Z


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

示例2:访问特定资源pods/readablepod

root@master30~ 11:04:24# kubectl create role pod-role --verb=get --resource=pods --resource-name=readabled --resource-name=anotherpod

–resource-name=readabled --resource-name=anotherpod 资源白名单

限定权限仅能操作名字为 readabled、anotherpod 的 Pod,不能操作同命名空间下其他 Pod。

  • 多资源名通过多次 --resource-name 叠加
  • 不加该参数:对该类型所有 pod生效
  • 限制单资源粒度权限,常用于精细化隔离

示例3:可以对项目中所有replicasets执行get list watch操作

root@master30~ 11:05:06# kubectl create role foo --verb=get,list,watch --resource=replicasets
修改 role
# 增加create权限
root@master30~ 11:05:55# kubectl edit roles -n default pod-role
......
rules:
- apiGroups:
  - ""
  resources:
  - pods
  verbs:
# 在verbs下添加相应权限
  - list
  - get
  - watch
  - create
apiGroups

K8s 所有资源都归属于API 组(apiGroup)apiGroups 就是告诉这条权限规则:你要授权的资源属于哪个 API 分组

API 组 + 资源类型(resources)二者必须匹配,权限才会生效,缺一不可。

对应关系:apiGroup/version = apiVersion

  • apiVersion: v1 → apiGroup = ""(空字符串,核心组 core)

  • apiVersion: apps/v1 → apiGroup = "apps"

  • apiVersion: networking.k8s.io/v1 → apiGroup = "networking.k8s.io"

  • 角色的 rules 属性中 apiGroups 默认为空

  • pod、service 资源的 apiVersion 是v1,apiGroups为""。

  • Deployment、DaemonSet 资源的 apiVersion 是apps/v1,apiGroups为"apps"。如下:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  creationTimestamp: null
  name: deployments-role
  namespace: default
rules:
- apiGroups:
  - "apps"
  resources:
  - deployments
  verbs:
  - get
  - list
  - watch

**示例1:**定义角色,无法操作deployments,因为apiGroups未指定apps。

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  creationTimestamp: null
  name: deployments-role
  namespace: default
rules:
- apiGroups:
  - ""
  resources:
  - deployments
  verbs:
  - get
  - list
  - watch

**示例2:**定义一个可以 scale deployments 的角色。

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  creationTimestamp: null
  name: deployments-role
  namespace: default
rules:
- apiGroups:
  - "apps"
  resources:
  - deployments
  # 额外添加以下资源
  - deployments/scale
  verbs:
  - get
  - list
  - watch
  # 额外添加以下权限
  - patch

**示例3:**定义一个对不同类型资源赋予不同权限的角色。

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

常见apiGroups

Type apiVersion apiGroups
Pod、Service、PersistentVolume、PersistentVolumeClaim v1 “”
Deployment、DaemonSet、StatefulSets apps/v1 apps
Job batch/v1 batch
CronJob batch/v1beta1 batch
Role RoleBinding ClusterRole ClusterRoleBinding rbac.authorization.k8s.io/v1 rbac.authorization.k8s.io
NetworkPolicy networking.k8s.io/v1 networking.k8s.io

每种类型资源的 apiVersion 都可以通过以下命令查询:

root@master30:~# kubectl explain deployment|grep VERSION
VERSION:  apps/v1

root@master30:~# kubectl explain networkpolicy|grep VERSION
VERSION:  networking.k8s.io/v1
# 或者
root@master30:~/auth# kubectl api-resources |grep -i deploy
deployments                       deploy       apps/v1                                true         Deployment
role 绑定

将 role 绑定给用户。

root@master30~ 11:07:49# kubectl create rolebinding default-pod-hgq -n default --role=pod-role --user=hgq --dry-run=client -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  creationTimestamp: null
  name: default-pod-hgq
  namespace: default
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: pod-role
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: User
  name: hgq
# 绑定 ns/default 中角色 pod-role 给 hgq
root@master30~ 11:07:56# kubectl create rolebinding default-pod-hgq -n default --role=pod-role --user=hgq
# 角色绑定完成后,角色的权限发生变化,用户获得的权限也会跟着动态变化。

root@master30~ 11:08:58# kubectl get rolebindings -n default default-pod-hgq
NAME              ROLE            AGE
default-pod-hgq   Role/pod-role   66s

root@master30~ 11:10:04# kubectl describe rolebindings -n default default-pod-hgq
Name:         default-pod-hgq
Labels:       <none>
Annotations:  <none>
Role:
  Kind:  Role
  Name:  pod-role
Subjects:
  Kind  Name  Namespace
  ----  ----  ---------
  User  hgq
 
# 验证
root@client~ 11:12:48# kubectl run web --image=docker.io/library/httpd --image-pull-policy=IfNotPresent -n default

root@client~ 11:13:05# kubectl get pods -n default
NAME   READY   STATUS    RESTARTS   AGE
web    1/1     Running   0          63s


root@client~ 11:13:13# kubectl get pods -n default -w
NAME   READY   STATUS    RESTARTS   AGE
web    1/1     Running   0          79s

role 回收

回收权限,通过删除rolebinding或clusterrolebinding

root@master30~ 11:22:03# kubectl delete -n default rolebindings default-pod-hgq
rolebinding.rbac.authorization.k8s.io "default-pod-hgq" deleted

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

clusterrole 管理

常见 clusterrole

kubernetes系统中已经预定义了很多clusterrole,常见的clusterrole如下:

  • view,对系统中几乎所有的对象都有get、list和watch权限。
  • edit,对系统中几乎所有的对象都有get、list和watch权限。其中部分对象额外具有 create、delete、deletecollection、patch、update 权限。
  • admin,对系统中大部分的对象具有所有权限。
  • cluster-admin,对系统中所有的对象具有所有权限。
创建 clusterrole
root@master30~ 11:32:41# kubectl create clusterrole -h
Usage:
  kubectl create clusterrole NAME --verb=verb --resource=resource.group
[--resource-name=resourcename] [--dry-run=server|client|none] [options]

示例1:创建一个可以get、list、watch所有项目中pods的clusterrole

root@master30:~# kubectl create clusterrole pod-role --verb=get,list,watch --resource=pods --dry-run=client -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  creationTimestamp: null
  name: pod-role
rules:
- apiGroups:
  - ""
  resources:
  - pods
  verbs:
  - get
  - list
  - watch
root@master30:~# kubectl create clusterrole pod-role --verb=get,list,watch --resource=pods

root@master30:~# kubectl get clusterrole pod-role
NAME       CREATED AT
pod-role   2021-08-25T14:31:21Z

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]

示例2:创建一个可以get、list、watch所有项目中pods/readablepod和pods/anotherpod的clusterrole

root@master30:~# kubectl create clusterrole pod-reader --verb=get --resource=pods --resource-name=readablepod --resource-name=anotherpod

示例3:创建一个可以get、list、watch所有项目中pods和pods/status的clusterrole

root@master30:~# kubectl create clusterrole foo --verb=get,list,watch --resource=pods,pods/status
修改 clusterrole
root@master30:~# kubectl edit clusterrole pod-role
......
rules:
- apiGroups:
  - ""
  resources:
  - pods
  verbs:
  - get
  - list
  - watch
  # 添加create权限
  - create
clusterrole 绑定

将clusterrole绑定给用户。

root@master30:~# kubectl create clusterrolebinding hgq-pod-role --clusterrole=pod-role --user=hgq --dry-run=client -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  creationTimestamp: null
  name: hgq-pod-role
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: pod-role
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: User
  name: hgq
root@master30:~# kubectl create clusterrolebinding hgq-pod-role --clusterrole=pod-role --user=hgq

root@master30:~# kubectl get clusterrolebinding hgq-pod-role
NAME             ROLE                   AGE
hgq-pod-role   ClusterRole/pod-role   35s

root@master30:~# kubectl describe clusterrolebinding hgq-pod-role
Name:         hgq-pod-role
Labels:       <none>
Annotations:  <none>
Role:
  Kind:  ClusterRole
  Name:  pod-role
Subjects:
  Kind  Name   Namespace
  ----  ----   ---------
  User  hgq

# client节点使用hgq用户测试权限
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
clusterrole 回收
root@master30:~# kubectl delete clusterrolebinding hgq-pod-role
删除 clusterrole
root@master30:~# kubectl delete clusterrole pod-reader

服务账户

参考:管理服务账号配置服务账号

服务账户概述

Service Account,即服务账户,pod 使用 Service Account 身份运行容器。赋予Service Account相应角色,则使用该Service Account身份运行的pod中进程将具有对应Service Account的权限,进而有权限管理Kubernetes集群。

在每个namespace中都有一个名称为default的Service Account,创建的pod都会以 Service Account-default身份运行。

root@master30:~# kubectl get sa
NAME      SECRETS   AGE
default   1         53d

# 创建一个pod,验证Service Account信息
root@master30:~# kubectl run web --image=docker.io/library/httpd --image-pull-policy=IfNotPresent
root@master30:~# kubectl get pod web -o yaml|grep serviceAccount
  serviceAccount: default
  serviceAccountName: default
      - serviceAccountToken:

服务账号与用户账号比较

Kubernetes 区分用户账号和服务账号的概念,主要基于以下原因:

类别 用户账号 服务账号
使用主体 针对人 针对Pod, Pod 中的应用程序通过服务账号访问集群。
范围 集群范围,集群中的用名称必须唯一。 命名空间范围,两个不同的名字空间可以包含具有相同名称的服务账号。
轻量程度 重量,通过认证模块配置。 轻量,用户为了具体的任务按需创建服务账号。将服务账户与用户分离开来, 使工作负载更易于遵从权限最小化原则。

服务账号令牌卷机制

默认情况下,Kubernetes 添加一个投射卷到 Pod, 此卷包括了访问 Kubernetes API 的令牌。

root@master30:~# kubectl get pod web -o 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

该清单片段定义了由三个数据源组成的投射卷。在当前场景中,每个数据源也代表该卷内的一条独立路径。这三个数据源是:

  1. serviceAccountToken 数据源,包含 kubelet 从 kube-apiserver 获取的令牌。 kubelet 使用 TokenRequest API 获取有时间限制的令牌**。为 TokenRequest 服务的这个令牌会在 Pod 被删除或定义的生命周期(默认为 1 小时)结束之后过期。**该令牌绑定到特定的 Pod, 并将其 audience(受众)设置为与 kube-apiserver 的 audience 相匹配。 这种机制取代了之前基于 Secret 添加卷的机制,之前 Secret 代表了针对 Pod 的 ServiceAccount 但不会过期。

    注意:没有特定的机制可以使通过 TokenRequest 签发的令牌无效。 如果你不再信任为某个 Pod 绑定的服务账号令牌, 你可以删除该 Pod。删除 Pod 将使其绑定的服务账号令牌过期。

  2. **configMap 数据源。**ConfigMap 包含一组证书颁发机构数据。 Pod 可以使用这些证书来确保自己连接到集群的 kube-apiserver(而不是连接到中间件或意外配置错误的对等点上)。

  3. downwardAPI 数据源,用于查找包含 Pod 的名字空间的名称, 并使该名称信息可用于在 Pod 内运行的应用程序代码。

Pod 内挂载这个特定卷的所有容器都可以访问上述信息。

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/name pace 
auth

服务账户管理

SA 创建
root@master30:~# kubectl create sa sa1
root@master30:~# kubectl get sa sa1
NAME   SECRETS   AGE
sa1    1         9s
SA 授权
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
# 选项--serviceaccoun指明Service Account时,格式为namespace:Service Account Name
SA 使用
# 删除 pod 重新创建
root@master30:~# kubectl delete pod web
root@master30:~# kubectl run web --image=docker.io/library/httpd --image-pull-policy=IfNotPresent --dry-run=client -o yaml > web.yaml
root@master30:~# vim web.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
  # 添加以下任一记录
  serviceAccount: sa1
  serviceAccountName: sa1

status: {}
root@master30:~# kubectl apply -f web.yaml
root@master30:~# kubectl get pod web -o yaml
......
spec:
......
  serviceAccount: sa1
  serviceAccountName: sa1

重要:虽然 pod/web 中 httpd 进程具有 clusterrole/cluster-admin 角色,但是它只是用来提供httpd服务,不会做其他操作。

回收权限
root@master30:~# kubectl delete clusterrolebindings auth-sa1-cluster-admin 
clusterrolebinding.rbac.authorization.k8s.io "auth-sa1-cluster-admin" deleted
使用案例

集群 dashboard 中 pod 使用服务账户管理集群。

手动管理服务账号 token

  • v1.22 之前的 Kubernetes 版本会自动创建凭据访问 Kubernetes API。 这种机制是:先创建令牌 Secret,然后将其挂载到正运行的 Pod 中。
  • 在包括 Kubernetes v1.28 在内最近的几个版本中,使用 TokenRequest API 直接获得 API 凭据, 并使用投射卷挂载到 Pod 中。这种方法获得的令牌具有绑定的生命周期, 当挂载的 Pod 被删除时这些令牌将自动失效。
创建临时令牌
root@master30:~# kubectl create sa sa1
root@master30:~# kubectl create token sa1 
eyJhbGciOiJSUzI1NiIsImt......

# 注意:服务账号属性中是看不到关联的token的。
root@master30:~# kubectl get sa sa1 -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  creationTimestamp: "2023-11-01T04:05:03Z"
  name: sa1
  namespace: hgq
  resourceVersion: "11548"
  uid: 4944f75b-43ae-43fc-abfd-f2d20b7375f1
创建永久令牌

创建一个带有特殊注解 kubernetes.io/service-account.name 的 Secret 对象。一旦你手动创建一个 Secret 并将其关联到 ServiceAccount, Kubernetes 控制平面就会自动将令牌填充到该 Secret 中。

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

当你删除一个与某 Secret 相关联的 ServiceAccount 时,Kubernetes 的控制面会自动清理该 Secret 中长期有效的令牌。

root@master30:~# kubectl delete sa sa1 
root@master30:~# kubectl get secrets 
No resources found in hgq namespace.

说明: 尽管存在手动创建长久 ServiceAccount 令牌的机制,但还是推荐使用 TokenRequest 获得短期的 API 访问令牌。

思考

**问题:**Kubernetes 为什么不提供用户管理和身份认证功能?

Kubernetes 采用 RBAC 模型 管理用户(User)和用户组(Group)权限。但是Kubernetes中找不到真正的用户以及用户组信息,甚至连对应的User Resource以及Group Resource都没有,Kubernetes无法列出可信任的User列表以及Group列表。

换句话说 Kubernetes 并没有提供用户管理和身份认证功能,除了Service Account外,所有的用户信息都依赖外部的用户管理系统来存储,因此通过api-serever根本无法列出User和Group。

**解答:**这其实挺符合UNIX设计哲学的,即 Do One Thing and Do It Well

Kubernetes只专注于做应用编排,其他的功能则提供接口集成,除了认证和鉴权,我们发现网络、存储也都如此。

这样做的好处也显而易见,用户账户信息与Kubernetes集群松耦合,便于集成企业已有的身份认证系统,如AD、LDAP、Keycloak等。

环境清理

root@master30:~# kubectl delete ns auth

httpd
imagePullPolicy: IfNotPresent
name: web
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always

添加以下任一记录

serviceAccount: sa1
serviceAccountName: sa1

status: {}


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

重要:虽然 pod/web 中 httpd 进程具有 clusterrole/cluster-admin 角色,但是它只是用来提供httpd服务,不会做其他操作。

回收权限
root@master30:~# kubectl delete clusterrolebindings auth-sa1-cluster-admin 
clusterrolebinding.rbac.authorization.k8s.io "auth-sa1-cluster-admin" deleted
使用案例

集群 dashboard 中 pod 使用服务账户管理集群。

手动管理服务账号 token

  • v1.22 之前的 Kubernetes 版本会自动创建凭据访问 Kubernetes API。 这种机制是:先创建令牌 Secret,然后将其挂载到正运行的 Pod 中。
  • 在包括 Kubernetes v1.28 在内最近的几个版本中,使用 TokenRequest API 直接获得 API 凭据, 并使用投射卷挂载到 Pod 中。这种方法获得的令牌具有绑定的生命周期, 当挂载的 Pod 被删除时这些令牌将自动失效。
创建临时令牌
root@master30:~# kubectl create sa sa1
root@master30:~# kubectl create token sa1 
eyJhbGciOiJSUzI1NiIsImt......

# 注意:服务账号属性中是看不到关联的token的。
root@master30:~# kubectl get sa sa1 -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  creationTimestamp: "2023-11-01T04:05:03Z"
  name: sa1
  namespace: hgq
  resourceVersion: "11548"
  uid: 4944f75b-43ae-43fc-abfd-f2d20b7375f1
创建永久令牌

创建一个带有特殊注解 kubernetes.io/service-account.name 的 Secret 对象。一旦你手动创建一个 Secret 并将其关联到 ServiceAccount, Kubernetes 控制平面就会自动将令牌填充到该 Secret 中。

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

当你删除一个与某 Secret 相关联的 ServiceAccount 时,Kubernetes 的控制面会自动清理该 Secret 中长期有效的令牌。

root@master30:~# kubectl delete sa sa1 
root@master30:~# kubectl get secrets 
No resources found in hgq namespace.

说明: 尽管存在手动创建长久 ServiceAccount 令牌的机制,但还是推荐使用 TokenRequest 获得短期的 API 访问令牌。

思考

**问题:**Kubernetes 为什么不提供用户管理和身份认证功能?

Kubernetes 采用 RBAC 模型 管理用户(User)和用户组(Group)权限。但是Kubernetes中找不到真正的用户以及用户组信息,甚至连对应的User Resource以及Group Resource都没有,Kubernetes无法列出可信任的User列表以及Group列表。

换句话说 Kubernetes 并没有提供用户管理和身份认证功能,除了Service Account外,所有的用户信息都依赖外部的用户管理系统来存储,因此通过api-serever根本无法列出User和Group。

**解答:**这其实挺符合UNIX设计哲学的,即 Do One Thing and Do It Well

Kubernetes只专注于做应用编排,其他的功能则提供接口集成,除了认证和鉴权,我们发现网络、存储也都如此。

这样做的好处也显而易见,用户账户信息与Kubernetes集群松耦合,便于集成企业已有的身份认证系统,如AD、LDAP、Keycloak等。

环境清理

root@master30:~# kubectl delete ns auth
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐