k8s Metric Server、Quota and Limits、Health Check、认证和授权
扩容和减容控制
扩容和缩容时间参数
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 分钟),缩容冷却时间。
持续高负载 → 扩容
-
每 15s 采集一次 CPU/内存指标
-
Pod 刚启动前 30 秒,视为 “启动中”,不参与 HPA 计算。
-
一次扩容动作完成后,再次等待 45s 冷却 才能下一次扩容
👉 结论:K8s 1.30 扩容最小间隔 = 45s
持续低负载 → 缩容
-
同样 15s 采样
-
指标长期低于阈值
-
需要满足 300s(5分钟)稳定低位 才会触发缩容
-
每次缩容后,再次锁定 5 分钟
设置扩容和减容时间参数
扩容和减容pod数量由kube-controller-manager管理,如需调快/调慢,直接修改 controller-manager 启动参数即可。
扩容和缩容pod是有冷却时间的,目的是防止流量抖动、瞬间峰值导致频繁炸裂扩容。
为了演示扩容和缩容效果,这里设置扩容冷却时间为30s(10+20),缩容冷却时间为60秒。
root@master30:~# vim /etc/kubernetes/manifests/kube-controller-manager.yaml
spec:
containers:
- command:
- kube-controller-manager
- --allocate-node-cidrs=true
......
# 增加下面三行参数
- --horizontal-pod-autoscaler-sync-period=10s
- --horizontal-pod-autoscaler-initial-readiness-delay=20s
- --horizontal-pod-autoscaler-downscale-stabilization=60s
保存退出,静态 Pod 会自动重启生效。
Horizontal Pod Autoscaler
学习参考:hpa
HPA 作用
控制器将从一系列API(metrics.k8s.io、custom.metrics.k8s.io 和 external.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:~# kubectl create deployment web --image=hub.laoma.cloud/library/nginx root@master30:~# kubectl get pods NAME READY STATUS RESTARTS AGE web-68b95c775c-xksf4 1/1 Running 0 4s
创建 hpa
Usage: kubectl autoscale (-f FILENAME | TYPE NAME | TYPE/NAME) [--min=MINPODS]--max=MAXPODS [--cpu-percent=CPU] [options] root@master30:~# kubectl autoscale deployment web --max=5 --min=2 --cpu-percent=80 root@master30:~# 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:~# kubectl get pods root@master30:~# kubectl get pods NAME READY STATUS RESTARTS AGE web-68b95c775c-48ngs 1/1 Running 0 13s web-68b95c775c-xksf4 1/1 Running 0 41s root@master30:~# kubectl get hpa NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE web Deployment/web cpu: <unknown>/80% 2 5 2 34s # CPU目标为unknown
要想看到 HPA 的 TARGETS 值必须满足2个条件:
-
安装 metric server。
-
为 pod 设定资源限制。
root@master30:~# kubectl edit deployments.apps web
# 修改spec.template.spec.containers.[N].resources属性,添加limit属性,如下:
resources:
limits:
cpu: 100m
memory: 200Mi
# 再次查看hpa
root@master30:~# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
web Deployment/web cpu: 0%/80% 2 5 2 83s
root@master30:~# kubectl expose deployment web --port=80 --target-port=80
root@master30:~# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
web ClusterIP 10.100.255.92 <none> 80/TCP 6s
压力测试
# 打开一个监控窗口 root@master30:~# \ while true do clear; kubectl get hpa;echo kubectl get pod;echo kubectl top pods sleep 1 done # 上 CPU 压力 root@master30:~# apt install -y apache2-utils root@master30:~# while true ;do ab -n 300000 -c 100 http://10.100.255.92/;sleep 1;done # -n 300000,总请求数 # -c 100,每次并发数
CPU负载 cpu: 98%
Every 2.0s: kubectl get hpa;echo;kubectl top pods master30.laoma.cloud: Sun Apr 19 09:57:22 2026 NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE web Deployment/web cpu: 100%/80% 2 5 2 8m32s NAME CPU(cores) MEMORY(bytes) web-6dcdc45c94-6gwd2 101m 3Mi web-6dcdc45c94-dvd2m 100m 3Mi
超过目标值后,HPA 新建了一个pod
Every 2.0s: kubectl get hpa;echo;kubectl top pods master30.laoma.cloud: Sun Apr 19 09:58:04 2026 NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE web Deployment/web cpu: 95%/80% 2 5 3 9m14s NAME CPU(cores) MEMORY(bytes) web-6dcdc45c94-2zbmb 93m 3Mi web-6dcdc45c94-6gwd2 100m 3Mi web-6dcdc45c94-dvd2m 90m 3Mi
HPA又新建了一个pod
Every 2.0s: kubectl get hpa;echo;kubectl top pods master30.laoma.cloud: Sun Apr 19 09:58:59 2026 NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE web Deployment/web cpu: 99%/80% 2 5 4 11m NAME CPU(cores) MEMORY(bytes) web-6dcdc45c94-2zbmb 100m 3Mi web-6dcdc45c94-6gwd2 100m 3Mi web-6dcdc45c94-dvd2m 97m 3Mi web-6dcdc45c94-tsjkr 99m 3Mi
HPA又新建了一个pod
Every 2.0s: kubectl get hpa;echo;kubectl top pods master30.laoma.cloud: Sun Apr 19 09:59:59 2026 NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE web Deployment/web cpu: 100%/80% 2 5 5 11m NAME CPU(cores) MEMORY(bytes) web-6dcdc45c94-2zbmb 101m 3Mi web-6dcdc45c94-6gwd2 100m 3Mi web-6dcdc45c94-dvd2m 100m 3Mi web-6dcdc45c94-hpg45 98m 3Mi web-6dcdc45c94-tsjkr 101m 3Mi
观察过程:
-
随着pod CPU使用率上升,自动扩展pod数量。
-
正常情况,30秒左右创建一个新pod,
-
即使pod CPU使用率超过阈值,但pod数量不会超过5。
-
停止压力测试,负载降低后,pod数量会逐步减少为2。
清理资源
# 删除 hpa root@master30:~# kubectl delete hpa web # 删除 deployment root@master30:~# kubectl delete deployment web # 保留 svc,后续使用
基于 Mem 使用率伸缩
我们仍然以nginx应用实践。想要Nginx 内存涨,要访问会占用内存的页面。最简单方法:让 Nginx 返回一个超大响应体。
准备资源
# worker节点创建big.img [root@worker31 ~]# mkdir /www [root@worker31 ~]# dd if=/dev/zero of=/www/big.img bs=1M count=200 [root@worker32 ~]# mkdir /www [root@worker32 ~]# dd if=/dev/zero of=/www/big.img bs=1M count=200 root@master30:~# vim deployment-web.yaml
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:
path: /www
root@master30:~# kubectl apply -f deployment-web.yaml
创建 hpa
root@master30:~# vim hpa-mem.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web
spec:
minReplicas: 2
maxReplicas: 5
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web
metrics:
- resource:
name: memory
target:
type: Utilization
averageUtilization: 60
type: Resource
root@master30:~# kubectl apply -f hpa-mem.yaml root@master30:~# kubectl get hpa NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE web Deployment/web memory: <unknown>/60% 2 5 1 13m # 目标值为空 # 稍等片刻,创建一个新的pod root@master30:~# kubectl get pods NAME READY STATUS RESTARTS AGE web-6dcdc45c94-2zj8s 1/1 Running 0 13m web-6dcdc45c94-w62pr 1/1 Running 0 13s # 稍等一会,再次查看 root@master30:~# kubectl get hpa NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE web Deployment/web memory: 1%/60% 2 5 2 53s
压力测试
# 打开一个监控窗口 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.99.129.137/big.img;sleep 1;done
负载上来了
Every 4.0s: kubectl get hpa;echo;kubectl top pods master30.laoma.cloud: Sun Apr 19 11:13:29 2026 NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE web Deployment/web memory: 86%/60% 2 5 2 16m NAME CPU(cores) MEMORY(bytes) web-88cff78bb-2b7vh 15m 174Mi web-88cff78bb-76pg2 15m 171Mi
新建了一个pods
Every 4.0s: kubectl get hpa;echo;kubectl top pods master30.laoma.cloud: Sun Apr 19 11:13:46 2026 NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE web Deployment/web memory: 58%/60% 2 5 3 16m NAME CPU(cores) MEMORY(bytes) web-88cff78bb-2b7vh 15m 174Mi web-88cff78bb-4hcm5 5m 3Mi web-88cff78bb-76pg2 15m 171Mi
又新建了一个pods
Every 4.0s: kubectl get hpa;echo;kubectl top pods master30.laoma.cloud: Sun Apr 19 11:15:46 2026 NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE web Deployment/web memory: 58%/60% 2 5 4 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
又新建了一个pods
Every 4.0s: kubectl get hpa;echo;kubectl top pods master30.laoma.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
观察过程:
-
随着pod MEM使用率上升,自动扩展pod数量。
-
即使pod MEM使用率超过阈值,但pod数量不会超过5。
清理资源
# 删除 hpa root@master30:~# kubectl delete hpa web # 删除 deployment root@master30:~# kubectl delete deployment web # 删除 svc root@master30:~# kubectl delete svc web
Vertical Pod Autoscaler
VPA 使用场景不多,这里不做实践演示。
VPA 作用
控制器将从一系列API(metrics.k8s.io、custom.metrics.k8s.io 和 external.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、内存 |
环境清理
root@master30:~# kubectl delete ns metric
Quota and Limits
学习参考:
环境准备
-
创建一个独立的名字空间quota,并切换到该ns
root@master30:~# kubectl create ns quota root@master30:~# kubectl config set-context --current --namespace quota
-
提前部署好 Metric Server
ResourceQuota
问题:当多个用户或团队共享Kubernetes集群时,有人会使用超过其基于公平原则所分配到的资源量。
解决:可以使用资源配额限制 Namespace 使用的资源。
资源配额,通过 ResourceQuota 对象来定义,对每个命名空间的资源消耗总量提供限制。
资源配额的工作方式如下:
-
不同的团队在不同的命名空间下工作。这可以通过 RBAC 强制执行。
-
集群管理员可以为每个命名空间创建一个或多个 ResourceQuota 对象。
-
当用户在命名空间下创建资源(如 Pod、Service 等)时,Kubernetes 的配额系统会跟踪集群的资源使用情况, 以确保使用的资源用量不超过 ResourceQuota 中定义的硬性资源限额。
-
如果资源创建或者更新请求违反了配额约束,那么该请求会报错(HTTP 403 FORBIDDEN), 并在消息中给出有可能违反的约束。
-
如果命名空间下的计算资源 (如
cpu和memory)的配额被启用, 则用户必须为这些资源设定请求值(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一样
单位说明:
-
CPU:1 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。
-
配额管理
重要说明: 如果项目级别配额限定了 request 和 limit,那么创建pod的时候必须指定 request 和 limit。
创建 ResourceQuota 对象
root@master30:~# kubectl create quota myquota --hard=pods=2,services=3,secrets=5,persistentvolumeclaims=10 root@master30:~# kubectl get resourcequotas NAME AGE REQUEST LIMIT my-quota 10s persistentvolumeclaims: 0/10, pods: 0/2, secrets: 1/5, services: 0/3 root@master30:~# kubectl describe quota myquota Name: myquota Namespace: quota Resource Used Hard -------- ---- ---- persistentvolumeclaims 0 10 pods 0 2 secrets 1 5 services 0 3
通过 yaml 文件创建
apiVersion: v1
kind: ResourceQuota
metadata:
name: myquota
spec:
hard:
persistentvolumeclaims: "10"
pods: "2"
secrets: "5"
services: "3"
测试配额
root@master30:~# kubectl create deployment web --image=hub.laoma.cloud/library/nginx --replicas=3
root@master30:~# kubectl get all
NAME READY STATUS RESTARTS AGE
pod/web-96d5df5c8-dl7qk 1/1 Running 0 40s
pod/web-96d5df5c8-j7fhh 1/1 Running 0 40s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/web 2/3 2 2 40s
NAME DESIRED CURRENT READY AGE
replicaset.apps/web-96d5df5c8 3 2 2 40s
root@master30:~# kubectl describe rs web-96d5df5c8
LAST SEEN TYPE REASON OBJECT MESSAGE
......
33s Warning FailedCreate replicaset/web-96d5df5c8 Error creating: pods "web-96d5df5c8-xg6c9" is forbidden: exceeded quota: myquota, requested: pods=1, used: pods=2, limited: pods=2
2s Warning FailedCreate replicaset/web-96d5df5c8 (combined from similar events): Error creating: pods "web-96d5df5c8-89p7j" is forbidden: exceeded quota: myquota, requested: pods=1, used: pods=2, limited: pods=2
25s Normal SuccessfulCreate replicaset/web-96d5df5c8 Created pod: web-96d5df5c8-bkzmd
34s Normal ScalingReplicaSet deployment/web Scaled up replica set web-96d5df5c8 to 3
# 超过配额,创建失败
# 修改配额 pod数量为10
root@master30:~# kubectl patch resourcequotas myquota -p '{"spec":{"hard":{"pods":10}}}'
# 此时重新扩展rs
root@master30:~# kubectl scale rs web-96d5df5c8 --replicas 3
# 再次验证pod数量
root@master30:~# kubectl get pods
NAME READY STATUS RESTARTS AGE
pod/web-96d5df5c8-ajcz2 1/1 Running 0 2s
pod/web-96d5df5c8-dl7qk 1/1 Running 0 60s
pod/web-96d5df5c8-j7fhh 1/1 Running 0 60s
# 清理环境
root@master30:~# kubectl delete deployments.apps web
root@master30:~# kubectl delete resourcequotas myquota
思考: 如果一个用户可以管理多个 namespace,能否限定该用户配额呢?
Request 和 Limits
如果命名空间下的计算资源 (如 cpu 和 memory)的配额被启用, 则用户必须为这些资源设定请求值(request)和约束值(limit),否则配额系统将拒绝 Pod 的创建。
pod.containers.resources 定义包含两部分:
-
requests,指明pod运行需要的最少计算资源,调度器查找具有充足计算资源的nodes。
-
limits,指明pod运行可以获得节点最多计算资源,用于阻止pod占用node太多计算资源。node使用Linux内核功能cgroup,限制pod资源使用。
测试-不指定计算资源
配额示例
root@master30:~# 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"
root@master30:~# kubectl apply -f resourcequota.yaml
pod 示例
root@master30:~# 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:~# 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:~# kubectl delete resourcequotas myquota
测试-Request
配额示例
root@master30:~# vim resourcequota.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: myquota
spec:
hard:
requests.cpu: 1000m
requests.memory: "2048Mi"
root@master30:~# kubectl apply -f resourcequota.yaml
pod 示例1:超上限
root@master30:~# vim pod-request-1.yaml
apiVersion: v1
kind: Pod
metadata:
name: web
labels:
app: web
spec:
containers:
- name: web
image: hub.laoma.cloud/library/httpd
imagePullPolicy: IfNotPresent
resources:
requests:
cpu: 2000m
memory: 4096Mi
ports:
- name: web
containerPort: 80
protocol: TCP
root@master30:~# 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:~# vim pod-request-2.yaml
apiVersion: v1
kind: Pod
metadata:
name: web
labels:
app: web
spec:
containers:
- name: web
image: hub.laoma.cloud/library/httpd
imagePullPolicy: IfNotPresent
resources:
requests:
cpu: 200m
memory: 1024Mi
ports:
- name: web
containerPort: 80
protocol: TCP
root@master30:~# kubectl get pod NAME READY STATUS RESTARTS AGE web 1/1 Running 0 43s # 验证使用情况 root@master30:~# kubectl describe resourcequotas myquota Name: myquota Namespace: quota Resource Used Hard -------- ---- ---- requests.cpu 200m 1 requests.memory 1Gi 2Gi # 删除pod和quota root@master30:~# kubectl delete pod web root@master30:~# kubectl delete resourcequotas myquota
测试-Limits
压力测试镜像
可以直接使用镜像 hub.laoma.cloud/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 hub.laoma.cloud/progrium/stress -m 1 --vm-bytes 512M # 压力测试CPU $ docker run --name stress hub.laoma.cloud/progrium/stress -c 1 # 压力测试IO $ docker run --name stress hub.laoma.cloud/progrium/stress –d 1 --hdd-bytes 3G
对于 pod:
apiVersion: v1
kind: Pod
metadata:
name: stress
spec:
containers:
- name: stress
image: hub.laoma.cloud/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:~# vim resourcequota.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: myquota
spec:
hard:
limits.cpu: 1000m
limits.memory: "2048Mi"
root@master30:~# kubectl apply -f resourcequota.yaml
测试 CPU 资源
pod示例:
root@master30:~# vim pod-limit-cpu.yaml
apiVersion: v1
kind: Pod
metadata:
name: stress
spec:
containers:
- name: stress
image: hub.laoma.cloud/progrium/stress
imagePullPolicy: IfNotPresent
args: ['-c','1']
resources:
limits:
cpu: 200m
memory: "256Mi"
root@master30:~# kubectl apply -f pod-limit-cpu.yaml
打开一个终端监控
kubectl top 命令需要提前部署Metrics-Server
root@master30:~# kubectl top pods NAME CPU(cores) MEMORY(bytes) stress 201m 0Mi
可以发现:CPU 使用率维持在 200m 左右。
# 删除 pod root@master30:~# kubectl delete pod stress --force
测试 MEMORY 资源
pod示例:
root@master30:~# vim pod-limit-memory.yaml
apiVersion: v1
kind: Pod
metadata:
name: stress
spec:
containers:
- name: stress
image: hub.laoma.cloud/progrium/stress
imagePullPolicy: IfNotPresent
args: ['-m','1','--vm-bytes','512M']
resources:
limits:
cpu: 200m
memory: "256Mi"
root@master30:~# kubectl apply -f pod-limit-memory.yaml
打开一个终端监控
root@master30:~# kubectl get pods -w NAME READY STATUS RESTARTS AGE stress 0/1 OOMKilled 1 (2s ago) 3s stress 0/1 CrashLoopBackOff 1 (2s ago) 4s
可以发现: Pod 状态为 OOMKilled,并进行restart。
# 删除 pod he root@master30:~# kubectl delete pod stress --force root@master30:~# kubectl delete resourcequotas myquota
总结
-
计算资源的 limits 总和是否会超过节点上资源总和?
答案:可能会。 假设 node可用MEMORY为1G。
每个pod内存 requests是256M,limit是512M。创建5个pod,pod实际占用内存也为256M(有可能小于256M)。
node上大概可以创建4个pod,而此时的limits总和是2G。
-
当计算资源的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:~# vim limits.yaml
apiVersion: v1
kind: LimitRange
metadata:
name: mylimit
spec:
limits:
- type: Container
max:
memory: 1024Mi
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:~# kubectl apply -f limits.yaml root@master30:~# kubectl get limitranges NAME CREATED AT mylimit 2021-09-09T04:09:15Z root@master30:~# 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:~# vim pod-without-limits.yaml
apiVersion: v1
kind: Pod
metadata:
name: stress
spec:
containers:
- name: stress
image: hub.laoma.cloud/progrium/stress
imagePullPolicy: IfNotPresent
args: ['-c','1']
结论:创建出来的pod的resources 与 limitranage 指定的相关默认值一致。
root@master30:~# kubectl apply -f pod-without-limits.yaml root@master30:~# kubectl top pods NAME CPU(cores) MEMORY(bytes) stress 501m 0Mi root@master30:~# kubectl get pod stress -o yaml
......
spec:
containers:
image: hub.laoma.cloud/progrium/stress
imagePullPolicy: IfNotPresent
name: stress
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 200m
memory: 256Mi
......
# 清理资源 root@master30:~# kubectl delete limitranges mylimit root@master30:~# kubectl delete pod stress --force
只指定 limit 值
示例 2-1:limit 值大于 max 值
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
containers:
- name: web
image: hub.laoma.cloud/library/nginx
resources:
limits:
cpu: 1.1
memory: 1100Mi
root@master30:~# kubectl apply -f limit.yml Error from server (Forbidden): error when creating "limit.yml": pods "web" is forbidden: [maximum cpu usage per Container is 1, but limit is 1100m, maximum memory usage per Container is 1Gi, but limit is 1181116006400m]
示例 2-2:limit 值小于 min 值
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
containers:
- name: web
image: hub.laoma.cloud/library/nginx
resources:
limits:
cpu: 60m
memory: 60Mi
root@master30:~# kubectl apply -f limit.yml Error from server (Forbidden): error when creating "limit.yml": 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: hub.laoma.cloud/library/nginx
resources:
limits:
cpu: 600m
memory: 600Mi
结论:
-
创建的容器limits值必须满足条件:min值<指定的limit值<max值
-
当只指定limits值时,requests值与limits值保持一致,而不是default request。
root@master30:~# kubectl get pod web -o yaml ...... spec: containers: - image: hub.laoma.cloud/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: hub.laoma.cloud/library/nginx
resources:
requests:
cpu: 1600m
memory: 600Mi
root@master30:~# kubectl apply -f limit4.yml The Pod "web" is invalid: * spec.containers[0].resources.requests: Invalid value: "1600m": must be less than or equal to cpu limit * spec.containers[0].resources.requests: Invalid value: "1600Mi": must be less than or equal to memory limit
示例 3-2:requests 小于 min 值
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
containers:
- name: web
image: hub.laoma.cloud/library/nginx
resources:
requests:
cpu: 60m
memory: 60Mi
root@master30:~# kubectl apply -f limit.yml Error from server (Forbidden): error when creating "limit.yml": 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: hub.laoma.cloud/library/nginx
resources:
requests:
cpu: 400m
memory: 400Mi
结论:
-
创建的容器requests值必须满足条件:min值<requests值<limits值
-
当只指定requests值时,limits值与default值保持一致。
root@master30:~# kubectl get pod web -o yaml ...... spec: containers: - image: hub.laoma.cloud/library/nginx imagePullPolicy: Always name: web resources: limits: cpu: 500m memory: 512Mi requests: cpu: 400m memory: 400Mi ......
限定资源类型
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
环境清理
root@master30:~# kubectl delete ns quota
Kubernetes Health Check
学习参考:配置存活、就绪和启动探针
环境准备
root@master30:~# kubectl create ns health root@master30:~# kubectl config set-context --current --namespace health
Health Check
应用可能会因为各种问题,变的 unhealthy,例如临时连接断开,配置错误,应用本身错误。
kubelet 使用 probes(探针),周期性地监控容器中应用是否为healthy状态,进一步决定什么时候要重启容器。 例如,当存活探针可以探测到应用死锁(应用在运行,但是无法继续执行后面的步骤)情况,进而重启pod,有助于提高应用的可用性,即使其中存在缺陷。
没有探测的情况,看一个例子:
# 创建一个普通 pod root@master30:~# kubectl run web --image=hub.laoma.cloud/library/httpd --image-pull-policy=IfNotPresent [root@laoma20 health]# kubectl describe pod web|grep '^IP:' IP: 10.224.73.16 root@master30:~# curl 10.224.73.16 <html><body><h1>It works!</h1></body></html> # 删除主页文件,即使pod中应用数据丢失,pod状态依然为Running 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> root@master30:~# kubectl get pod NAME READY STATUS RESTARTS AGE web 1/1 Running 0 51s # 清理环境 root@master30:~# kubectl delete pod web --force
Probe Type
kubelet 使用启动探针来了解应用容器何时启动。 如果配置了这类探针,存活探针和就绪探针成功之前不会重启,确保这些探针不会影响应用的启动。 启动探针可以用于对慢启动容器进行存活性检测,避免它们在启动运行之前就被杀掉。
-
LivenessProbe:用于确定pod中应用是否处于healthy状态。如果liveness probe检测的状态为unhealthy,则控制器重启pod。
-
ReadinessProbe:用于确定pod中应用是否可以提供服务。如果返回失败状态,则服务将从endpoints 中删除容器ip地址。即使容器处于运行状态,也不接受代理发过来的请求。
-
StartupProbe:用于确定pod是否成功初始化。 如果指定,则在成功完成之前不会执行其他探测。如果此探测失败,Pod 将重新启动,就像 livenessProbe 失败一样。 这可用于在 Pod 生命周期开始时提供不同的探测参数,此时加载数据或预热缓存可能需要比稳态操作期间更长的时间。 这无法更新。
我们这里不深入讨论StartupProbe。
Checking Methods
探针检查容器有四种不同的方法:
-
httpGet,对容器的 IP 地址上指定端口和路径执行 HTTP
GET请求。如果响应的状态码大于等于 200 且小于 400,则诊断被认为是成功的。 -
exec,在容器内执行指定命令。如果命令退出时返回码为 0,则认为诊断成功。
-
tcpSocket,对容器的 IP 地址上的指定端口执行 TCP 检查。如果端口打开,则诊断被认为是成功的。 如果远程系统(容器)在打开连接后立即将其关闭,这算作是健康的。
-
grpc,使用 gRPC 执行一个远程过程调用。 目标应该实现 gRPC 健康检查。 如果响应的状态是 "SERVING",则认为诊断成功。
我们这里不讨论 grpc 方法。
HTTP Checks-httpGet
当使用HTTP Checks,控制器使用webhoook判定容器健康情况。如果HTTP的响应码在200-399之间,判定check成功。适应范围:可以返回HTTP状态码应用。
livenessProbe
root@master30:~# 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: hub.laoma.cloud/library/httpd
imagePullPolicy: IfNotPresent
name: httpd
# 添加livenessProbe部分
livenessProbe:
failureThreshold: 3
initialDelaySeconds: 5
periodSeconds: 5
successThreshold: 1
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:~# kubectl apply -f deploy-httpGet-liveness.yaml root@master30:~# kubectl get pod NAME READY STATUS RESTARTS AGE web-85c6ff748f-qwszz 1/1 Running 0 12m root@master30:~# kubectl describe pod web-85c6ff748f-j92jn|grep '^IP:' IP: 10.98.146.216 # 删除主页文件 root@master30:~# kubectl exec web-85c6ff748f-qwszz -- bash -c 'rm htdocs/index.html' # 观察pod状态,RESTARTS次数变位1,再次访问 root@master30:~# kubectl get pod NAME READY STATUS RESTARTS AGE web-85c6ff748f-qwszz 1/1 Running 1 13m # 容器删除需要一些时间,由参数terminationGracePeriodSeconds设定,默认值为30s。 # 只有等容器删除,并创建完成后才会继续检测 root@master30:~# curl 10.98.146.216 <html><body><h1>It works!</h1></body></html> # 清理环境 root@master30:~# kubectl delete deployments.apps web
readinessProbe
root@master30:~# 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: hub.laoma.cloud/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:~# kubectl apply -f deploy-httpGet-readiness.yaml
root@master30:~# kubectl expose deployment web --port=80 --target-port=80
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主页文件
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
root@master30:~# for i in {1..90};do curl -s 10.96.180.30;done|sort |uniq -c
24 web-9479dc55c-6bpg7
33 web-9479dc55c-d2gbn
33 web-9479dc55c-hqh8q
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主页文件
root@master30:~# kubectl exec -it web-9479dc55c-6bpg7 -- rm -f htdocs/index.html
# web 服务的后端没有pod的ip
root@master30:~# kubectl get endpoints web
NAME ENDPOINTS AGE
web 10.224.73.15:80,10.224.73.34:80 23m
# 访问svc,后端无法看到 web-9479dc55c-6bpg7
root@master30:~# for i in {1..90};do curl -s 10.96.180.30;done|sort |uniq -c
50 web2
40 web3
# 观察web1状态,READY为0,RESTARTS数量为0
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
# 清理环境
root@master30:~# kubectl delete deployments.apps web
Execution Checks-exec
当使用容器执行检测,kubelet代理将在容器内执行命令。返回值是0,代表check成功。
示例1:检测容器自带文件
root@master30:~# 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: 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
# 创建应用 root@master30:~# kubectl apply -f deploy-exec-liveness.yaml 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
示例2:检测自定义文件
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
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: busybox
name: busybox
spec:
containers:
- image: busybox
imagePullPolicy: IfNotPresent
name: busybox
# 添加args参数
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
TCP Socket Checks-tcpSocket
当使用TCP socket checks,kubelet代理尝试打开容器socket。如果check可以建立连接,判定check成功。
示例:liveness probe使用TCP Socket check
root@master30:~# 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: hub.laoma.cloud/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会启动新副本, 然后发生了如下事件:
-
正常情况下新副本需要10秒钟完成准备工作, 在此之前无法响应业务请求。
-
由于人为配置错误, 副本始终无法完成准备工作(比如无法连接后端数据库)。
如果没有配置Health Check, 会出现怎样的情况?
因为新副本本身没有异常退出, 默认的Health Check机制会认为容器已经就绪, 进而会逐步用新副本替换现有副本, 其结果就是: 当所有旧副本都被替换后, 整个应用将无法处理请求, 无法对外提供服务。 如果这是发生在重要的生产系统上, 后果会非常严重。
如果正确配置了Health Check, 新副本只有通过了探测才会被添加到Service; 如果没有通过探测, 现有副本不会被全部替换, 业务仍然正常进行。
环境清理
root@master30:~# kubectl delete ns health
Kubernetes 认证和授权
学习参考:API 访问控制
环境准备
root@master30:~# kubectl create ns auth root@master30:~# kubectl config set-context --current --namespace 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名字空间中请求写(create或update)对象或在其它名字空间中请求读取(get)对象,其鉴权请求会被拒绝。
注意:Kubernetes 鉴权要求使用公共 REST 属性与现有的组织范围或云提供商范围的访问控制系统进行交互。 使用 REST 格式很重要,因为这些控制系统可能会与 Kubernetes API 之外的 API 交互。
Kubernetes 支持多种鉴权模块,例如 ABAC 模式、RBAC 模式和 Webhook 模式等。 管理员创建集群时,他们配置应在 API 服务器中使用的鉴权模块。 如果配置了多个鉴权模块,则 Kubernetes 会检查每个模块,任意一个模块鉴权该请求,请求即可继续; 如果所有模块拒绝了该请求,请求将会被拒绝(HTTP 状态码 403)。
要了解更多有关 Kubernetes 鉴权的更多信息,包括有关使用支持鉴权模块创建策略的详细信息, 请参阅鉴权。
准入控制
这一操作如上图的步骤 ③ 所示。
-
准入控制器对创建、修改、删除或(通过代理)连接对象的请求进行操作。 当有多个准入控制器被配置时,服务器将依次调用它们。准入控制器不会对仅读取对象的请求起作用。准入控制模块是可以修改或拒绝请求的软件模块。 除鉴权模块可用的属性外,准入控制模块还可以访问正在创建或修改的对象的内容。
-
与身份认证和鉴权模块不同,如果任何准入控制器模块拒绝某请求,则该请求将立即被拒绝。除了拒绝对象之外,准入控制器还可以为字段设置复杂的默认值。
-
请求通过所有准入控制器后,将使用检验例程检查对应的 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。
每个 API 请求必须包含一个用户:普通用户相关或者服务账号,客户端也可以发起匿名请求。这意味着集群内外的每个进程在向 API 服务器发起请求时都必须通过身份认证,否则会被视作匿名用户。
身份认证策略
Kubernetes 通过身份认证插件认证 API 请求的身份。HTTP 请求发给 API 服务器时,插件会将以下属性关联到请求本身:
-
用户名:用来辩识最终用户的字符串。常见的值可以是
kube-admin或jane@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:~# 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 参数。
root@master30:~# 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:~# 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@client:~# apt install -y kubectl=1.30.2-00
准备申请材料
# 创建私钥 root@client:~# openssl genrsa -out laoma.key 2048 # 根据私钥,创建请求证书 root@client:~# openssl req -new -key laoma.key -out laoma.csr -subj '/CN=laoma/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=laoma@lab.example.com" # 客户端将自己请求证书发给kubernetes管理员 root@client:~# scp laoma.csr root@master30:
创建用户凭据
管理员使用以下资源文件创建用户的kubeconfig。
# 使用ca.crt和ca.key签名laoma.csr,得到laoma.crt证书 root@master30:~# openssl x509 -req -in laoma.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out laoma.crt -days 1095 # 导出kubeconfig模版 root@master30:~# kubectl config view > config.tpl # 将kubeconfig模板、laoma.crt和kubernetes的ca证书发给客户端 root@master30:~# scp config.tpl laoma.crt /etc/kubernetes/pki/ca.crt root@client:~
创建 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: laoma name: laoma@kubernetes current-context: laoma@kubernetes kind: Config users: - name: laoma
# 设置 cluster root@client:~# mv config.tpl config root@client:~# 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:~# kubectl config set-credentials laoma --kubeconfig=config --client-key=laoma.key --client-certificate=laoma.crt --embed-certs # --client-key 指定用户私钥 # --client-certificate 指定服务器为用户生成的证书 # 设置context root@client:~# kubectl config set-context laoma --kubeconfig=config --namespace=default --cluster=kubernetes --user=laoma # --namespace指定Namespace # --cluster指定集群 # --user指定用户 # 授权用户laoma集群管理员角色,后续详细讲解角色管理 root@master30:~# kubectl create clusterrolebinding laoma-admin --clusterrole=cluster-admin --user=laoma # 验证结果 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
方法二:使用文本编辑器创建(不推荐)。
# 获取文件ca.crt base64编码,填充到certificate-authority-data
root@client:~# cat ca.crt | base64 | tr -d '\n'
# 获取文件laoma.key base64编码,填充到client-key-data
root@client:~# cat laoma.key | base64 | tr -d '\n'
# 获取文件laoma.crt base64编码,填充到client-certificate-data
root@client:~# cat laoma.crt | base64 | tr -d '\n'
# 使用上面的base64编码修改配置文件对应值
root@master30:~# kubectl config view
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: DATA+OMITTED
server: https://10.1.8.30:6443
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: kubernetes-laoma
name: kubernetes-laoma@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
- name: kubernetes-laoma
user:
client-certificate-data: REDACTED
client-key-data: REDACTED
删除账户
# 通过删除证书进行删除账户即可,为了后续操作方便,该账户不删除 # k8s删除用户csr资源后,客户端用户仍可以访问集群。 # 解释:用户认证功能由x509提供,与k8s无关。 # 如果不予许用户登录,应该从x509认证机制方面下手,例如ca.crt将相应客户端csr加入黑名单。 # 删除权限 root@master30:~# kubectl delete clusterrolebindings.rbac.authorization.k8s.io laoma-admin # 验证权限 root@client:~# kubectl get nodes Error from server (Forbidden): nodes is forbidden: User "laoma" 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
我们这里使用RBAC和Node模式。
支持的模式:
-
Node,是节点专用的鉴权模式,根据调度到 kubelet 上运行的 Pod 为 kubelet 授予权限。 要了解有关使用节点鉴权模式的更多信息,请参阅节点鉴权。
-
ABAC,基于属性的访问控制(ABAC)定义了一种访问控制范型,通过使用将属性组合在一起的策略, 将访问权限授予用户。策略可以使用任何类型的属性(用户属性、资源属性、对象,环境属性等)。 要了解有关使用 ABAC 模式的更多信息,请参阅 ABAC 模式。
-
RBAC,基于角色的访问控制(RBAC) 是一种基于企业内个人用户的角色来管理对计算机或网络资源的访问的方法。要了解有关使用 RBAC 模式的更多信息,请参阅 RBAC 模式。
-
被启用之后,RBAC(基于角色的访问控制)使用
rbac.authorization.k8s.ioAPI 组来驱动鉴权决策,从而允许管理员通过 Kubernetes API 动态配置权限策略。 -
要启用 RBAC,请使用
--authorization-mode = RBAC启动 API 服务器。
-
-
Webhook —— WebHook 是一个 HTTP 回调:发生某些事情时调用的 HTTP POST; 通过 HTTP POST 进行简单的事件通知。 实现 WebHook 的 Web 应用程序会在发生某些事情时将消息发布到 URL。 要了解有关使用 Webhook 模式的更多信息,请参阅 Webhook 模式。
-
AlwaysAllow,允许用户所有请求。
# 前面我们删除了用户laoma权限,更改为AlwaysAllow模式,测试laoma用户权限。 root@master30:~# vim /etc/kubernetes/manifests/kube-apiserver.yaml ...... # 在spec.containers下的command中添加参数- --basic-auth-file=/etc/kubernets/pki/aa.csv spec: containers: - command: - --authorization-mode=AlwaysAllow ...... # 更改完成后,kubernetes会自动重启pod/kube-apiserver,等该pod状态为running再次验证。 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
-
AlwaysDeny,拒绝用户所有请求,不管用户是否具有权限,但不限制admin用户。
role 管理
kubernetes 方便管理权限,将一组特定权限赋予角色,然后将角色赋予用户,那么用户将继承该角色具有的权限。
角色分类
-
role,namespace 角色,限定用户访问特定namespace。role绑定给用户,称之为rolebinding。
-
clusterrole,cluster 角色,可以管理集群,包括所有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 "laoma" 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:~# kubectl get pod -n kube-system
NAME READY STATUS RESTARTS AGE
calico-kube-controllers-6dfcd885bf-lq4n5 1/1 Running 4 52d
calico-node-44cf4 1/1 Running 4 52d
calico-node-48sm4 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 "laoma" cannot get resource "pods" in API group "" in the namespace "kube-system"
更多API信息参考:Kubernetes API | Kubernetes
创建 role
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:可以对项目中所有pods执行get、list、watch操作
root@master30:~# 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:~# kubectl create role pod-role --verb=get,list,watch --resource=pods -n default 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:访问特定资源pods/readablepod
root@master30:~# kubectl create role pod-role --verb=get --resource=pods \ --resource-name=readablepod --resource-name=anotherpod
示例3:可以对项目中所有replicasets执行get list watch操作
root@master30:~# kubectl create role foo --verb=get,list,watch --resource=replicasets
修改 role
# 增加create权限 root@master30:~# kubectl edit roles -n default pod-role ...... rules: - apiGroups: - "" resources: - pods verbs: # 在verbs下添加相应权限 - list - get - watch - create
apiGroups
-
角色的 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:~# kubectl create rolebinding default-pod-laoma -n default --role=pod-role --user=laoma --dry-run=client -o yaml
apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: creationTimestamp: null name: default-pod-laoma namespace: default roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: pod-role subjects: - apiGroup: rbac.authorization.k8s.io kind: User name: laoma
# 绑定 ns/default 中角色 pod-role 给 laoma root@master30:~# kubectl create rolebinding default-pod-laoma -n default --role=pod-role --user=laoma # 角色绑定完成后,角色的权限发生变化,用户获得的权限也会跟着动态变化。 root@master30:~# kubectl get rolebindings -n default default-pod-laoma NAME ROLE AGE default-pod-laoma Role/pod-role 2m15s root@master30:~# kubectl describe rolebindings -n default default-pod-laoma Name: default-pod-laoma Labels: <none> Annotations: <none> Role: Kind: Role Name: pod-role Subjects: Kind Name Namespace ---- ---- --------- User laoma # 验证 root@client:~# kubectl run web --image=hub.laoma.cloud/library/httpd --image-pull-policy=IfNotPresent -n default 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
role 回收
root@master30:~# kubectl delete rolebindings default-pod-laoma -n kube-system rolebinding.rbac.authorization.k8s.io "default-pod-laoma" deleted # 再次验证 root@client:~# kubectl get pod -n default Error from server (Forbidden): pods is forbidden: User "laoma" cannot list resource "pods" in API group "" in the namespace "default"
role 删除
root@master30:~# kubectl delete roles pod-role -n default
实践 1:角色管理
-
在 auth 命名空间,创建角色 pod-reader,针对pod,具备权限get、list、watch
-
赋予 laoma 用户 auth 命名空间 角色 pod-reader
-
管理员身份在 auth 命名空间创建 deployment
-
回收laoma 用户 集群管理员角色 cluster-admin(如果存在)
-
laoma 用户验证:查看deployment和pod
-
清理资源:回收用户角色,删除 deployment
clusterrole 管理
常见 clusterrole
kubernetes系统中已经预定义了很多clusterrole,常见的clusterrole如下:
-
view,对系统中几乎所有的对象都有get、list和watch权限。
-
edit,对系统中几乎所有的对象都有get、list和watch权限。其中部分对象额外具有 create、delete、deletecollection、patch、update 权限。
-
admin,对系统中大部分的对象具有所有权限。
-
cluster-admin,对系统中所有的对象具有所有权限。
创建 clusterrole
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]
示例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 laoma-pod-role --clusterrole=pod-role --user=laoma --dry-run=client -o yaml
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: creationTimestamp: null name: laoma-pod-role roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: pod-role subjects: - apiGroup: rbac.authorization.k8s.io kind: User name: laoma
root@master30:~# kubectl create clusterrolebinding laoma-pod-role --clusterrole=pod-role --user=laoma root@master30:~# kubectl get clusterrolebinding laoma-pod-role NAME ROLE AGE laoma-pod-role ClusterRole/pod-role 35s root@master30:~# kubectl describe clusterrolebinding laoma-pod-role Name: laoma-pod-role Labels: <none> Annotations: <none> Role: Kind: ClusterRole Name: pod-role Subjects: Kind Name Namespace ---- ---- --------- User laoma # client节点使用laoma用户测试权限 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 laoma-pod-role
删除 clusterrole
root@master30:~# kubectl delete clusterrole pod-reader
实践 2:使用现有集群角色
-
赋予 laoma 用户集群角色 admin,指定在auth命名空间
-
laoma 用户在 auth 命名空间:创建、查看和删除 deployment
-
laoma 用户在 default 命名空间:创建、查看和删除 deployment
-
回收 laoma 用户权限
结果:绑定集群角色的时候,限定特定命名空间是没有意义的,仍然针对集群级别所有命名空间生效。
实践 3:自定义集群角色
-
创建 clusterrole 名称 cluster-pod-deploy-reader,能够查看pod和deployment
-
赋予给 laoma 用户
-
laoma 用户在 auth 命名空间中:查看pod、deployment
-
laoma 用户在 kube-system 命名空间中:查看pod、deployment
-
删除集群角色绑定和集群角色
服务账户
服务账户概述
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=hub.laoma.cloud/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
该清单片段定义了由三个数据源组成的投射卷。在当前场景中,每个数据源也代表该卷内的一条独立路径。这三个数据源是:
-
serviceAccountToken数据源,包含 kubelet 从 kube-apiserver 获取的令牌。 kubelet 使用 TokenRequest API 获取有时间限制的令牌。为 TokenRequest 服务的这个令牌会在 Pod 被删除或定义的生命周期(默认为 1 小时)结束之后过期。该令牌绑定到特定的 Pod, 并将其 audience(受众)设置为与kube-apiserver的 audience 相匹配。 这种机制取代了之前基于 Secret 添加卷的机制,之前 Secret 代表了针对 Pod 的 ServiceAccount 但不会过期。注意:没有特定的机制可以使通过 TokenRequest 签发的令牌无效。 如果你不再信任为某个 Pod 绑定的服务账号令牌, 你可以删除该 Pod。删除 Pod 将使其绑定的服务账号令牌过期。
-
configMap数据源。ConfigMap 包含一组证书颁发机构数据。 Pod 可以使用这些证书来确保自己连接到集群的 kube-apiserver(而不是连接到中间件或意外配置错误的对等点上)。 -
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=hub.laoma.cloud/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: laoma 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 laoma 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
更多推荐





所有评论(0)