K8s 资源管控与权限实战:HPA 弹性伸缩、资源配额、探针、RBAC 权限全解析
K8s 资源管控与权限实战:HPA 弹性伸缩、资源配额、探针、RBAC 权限全解析
本手册由
master30_2026-08-13_10_13_37.log(10:13–17:04)+worker31_2026-08-13_15_27_14.log(15:27–17:04)合并整理而成,在 kubeadm v1.30.2 集群(master30 + worker31/32,运行第 8 天)上完成。
四条主线:① HPA 内存指标弹性伸缩 → ② 资源管理(ResourceQuota 配额 + LimitRange 默认限额)→ ③ 健康检查探针(liveness / readiness 三种方式)→ ④ 认证授权(证书签发 + kubeconfig + RBAC)。命令提示符保留真实时间戳。
📑 目录
| # | 章节 | 核心内容 |
|---|---|---|
| 一 | 整体框架与实验总览 | 实验流程图、节点环境、主题速览 |
| 二 | HPA 内存指标自动扩缩容 | 100MB 大文件压测、60%→20%、2→3 副本 |
| 三 | ResourceQuota 资源配额 | 对象配额、计算资源配额、patch 调整、OOMKilled |
| 四 | LimitRange 默认限额 | max/min/default/defaultRequest 自动注入 |
| 五 | 探针实战:liveness(httpGet) | 删首页文件、RESTARTS 自愈 |
| 六 | 探针实战:liveness(exec / tcpSocket) | busybox /tmp/healthy、80 端口探测 |
| 七 | 探针实战:readiness 就绪探针 | 0/1 摘除、endpoints、curl 轮询验证 |
| 八 | 认证:创建用户与证书签发 | genrsa/req、CA 签名、openssl 验证 |
| 九 | 认证:kubeconfig 配置 | set-cluster/credentials/context、server 为空修复 |
| 十 | 授权:RBAC 角色与绑定 | clusterrolebinding、Role、RoleBinding |
| 十一 | 环境清理与恢复 | 命名空间与资源回收 |
| 十二 | 常见错误与排查(重点) | 本次实验全部报错:原因 + 修复 |
| 十三 | 命令速查表与小结 | HPA/配额/探针/证书/RBAC 常用命令 |
一、整体框架与实验总览
1.1 实验流程图
1.2 节点环境
| 节点 | 主机名 | IP | 角色 |
|---|---|---|---|
| master30 | master30.ningcode.cn | 10.1.8.30 | 控制平面 |
| worker31 | worker31.ningcode.cn | 10.1.8.31 | 工作节点 |
| worker32 | worker32.ningcode.cn | 10.1.8.32 | 工作节点 |
- Pod 网段
10.224.0.0/16;Service 网段10.96.0.0/12;API Server 地址https://master30.ningcode.cn:6443 - 私有镜像仓库
hub.laoma.cloud(nginx/httpd/busybox 均已缓存,IfNotPresent秒起);压测镜像hub.laoma.cloud/progrium/stress - 网络插件 Calico;metrics-server 已就绪(
kubectl top可用);本次认证实验用 worker31 模拟外部客户端
1.3 主题速览
| 主题 | 核心知识点 | 时间范围 | 节点 |
|---|---|---|---|
| HPA 内存伸缩 | memory Utilization、ab 压测 100MB 文件、2→3 副本 | 10:13–10:41 | master30 |
| ResourceQuota | pods/services 对象配额、requests/limits 计算资源配额、patch | 11:24–11:37 | master30 |
| LimitRange | max/min/default/defaultRequest 自动注入 | 11:37–11:45 | master30 |
| liveness 探针 | httpGet / exec / tcpSocket、RESTARTS 自愈 | 13:55–14:47 | master30 |
| readiness 探针 | 0/1 摘除、endpoints、curl 轮询 | 14:02–14:07 | master30 |
| 认证授权 | CSR 签发、kubeconfig、cluster-admin / Role / RoleBinding | 15:25–17:04 | master30 + worker31 |
二、HPA 内存指标自动扩缩容
本次实验目标:用 HPA 按内存使用率自动扩缩容。先做一次 60% 目标值,因负载压不上去(环境限制),改为 20% 后成功从 2 副本弹到 3 副本。
2.1 准备:删除旧命名空间、确认集群状态
root@master30 ~ 10:13:32# kubectl delete ns scheduler
namespace "scheduler" deleted
# 查看集群 Service(ingress-nginx 的 EXTERNAL-IP 仍为 <pending>,本次实验不涉及)
root@master30 ~ 10:14:20# kubectl get svc -A
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 7d20h
ingress-nginx ingress-nginx-controller LoadBalancer 10.97.52.107 <pending> 80:32455/TCP,443:30093/TCP 41h
kube-system kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 7d20h
kube-system metrics-server ClusterIP 10.105.250.124 <none> 443/TCP 17h
2.2 制造 100MB 测试文件(hostPath 挂载)
下面的
deployment.yaml用hostPath把节点的/www目录挂到 nginx 的/usr/share/nginx/html,压测时下载/big.img大文件制造内存压力。/www的创建命令应在运行 Pod 的工作节点上执行(本次在 master30 上直接执行,若复现请先确认 Pod 调度到哪个节点)。
root@master30 ~ 10:14:24# mkdir /www
root@master30 ~ 10:14:38# dd if=/dev/zero of=/www/big.img bs=1M count=100
100+0 records in
100+0 records out
104857600 bytes (105 MB, 100 MiB) copied, 0.218516 s, 480 MB/s
# 实验结束后删除大文件(注意:输入 rm -rf / 后按 Tab 会列出根目录,极易误删,务必小心)
root@master30 ~ 10:15:45# rm -rf /www/big.img
2.3 Deployment:nginx + hostPath + 资源限额
root@master30 ~ 10:15:57# vim deployment.yaml
root@master30 ~ 10:16:51# cat deployment.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
第一次 apply 时报
namespaces "web" not found——当前 context 指向web命名空间但该命名空间还没创建,先建命名空间再部署即可:
root@master30 ~ 10:19:06# kubectl config get-contexts
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
* kubernetes-admin@kubernetes kubernetes kubernetes-admin web
root@master30 ~ 10:19:14# kubectl create namespace web
namespace/web created
root@master30 ~ 10:19:14# kubectl apply -f deployment.yaml
deployment.apps/web created
2.4 创建 HPA:内存指标 60%
root@master30 ~ 10:19:46# vim hpa-mem.yaml
root@master30 ~ 10:20:03# kubectl apply -f hpa-mem.yaml
horizontalpodautoscaler.autoscaling/web created
root@master30 ~ 10:20:09# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
web Deployment/web memory: <unknown>/60% 2 5 0 5s
root@master30 ~ 10:20:30# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
web Deployment/web memory: 7%/60% 2 5 2 27s
第一次
hpa-mem.yaml的指标为memory 平均利用率 60%(averageUtilization: 60,min 2 / max 5),指标采集需要十几秒,刚创建时显示<unknown>,随后变成7%/60%,副本数 2。
2.5 Service 暴露 + ab 压测
# 打开一个监控窗口(注释形式记录,未实际执行)
root@master30 ~ 10:20:48# # watch -n 4 'kubectl get hpa;echo;kubectl top pods'
# 上 MEM 压力,重用上面的 big.img
root@master30 ~ 10:20:52# # 上 MEM 压力,重用上面的
root@master30 ~ 10:21:16# kubectl expose deployment web
service/web exposed
root@master30 ~ 10:21:38# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
web ClusterIP 10.105.68.225 <none> 80/TCP 2s
# 循环压测:100 并发反复下载 100MB 大文件
root@master30 ~ 10:21:40# while true ;do ab -n 300000 -c 100 http://10.105.68.225/big.img;sleep 1;done
This is ApacheBench, Version 2.3 <$Revision: 1903618 $>
......
ab 压测输出较长,这里省略。压了十几分钟后内存使用率一直上不去(节点资源有限),于是注释记录:“这里负载一直上不去,实验环境我们改一下指标”,把 HPA 目标从 60% 调到 20%。
2.6 调整指标为 20% 并重新部署
root@master30 ~ 10:34:02# vim hpa-mem.yaml
root@master30 ~ 10:34:18# cat 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: 20
type: Resource
# 删除旧的 deployment / hpa / service,重新 apply
root@master30 ~ 10:35:24# kubectl delete deployments.apps web
deployment.apps "web" deleted
root@master30 ~ 10:35:37# kubectl delete horizontalpodautoscalers.autoscaling web
horizontalpodautoscaler.autoscaling "web" deleted
root@master30 ~ 10:35:54# kubectl delete service web
service "web" deleted
root@master30 ~ 10:36:06# kubectl apply -f deployment.yaml
deployment.apps/web created
root@master30 ~ 10:36:14# kubectl apply -f hpa-mem.yaml
horizontalpodautoscaler.autoscaling/web created
root@master30 ~ 10:36:35# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
web Deployment/web memory: 1%/20% 2 5 2 20s
# 重新暴露 Service(注意 ClusterIP 会变)
root@master30 ~ 10:37:07# kubectl expose deployment web
service/web exposed
root@master30 ~ 10:37:24# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
web ClusterIP 10.99.152.229 <none> 80/TCP 6s
2.7 压测与扩缩容结果
# 继续用新 Service IP 压测
root@master30 ~ 10:37:26# while true ;do ab -n 300000 -c 100 http://10.99.152.229/big.img;sleep 1;done &
[1] 81943
This is ApacheBench, Version 2.3 <$Revision: 1903618 $>
......
# 监控窗口(watch)中看到的结果:
root@master30 ~ 10:40:57# : << END
Every 4.0s: kubectl get hpa;echo;kubectl top pods master30.ningcode.cn: Thu Aug 13 10:40:43 2026
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
web Deployment/web memory: 17%/20% 2 5 3 4m23s
NAME CPU(cores) MEMORY(bytes)
web-88cff78bb-6f6vz 18m 34Mi
web-88cff78bb-wb9kd 19m 34Mi
web-88cff78bb-xrht4 21m 34Mi
END
root@master30 ~ 10:41:08# #环境配置有限,节点内存负载一直上不去但是能看到已经弹出来了一个pod
结论:目标 20% 生效后,HPA 从 min 2 副本自动弹出第 3 个 Pod(2→3)。环境内存有限,负载没法继续往上顶,但“按内存自动扩容”的机制已完整验证。压测结束后清理整个
web命名空间:
root@master30 ~ 10:41:45# kubectl delete ns web
namespace "web" deleted
三、ResourceQuota 资源配额
ResourceQuota 限制一个命名空间内可创建的资源总量(对象个数 + 计算资源总和)。本次分三步:对象数量配额 → 计算资源配额(requests/limits)→ 压测验证 limits 上限。
3.1 对象数量配额(pods / services / secrets / pvc)
root@master30 ~ 11:24:49# kubectl create ns quota
namespace/quota created
root@master30 ~ 11:24:54# kubectl config set-context --current --namespace quota
Context "kubernetes-admin@kubernetes" modified.
root@master30 ~ 11:26:26# kubectl create quota myquota --hard=pods=2,services=3,secrets=5,persistentvolumeclaims=10
resourcequota/myquota created
root@master30 ~ 11:26:38# kubectl get resourcequotas
NAME AGE REQUEST LIMIT
myquota 14s persistentvolumeclaims: 0/10, pods: 0/2, secrets: 0/5, services: 0/3
root@master30 ~ 11:26:52# kubectl describe quota myquota
Name: myquota
Namespace: quota
Resource Used Hard
-------- ---- ----
persistentvolumeclaims 0 10
pods 0 2
secrets 0 5
services 0 3
副本数超配额被拒:创建 --replicas 3 的 Deployment,只有 2 个 Pod 能起来,第 3 个被配额拦截:
root@master30 ~ 11:27:18# kubectl create deployment web --image=hub.laoma.cloud/library/nginx --replicas 3
deployment.apps/web created
root@master30 ~ 11:27:54# kubectl get all
NAME READY STATUS RESTARTS AGE
pod/web-759dfd9847-vhhr4 1/1 Running 0 4s
pod/web-759dfd9847-zm74n 1/1 Running 0 4s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/web 2/3 2 2 4s
# describe RS 查看事件:明确提示 exceeded quota
root@master30 ~ 11:27:58# kubectl describe rs web-759dfd9847
......
Events:
Warning FailedCreate 20s replicaset-controller Error creating: pods "web-759dfd9847-kks77" is forbidden: exceeded quota: myquota, requested: pods=1, used: pods=2, limited: pods=2
......
调大配额后恢复:用 kubectl patch 把 pods 上限改成 10,再 scale 副本即可:
root@master30 ~ 11:28:14# kubectl patch resourcequotas myquota -p '{"spec":{"hard":{"pods":10}}}'
resourcequota/myquota patched
root@master30 ~ 11:28:55# kubectl scale rs web-759dfd9847 --replicas 3
replicaset.apps/web-759dfd9847 scaled
root@master30 ~ 11:29:11# kubectl get pods
NAME READY STATUS RESTARTS AGE
web-759dfd9847-n8wtw 1/1 Running 0 1s
web-759dfd9847-vhhr4 1/1 Running 0 78s
web-759dfd9847-zm74n 1/1 Running 0 78s
# 清理
root@master30 ~ 11:29:12# kubectl delete deployments.apps web
deployment.apps "web" deleted
root@master30 ~ 11:29:18# kubectl delete resourcequotas myquota
resourcequota "myquota" deleted
3.2 计算资源配额(requests / limits)
配额同时声明 CPU/内存的 requests 与 limits 总量(YAML 方式,效果与命令行一致):
root@master30 ~ 11:29:22# vim resourcequota.yaml
root@master30 ~ 11:32:18# cat 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"
不写资源的 Pod 被拒:配额要求命名空间内每个 Pod 必须显式声明计算资源:
root@master30 ~ 11:32:59# 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
3.3 只限制 requests:单 Pod 超上限被拒
把配额改成只限制 requests(requests.cpu: 1000m、requests.memory: 2048Mi),分别测试超限与合规:
root@master30 ~ 11:33:51# kubectl delete resourcequotas myquota
resourcequota "myquota" deleted
root@master30 ~ 11:34:11# cat resourcequota.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: myquota
spec:
hard:
requests.cpu: 1000m
requests.memory: "2048Mi"
# 测试-Request:pod-request-1 请求 2000m/4096Mi,超过配额总上限
root@master30 ~ 11:34:45# 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
root@master30 ~ 11:34:53# #此时超上限了
# pod-request-2 请求 200m/1024Mi,合规 → 创建成功
root@master30 ~ 11:35:34# kubectl apply -f pod-request-2.yaml
pod/web created
root@master30 ~ 11:36:02# kubectl describe resourcequotas myquota
Name: myquota
Namespace: quota
Resource Used Hard
-------- ---- ----
requests.cpu 200m 1
requests.memory 1Gi 2Gi
# 清理
root@master30 ~ 11:36:10# kubectl delete pod web
pod "web" deleted
root@master30 ~ 11:36:40# kubectl delete resourcequotas myquota
resourcequota "myquota" deleted
3.4 只限制 limits:stress 压测触顶(OOMKilled)
root@master30 ~ 11:36:46# #用stress 进行压力测试
root@master30 ~ 11:37:30# cat resourcequota.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: myquota
spec:
hard:
limits.cpu: 1000m
limits.memory: "2048Mi"
CPU 压测(pod-limit-cpu.yaml,stress -c 1,与后面 pod-without-limits.yaml 同结构):
root@master30 ~ 11:38:09# kubectl apply -f pod-limit-cpu.yaml
pod/stress created
root@master30 ~ 11:38:18# kubectl top pods
NAME CPU(cores) MEMORY(bytes)
stress 201m 0Mi
root@master30 ~ 11:39:24# kubectl delete pod stress --force
pod "stress" force deleted
内存压测(pod-limit-memory.yaml,stress 压内存超过 Pod limit):
root@master30 ~ 11:40:09# kubectl apply -f pod-limit-memory.yaml
pod/stress created
root@master30 ~ 11:40:20# kubectl get pods -w
NAME READY STATUS RESTARTS AGE
stress 0/1 CrashLoopBackOff 1 (4s ago) 8s
stress 1/1 Running 2 (16s ago) 20s
stress 0/1 OOMKilled 2 (17s ago) 21s
^C
root@master30 ~ 11:40:49# #发现pod状态为oomkilled,进行restart
内存超过 limit 后容器被 OOMKilled,kubelet 自动重启(CrashLoopBackOff 循环)。清理:
root@master30 ~ 11:41:04# kubectl delete pod stress --force
root@master30 ~ 11:41:13# kubectl delete resourcequotas myquota
四、LimitRange 默认限额
LimitRange 为命名空间内未声明 resources 的 Pod/容器自动注入默认值,并约束 min/max 范围,与 ResourceQuota(总量)互补。
4.1 创建 LimitRange
root@master30 ~ 11:41:19# vim limits.yaml
root@master30 ~ 11:41:42# cat 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
root@master30 ~ 11:41:51# kubectl get limitranges
NAME CREATED AT
mylimit 2026-08-13T03:41:51Z
root@master30 ~ 11:41:56# 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 -
4.2 不写 resources 的 Pod 自动获得默认值
root@master30 ~ 11:42:09# vim pod-without-limits.yaml
root@master30 ~ 11:43:56# cat 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']
root@master30 ~ 11:44:03# kubectl apply -f pod-without-limits.yaml
pod/stress created
root@master30 ~ 11:44:28# kubectl top pods
NAME CPU(cores) MEMORY(bytes)
stress 499m 0Mi
root@master30 ~ 11:44:29# #创建出来的pod的resources 与 limitranage 指定的相关默认值一致
root@master30 ~ 11:44:37# kubectl get pod stress -o yaml
......
kubernetes.io/limit-ranger: 'LimitRanger plugin set: cpu, memory request for container stress; cpu, memory limit for container stress'
......
containers:
- args:
- -c
- "1"
image: hub.laoma.cloud/progrium/stress
imagePullPolicy: IfNotPresent
name: stress
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 200m
memory: 256Mi
......
关键验证:Pod 未写 resources,
LimitRanger自动注入了default(limits cpu 500m / memory 512Mi)和defaultRequest(requests cpu 200m / memory 256Mi),与 LimitRange 定义完全一致。清理:
root@master30 ~ 11:44:58# kubectl delete limitranges mylimit
limitrange "mylimit" deleted
root@master30 ~ 11:45:06# kubectl delete pod web --force
# 午休后继续:清理 stress 与 quota 命名空间
root@master30 ~ 13:34:41# kubectl delete ns quota
namespace "quota" deleted
五、探针实战(一):liveness 存活探针 httpGet
存活探针失败时 kubelet 会重启容器(RESTARTS +1)。本实验用 httpd 镜像,删掉首页文件制造故障。
5.1 基线:正常访问与制造故障
root@master30 ~ 13:55:39# kubectl create ns health
namespace/health created
root@master30 ~ 13:55:48# kubectl config set-context --current --namespace health
Context "kubernetes-admin@kubernetes" modified.
root@master30 ~ 13:55:52# kubectl run web --image=hub.laoma.cloud/library/httpd --image-pull-policy=IfNotPresent
pod/web created
root@master30 ~ 13:56:31# kubectl describe pod web|grep IP
IP: 10.224.195.155
root@master30 ~ 13:56:44# curl 10.224.195.155
<html><body><h1>It works!</h1></body></html>
# 删除首页文件(注意 -- 是 kubectl exec 的参数分隔符,-rm / --rm 都是错误写法)
root@master30 ~ 13:57:43# kubectl exec web -- rm -f htdocs/index.html
root@master30 ~ 13:57:49# curl 10.224.195.155
<!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 ~ 13:58:06# kubectl delete pod web --force
pod "web" force deleted
没有探针时:删了首页文件 Pod 照常 Running(只是访问变成目录列表)。加上 liveness 探针后,容器会被自动重启修复。
5.2 httpGet liveness 配置
root@master30 ~ 13:58:13# vim deploy-httpGet-liveness.yaml
root@master30 ~ 13:59:30# cat 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
root@master30 ~ 13:58:56# kubectl apply -f deploy-httpGet-liveness.yaml
deployment.apps/web created
root@master30 ~ 13:59:12# kubectl describe pod web-7dfcbbb5df-dx8jx |grep IP
IP: 10.224.83.170
root@master30 ~ 13:59:50# curl 10.224.83.170
<html><body><h1>It works!</h1></body></html>
5.3 制造故障:删除首页 → RESTARTS 变为 1
root@master30 ~ 14:00:20# kubectl exec web-7dfcbbb5df-dx8jx -- bash -c 'rm htdocs/index.html'
# 连续观察:RESTARTS 变为 1(容器已被 liveness 探针杀掉并重启)
root@master30 ~ 14:01:04# kubectl get pods
NAME READY STATUS RESTARTS AGE
web-7dfcbbb5df-dx8jx 1/1 Running 1 (8s ago) 2m5s
# 容器重启后首页文件被镜像重新还原,再次访问恢复正常
root@master30 ~ 14:01:06# curl 10.224.83.170
<html><body><h1>It works!</h1></body></html>
root@master30 ~ 14:01:17# kubectl delete deployments.apps web
deployment.apps "web" deleted
探针配置说明:
initialDelaySeconds: 5(启动 5s 后再探测)、periodSeconds: 5(每 5s 探测一次)、failureThreshold: 3(连续 3 次失败才重启)、timeoutSeconds: 10(单次探测超时)。
六、探针实战(二):liveness exec 与 tcpSocket
6.1 exec 探针:httpd 检查首页文件存在
root@master30 ~ 14:07:33# vim deploy-exec-liveness.yaml
root@master30 ~ 14:36:48# cat 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 ~ 14:36:53# kubectl apply -f deploy-exec-liveness.yaml
deployment.apps/web created
# 删首页 → 探针 cat 失败 → 容器重启
root@master30 ~ 14:37:07# kubectl exec web-546966967b-jgbz7 -- bash -c 'rm htdocs/index.html'
root@master30 ~ 14:37:30# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-546966967b-jgbz7 1/1 Running 0 31s
6.2 exec 探针:busybox 定时销毁 /tmp/healthy
先导出 busybox 模板,再改造成“创建 /tmp/healthy → 10 秒后删除”的测试 Pod:
root@master30 ~ 14:37:35# kubectl run busybox --image=busybox --image-pull-policy=IfNotPresent -o yaml --dry-run=client > busybox.yaml
root@master30 ~ 14:38:36# vim deploy-exec-busybox.yaml
root@master30 ~ 14:41:48# cat deploy-exec-busybox.yaml
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
root@master30 ~ 14:39:12# kubectl apply -f deploy-exec-busybox.yaml
pod/busybox created
第一次 apply 的版本没写
args,busybox 启动即退出,Pod 一直CrashLoopBackOff;补上args(touch healthy → 10s 后删除)后正常。在另一个终端观察事件:
root@master30 ~ 14:42:24# kubectl get pods
NAME READY STATUS RESTARTS AGE
busybox 1/1 Running 0 3s
web-546966967b-jgbz7 1/1 Running 1 (4m42s ago) 5m23s
root@master30 ~ 14:43:04# kubectl describe pod busybox |tail -10
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 2m39s default-scheduler Successfully assigned health/busybox to worker31.ningcode.cn
Normal Pulled 49s (x3 over 2m39s) kubelet Container image "busybox" already present on machine
Normal Created 49s (x3 over 2m39s) kubelet Created container busybox
Normal Started 49s (x3 over 2m39s) kubelet Started container busybox
Warning Unhealthy 24s (x9 over 2m24s) kubelet Liveness probe failed: ls: /tmp/healthy: No such file or directory
Normal Killing 24s (x3 over 2m14s) kubelet Container busybox failed liveness probe, will be restarted
root@master30 ~ 14:45:30# kubectl delete pod busybox --force
pod "busybox" force deleted
6.3 tcpSocket 探针:检查 80 端口
root@master30 ~ 14:45:12# vim deploy-tcpSocket-liveness.yaml
root@master30 ~ 14:46:01# cat 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
root@master30 ~ 14:46:41# kubectl apply -f deploy-tcpSocket-liveness.yaml
deployment.apps/web configured
root@master30 ~ 14:47:03# kubectl get all
NAME READY STATUS RESTARTS AGE
pod/web-79869c84fd-8ghzf 1/1 Running 0 4s
......
root@master30 ~ 14:47:07# kubectl describe pods web-79869c84fd-8ghzf
......
httpd:
Container ID: containerd://e6c1939c2d2be10c410945214b5d4422fd1e51d89cde7199b1ac07800b7854d5
Image: hub.laoma.cloud/library/httpd
State: Running
Started: Thu, 13 Aug 2026 14:47:03 +0800
Ready: True
Restart Count: 0
Liveness: tcp-socket :80 delay=5s timeout=10s period=5s #success=1 #failure=3
......
describe输出里能看到探针定义:Liveness: tcp-socket :80 delay=5s timeout=10s period=5s #success=1 #failure=3。
七、探针实战(三):readiness 就绪探针
readiness 与 liveness 的区别:readiness 失败只把 Pod 标记为 NotReady(0/1)并从 Service Endpoints 摘除,不会重启容器;liveness 失败会重启容器。本实验用 3 副本 + Service 验证“流量只进 Ready Pod”。
7.1 配置 readiness(httpGet)并暴露 Service
root@master30 ~ 14:02:31# vim deploy-httpGet-readiness.yaml
root@master30 ~ 14:03:00# kubectl apply -f deploy-httpGet-readiness.yaml
deployment.apps/web created
root@master30 ~ 14:03:10# kubectl expose deployment web --port=80 --target-port=80
service/web exposed
root@master30 ~ 14:03:31# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
web ClusterIP 10.108.251.21 <none> 80/TCP 5s
root@master30 ~ 14:03:36# kubectl get pods
NAME READY STATUS RESTARTS AGE
web-5d6964b9f5-6kwvt 1/1 Running 0 33s
web-5d6964b9f5-9wj4q 1/1 Running 0 33s
web-5d6964b9f5-blz4z 1/1 Running 0 33s
deploy-httpGet-readiness.yaml与第 5 章的 httpGet liveness 结构一致,只是把livenessProbe换成readinessProbe(path/index.html、port 80),并把副本数改为 3(日志中该文件未 cat,按同结构整理)。
7.2 给每个 Pod 写入各自的首页内容
# 把 pod 名写进各自的 index.html,用于区分流量打到了谁
root@master30 ~ 14:03:43# 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 ~ 14:05:30# kubectl get endpoints web
NAME ENDPOINTS AGE
web 10.224.83.171:80,10.224.83.172:80,10.224.83.173:80 2m22s
7.3 故障注入:删掉一个 Pod 的首页 → 自动摘除
root@master30 ~ 14:06:12# kubectl exec -it web-5d6964b9f5-6kwvt -- rm -f htdocs/index.html
# 90 次访问统计:只剩两个 Pod 的响应(6kwvt 已被摘除)
root@master30 ~ 14:06:47# for i in {1..90};do curl -s 10.108.251.21;done|sort |uniq -c
45 web-5d6964b9f5-9wj4q
45 web-5d6964b9f5-blz4z
root@master30 ~ 14:06:59# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-5d6964b9f5-6kwvt 0/1 Running 0 3m59s
web-5d6964b9f5-9wj4q 1/1 Running 0 3m59s
web-5d6964b9f5-blz4z 1/1 Running 0 3m59s
关键输出:删首页的
6kwvt变成0/1(NotReady),但 RESTARTS 仍为 0(readiness 不重启容器);90 次 curl 全部命中两个 Ready Pod(各 45 次)。kubectl get endpoints web稍等片刻后同样只剩 2 个地址。清理:
root@master30 ~ 14:07:14# kubectl delete deployments.apps web
deployment.apps "web" deleted
root@master30 ~ 14:47:37# kubectl delete ns health
namespace "health" deleted
八、认证(一):创建用户与证书签发
目标:在 worker31 上生成用户
ningcode的私钥与 CSR,由 master30 用集群 CA 签发证书,再用证书+私钥配置独立的 kubeconfig 访问集群。命令提示符分别保留root@worker31(客户端)与root@master30(控制平面)时间戳。
8.1 客户端生成私钥与 CSR(worker31)
root@worker31 ~ 15:27:05# openssl genrsa -out ningcode.key 2048
root@worker31 ~ 15:27:37# openssl req -new -key ningcode.key -out ningcode.csr -subj '/CN=ningcode/O=kubernets'
CN=ningcode即 Kubernetes 用户名,O=kubernets是组织(日志原文如此,规范写法应为O=kubernetes)。顺手想再生成一个 servera 证书,但私钥没生成导致报错:
root@worker31 ~ 15:28:17# 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=ningcode@lab.example.com"
Could not open file or uri for loading private key from servera.key
40179013AB790000:error:16000069:STORE routines:ossl_store_get0_loader_int:unregistered scheme:../crypto/store/store_register.c:237:scheme=file
40179013AB790000:error:80000002:system library:file_open:No such file or directory:../providers/implementations/storemgmt/file_store.c:267:calling stat(servera.key)
# 把 CSR 发给 master30
root@worker31 ~ 15:30:32# scp ningcode.csr root@master30:
ningcode.csr 100% 915 1.7MB/s 00:00
8.2 master 用集群 CA 签发证书(master30)
root@master30 ~ 15:26:51# openssl x509 -req -in ningcode.csr \
-CA /etc/kubernetes/pki/ca.crt \
-CAkey /etc/kubernetes/pki/ca.key \
-CAcreateserial \
-out ningcode.crt -days 365
Certificate request self-signature ok
subject=CN = ningcode, O = kubernets
# 签发完成后把证书回传给 worker31
root@master30 ~ 15:43:03# scp ningcode.crt root@worker31:~
ningcode.crt 100% 1017 1.9MB/s 00:00
第一次签发用了
-days 1095(3 年),后面重签为-days 365(1 年)。签发对象就是集群 CA 文件/etc/kubernetes/pki/ca.crt+ca.key,只有 master 上有。
8.3 证书验证(worker31)
# 查看 CA 证书:Issuer/Subject 均为 CN = kubernetes,有效期 10 年
root@worker31 ~ 15:43:25# openssl x509 -in ca.crt -text -noout
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 5842064140364811621 (0x51132e62117e0d65)
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN = kubernetes
Validity
Not Before: Aug 5 05:43:04 2026 GMT
Not After : Aug 2 05:48:04 2036 GMT
Subject: CN = kubernetes
......
X509v3 extensions:
X509v3 Key Usage: critical
Digital Signature, Key Encipherment, Certificate Sign
X509v3 Basic Constraints: critical
CA:TRUE
......
# 查看用户证书:Issuer 是 kubernetes CA,Subject 是 CN=ningcode, O=kubernets
root@worker31 ~ 15:44:25# openssl x509 -in ningcode.crt -text -noout
Certificate:
Data:
Version: 1 (0x0)
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN = kubernetes
Validity
Not Before: Aug 13 07:43:03 2026 GMT
Not After : Aug 13 07:43:03 2027 GMT
Subject: CN = ningcode, O = kubernets
......
# 查看私钥
root@worker31 ~ 15:44:32# openssl rsa -in ningcode.key -text -noout
Private-Key: (2048 bit, 2 primes)
modulus:
00:b0:a6:45:f1:00:fb:17:6b:f1:36:5f:42:c1:da:
......
验证通过:
ningcode.crt由CN = kubernetes的集群 CA 签发,有效期 1 年。这样客户端就拿到了“身份凭证”。
九、认证(二):kubeconfig 配置
在 master30 上导出 admin 的 kubeconfig 作为模板,连同用户证书、CA 证书一起 scp 到 worker31,再在 worker31 上用
kubectl config set-*改造成 ningcode 的独立配置。
9.1 导出模板并下发(scp 排错)
# 导出 kubeconfig 模版
root@master30 ~ 15:32:48# kubectl config view > config.tpl
# 将kubeconfig模板、ningcode.crt和kubernetes的ca证书发给客户端
# 先打错用户名 roor@(密码认证被拒),又写错主机 client(解析不了),最后才成功:
root@master30 ~ 15:33:18# scp config.tpl ningcode.crt /etc/kubernetes/pki/ca.crt roor@worker31:~
roor@worker31's password:
Permission denied, please try again.
root@master30 ~ 15:34:59# scp config.tpl ningcode.crt /etc/kubernetes/pki/ca.crt root@client:~
ssh: Could not resolve hostname client: Temporary failure in name resolution
scp: Connection closed
root@master30 ~ 15:36:45# scp config.tpl ningcode.crt /etc/kubernetes/pki/ca.crt root@worker31:~
config.tpl 100% 453 1.1MB/s 00:00
ningcode.crt 100% 1017 1.3MB/s 00:00
ca.crt 100% 1107 1.2MB/s 00:00
9.2 set-cluster / set-credentials / set-context(worker31)
# worker31 直接创建独立的 config(导出的 config.tpl 实际未使用;注意模板是从 admin 视角导出的,自带 admin 用户,后面小节会说明)
# 注意:set-cluster 要在客户端节点(worker31)执行——在 master30 上执行会因找不到 ca.crt 报错
root@worker31 ~ 15:31:47# kubectl config set-cluster kubernetes --kubeconfig=config --certificate-authority=ca.crt --embed-certs
Cluster "kubernetes" set.
root@worker31 ~ 15:39:34# kubectl config set-credentials ningcode --kubeconfig=config --client-key=ningcode.key --client-certificate=ningcode.crt --embed-certs
User "ningcode" set.
root@worker31 ~ 15:40:26# kubectl config set-context ningcode --kubeconfig=config --namespace=default --cluster=kubernetes --user=ningcode
Context "ningcode" created.
中间误把一段“注释 + 命令块”直接粘贴到了终端(
rm -f config后把# 设置集群/用户/上下文/切换上下文整段执行),相当于把上面的命令重新跑了一遍,最终结果一致:Switched to context "ningcode"。三个子命令的含义:
set-cluster:写集群地址与 CA(--embed-certs把证书内容内嵌进文件)set-credentials:写用户私钥与客户端证书set-context:把“集群 + 用户 + 命名空间”组合成一个上下文
9.3 坑:server 地址为空 → localhost:8080 refused
# 第一次用新 config 访问:连的是 localhost:8080,被拒
root@worker31 ~ 15:44:35# kubectl get nodes --kubeconfig=config
E0813 15:44:39.441749 214295 memcache.go:265] couldn't get current server API group list: Get "http://localhost:8080/api?timeout=32s": dial tcp 127.0.0.1:8080: connect: connection refused
......
The connection to the server localhost:8080 was refused - did you specify the right host or port?
# 检查发现 server 地址是空的
root@worker31 ~ 15:44:39# cat config|grep server:
server: ""
# 用 vim 补上集群 API Server 地址
root@worker31 ~ 15:45:13# vim config
# server: https://master30.ningcode.cn:6443
root@worker31 ~ 15:46:41# kubectl get nodes --kubeconfig=config
NAME STATUS ROLES AGE VERSION
master30.ningcode.cn Ready control-plane 8d v1.30.2
worker31.ningcode.cn Ready <none> 8d v1.30.2
worker32.ningcode.cn Ready <none> 8d v1.30.2
root@worker31 ~ 15:46:44# #原因是config文件的server地址为空,没有指定master
原因:
set-cluster时没有携带--server=https://master30.ningcode.cn:6443,导致 config 里server为空,kubectl 只能退回到默认的 localhost:8080。补上 server 地址后一切正常。
9.4 以 ningcode 身份访问集群
root@worker31 ~ 15:49:33# kubectl config get-contexts --kubeconfig=config
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
* ningcode kubernetes ningcode default
# 注意:worker31 上没有默认 kubeconfig,不带 --kubeconfig 的 kubectl 全部报 localhost:8080 refused
root@worker31 ~ 16:49:08# kubectl get nodes
E0813 16:51:27.316100 248858 memcache.go:265] couldn't get current server API group list: Get "http://localhost:8080/api?timeout=32s": dial tcp 127.0.0.1:8080: connect: connection refused
......
# 带上 --kubeconfig=config 用 ningcode 身份建 Pod(此时 ningcode 已被授予 cluster-admin,见第十章)
root@worker31 ~ 16:52:10# kubectl run web --image=hub.laoma.cloud/library/httpd --image-pull-policy=IfNotPresent -n default --kubeconfig=config
pod/web created
root@worker31 ~ 16:53:08# kubectl get pod -n default --kubeconfig=config
NAME READY STATUS RESTARTS AGE
web 1/1 Running 0 17s
root@worker31 ~ 16:58:10# kubectl get pods -o wide --kubeconfig=config
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web 1/1 Running 0 6m 10.224.195.158 worker31.ningcode.cn <none> <none>
# 尝试用 ningcode.kubeconfig(master 上没生成该文件,报错)
root@worker31 ~ 17:00:01# kubectl get pod -n default --kubeconfig=ningcode.kubeconfig
error: stat ningcode.kubeconfig: no such file or directory
root@worker31 ~ 17:03:30# #这个 config 是 admin 的 kubeconfig,是集群超级管理员,跟 ningcode 用户没关系
最后的注释很关键:worker31 上这份
config是从 admin 模板改出来的,里面还保留着 admin 用户;当前使用的上下文是ningcode,真正生效的身份是 ningcode(此时有 cluster-admin 权限)。要做细粒度 RBAC 验证,需要先撤掉过大的授权。
十、授权:RBAC 角色与绑定
10.1 ClusterRoleBinding:快速授予集群管理员
root@master30 ~ 15:39:31# # 授权用户ningcode集群管理员角色,后续详细讲解角色管理
root@master30 ~ 15:41:07# kubectl create clusterrolebinding ningcode-admin --clusterrole=cluster-admin --user=ningcode
clusterrolebinding.rbac.authorization.k8s.io/ningcode-admin created
cluster-admin是集群内置的超级管理员 ClusterRole;绑定后 ningcode 对全集群有全部权限(第 9 章 worker31 能kubectl run web就是靠它)。下面开始精细化的 Role / RoleBinding。
10.2 创建 Role:get / list / watch Pod
# kubectl create role -h 帮助较长,省略 ......
# 先生成 YAML 看效果(dry-run)
root@master30 ~ 16:43:39# 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 ~ 16:43:51# kubectl create role pod-role --verb=get,list,watch --resource=pods -n default
role.rbac.authorization.k8s.io/pod-role created
root@master30 ~ 16:44:07# 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]
10.3 Role 限定资源名(resourceNames)
# 只允许 get 指定名字的 Pod(readablepod / anotherpod)
root@master30 ~ 16:44:13# kubectl create role pod-role --verb=get --resource=pods --resource-name=readablepod --resource-name=anotherpod
role.rbac.authorization.k8s.io/pod-role created
root@master30 ~ 16:45:47# kubectl get roles pod-role -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
creationTimestamp: "2026-08-13T08:44:31Z"
name: pod-role
namespace: auth
......
rules:
- apiGroups:
- ""
resourceNames:
- readablepod
- anotherpod
resources:
- pods
verbs:
- get
注意坑:第二次创建没有带
-n default,而当前 context 的命名空间是auth,所以这个带resourceNames的 pod-role 建到了auth命名空间(yaml 里namespace: auth)。默认命名空间里那个pod-role仍是 get/list/watch。
10.4 编辑 Role 增加 create 权限
root@master30 ~ 16:44:50# kubectl edit roles -n default pod-role
role.rbac.authorization.k8s.io/pod-role edited
root@master30 ~ 16:46:09# # 在verbs下添加相应权限 - create
用
kubectl edit把default命名空间里pod-role的 verbs 增加create。
10.5 RoleBinding:把用户绑到 Role
# 先生成 YAML(dry-run),再正式创建
root@master30 ~ 16:46:25# kubectl create rolebinding default-pod-ningcode -n default --role=pod-role --user=ningcode --dry-run=client -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
creationTimestamp: null
name: default-pod-ningcode
namespace: default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: pod-role
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: ningcode
root@master30 ~ 16:47:54# kubectl create rolebinding default-pod-ningcode -n default --role=pod-role --user=ningcode
rolebinding.rbac.authorization.k8s.io/default-pod-ningcode created
root@master30 ~ 16:48:37# kubectl get rolebindings -n default default-pod-ningcode
NAME ROLE AGE
default-pod-ningcode Role/pod-role 10s
root@master30 ~ 16:48:47# kubectl describe rolebindings -n default default-pod-ningcode
Name: default-pod-ningcode
Labels: <none>
Annotations: <none>
Role:
Kind: Role
Name: pod-role
Subjects:
Kind Name Namespace
---- ---- ---------
User ningcode
至此:用户
ningcode(default 命名空间)→ RoleBindingdefault-pod-ningcode→ Rolepod-role(pods get/list/watch/create)。第 9 章 worker31 上--kubeconfig=config的所有操作能成功,是因为当时 cluster-admin 绑定还在;做细粒度权限验证前应先删除ningcode-admin这个 clusterrolebinding,否则会被超级管理员权限覆盖。
10.6 清理 RBAC 资源
# 误以为绑定建在 kube-system,删除时 NotFound(实际绑定在 default 命名空间)
root@master30 ~ 16:52:31# kubectl delete rolebindings default-pod-laoma -n kube-system
Error from server (NotFound): rolebindings.rbac.authorization.k8s.io "default-pod-laoma" not found
root@master30 ~ 16:54:32# kubectl delete rolebindings.rbac.authorization.k8s.io default-pod-ningcode -n kube-system
Error from server (NotFound): rolebindings.rbac.authorization.k8s.io "default-pod-ningcode" not found
root@master30 ~ 16:55:04# kubectl get rolebindings.rbac.authorization.k8s.io
No resources found in auth namespace.
root@master30 ~ 16:56:27# kubectl get rolebindings -n default default-pod-ningcode
NAME ROLE AGE
default-pod-ningcode Role/pod-role 8m31s
# 在正确的命名空间删除
root@master30 ~ 16:57:08# kubectl delete rolebindings default-pod-ningcode -n default
rolebinding.rbac.authorization.k8s.io "default-pod-ningcode" deleted
root@master30 ~ 16:57:56# kubectl delete roles pod-role -n default
role.rbac.authorization.k8s.io "pod-role" deleted
十一、环境清理与恢复
| 资源 | 命令 | 说明 |
|---|---|---|
| HPA 命名空间 | kubectl delete ns web |
10:41 清理,回收 deployment/hpa/svc |
| 配额命名空间 | kubectl delete ns quota |
13:34 清理,回收 quota/limitrange/stress |
| 探针命名空间 | kubectl delete ns health |
14:47 清理 |
| RBAC 绑定 | kubectl delete rolebindings default-pod-ningcode -n default |
16:57 清理 |
| RBAC 角色 | kubectl delete roles pod-role -n default |
16:57 清理 |
| 认证命名空间 auth | 本次未删除(上下文切到 auth 后未回收) | 可 kubectl delete ns auth |
| 测试大文件 | rm -rf /www/big.img |
工作节点本地文件,实验后删除 |
提醒:
auth命名空间与ningcode-adminclusterrolebinding、worker31 上的ningcode.key/.csr/.crt/config在实验结束后仍保留,复现或继续实验可直接复用;若要彻底清理,需在 master30 删除命名空间与绑定,在 worker31 删除相关文件。
十二、常见错误与排查(重点)
以下全部来自本次实验的真实报错,按主题分组。
12.1 HPA / 压测专项
| 现象 | 原因 | 修复 |
|---|---|---|
kubectl get svc -a 报 unknown shorthand flag: 'a' |
-a 不是合法参数,应为 -A |
用 kubectl get svc -A |
apply deployment 报 namespaces "web" not found |
context 指向 web 命名空间,但 ns 还没创建 | 先 kubectl create namespace web 再 apply |
HPA 显示 memory: <unknown>/60% |
指标采集有延迟(metrics-server 拉取 + HPA 轮询) | 等 15–30 秒再 kubectl get hpa |
| 压测半天副本不增加(负载上不去) | 节点内存有限,实际使用率达不到 60% | 把 target 从 60% 调到 20%(本次改后 2→3) |
ab 报 Connection refused (111) |
Service 重建后 ClusterIP 变了,还在压旧 IP | kubectl get svc 拿新 IP 重新压 |
| ab 压测输出刷屏 | 循环压测日志太长 | 后台运行 & 或重定向到文件,只看监控窗口 |
12.2 配额 / 限额专项
| 现象 | 原因 | 修复 |
|---|---|---|
| Deployment 3 副本只起 2 个 | ResourceQuota 限制 pods=2 |
kubectl describe rs 看 FailedCreate 事件;kubectl patch resourcequotas myquota -p '{"spec":{"hard":{"pods":10}}}' 调大配额 |
创建 Pod 报 must specify limits.cpu/limits.memory/requests.cpu/requests.memory |
配额声明了计算资源,要求 Pod 必须显式声明 | 在 Pod 的 resources 里补上 requests/limits |
Pod 报 exceeded quota: requested: requests.cpu=2... limited: requests.cpu=1 |
单个 Pod 的请求超过配额总上限 | 减小 Pod 请求,或调大配额 |
stress 容器 OOMKilled + CrashLoopBackOff |
压测内存超过 limit(被内核 OOM 杀掉) | 减小压测参数;这是预期演示效果 |
kubectl top pods 报 metrics not available yet |
metrics-server 尚未采集到数据 | 等几秒再查 |
12.3 探针专项
| 现象 | 原因 | 修复 |
|---|---|---|
kubectl exec web -rm / --rm 报 unknown flag |
exec 的参数分隔符用错 | 用 kubectl exec web -- rm -f ...(-- 后才是容器内命令) |
liveness 失败后 RESTARTS 变为 1 |
探针连续失败,kubelet 重启容器 | 检查探针 path/port/command 是否正确,容器内是否真的可探测 |
| busybox 一直 CrashLoopBackOff | busybox 没有常驻进程,启动即退出 | args 里加 sleep 保活(本次 touch /tmp/healthy; sleep 10; rm -rf /tmp/healthy; sleep 100) |
探针事件 Liveness probe failed: ls: /tmp/healthy: No such file or directory |
exec 探测命令返回非 0 | 预期故障演示,容器会被自动重启 |
readiness 失败后 Pod 0/1 但 RESTARTS 不变 |
readiness 只影响就绪状态,不重启容器 | 正常现象;流量已从 endpoints 摘除 |
| readiness 摘除有延迟 | periodSeconds 5s × failureThreshold 3 | 稍等片刻再验证 endpoints |
12.4 认证 / kubeconfig / RBAC 专项
| 现象 | 原因 | 修复 |
|---|---|---|
openssl req 报 Could not open file or uri for loading private key from servera.key |
私钥文件不存在(没生成或名字打错) | 先 openssl genrsa -out servera.key 2048,再生成 CSR |
scp 用 roor@worker31 反复要密码 |
用户名打错 | 改为 root@worker31 |
scp 用 root@client 报 Could not resolve hostname client |
主机名写错(client 不存在) | 写真实主机名 worker31 |
master30 上 set-cluster 报 could not stat certificate-authority file ca.crt |
在 master 上执行了本应在客户端执行的命令,master 当前目录没有 ca.crt | 在 worker31(客户端)上执行 kubectl config set-cluster ... --certificate-authority=ca.crt |
kubectl get nodes --kubeconfig=config 报 localhost:8080 ... connection refused |
config 里 server: 为空(set-cluster 没带 --server) |
vim config 补 server: https://master30.ningcode.cn:6443 |
worker31 上不带 --kubeconfig 的 kubectl 全部 refused |
worker31 没有默认的 ~/.kube/config |
每次命令加 --kubeconfig=config,或 export KUBECONFIG=... |
--kubeconfig=ningcode.kubeconfig 报 no such file or directory |
master 上根本没生成 ningcode.kubeconfig 文件(scp 时也报 stat 失败) |
使用 worker31 自己组装的 config;或先在 master 上生成该文件再下发 |
| 删除 rolebinding 报 NotFound | 命名空间写错(绑定在 default,却去 kube-system 删) | kubectl get rolebindings -A 确认位置后再删 |
create role 第二次不带 -n,角色建到了 auth 命名空间 |
当前 context 的 namespace 是 auth | 创建资源显式加 -n default,或用 kubectl config set-context --current --namespace 切回 |
| ningcode 权限“看起来”比 Role 大 | cluster-admin 的 ClusterRoleBinding 还没删,覆盖了细粒度 Role | 验证细粒度权限前先 kubectl delete clusterrolebinding ningcode-admin |
kubectl get roles pod-role -o yaml 看到 namespace: auth |
查看的是 auth 里的同名 Role | 用 -n default 指定命名空间查看 |
12.5 操作安全提醒
| 现象 | 说明 |
|---|---|
输入 rm -rf / 后按 Tab 列出了根目录所有文件 |
Tab 补全会把 / 展开成根目录下所有路径,一旦回车后果不堪设想。本次是补全后接着输入 /www/big.img,实际执行的是 rm -rf /www/big.img;任何时候都不要对 rm -rf / 回车 |
十三、命令速查表与小结
13.1 命令速查表
| 场景 | 命令 |
|---|---|
| 创建内存 HPA | kubectl apply -f hpa-mem.yaml(kubectl get hpa 查看) |
| 监控 HPA 与用量 | watch -n 4 'kubectl get hpa;echo;kubectl top pods' |
| 制造下载压力 | ab -n 300000 -c 100 http://<svc-ip>/big.img 或循环 while true;do ...;sleep 1;done |
| 创建对象配额 | kubectl create quota myquota --hard=pods=2,services=3,secrets=5,persistentvolumeclaims=10 |
| 查看/调整配额 | kubectl get resourcequotas / kubectl describe quota myquota / kubectl patch resourcequotas myquota -p '{"spec":{"hard":{"pods":10}}}' |
| 创建 LimitRange | kubectl apply -f limits.yaml;kubectl get limitranges / kubectl describe limitranges mylimit |
| 探针三方式 | httpGet: path/port、exec: command、tcpSocket: port,配合 failureThreshold/periodSeconds 等 |
| 查看重启/事件 | kubectl get pods(RESTARTS)、kubectl describe pod <pod>(Events) |
| 生成密钥与 CSR | openssl genrsa -out ningcode.key 2048;openssl req -new -key ningcode.key -out ningcode.csr -subj '/CN=ningcode/O=kubernetes' |
| CA 签发证书 | openssl x509 -req -in ningcode.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out ningcode.crt -days 365 |
| 查看证书 | openssl x509 -in ningcode.crt -text -noout |
| 配置 kubeconfig | kubectl config set-cluster/set-credentials/set-context --kubeconfig=config --embed-certs |
| 指定配置访问 | kubectl get nodes --kubeconfig=config |
| 授权 cluster-admin | kubectl create clusterrolebinding ningcode-admin --clusterrole=cluster-admin --user=ningcode |
| 创建 Role | kubectl create role pod-role --verb=get,list,watch --resource=pods -n default |
| Role 限定资源名 | kubectl create role pod-role --verb=get --resource=pods --resource-name=readablepod --resource-name=anotherpod |
| 绑定用户 | kubectl create rolebinding default-pod-ningcode -n default --role=pod-role --user=ningcode |
| 查看 RBAC | kubectl get roles / rolebindings / clusterrolebindings;kubectl describe roles <名> -n <ns> |
| 生成 YAML 预览 | 上述 create 命令加 --dry-run=client -o yaml |
13.2 小结
- HPA 内存扩容要看 Utilization 基准:
averageUtilization是相对 Pod 内存限额的使用率,指标采集有延迟;实验环境负载压不上去时把 target 调低(60%→20%)即可观察 2→3 副本的扩容过程 - ResourceQuota 管“总量”,LimitRange 管“默认值”:Quota 超限会直接拒绝创建(FailedCreate/exceeded quota),Quota 声明了 requests/limits 后每个 Pod 必须显式声明计算资源;LimitRange 会给没写 resources 的 Pod 自动注入 default/defaultRequest
- liveness 与 readiness 分工不同:liveness 失败 → kubelet 重启容器(RESTARTS+1);readiness 失败 → Pod 变 0/1 并从 Service Endpoints 摘除,流量只到 Ready Pod;两者都支持 httpGet / exec / tcpSocket 三种探测
- 认证三件套:客户端
genrsa + req生成密钥与 CSR → master 用集群 CAopenssl x509 -req签发 → kubeconfig 里set-cluster / set-credentials / set-context组装身份;--embed-certs把证书内嵌进文件 - kubeconfig 常见坑:
set-cluster忘记--server会退化成 localhost:8080;--kubeconfig文件名必须真实存在;worker 节点默认没有 kubeconfig,命令都要带--kubeconfig - RBAC 授权链路:Role(命名空间级)或 ClusterRole(集群级)→ RoleBinding/ClusterRoleBinding 绑定 User/ServiceAccount;权限验证前先删掉过大的 cluster-admin 绑定,否则细粒度 Role 形同虚设
本手册基于
master30_2026-08-13_10_13_37.log+worker31_2026-08-13_15_27_14.log整理,命令时间戳均为原始记录。
更多推荐
所有评论(0)