Kubernetes 网络策略与调度实战笔记(NetworkPolicy · 亲和/反亲和 · 污点容忍 · HPA)
Kubernetes 网络策略与调度实战笔记(NetworkPolicy · 亲和/反亲和 · 污点容忍 · HPA)
本手册由
kaitou.txt(会话开头未录制部分,09:27–10:27)+master30_2026-08-12_10_27_42.log(10:27–17:22)合并整理而成,在 kubeadm v1.30.2 集群(master30 + worker31/32,运行第 7 天)上完成。
三条主线:① NetworkPolicy 网络策略(podSelector / namespaceSelector / ipBlock / 端口 / 默认拒绝)→ ② 调度进阶(nodeName / nodeSelector / 亲和与反亲和 / 污点与容忍)→ ③ Metrics-Server 与 HPA 弹性伸缩。命令提示符保留真实时间戳,报错与修复全部来自实际操作;
📑 目录
| # | 章节 | 核心内容 |
|---|---|---|
| 一 | 整体框架与实验总览 | 实验流程图、节点环境、主题速览 |
| 二 | 实验准备:命名空间、Pod、NodePort 与集群 DNS | web/ningcode 命名空间、web1/web2、跨命名空间解析 |
| 三 | NetworkPolicy 基础:podSelector 入站控制 | 只放行指定标签 Pod、标签改写的坑 |
| 四 | NetworkPolicy:namespaceSelector 与跨命名空间 | 命名空间标签、project=myproject、DNS 解析限制 |
| 五 | NetworkPolicy:ipBlock 与端口控制 | 网段放行/排除、端口范围、ICMP 被拒 |
| 六 | NetworkPolicy 默认策略:出入站四组合 | 默认允许/拒绝 Ingress、Egress、全拒绝 |
| 七 | 调度基础:nodeName 与 nodeSelector | 指定节点、节点标签、删除 nodeSelector |
| 八 | 节点亲和:nodeAffinity | required + preferred、标签与 Pending 的关联 |
| 九 | Pod 亲和与反亲和 | podAffinity / podAntiAffinity / 多条件组合 |
| 十 | 污点与容忍:taint / tolerations | NoSchedule / NoExecute、FailedScheduling 事件 |
| 十一 | Metrics-Server 部署与资源监控 | components.yaml、改镜像、kubectl top |
| 十二 | HPA 弹性伸缩实战 | autoscale、资源限额、ab 压测、缩容回弹 |
| 十三 | 环境清理与恢复 | 删除命名空间、清理标签与污点 |
| 十四 | 常见错误与排查(重点) | 本次实验全部报错:原因 + 修复 |
| 十五 | 命令速查表与小结 | NetworkPolicy / 调度 / HPA 常用命令 |
一、整体框架与实验总览
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 - 私有镜像仓库
hub.laoma.cloud(nginx 镜像hub.laoma.cloud/library/nginx,节点上已缓存,用--image-pull-policy=IfNotPresent秒起) - 网络插件 Calico,支持 NetworkPolicy;本次 kubectl 上下文按实验分别在
web、scheduler命名空间间切换
1.3 主题速览
| 主题 | 核心知识点 | 时间范围 |
|---|---|---|
| 实验准备 | NodePort、集群内/跨命名空间 DNS(web1.web) | 09:27–10:27(kaitou.txt) |
| NetworkPolicy | podSelector / namespaceSelector / ipBlock / 端口 / 默认策略 | 10:27–12:13 |
| 调度基础 | nodeName 指定节点、nodeSelector 节点标签 | 12:13–13:45 |
| 亲和/反亲和 | nodeAffinity、podAffinity、podAntiAffinity、多条件 | 13:45–15:03 |
| 污点与容忍 | NoSchedule / NoExecute、FailedScheduling | 15:03–15:59 |
| 弹性伸缩 | Metrics-Server、kubectl top、HPA、ab 压测 | 17:03–17:22 |
二、实验准备:命名空间、Pod、NodePort 与集群 DNS
本段命令来自
kaitou.txt(日志没录上的开头部分),时间 09:27–10:27。
2.1 建命名空间并切换上下文
root@master30 ~ 09:27:43# kubectl create ns web
namespace/web created
# 想用 kubens 插件,但没装
root@master30 ~ 10:21:31# kubens web
-bash: kubens: command not found
# 标准做法:直接改当前上下文的命名空间
root@master30 ~ 10:21:35# kubectl config set-context --current --namespace web
Context "kubernetes-admin@kubernetes" modified.
2.2 创建 web1 / web2 两个 Pod
root@master30 ~ 10:22:04# kubectl run web1 --image=hub.laoma.cloud/library/nginx --image-pull-policy=IfNotPresent
pod/web1 created
root@master30 ~ 10:22:41# kubectl exec -it web1 -- bash -c 'echo Hello web1 > /usr/share/nginx/html/index.html'
root@master30 ~ 10:22:52# kubectl run web2 --image=hub.laoma.cloud/library/nginx --image-pull-policy=IfNotPresent
pod/web2 created
root@master30 ~ 10:23:02# kubectl exec -it web2 -- bash -c 'echo Hello web2 > /usr/share/nginx/html/index.html'
2.3 用 NodePort 暴露服务
root@master30 ~ 10:23:33# kubectl expose pod web1 --port=80 --target-port=80 --type=NodePort
service/web1 exposed
root@master30 ~ 10:24:04# kubectl expose pod web2 --port=80 --target-port=80 --type=NodePort
service/web2 exposed
root@master30 ~ 10:24:13# kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
web1 NodePort 10.104.31.85 <none> 80:30280/TCP 13s
web2 NodePort 10.110.240.75 <none> 80:30167/TCP 4s
- 集群外可通过任意节点
IP:30280访问 web1(后面 NetworkPolicy 实验会用到 30280)
2.4 集群内:用 Service 短名访问
# 建一个测试 Pod(同命名空间 web)
root@master30 ~ 10:24:17# kubectl run test --image=hub.laoma.cloud/library/nginx
pod/test created
root@master30 ~ 10:24:42# kubectl exec -it test -- curl -s web1
Hello web1
root@master30 ~ 10:24:55# kubectl exec -it test -- curl -s web2
Hello web2
2.5 跨命名空间:服务名.命名空间名
root@master30 ~ 10:24:58# kubectl create ns ningcode
namespace/ningcode created
# 第一次漏了 --image
root@master30 ~ 10:25:12# kubectl run test -n ningcode -- curl -s web1.web
error: required flag(s) "image" not set
root@master30 ~ 10:26:43# kubectl run test -n ningcode --image=hub.laoma.cloud/library/nginx
pod/test created
# 跨命名空间 DNS 规则:web1.web = 服务 web1 + 命名空间 web
root@master30 ~ 10:26:54# kubectl exec -n ningcode test -- curl -s web1.web
Hello web1
root@master30 ~ 10:27:31# kubectl exec -n ningcode test -- curl -s web2.web
Hello web2
2.6 要点
- 集群内 DNS:同命名空间用
服务名,跨命名空间用服务名.命名空间名(web1.web 中 web 是命名空间名,容易看错) - 切命名空间标准做法是
kubectl config set-context --current --namespace xxx(kubens 需要额外安装) kubectl run的--后面是容器命令,镜像必须用--image单独指定
三、NetworkPolicy 基础:podSelector 入站控制
3.1 概念
- NetworkPolicy 是集群内部防火墙,由网络插件(Calico)执行,白名单模式:不写策略默认全放行;一旦有策略命中某个 Pod,未放行的流量全部被拒
podSelector:策略作用于哪些 Pod(目标);from/to:放行哪些来源 / 去向policyTypes声明只控制 Ingress(入站)还是 Egress(出站)
3.2 实验环境
日志主体从 10:27 开始,先用 NodePort 验证集群外访问,再写第一个策略:
root@master30 ~ 10:27:49# curl 10.104.31.85
Hello web1
root@master30 ~ 10:28:52# curl 10.110.240.75
Hello web2
3.3 第一个策略:只放行 run=test 的 Pod 访问 web1 的 80 端口
# netpol.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: my-network-policy
spec:
podSelector:
matchLabels:
run: web1
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
run: test
ports:
- protocol: TCP
port: 80
root@master30 ~ 10:34:23# kubectl apply -f netpol.yaml
networkpolicy.networking.k8s.io/my-network-policy created
# 资源类型简写 netpol
root@master30 ~ 10:34:36# kubectl get netpo
error: the server doesn't have a resource type "netpo"
root@master30 ~ 10:34:42# kubectl get netpol
NAME POD-SELECTOR AGE
my-network-policy run=web1 8s
3.4 验证:放行名单内的才能访问
# test(run=test,同命名空间)能访问 web1
root@master30 ~ 10:34:44# kubectl exec test -- curl -s web1
Hello web1
root@master30 ~ 10:35:25# kubectl exec test -- curl -s web2
Hello web2 # web2 没有策略,不受影响
# 自身访问不受影响;web2(run=web2,不在放行名单)被拦
root@master30 ~ 10:36:07# kubectl exec web1 -- curl -s web1
Hello web1
root@master30 ~ 10:44:32# kubectl exec web2 -- curl -s web1
^C # 挂起 → 被 NetworkPolicy 拦截
# 跨命名空间:ningcode 里的 test 标签虽是 run=test,但 podSelector 只匹配策略同命名空间的 Pod
root@master30 ~ 10:45:24# kubectl exec test -n ningcode -- curl -s --connect-timeout 3 web1.web
command terminated with exit code 28 # 超时
root@master30 ~ 10:46:18# #不同namespace中pod,标签不满足无法访问
3.5 把标签改掉:流量立刻被拦
# test 标签原来是 run=test(放行名单),改成 run=web2 后不再满足条件
root@master30 ~ 10:46:40# kubectl label pod test run=web2 --overwrite
pod/test labeled
root@master30 ~ 10:47:15# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
test 1/1 Running 0 22m run=web2
web1 1/1 Running 0 24m run=web1
web2 1/1 Running 0 24m run=web2
# 标签一改,立刻超时
root@master30 ~ 10:47:22# kubectl exec test -- curl -s --connect-timeout 3 web1
command terminated with exit code 28
印证:NetworkPolicy 按来源 Pod 的标签实时判断,标签变了策略立刻生效。
3.6 放行所有 Pod:podSelector: {}
# netpol.yaml(v2)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: my-network-policy
spec:
podSelector:
matchLabels:
run: web1
policyTypes:
- Ingress
ingress:
- from:
- podSelector: {}
ports:
- protocol: TCP
port: 80
root@master30 ~ 10:48:48# kubectl apply -f netpol.yaml
networkpolicy.networking.k8s.io/my-network-policy configured
root@master30 ~ 10:48:59# kubectl exec test -- curl -s web1
Hello web1
# 注意 -- 与 curl 之间要有空格
root@master30 ~ 10:49:12# kubectl exec web2 --curl -s web1
error: unknown flag: --curl
root@master30 ~ 10:49:52# kubectl exec web2 -- curl -s web1
Hello web1 # 所有 Pod 都能访问 web1:80
3.7 要点
podSelector: {}表示放行所有来源 Pod(注意作用目标是 web1,来源是空选择器)- 策略是"白名单":从
run=test精确匹配换成{}全放行,验证时看的是当前生效的版本,改 YAML 忘了 apply 是排查时最常见的错觉 kubectl get netpol可看策略列表;kubectl describe netpol可看规则详情
四、NetworkPolicy:namespaceSelector 与跨命名空间
4.1 按命名空间标签放行
podSelector 只能匹配同命名空间的 Pod;要放行其他命名空间的 Pod,用 namespaceSelector(按命名空间的标签匹配):
# netpol.yaml(v3)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: my-network-policy
namespace: web
spec:
podSelector:
matchLabels:
run: web1
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
project: myproject
ports:
- protocol: TCP
port: 80
root@master30 ~ 11:06:29# kubectl apply -f netpol.yaml
networkpolicy.networking.k8s.io/my-network-policy configured
# 命名空间还没打标签 → 本命名空间的 test 也被拦(命名空间标签不满足)
root@master30 ~ 11:06:37# kubectl exec test -- curl -s --connect-timeout 3 web1
command terminated with exit code 28
root@master30 ~ 11:08:39# #此时无法访问,必须要有ns为myproject才能访问
给命名空间打标签:
root@master30 ~ 11:09:00# kubectl label namespaces ningcode project=myproject
namespace/ningcode labeled
# ningcode 命名空间满足 project=myproject → 放行
root@master30 ~ 11:09:24# kubectl exec -n ningcode test -- curl -s web1.web
Hello web1
root@master30 ~ 11:09:43# kubectl exec -n ningcode test -- curl -s web2.web
Hello web2
4.2 放行所有命名空间:namespaceSelector: {}
# netpol.yaml(v4)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: my-network-policy
namespace: web
spec:
podSelector:
matchLabels:
run: web1
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector: {}
ports:
- protocol: TCP
port: 80
root@master30 ~ 11:10:01# kubectl apply -f netpol.yaml
networkpolicy.networking.k8s.io/my-network-policy configured
root@master30 ~ 11:10:07# kubectl exec test -- curl -s web1
Hello web1 # 本命名空间恢复访问
root@master30 ~ 11:11:48# kubectl exec web2 -- curl -s web1
Hello web1
4.3 DNS 解析的"假拦截"
# 跨命名空间用短名 web1 解析失败(DNS 不认短名,不是策略拦的)
root@master30 ~ 11:11:53# kubectl exec -n ningcode test -- curl -s web1
command terminated with exit code 6 # Could not resolve host
root@master30 ~ 11:12:09# #被dns限制了
# 用全限定名 web1.web.svc.cluster.local 就通了
root@master30 ~ 11:15:31# kubectl exec -n ningcode test -- curl -s web1.web.svc.cluster.local
Hello web1
4.4 要点
- 放行外部命名空间必须用
namespaceSelector;podSelector只匹配策略所在命名空间 namespaceSelector: {}放行所有命名空间;也可与podSelector组合(见第六章多条件)- 排查顺序:先确认能不能解析(exit 6 = DNS),再确认策略是否放行(exit 28 = 超时被拦)
- 命名空间标签用
kubectl label ns <名称> key=value打,kubectl get ns --show-labels查看
五、NetworkPolicy:ipBlock 与端口控制
5.1 按网段放行(ipBlock)
# netpol.yaml(v5)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: my-network-policy
namespace: web
spec:
podSelector:
matchLabels:
run: web1
policyTypes:
- Ingress
ingress:
- from:
- ipBlock:
# 放行 网段10.1.8.0/24
cidr: 10.1.8.0/24
# 不放行网段
except:
- 10.1.8.128/26
ports:
- protocol: TCP
port: 80
root@master30 ~ 11:40:37# kubectl apply -f netpol.yaml
networkpolicy.networking.k8s.io/my-network-policy configured
# 集群内 Pod(10.224.x.x)不在 10.1.8.0/24 内 → 全部被拦
root@master30 ~ 11:40:44# kubectl exec test -- curl -s web1
^C
root@master30 ~ 11:40:53# #访问失败
root@master30 ~ 11:40:56# kubectl exec -n ningcode test -- curl -s web1.web
^C
5.2 宿主机走 NodePort 验证
root@master30 ~ 11:41:46# curl http://10.1.8.30:30280
^C
root@master30 ~ 11:42:06# curl http://10.1.8.31:30280
^C
root@master30 ~ 11:42:10# curl http://10.1.8.32:30280
Hello web1
root@master30 ~ 11:42:12# curl http://10.1.8.32:30280
Hello web1
root@master30 ~ 11:42:14# # 理论上:10.1.8.0/24网段中主机都可以通过集群任意节点访问web1
现象与理论有出入:master30 / worker31 上 curl 会挂起(^C),worker32 上正常返回。ipBlock 策略作用于 Pod 入站,NodePort 流量要经过 kube-proxy DNAT 后再被策略检查,来源判断和路径相关——实际以 worker32 通过为准,其它节点卡住的情况值得再深挖(本节记录现象,供后续研究)。
5.3 端口控制与 ICMP
把策略改成"只放行来源 Pod 的 32000–32768 端口"(port + endPort 表示端口范围):
# netpol.yaml(v6)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: my-network-policy
namespace: web
spec:
podSelector:
matchLabels:
run: web1
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
run: test
ports:
- protocol: TCP
port: 32000
endPort: 32768
root@master30 ~ 12:11:06# cat netpol.yaml # 内容如上
root@master30 ~ 12:11:10# #限定端口范围
root@master30 ~ 12:11:21# vim netpol.yaml
用 ubuntu Pod 测 ping(标签 run=test,但 ICMP 不在策略放行的 TCP 32000–32768 内):
root@master30 ~ 11:43:22# kubectl run --rm -it ubuntu -l run=test --image ubuntu -- bash
# 容器内装 ping 工具
root@ubuntu:/# apt update
root@ubuntu:/# apt install -y iputils-ping
......
root@ubuntu:/# ping -c1 10.224.193.67
PING 10.224.193.67 (10.224.193.67) 56(84) bytes of data.
--- 10.224.193.67 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms
# ping 服务名也一样不通(Destination Port Unreachable = 策略丢弃了 ICMP)
root@ubuntu:/# ping web1 -c1
PING web1.web.svc.cluster.local (10.104.31.85) 56(84) bytes of data.
From web1.web.svc.cluster.local (10.104.31.85) icmp_seq=1 Destination Port Unreachable
--- web1.web.svc.cluster.local ping statistics ---
1 packets transmitted, 0 received, +1 errors, 100% packet loss, time 0ms
root@ubuntu:/# exit
pod "ubuntu" deleted
5.4 要点
ipBlock按来源/去向 IP 网段匹配,except排除部分网段;适用于"只允许办公网段访问"port: 起始端口 + endPort: 结束端口可以限定端口范围;协议可以是 TCP / UDP / SCTP- NetworkPolicy 是四层策略:只放行声明的协议+端口,ICMP(ping)没有声明就会被拒——排查"ping 不通但 curl 通"先想到这一点
kubectl run --rm -it加-l run=test可以临时造一个带标签的测试 Pod,用完自动删除
六、NetworkPolicy 默认策略:出入站四组合
白名单逻辑下,只要创建一条"空规则"策略就能实现默认拒绝/默认允许:
6.1 默认允许 / 拒绝组合
| 策略名 | YAML 关键字段 | 效果 |
|---|---|---|
| allow-all-ingress | ingress: [- {}] |
默认允许所有入站 |
| default-deny-ingress | 只有 policyTypes: [Ingress],无 ingress 规则 |
默认拒绝所有入站 |
| allow-all-egress | egress: [- {}] |
默认允许所有出站 |
| default-deny-egress | 只有 policyTypes: [Egress],无 egress 规则 |
默认拒绝所有出站 |
| default-deny-all | policyTypes: [Ingress, Egress],无规则 |
入站出站全拒绝 |
# 默认允许所有入站流量
root@master30 ~ 12:12:25# cat netpol.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-ingress
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- {}
# 默认拒绝所有入站流量(有 Ingress 就默认拒绝,不放任何规则)
root@master30 ~ 12:12:42# cat netpol.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
spec:
podSelector: {}
policyTypes:
- Ingress
# 默认允许所有出站流量
root@master30 ~ 12:12:58# cat netpol.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-egress
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- {}
# 默认拒绝所有出站流量
root@master30 ~ 12:13:13# cat netpol.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
spec:
podSelector: {}
policyTypes:
- Egress
# 默认拒绝所有入站和所有出站流量
root@master30 ~ 12:13:28# cat netpol.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
6.2 多条件规则:from 里三选一
同一组 ports 下可以写多个 from 条目,满足任意一个即放行(逻辑 OR):
# netpol.yaml(多条件规则)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: my-network-policy
namespace: web
spec:
podSelector:
matchLabels:
run: web1
policyTypes:
- Ingress
ingress:
- from:
- ipBlock:
cidr: 10.1.1.0/24
- namespaceSelector:
matchLabels:
project: myproject
- podSelector:
matchLabels:
run: test
ports:
- protocol: TCP
port: 80
- 含义:web1 的 80 端口,允许来自
10.1.1.0/24或 命名空间标签project=myproject或 同命名空间标签run=test的流量 - 注意:如果想把
namespaceSelector和podSelector变成"且"关系,要写进同一个from条目里(缩进层级不同,逻辑不同)
6.3 要点
- 默认拒绝套路:
podSelector: {}+policyTypes: [Ingress](或 Egress),规则留空 - 默认允许套路:在对应方向写一条空规则
- {} - 删除策略用
kubectl delete netpol <name>;实验做完kubectl delete ns web ningcode直接整体清理(见第十三章)
七、调度基础:nodeName 与 nodeSelector
7.1 概念
| 方式 | 作用 | 特点 |
|---|---|---|
nodeName |
直接指定节点名 | 硬绑定,绕过调度器,节点不存在时 Pod 一直 Pending |
nodeSelector |
按节点标签选择 | 匹配所有带该标签的节点,可多节点 |
nodeAffinity |
节点亲和表达式 | 支持 In/NotIn/Exists 等操作符,有 required / preferred 两种 |
实验用 scheduler 命名空间:
root@master30 ~ 12:13:43# kubectl create ns scheduler
namespace/scheduler created
root@master30 ~ 12:14:03# kubectl config set-context --current --namespace scheduler
Context "kubernetes-admin@kubernetes" modified.
7.2 nodeName 指定节点:写错主机名 → Pending
root@master30 ~ 12:14:07# kubectl create deployment webapp --image nginx --replicas 2 -o yaml --dry-run=client > deploy-with-nodeName.yaml
在生成的 YAML 里加 nodeName。第一次写成了 worker32.laoma.cloud(节点实际叫 worker32.ningcode.cn):
root@master30 ~ 12:15:28# kubectl apply -f deploy-with-nodeName.yaml
deployment.apps/webapp created
root@master30 ~ 12:15:36# kubectl get pods -o wide |awk '{print $1,$3,$7}'
NAME STATUS NODE
webapp-55c6b896bc-gp6mj Pending worker32.laoma.cloud
webapp-55c6b896bc-ptf9r Pending worker32.laoma.cloud
修正主机名后(并把镜像换成私有仓库,官方 nginx 拉取太慢):
# deploy-with-nodeName.yaml(关键段)
spec:
template:
spec:
# 添加nodeName配置
nodeName: worker32.ningcode.cn
containers:
- image: hub.laoma.cloud/library/nginx
name: nginx
root@master30 ~ 12:20:50# kubectl apply -f deploy-with-nodeName.yaml
deployment.apps/webapp created
root@master30 ~ 12:21:20# kubectl get pods -o wide | awk '{print $1,$3,$7}'
NAME STATUS NODE
webapp-8667d9c9c-7spk6 Running worker32.ningcode.cn
webapp-8667d9c9c-p6x2p Running worker32.ningcode.cn
7.3 nodeSelector:按节点标签调度
# 给 worker32 打标签 disktype=ssd
root@master30 ~ 13:40:49# kubectl label node worker32.ningcode.cn disktype=ssd
node/worker32.ningcode.cn labeled
root@master30 ~ 13:41:00# kubectl get node -L disktype
NAME STATUS ROLES AGE VERSION DISKTYPE
master30.ningcode.cn Ready control-plane 6d23h v1.30.2
worker31.ningcode.cn Ready <none> 6d22h v1.30.2
worker32.ningcode.cn Ready <none> 6d22h v1.30.2 ssd
把 deploy-with-nodeName.yaml 里的 nodeName 换成 nodeSelector(nodeSelector: {disktype: ssd}),apply 后 Pod 仍落在 worker32(唯一带 ssd 标签的节点):
root@master30 ~ 13:41:45# kubectl apply -f deploy-with-nodeName.yaml
deployment.apps/webapp configured
root@master30 ~ 13:41:57# kubectl get pods -o wide | awk '{print $1,$3,$7}'
NAME STATUS NODE
webapp-6d6d67484b-pcttl Running worker32.ningcode.cn
webapp-6d6d67484b-sfvmg Running worker32.ningcode.cn
删除节点标签后,已运行的 Pod 不受影响(调度是一次性的):
root@master30 ~ 13:42:59# kubectl label node worker32.ningcode.cn disktype-
node/worker32.ningcode.cn unlabeled
7.4 删除 Deployment 里的 nodeSelector
root@master30 ~ 13:44:24# # 删除deployment的nodeSelector
root@master30 ~ 13:44:52# kubectl patch deployment webapp --type json \
-p '[{"op":"remove","path":"/spec/template/spec/nodeSelector"}]'
deployment.apps/webapp patched
# 滚动更新后两个副本分散到不同节点
root@master30 ~ 13:45:02# kubectl get pods -o wide |awk '{print $1,$3,$7}'
NAME STATUS NODE
webapp-686868b8c6-58l2w Running worker32.ningcode.cn
webapp-686868b8c6-n6s5c Running worker31.ningcode.cn
7.5 要点
nodeName是硬指定:主机名写错 / 节点不存在时 Pod 永久 Pending,describe pod看不到调度事件(绕过了调度器)nodeSelector匹配"带标签的节点集合",更适合多节点按属性(磁盘、GPU、区域)分流- 删除 nodeSelector 用
kubectl patch deployment ... --type json -p '[{"op":"remove",...}]',模板一变就会触发滚动更新 - 判断节点主机名用
kubectl get nodes(本集群是worker31/32.ningcode.cn,不是 laoma.cloud)
八、节点亲和:nodeAffinity
8.1 YAML:required + preferred
# deploy-with-nodeAffinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: web
name: web
spec:
replicas: 2
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: CPU
operator: In
values:
- L1
- L2
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: MEM
operator: In
values:
- L1
- L2
containers:
- image: hub.laoma.cloud/library/nginx
name: nginx
requiredDuringScheduling...:硬要求,必须满足 CPU In (L1, L2),否则 PendingpreferredDuringScheduling...:软偏好,尽量满足 MEM In (L1, L2),满足不了也能调度(weight 越大优先级越高)
8.2 先 apply 后打标签:从 Pending 到 Running
节点还没有 CPU 标签,直接 apply → 两个副本都 Pending:
root@master30 ~ 14:00:16# kubectl apply -f deploy-with-nodeAffinity.yaml
deployment.apps/web created
root@master30 ~ 14:00:43# kubectl get pods
NAME READY STATUS RESTARTS AGE
web-c74fd5fbd-2lb9l 0/1 Pending 0 3s
web-c74fd5fbd-55m9w 0/1 Pending 0 3s
给节点打上标签后,调度器自动把 Pod 调度上去(required 条件被满足):
root@master30 ~ 14:00:46# kubectl label nodes worker31.ningcode.cn CPU=L1
node/worker31.ningcode.cn labeled
root@master30 ~ 14:01:19# kubectl label nodes worker32.ningcode.cn CPU=L2
node/worker32.ningcode.cn labeled
root@master30 ~ 14:01:25# kubectl get pods -o wide --no-headers |awk '{print $1,$3,$7}'
web-c74fd5fbd-2lb9l Running worker31.ningcode.cn
web-c74fd5fbd-55m9w Running worker31.ningcode.cn
8.3 观察 preferred 偏好的作用
再次重启(新 Pod 会重新调度),此时 worker32 补上 MEM=L2 标签:
root@master30 ~ 14:01:51# kubectl rollout restart deployment web
deployment.apps/web restarted
root@master30 ~ 14:02:09# kubectl get pods -o wide --no-headers |awk '{print $1,$3,$7}'
web-758688877c-nk5q7 Running worker31.ningcode.cn
web-758688877c-rzwcn Running worker32.ningcode.cn
# worker32 再补一个 MEM=L2(preferred 的匹配项)
root@master30 ~ 14:02:21# kubectl label nodes worker32.ningcode.cn MEM=L2
node/worker32.ningcode.cn labeled
root@master30 ~ 14:03:14# kubectl rollout restart deployment web
deployment.apps/web restarted
root@master30 ~ 14:03:17# kubectl get pods -o wide --no-headers |awk '{print $1,$3,$7}'
web-5c5dd94447-djkzl Running worker32.ningcode.cn
web-5c5dd94447-qp2bp Running worker32.ningcode.cn
两个节点都满足 required(CPU L1/L2),MEM=L2 的 worker32 成为 preferred 优先项,重启后两个副本都倾向它。
8.4 清理
root@master30 ~ 14:03:23# kubectl delete deployments.apps web
deployment.apps "web" deleted
root@master30 ~ 14:03:33# kubectl label nodes worker31.ningcode.cn CPU-
root@master30 ~ 14:03:48# kubectl label nodes worker32.ningcode.cn CPU-
root@master30 ~ 14:03:52# kubectl label nodes worker32.ningcode.cn MEM-
8.5 要点
- 亲和表达式支持
In / NotIn / Exists / DoesNotExist / Gt / Lt操作符,比 nodeSelector 的等值匹配灵活得多 - required 不满足 → Pending(和 nodeSelector 一样);preferred 只是"尽量",不影响最终可用性
- 节点标签随时可加可删(
kubectl label node xxx key-删除),但只影响之后的调度
九、Pod 亲和与反亲和
9.1 概念
podAffinity:新 Pod 倾向于和某些 Pod 同节点/同拓扑域(如:缓存和业务同机)podAntiAffinity:新 Pod 避免和某些 Pod 同节点(如:高可用副本分散到不同节点)- 必须指定
topologyKey:在哪个维度上"同"或"不同"(kubernetes.io/hostname= 节点,topology.kubernetes.io/zone= 可用区)
9.2 podAffinity:web2 跟着 web1 走
先给 worker31 打 zone 标签,再让 web2 亲和 web1:
# 注意:节点名写错会报 NotFound
root@master30 ~ 14:27:04# kubectl label node worker31.laoma.cloud topology.kubernetes.io/zone=v
Error from server (NotFound): nodes "worker31.laoma.cloud" not found
root@master30 ~ 14:28:54# kubectl label node worker31.ningcode.cn topology.kubernetes.io/zone=v
node/worker31.ningcode.cn labeled
# deploy-with-podaffinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web1
spec:
replicas: 1
selector: {matchLabels: {app: web1}}
template:
metadata: {labels: {app: web1}}
spec:
nodeName: worker31.ningcode.cn # web1 固定到 worker31
containers:
- name: web-server
image: hub.laoma.cloud/library/nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: web2
spec:
replicas: 1
selector: {matchLabels: {app: web2}}
template:
metadata: {labels: {app: web2}}
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values: [web1]
topologyKey: topology.kubernetes.io/zone
containers:
- name: web-server
image: hub.laoma.cloud/library/nginx
验证:web2 因为亲和 web1(且 zone=v 只有 worker31 有),被调度到 worker31:
root@master30 ~ 14:32:06# kubectl apply -f deploy-with-podaffinity.yaml
root@master30 ~ 14:32:24# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
web1-79958b69cb-5ggcq 1/1 Running 0 20s 10.224.195.139 worker31.ningcode.cn
web2-767459494f-rdjxt 1/1 Running 0 35s 10.224.195.140 worker31.ningcode.cn
(第一次实验时 web2 一直 Pending,因为 web1 的 nodeName 写成了不存在的 worker31.laoma.cloud,两个条件互相冲突,删掉旧 Pod 重来后正常——见第十四章)
9.3 podAntiAffinity:store 副本分散到不同节点
# deploy-with-podAntiAffinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: store
spec:
replicas: 3
selector: {matchLabels: {app: store}}
template:
metadata: {labels: {app: store}}
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values: [store]
topologyKey: "kubernetes.io/hostname"
containers:
- name: web-server
image: hub.laoma.cloud/library/nginx
root@master30 ~ 14:35:16# kubectl apply -f deploy-with-podAntiAffinity.yaml
deployment.apps/store created
# 3 个副本、3 个节点:前两个 Running,第 3 个 Pending(反亲和:不能和 store 同节点)
root@master30 ~ 14:36:15# kubectl get all
pod/store-bff9b9ffd-954l4 1/1 Running 0 40s
pod/store-bff9b9ffd-lr6jv 1/1 Running 0 40s
pod/store-bff9b9ffd-wnp2b 0/1 Pending 0 40s
集群只有 3 个节点,
required反亲和"每个节点最多一个 store"最多只能放 3 个副本,多出来的 Pending——这就是反亲和 required 的硬约束。
9.4 多条件组合:web-store = 反亲和自己 + 亲和 store
# deploy-with-multi-Affinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-store
spec:
replicas: 1
selector: {matchLabels: {app: web-store}}
template:
metadata: {labels: {app: web-store}}
spec:
affinity:
podAntiAffinity: # 自己不能和其他 web-store 同节点
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values: [web-store]
topologyKey: "kubernetes.io/hostname"
podAffinity: # 但必须和 store 同节点
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values: [store]
topologyKey: "kubernetes.io/hostname"
containers:
- name: web-app
image: hub.laoma.cloud/library/nginx
root@master30 ~ 14:39:05# kubectl apply -f deploy-with-multi-Affinity.yaml
deployment.apps/web-store created
# 扩容:store 只有 1 个副本(在 worker31),web-store 必须跟它同节点、又不能重复
root@master30 ~ 14:41:20# kubectl scale deployment store --replicas 1
root@master30 ~ 14:41:26# kubectl scale deployment web-store --replicas 2
root@master30 ~ 14:41:45# ## 在亲和性作用下,只有worker32节点具备满足条件的pod
root@master30 ~ 14:42:03# ## 在反亲和性作用下,worker32节点上已经运行了一个副本
结果:第 2 个 web-store 副本 Pending——它必须和 store 同节点,但那个节点已经有 web-store 副本(反亲和冲突),没有节点能同时满足两个 required 条件。
9.5 要点
podAffinity/podAntiAffinity的topologyKey决定"同/不同"的粒度:hostname 是节点级,zone 是可用区级- 两个 required 条件叠加可能无解 → Pod 一直 Pending,
kubectl describe pod看事件最直接 - 高可用部署用反亲和 required + 节点数 ≥ 副本数;业务就近放一起用亲和
- 副本多、节点少时,反亲和 required 会拒绝多余副本,这是预期行为
十、污点与容忍:taint / tolerations
10.1 概念
- **污点(taint)**打在节点上,让普通 Pod"不敢来";**容忍(toleration)**打在 Pod 上,“我受得了这个污点”
- 三种效果:
NoSchedule:不调度新 Pod(已在运行的不管)PreferNoSchedule:尽量不调度(软)NoExecute:不调度 + 驱逐已在运行的 Pod
10.2 打污点
# worker31:CPU=L1:NoSchedule;worker32:CPU=L2:NoSchedule
root@master30 ~ 15:05:54# kubectl taint nodes worker31.ningcode.cn CPU=L1:NoSchedule
node/worker31.ningcode.cn tainted
root@master30 ~ 15:50:09# kubectl taint nodes worker32.ningcode.cn CPU=L2:NoSchedule
node/worker32.ningcode.cn tainted
10.3 容忍度不匹配 → Pending
# deploy-with-tolerations.yaml(v1:容忍 CPU=L3,但污点是 L1/L2 → 不匹配)
apiVersion: apps/v1
kind: Deployment
metadata:
labels: {app: web}
name: web
spec:
replicas: 2
selector: {matchLabels: {app: web}}
template:
metadata: {labels: {app: web}}
spec:
tolerations:
- key: "CPU"
operator: "Equal"
value: "L3"
effect: "NoSchedule"
containers:
- name: nginx
image: hub.laoma.cloud/library/nginx
imagePullPolicy: IfNotPresent
root@master30 ~ 15:50:35# kubectl apply -f deploy-with-tolerations.yaml
deployment.apps/web created
root@master30 ~ 15:50:44# kubectl get pods
web-7d9d5f896c-k9nsc 0/1 Pending 0 4s
web-7d9d5f896c-r6wrt 0/1 Pending 0 4s
看事件,三个节点都有未匹配的污点:
root@master30 ~ 15:51:24# kubectl describe pod web-7d9d5f896c-qqjl2
...
Tolerations: CPU=L3:NoSchedule
node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Warning FailedScheduling 19s default-scheduler 0/3 nodes are available: 1 node(s) had untolerated taint {CPU: L1}, 1 node(s) had untolerated taint {CPU: ...
root@master30 ~ 15:51:42# # 事件表明3个节点上的污点与pod不匹配,所以无法创建pod
10.4 容忍度匹配 → 正常调度
把容忍值改成 L1(匹配 worker31 的 CPU=L1 污点):
# deploy-with-tolerations.yaml(v2:容忍 CPU=L1 的 NoSchedule 和 NoExecute)
spec:
template:
spec:
tolerations:
- key: "CPU"
operator: "Equal"
value: "L1"
effect: "NoSchedule"
- key: "CPU"
operator: "Equal"
value: "L1"
effect: "NoExecute"
root@master30 ~ 15:52:33# kubectl apply -f deploy-with-tolerations.yaml
deployment.apps/web created
root@master30 ~ 15:52:39# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
web-78945b5cc-87j8w 1/1 Running 0 5s 10.224.195.144 worker31.ningcode.cn
web-78945b5cc-tmb97 1/1 Running 0 5s 10.224.195.145 worker31.ningcode.cn
两个副本都落在 worker31(唯一能容忍 CPU=L1 污点的节点)。
10.5 多个污点 → 需要多个容忍
再给 worker31 加 MEM=L2 污点后,只有 CPU=L1 容忍就不够了(还要容忍 MEM=L2):
root@master30 ~ 15:55:09# kubectl taint nodes worker31.ningcode.cn MEM=L2:NoSchedule
# worker31 现在有 CPU=L1 和 MEM=L2 两个 NoSchedule 污点
root@master30 ~ 15:57:25# kubectl describe pod web-745f6fc44d-bhjt9
Events:
Warning FailedScheduling 27s default-scheduler 0/3 nodes are available: 1 node(s) had untolerated taint {CPU: }, 1 node(s) had untolerated taint {MEM: L2}, 1 node(...
补上 MEM=L2 的容忍后恢复:
# deploy-with-tolerations.yaml(v3:CPU=L1 NoSchedule/NoExecute + MEM=L2 NoSchedule)
spec:
template:
spec:
tolerations:
- key: "CPU"
operator: "Equal"
value: "L1"
effect: "NoSchedule"
- key: "CPU"
operator: "Equal"
value: "L1"
effect: "NoExecute"
- key: "MEM"
operator: "Equal"
value: "L2"
effect: "NoSchedule"
root@master30 ~ 15:58:24# kubectl apply -f deploy-with-tolerations.yaml
root@master30 ~ 15:58:30# kubectl get pods -o wide
web-65bfcb4fc-d6pw5 1/1 Running 0 8s 10.224.195.146 worker31.ningcode.cn
web-65bfcb4fc-qt448 1/1 Running 0 8s 10.224.195.147 worker31.ningcode.cn
10.6 清理污点
# 删除污点:key:Effect- 结尾
root@master30 ~ 15:58:38# kubectl taint nodes worker31.ningcode.cn CPU=L1:NoSchedule-
root@master30 ~ 15:59:05# kubectl taint nodes worker31.ningcode.cn CPU=L2:NoSchedule-
root@master30 ~ 15:59:11# kubectl taint nodes worker31.ningcode.cn MEM=L2:NoSchedule-
root@master30 ~ 15:59:20# kubectl taint nodes worker32.ningcode.cn CPU=L2:NoSchedule-
root@master30 ~ 15:59:27# kubectl taint nodes worker31.ningcode.cn CPU=L1:NoExecute-
10.7 要点
- 容忍度 = 污点 key + value + effect 全部匹配(Equal);只想"不管值"用
operator: Exists NoExecute不仅阻止调度,还会驱逐节点上不匹配的现有 Pod(本次只观察了 NoSchedule 场景)- Pod Pending 排查必看
kubectl describe pod的 Events:had untolerated taint {key: value}直接点名问题 - 删除污点是
kubectl taint node <节点> <key>:<effect>-,注意 key 不带 value 也能按 key 删
十一、Metrics-Server 部署与资源监控
11.1 下载清单并替换镜像
root@master30 ~ 15:59:49# wget -O components.yaml http://192.168.46.200/class/course-materials/softwares/stage03/metrics-server-components-v0.7.1.yaml
--2026-08-12 17:03:07-- http://192.168.46.200/class/course-materials/softwares/stage03/metrics-server-components-v0.7.1.yaml
components.yaml 100%[=========================================>] 4.24K --.-KB/s in 0s
root@master30 ~ 17:03:07# # 修改 Metrics-Server,不校验tls
root@master30 ~ 17:03:17# grep image: components.yaml
image: registry.k8s.io/metrics-server/metrics-server:v0.7.1
# 换成私有仓库
root@master30 ~ 17:04:47# sed -i 's/registry.k8s.io/hub.laoma.cloud/g' components.yaml
日志中还在 vim 里取消了 metrics-server 对 TLS 的校验(
--kubelet-insecure-tls),离线环境 kubelet 自签证书必须关校验,否则 metrics 采集失败。
11.2 部署并验证
root@master30 ~ 17:05:17# kubectl apply -f components.yaml
serviceaccount/metrics-server created
clusterrole.rbac.authorization.k8s.io/system:aggregated-metrics-reader created
clusterrole.rbac.authorization.k8s.io/system:metrics-server created
......
deployment.apps/metrics-server created
apiservice.apiregistration.k8s.io/v1beta1.metrics.k8s.io created
# 等 Pod Running(0/1 → 1/1)
root@master30 ~ 17:05:45# kubectl get pods -n kube-system |grep metric
metrics-server-5b98f887f4-w8f22 0/1 Running 0 30s
# 节点资源用量
root@master30 ~ 17:05:59# kubectl top node
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
master30.ningcode.cn 87m 2% 1757Mi 46%
worker31.ningcode.cn 37m 0% 967Mi 25%
worker32.ningcode.cn 44m 1% 999Mi 26%
# 命名空间内 Pod 资源用量
root@master30 ~ 17:06:10# kubectl top pods -n kube-system
NAME CPU(cores) MEMORY(bytes)
calico-kube-controllers-5b778fd8ff-cdsb9 2m 19Mi
coredns-5485cd7479-p6h4r 2m 11Mi
......
metrics-server-5b98f887f4-w8f22 4m 15Mi
11.3 调快 HPA 同步周期(可选优化)
# 给 kube-controller-manager 增加启动参数(静态 Pod 清单)
root@master30 ~ 17:06:22# vim /etc/kubernetes/manifests/kube-controller-manager.yaml
在 containers[0].command 列表里追加三个参数(日志注释"添加三行"):
- --horizontal-pod-autoscaler-sync-period=10s
- --horizontal-pod-autoscaler-initial-readiness-delay=20s
- --horizontal-pod-autoscaler-downscale-stabilization=60s
sync-period=10s:HPA 每 10 秒检查一次指标(默认 15s)initial-readiness-delay=20s:Pod 启动后 20 秒内不算未就绪downscale-stabilization=60s:缩容冷却 60 秒,防止抖动
改完 kube-controller-manager 会自动重启(静态 Pod),
kubectl get pods -n kube-system |grep controller确认新 Pod 起来即可。
11.4 要点
kubectl top的数据来自 metrics-server(Metrics API),没有它kubectl top会报错- 离线环境两个必改:镜像换私有仓库、
--kubelet-insecure-tls关校验 - HPA 依赖"每个 Pod 的 CPU 使用率",Pod 不写
resources.requests时 HPA 无法计算利用率
十二、HPA 弹性伸缩实战
12.1 创建 Deployment 并配置 HPA
root@master30 ~ 17:07:52# kubectl create deployment web --image=hub.laoma.cloud/library/nginx
deployment.apps/web created
# 2~5 副本,CPU 利用率目标 80%
root@master30 ~ 17:09:24# kubectl autoscale deployment web --max=5 --min=2 --cpu-percent=80
horizontalpodautoscaler.autoscaling/web autoscaled
# 导出 HPA 定义(tee 同时存文件)
root@master30 ~ 17:09:41# kubectl get hpa web -o yaml |tee hpa-cpu.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web
namespace: scheduler
spec:
maxReplicas: 5
metrics:
- resource:
name: cpu
target:
averageUtilization: 80
type: Utilization
type: Resource
minReplicas: 2
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web
# 刚创建时指标还没采集到,TARGETS 显示 <unknown>
root@master30 ~ 17:10:10# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
web Deployment/web cpu: <unknown>/80% 2 5 2 36s
12.2 关键:给 Pod 设置资源限额
HPA 计算的是"平均利用率 = 实际使用 / requests",Pod 没写 requests 时无法计算,所以用 edit 打开 Deployment 把 resources 取消注释并保存:
root@master30 ~ 17:10:17# kubectl edit deployments.apps web
root@master30 ~ 17:12:14# kubectl edit deployments.apps web
deployment.apps/web edited
root@master30 ~ 17:13:34# 修改pod设定资源限额
# 在容器里配置(取消注释后保存)
resources:
limits:
cpu: 500m
requests:
cpu: 100m
12.3 暴露 Service 并压测
root@master30 ~ 17:14:03# kubectl expose deployment web --port=80 --target-port=80
service/web exposed
root@master30 ~ 17:14:08# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
web ClusterIP 10.97.246.181 <none> 80/TCP 7s
# 安装压测工具 ab(apache2-utils)
root@master30 ~ 17:14:30# apt install -y apache2-utils
# 监控窗口看 HPA;压测窗口:100 并发循环压
root@master30 ~ 17:15:13# while true ;do ab -n 300000 -c 100 http://10.97.246.181/;sleep 1;done
This is ApacheBench, Version 2.3
Benchmarking 10.97.246.181 (be patient)
Completed 30000 requests
......
Concurrency Level: 100
Time taken for tests: 25.650 seconds
Complete requests: 96966
Failed requests: 9
Requests per second: 3780.38 [#/sec] (mean)
12.4 结果:自动扩容到 5,停压后缩回 2
压测中 HPA 把副本从 2 扩到 5(达到 maxReplicas),CPU 使用率回落到 17%:
# 监控窗口抓到的状态(压测中)
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
web Deployment/web cpu: 17%/80% 2 5 5 9m7s
# 5 个副本全部 Running
NAME READY STATUS RESTARTS AGE
web-7cf9fd6bcb-2fpvf 1/1 Running 0 2m17s
web-7cf9fd6bcb-6mvgj 1/1 Running 0 5m18s
web-7cf9fd6bcb-mgk5t 1/1 Running 0 96s
web-7cf9fd6bcb-wbchx 1/1 Running 0 5m17s
web-7cf9fd6bcb-xkwvt 1/1 Running 0 46s
停止压测后(^C),CPU 掉到 0%,等待缩容冷却(60s)后自动缩回 minReplicas=2:
root@master30 ~ 17:19:28# #最大副本为5个,停止压力测试,会清除pod
# 停压后
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
web Deployment/web cpu: 0%/80% 2 5 2 12m
NAME READY STATUS RESTARTS AGE
web-7cf9fd6bcb-mgk5t 1/1 Running 0 4m35s
web-7cf9fd6bcb-wbchx 1/1 Running 0 8m16s
12.5 要点
- HPA 工作流:metrics-server 采集 → 每 10s(默认 15s)算平均利用率 → 超出 target 扩容、低于 target 且过了冷却期缩容
- Pod 必须声明
resources.requests,否则 TARGETS 一直是<unknown>,HPA 不干活 - 扩缩是渐进的:CPU 超 80% 会按比例计算目标副本数,上限 maxReplicas=5
ab -n 300000 -c 100:总数 30 万、并发 100;注意先确认 Service IP 再压(压错 IP 会白等)
十三、环境清理与恢复
13.1 清理 NetworkPolicy 实验环境
# 一次性删除三个命名空间(web 有策略、ningcode 有跨 ns 测试 Pod)
root@master30 ~ 12:13:30# kubectl delete ns network web ningcode
namespace "web" deleted
namespace "ningcode" deleted
Error from server (NotFound): namespaces "network" not found # 打错了,忽略
13.2 清理调度实验环境
# 删除节点标签
kubectl label node worker31.ningcode.cn CPU-
kubectl label node worker32.ningcode.cn CPU-
kubectl label node worker32.ningcode.cn MEM-
kubectl label node worker31.ningcode.cn topology.kubernetes.io/zone-
# 删除污点
kubectl taint nodes worker31.ningcode.cn CPU=L1:NoSchedule-
kubectl taint nodes worker31.ningcode.cn CPU=L2:NoSchedule-
kubectl taint nodes worker31.ningcode.cn MEM=L2:NoSchedule-
kubectl taint nodes worker32.ningcode.cn CPU=L2:NoSchedule-
kubectl taint nodes worker31.ningcode.cn CPU=L1:NoExecute-
# 删除 Deployment / Service
kubectl delete deployment --all -n scheduler
kubectl delete svc --all -n scheduler
实验产生的 YAML 文件(netpol.yaml、deploy-with-*.yaml、hpa-cpu.yaml 等)保留在 master 上,可留作模板。
13.3 检查集群状态
kubectl get nodes # 3 节点全部 Ready
kubectl get ns # 只剩系统命名空间
kubectl get netpol -A # 网络策略已随 ns 删除
kubectl get hpa -A # HPA 随 deployment 清理
13.4 要点
- 命名空间是天然的资源隔离边界,删 ns = 里面所有资源(含 NetworkPolicy)一起删,实验收尾最省事
- 节点标签 / 污点是"全局"的,不随命名空间删除,必须手动清理,否则影响后续实验调度
- 误删资源前先
kubectl get <类型>确认名称,避免出现namespaces "network" not found这类拼写错误
十四、常见错误与排查(重点)
14.1 NetworkPolicy 专项
| 现象 | 原因 | 修复 |
|---|---|---|
curl 报 exit code 6(Could not resolve host) |
DNS 解析失败,不是策略拦的 | 跨命名空间用 服务名.命名空间名 或全限定名 .svc.cluster.local |
curl 挂起 / exit code 28(超时) |
NetworkPolicy 未放行该来源 | 检查 from 条件(podSelector 同 ns 才匹配、namespaceSelector 要打标签) |
| ping 不通但 curl 通 | 策略只放行了声明的 TCP 端口 | 需要 ICMP 时在 ports 里声明协议,或接受"四层策略"行为 |
| 改完 YAML 忘了 apply,验证结果不变 | 生效的是当前已 apply 的版本 | kubectl apply -f netpol.yaml 后重新验证 |
podSelector 放行不了别的命名空间 Pod |
podSelector 只匹配同命名空间 | 改用 namespaceSelector |
14.2 调度 / 污点专项
| 现象 | 原因 | 修复 |
|---|---|---|
| required 亲和/反亲和叠加后 Pending | 约束无解(没有节点同时满足) | 检查亲和条件与节点/副本分布,放宽 required 或加节点 |
| 副本数 > 节点数 + 反亲和 required | 每节点最多 1 个副本 | 少于节点数部署,或用 preferred |
| 打标签后 Pending 的 Pod 变 Running | nodeSelector/affinity 条件被满足 | 正常;节点标签可后补,调度器会自动调度 |
kubectl top 报错 |
metrics-server 没装 / 没就绪 | 部署 metrics-server 并等 Pod Ready |
14.3 概念要点对照
| 对比项 | 说明 |
|---|---|
| nodeName vs nodeSelector vs nodeAffinity | 硬指定节点 / 标签等值匹配 / 表达式匹配(支持操作符 + 软硬两档) |
| podAffinity vs podAntiAffinity | 跟谁"同" vs 跟谁"不同",都要 topologyKey |
| NoSchedule vs NoExecute | 只挡新调度 / 还驱逐存量 Pod |
| NetworkPolicy 的 from 多条件 | 多个条目 = OR;同一条目内嵌套 podSelector+namespaceSelector = AND |
| HPA 计算前提 | Pod 必须有 resources.requests,metrics-server 必须可用 |
十五、命令速查表与小结
15.1 命令速查表
| 场景 | 命令 |
|---|---|
| 切换命名空间 | kubectl config set-context --current --namespace <ns> |
| 跨命名空间解析 | curl <服务名>.<命名空间名> 或 <服务名>.<ns>.svc.cluster.local |
| 打节点标签 | kubectl label node <节点> key=value;删除 kubectl label node <节点> key- |
| 打命名空间标签 | kubectl label ns <ns> key=value |
| 打污点 | kubectl taint nodes <节点> key=value:NoSchedule;删除 ... key:NoSchedule- |
| 看调度事件 | kubectl describe pod <pod>(Events 里的 FailedScheduling) |
| 查 NetworkPolicy | kubectl get netpol / kubectl describe netpol <名> |
| 临时测试 Pod | kubectl run --rm -it ubuntu -l run=test --image ubuntu -- bash |
| 装压测工具 | apt install -y apache2-utils,ab -n 300000 -c 100 http://<IP>/ |
| 看资源用量 | kubectl top node / kubectl top pods |
| 创建 HPA | kubectl autoscale deployment <名> --max=5 --min=2 --cpu-percent=80 |
| 看 HPA | kubectl get hpa / kubectl get hpa <名> -o yaml |
| 删除 HPA | kubectl delete hpa <名> |
15.2 小结
- NetworkPolicy 是白名单防火墙:不写策略全放行,写了策略没放行的全拒绝;
podSelector(同 ns)、namespaceSelector(跨 ns)、ipBlock(按网段)三种来源匹配要分清 - DNS 与策略是两回事:
exit code 6是解析失败(短名问题),exit code 28才是被策略拦截(超时)——排障先分清 - 调度三条路径:nodeName 硬指定(写错就 Pending)、nodeSelector 标签等值、亲和表达式(支持操作符 + required/preferred),优先级从高到低灵活度从低到高
- 反亲和是"高可用"的开关:副本分散要 required + 节点数 ≥ 副本数;多个 required 亲和叠加可能无解,
describe pod看事件最直接 - 污点与容忍必须严格配对:key/value/effect 全匹配才生效;NoExecute 会驱逐存量 Pod;事件里
untolerated taint {key: value}直接点名 - HPA 三件套:metrics-server 就绪 + Pod 有 resources.requests + target 合理;压测会扩到 max,停压会缩回 min(有 60s 冷却),全程
kubectl get hpa -w可观察
完。本手册基于
kaitou.txt+master30_2026-08-12_10_27_42.log整理,命令时间戳均为原始记录;省略了重复命令与长输出,报错、修复、验证结果与日志一致。
更多推荐
所有评论(0)