Kubernetes调度机制
Kubernetes通过调度器(Scheduler)管理Pod与Node的匹配,核心调度方式如下:
1 ) 预选(Predicates)与优选(Priorities)机制
-
预选:基于硬性条件(如CPU/内存余量、端口冲突)过滤不符合要求的Node
yaml# 资源请求示例 resources: requests: memory: "128Mi" cpu: "250m" -
优选:在预选基础上通过算法(如
LeastRequestedPriority)优化负载均衡 -
技术细节:
PodFitsResources过滤资源不足节点;SelectorSpreadPriority分散多副本Pod
2 ) 标签选择器(nodeSelector)
为Node打标签(如disk=ssd),在Pod中定向调度:
yaml
spec:
nodeSelector:
disk: ssd
3 ) 节点亲和性(nodeAffinity)
支持软硬策略:
- 硬策略(
requiredDuringSchedulingIgnoredDuringExecution):必须满足条件 - 软策略(
preferredDuringSchedulingIgnoredDuringExecution):优先满足
示例1:
yaml
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # 硬策略
nodeSelectorTerms:
- matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values: [zone-a]
preferredDuringSchedulingIgnoredDuringExecution: # 软策略
- weight: 1
preference:
matchExpressions:
- key: gpu
operator: In
values: ["nvidia"]
示例2:
yaml
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # 硬策略
nodeSelectorTerms:
- matchExpressions:
- key: gpu-type
operator: In
values: ["a100"]
preferredDuringSchedulingIgnoredDuringExecution: # 软策略
- weight: 1
preference:
matchExpressions:
- key: zone
operator: In
values: ["us-west1-a"]
Node节点故障排查(断电场景)
当Node断电恢复后Pod无法启动,按以下步骤排查:
1 ) 检查污点(Taint)状态
Node异常时自动添加node.kubernetes.io/unreachable污点,恢复后手动删除:
bash
kubectl describe node <node-name> | grep Taints # 检查污点
kubectl taint nodes <node-name> node.kubernetes.io/unreachable:NoSchedule- # 手动删除
2 )验证主机名与kubelet服务
-
主机名不一致导致失联:
bashhostnamectl # 验证主机名 systemctl status kubelet | grep hostname # 检查配置一致性 -
重启服务并检查日志:
bashsystemctl restart kubelet && systemctl restart docker && systemctl status kubelet # 重启服务 journalctl -u kubelet -n 100 --no-pager | grep -E 'error|fail'
3 ) 确认Node资源状态
检查资源耗尽情况:
bash
# 若Node处于`NotReady`,检查资源是否耗尽(如`kubectl describe node <node-name>`中的`Conditions`字段)
kubectl describe node <node-name> | grep -A 5 Conditions # 查看Conditions字段
kubectl top nodes # 查看资源使用
Pod资源超限故障处理
| 故障类型 | 检测方法 | 解决方案 |
|---|---|---|
| Pod数量超限 | kubectl describe node 查看Allocatable |
修改kubelet参数:--max-pods=150 或扩展集群节点 kubectl scale deployment <name> --replicas=5 |
| 容器资源超主机容量 | 对比Pod的limits与节点资源 |
调整资源配置: resources.limits.memory: "4Gi" |
| 宿主机资源不足 | kubectl top nodes |
扩容工作节点或优化现有工作负载 |
Pod资源配置超过宿主机容量
示例:宿主机内存5Gi,但Pod申请6Gi。需调整YAML中的resources限制:
yaml
# 资源限制正确配置示例
containers:
- name: app-container
resources:
limits:
cpu: "2"
memory: "4Gi"
requests:
cpu: "1"
memory: "2Gi"
宿主机资源不足
直接扩容Node或迁移Pod至新节点
Pod自动扩缩容策略
| 类型 | 机制 | 适用场景 | 配置示例 |
|---|---|---|---|
| HPA | 基于CPU/内存指标扩缩副本 | 流量波动大的无状态应用 | kubectl autoscale deployment nginx --cpu-percent=80 --min=2 --max=10 |
| KPA | 基于HTTP请求量扩缩容 | 请求驱动型服务(如Serverless) | 无需CPU指标,依赖Knative框架实现 |
| VPA | 动态调整Pod资源请求量 | 资源需求周期性变化的应用 | yaml<br>apiVersion: autoscaling.k8s.io/v1<br>kind: VerticalPodAutoscaler<br>spec:<br> targetRef:<br> name: my-app |
技术细节:HPA依赖Metrics Server;VPA避免与HPA混用导致冲突
yaml
# HPA配置示例
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
spec:
scaleTargetRef:
kind: Deployment
name: myapp
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 80
# VPA配置示例
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
spec:
targetRef:
kind: Deployment
name: my-app
三种核心扩缩容机制
-
HPA(水平Pod扩缩容)
基于CPU/内存等指标动态调整副本数
bashkubectl autoscale deployment nginx --cpu-percent=80 --min=2 --max=10 -
KPA(Knative扩缩容)
根据HTTP请求量自动扩缩,适用于Serverless场景
-
VPA(垂直Pod扩缩容)
自动调整Pod资源请求量,保持资源配置比例
yamlapiVersion: autoscaling.k8s.io/v1 kind: VerticalPodAutoscaler spec: targetRef: apiVersion: "apps/v1" kind: Deployment name: my-app
Service访问异常排查
不匹配 匹配 异常 正常 无 有 Service访问异常 标签匹配检查 修正selector标签 kube-proxy状态 重启kube-proxy服务 Endpoint是否存在 检查Pod就绪状态 检查网络插件
关键命令:
bash
kubectl get endpoints <service-name> # 验证后端Pod
kubectl describe svc <service> -o jsonpath='{.spec.selector}' # 检查标签选择器
kubectl logs -n kube-system <kube-proxy-pod> # 诊断代理服务
kubectl get networkpolicy # 检查网络策略
1 ) 标签匹配验证
检查Service的selector是否匹配Pod标签:
bash
kubectl get endpoints <service-name> # 确认后端Pod列表
2 ) kube-proxy服务状态
- 重启服务:
systemctl restart kube-proxy。 - 日志分析:
journalctl -u kube-proxy检查iptables/IPVS错误
3 ) 网络策略
确保NetworkPolicy未阻断流量(如kubectl get networkpolicy)
Pod删除失败场景处理
| 原因 | 解决方案 |
|---|---|
| 被资源引用锁定 | 先删除关联资源:kubectl delete deployment/my-app 或 kubectl delete deploy <name> |
| Pod状态异常 | 修复根本问题(kubectl logs <pod-name> -p查日志) |
| Node故障 | 恢复通信或强制驱逐:kubectl drain <node> --force |
| K8s版本缺陷 | 升级集群:kubectl upgrade |
| 强制删除(终极方案) | kubectl delete pod <name> --grace-period=0 --force |
| API通信中断 | 检查控制平面网络连通性 |
pause容器的作用,作为Pod根容器提供关键功能:
- 创建共享网络命名空间(
eth0虚拟网卡) - 建立数据卷供业务容器挂载
- 通过
veth pair连接宿主机网桥(如cni0) - 业务容器通过
join共享其网络栈
核心价值:确保同Pod内容器间通信隔离和存储共享
Pod网络与通信模型
1 ) Pause容器作用
- 作为Pod的根容器,创建共享网络命名空间和数据卷,供业务容器挂载
- 创建共享网络命名空间和数据卷
- 建立
veth pair连接宿主机网桥(如cni0)- 创建虚拟网卡
eth0,一端连接宿主机网桥(如cni0),另一端接入Pod
- 创建虚拟网卡
2 ) 通信机制
跨节点通信 同节点通信 通过veth pair 转发流量 VXLAN封装 隧道传输 flannel.1 PodC Node2/flannel.1 Bridge PodA PodB
Flannel实现细节:
- 每个Node部署
flanneld,创建flannel.1虚拟网卡 - 跨主机流量通过VXLAN隧道传输
| 类型 | 实现方式 |
|---|---|
| 同节点Pod通信 | 通过网桥(cni0)直接转发 |
| 跨节点Pod通信 | Overlay网络(如Flannel VXLAN模式): - 流量路径:Pod → 网桥 → flannel.1 → 物理网卡 → 目标Node |
2.1 同节点Pod通信
通过veth pair 转发流量 PodA cni0网桥 PodB
- 直接通过宿主机网桥转发(如Flannel的
cni0)
2.2 跨节点Pod通信
-
CNI插件(如Flannel)创建
flannel.1虚拟网卡 -
流量封装为VXLAN隧道传输:
bash# Flannel网卡验证 ip addr show flannel.1 # 查看网卡配置 ip route show | grep flannel # 验证路由
2.3 路径:
- Pod → 网桥 →
flannel.1→ 物理网卡 → 目标Node → Pod
核心要点:
- 每个Pod分配唯一IP
- Service通过iptables/IPVS实现负载均衡
跨命名空间服务调用
1 ) 全限定域名(FQDN)访问
<service-name>.<namespace>.svc.cluster.local- 示例:
curl http://nginx.test.svc.cluster.local
2 ) ExternalName Service
场景1:代理外部服务
yaml
apiVersion: v1
kind: Service
spec:
type: ExternalName
externalName: www.example.com
场景2:代理跨命名空间服务
yaml
apiVersion: v1
kind: Service
metadata:
name: cross-ns-proxy
namespace: default
spec:
type: ExternalName
externalName: nginx-service.test-ns.svc.cluster.local
Docker镜像优化方法
| 方法 | 示例/命令 | 优化效果 |
|---|---|---|
| 精简基础镜像 | FROM alpine:latest |
减少基础层体积 |
| 合并RUN指令 | RUN apt update && apt install -y git && rm -rf /var/lib/apt/lists/* |
减少层数+清理缓存 |
| 多阶段构建 | dockerfile<br>FROM golang AS builder<br>COPY . .<br>RUN go build<br>FROM alpine<br>COPY --from=builder /app /app |
分离构建/运行环境 |
| 排除非必要文件 | .dockerignore中声明*.log, tmp/ |
减小镜像体积 |
| 单层指令合并 | RUN指令合并操作与清理步骤 |
避免冗余缓存产生 |
合并指令减少层数
dockerfile
RUN apt-get update && apt-get install -y git \
&& rm -rf /var/lib/apt/lists/* # 清理中间文件
多阶段构建示例
dockerfile
FROM golang:1.18 AS builder
COPY . /app
RUN go build -o /app/main
FROM alpine:3.15
COPY --from=builder /app/main /main
CMD ["/main"]
日志收集策略
1 ) 标准化输出日志
-
路径:
/var/log/containers/<pod>_<ns>_<container>-<id>.log- 示例路径:
/var/lib/docker/containers/<container-id>/*.log
- 示例路径:
-
DaemonSet部署Filebeat:
bashkubectl apply -f - <<EOF apiVersion: apps/v1 kind: DaemonSet spec: template: spec: volumes: - name: varlog hostPath: path: /var/log/containers containers: - name: filebeat volumeMounts: - mountPath: /logs name: varlog EOF或
yamlvolumes: - name: docker-logs hostPath: path: /var/lib/docker/containers containers: - name: filebeat image: docker.elastic.co/beats/filebeat:7.14.0 volumeMounts: - name: docker-logs mountPath: /logs
2 ) 容器内部日志收集
Sidecar模式共享卷:
yaml
volumes:
- name: shared-logs
emptyDir: {}
containers:
- name: app
volumeMounts:
- mountPath: /app/logs
name: shared-logs
- name: filebeat-sidecar
volumeMounts:
- mountPath: /sidecar-logs
name: shared-logs
或
yaml
多容器Pod共享日志卷
apiVersion: v1
kind: Pod
metadata:
name: log-collector
spec:
volumes:
- name: log-volume
emptyDir: {}
containers:
- name: app
image: nginx
volumeMounts:
- name: log-volume
mountPath: /app/logs
- name: filebeat
image: elastic/filebeat
volumeMounts:
- name: log-volume
mountPath: /logs
关键总结
- 调度核心:预选/优选保证资源匹配,
nodeAffinity提供策略灵活性 - 故障排查:污点管理、kubelet状态及资源限制是关键切入点
- 网络架构:Pause容器初始化环境,CNI插件实现跨节点通信
- 运维实践:日志收集需区分输出类型,镜像优化聚焦层数与体积控制
- 扩展能力:HPA/VPA/KPA覆盖动态扩缩容场景,ExternalName解决跨空间调用