Kubernetes 进阶
访问模式(accessModes) PV 回收策略(persistentVolumeReclaimPolicy) 无需管理员手动创建 PV,PVC 通过 storageClassName 指定 StorageClass,控制器自动创建匹配的 PV。 Ingress 是集群入口的 HTTP/HTTPS 路由规则,需要配合 Ingress Controller(如 Nginx Ingress)使用。Ingress Controller 本身作为 Deployment 运行在集群中,监听 Ingress 资源变化并动态配置反向代理。 Ingress YAML
官方文档:https://kubernetes.io/zh-cn/docs/home/
适用版本:Kubernetes 1.29+(2026-05-07 核实)
存储
Volume 类型对比
| 类型 | 生命周期 | 说明 | 典型使用场景 |
|---|---|---|---|
emptyDir |
与 Pod 相同 | Pod 创建时分配,Pod 删除时数据清除;同一 Pod 内容器可共享 | 容器间临时文件共享、缓存、日志中转 |
hostPath |
与 Node 相关 | 挂载宿主机目录或文件,Pod 删除后数据保留在节点上 | 访问宿主机日志、Docker socket;不推荐生产使用 |
configMap |
与 ConfigMap 相同 | 将 ConfigMap 的数据挂载为文件 | 配置文件注入 |
secret |
与 Secret 相同 | 将 Secret 数据挂载为文件(内存中的 tmpfs) | 证书、密钥文件注入 |
persistentVolumeClaim |
独立于 Pod | 引用已有的 PVC,数据持久化存储 | 数据库、有状态应用 |
PV / PVC / StorageClass 三者关系
StorageClass(存储类)
│
│ 动态供应
▼
PersistentVolume(PV)── 静态绑定 ──► PersistentVolumeClaim(PVC)
│
│ 引用
▼
Pod spec
- PersistentVolume(PV):集群管理员预先创建(静态)或由 StorageClass 自动创建(动态)的存储资源,描述实际存储的位置和容量。
- PersistentVolumeClaim(PVC):用户对存储的申请,声明所需容量和访问模式,Kubernetes 自动匹配合适的 PV 进行绑定。
- StorageClass:存储的"模板",定义存储驱动(provisioner)和参数,支持动态按需创建 PV。
访问模式(accessModes)
| 模式 | 缩写 | 说明 |
|---|---|---|
ReadWriteOnce |
RWO | 只能被单个 Node 以读写方式挂载 |
ReadOnlyMany |
ROX | 可被多个 Node 以只读方式挂载 |
ReadWriteMany |
RWX | 可被多个 Node 以读写方式挂载(需要共享文件系统,如 NFS) |
PV 回收策略(persistentVolumeReclaimPolicy)
| 策略 | 说明 |
|---|---|
Retain |
PVC 删除后 PV 保留,需手动处理数据 |
Delete |
PVC 删除后 PV 及底层存储一并删除(动态供应的默认行为) |
Recycle |
已废弃,不推荐使用 |
动态供应(Dynamic Provisioning)
无需管理员手动创建 PV,PVC 通过 storageClassName 指定 StorageClass,控制器自动创建匹配的 PV。
# StorageClass 示例(使用 AWS EBS)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast
provisioner: ebs.csi.aws.com
parameters:
type: gp3
reclaimPolicy: Delete
allowVolumeExpansion: true
# PVC 使用动态供应
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
storageClassName: fast
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
# Pod 引用 PVC
volumes:
- name: data
persistentVolumeClaim:
claimName: my-pvc
网络
Ingress
Ingress 是集群入口的 HTTP/HTTPS 路由规则,需要配合 Ingress Controller(如 Nginx Ingress)使用。Ingress Controller 本身作为 Deployment 运行在集群中,监听 Ingress 资源变化并动态配置反向代理。
Ingress YAML 结构
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
tls:
- hosts:
- example.com
secretName: tls-secret # 包含 tls.crt 和 tls.key 的 Secret
rules:
- host: example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-service
port:
number: 8080
- path: /
pathType: Prefix
backend:
service:
name: frontend-service
port:
number: 80
spec.rules 字段
| 字段 | 类型 | 说明 |
|---|---|---|
host |
字符串 | 域名,省略则匹配所有 Host |
http.paths[].path |
字符串 | URL 路径前缀或精确匹配 |
http.paths[].pathType |
字符串 | Prefix(前缀)、Exact(精确)、ImplementationSpecific |
http.paths[].backend.service.name |
字符串 | 后端 Service 名称 |
http.paths[].backend.service.port.number |
整数 | 后端 Service 端口 |
spec.tls 字段
| 字段 | 类型 | 说明 |
|---|---|---|
hosts |
列表 | TLS 生效的域名列表 |
secretName |
字符串 | 存储 TLS 证书的 Secret 名称 |
NetworkPolicy
NetworkPolicy 限制 Pod 间的网络通信,默认 Pod 可以互相访问;创建 NetworkPolicy 后仅允许规则匹配的流量通过。需要 CNI 插件支持(如 Calico、Cilium)。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-allow
namespace: production
spec:
podSelector:
matchLabels:
role: api # 该策略作用于带此标签的 Pod
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
role: frontend # 只允许 frontend Pod 访问
ports:
- protocol: TCP
port: 8080
egress:
- to:
- podSelector:
matchLabels:
role: database
ports:
- protocol: TCP
port: 5432
调度
nodeSelector / nodeAffinity / podAffinity / podAntiAffinity 对比
| 特性 | 描述 | 灵活性 |
|---|---|---|
nodeSelector |
简单键值对标签匹配,强制要求 Pod 调度到带有指定标签的节点 | 低 |
nodeAffinity |
节点亲和性,支持 In、NotIn、Exists 等操作符,可区分硬性(requiredDuringScheduling)和软性(preferredDuringScheduling)要求 |
高 |
podAffinity |
Pod 亲和性,要求调度到与指定 Pod 相同的节点/拓扑域(如同一区域),常用于低延迟场景 | 高 |
podAntiAffinity |
Pod 反亲和性,要求调度到与指定 Pod 不同的节点/拓扑域,常用于高可用(避免单点) | 高 |
# nodeAffinity 示例
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values: ["us-east-1a", "us-east-1b"]
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: my-app
topologyKey: kubernetes.io/hostname # 尽量分散到不同节点
Taints 与 Tolerations
Taint 给节点打上"污点",阻止没有对应 Toleration 的 Pod 调度到该节点。
# 给节点添加 Taint
kubectl taint nodes node1 key=value:NoSchedule
# 删除 Taint
kubectl taint nodes node1 key=value:NoSchedule-
Taint 效果(effect):
| 效果 | 说明 |
|---|---|
NoSchedule |
不允许调度新 Pod(已运行的 Pod 不受影响) |
PreferNoSchedule |
尽量不调度,但没有其他节点时仍可调度 |
NoExecute |
不允许调度,且驱逐已运行的不容忍 Pod |
Toleration 允许 Pod 容忍节点的污点:
tolerations:
- key: "key"
operator: "Equal"
value: "value"
effect: "NoSchedule"
- key: "node.kubernetes.io/not-ready"
operator: "Exists"
effect: "NoExecute"
tolerationSeconds: 300 # 最多容忍 300 秒后驱逐
PriorityClass
定义 Pod 的调度优先级,高优先级 Pod 在资源不足时可抢占低优先级 Pod。
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000000 # 数值越大优先级越高
globalDefault: false
description: "用于关键生产服务"
# Pod 使用 PriorityClass
spec:
priorityClassName: high-priority
自动扩缩容
HorizontalPodAutoscaler(HPA)
HPA 根据 CPU、内存或自定义指标自动调整 Deployment/StatefulSet 的副本数。需要集群安装 metrics-server。
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: my-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # CPU 使用率超过 70% 时扩容
- type: Resource
resource:
name: memory
target:
type: AverageValue
averageValue: 512Mi
HPA spec 核心字段
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
scaleTargetRef |
对象 | 必填 | 指定被扩缩容的目标资源 |
minReplicas |
整数 | 1 |
最小副本数,HPA 不会将副本降至此值以下 |
maxReplicas |
整数 | 必填 | 最大副本数 |
metrics |
列表 | 必填 | 扩缩容指标列表 |
behavior.scaleUp |
对象 | 无 | 扩容行为配置(稳定窗口、步长) |
behavior.scaleDown |
对象 | 无 | 缩容行为配置(稳定窗口默认 5 分钟,防止频繁缩容) |
metrics 类型
| 类型 | 说明 |
|---|---|
Resource |
基于 CPU/内存使用率或绝对值 |
ContainerResource |
针对 Pod 内特定容器的资源指标 |
Pods |
Pod 级别自定义指标(如每秒请求数) |
Object |
集群对象的指标(如 Ingress 的 QPS) |
External |
外部系统指标(如消息队列积压量) |
StatefulSet
与 Deployment 的区别
| 特性 | Deployment | StatefulSet |
|---|---|---|
| Pod 名称 | 随机后缀(如 app-abc12) |
有序且固定(如 app-0、app-1) |
| 部署/删除顺序 | 并行,无序 | 有序(0, 1, 2...),删除时反序 |
| 网络标识 | Pod IP 变化 | 稳定 DNS 名称(通过 Headless Service)如 app-0.my-svc |
| 存储 | 共享或无状态 | 每个 Pod 有独立的 PVC(volumeClaimTemplates) |
适用场景
- 数据库:MySQL、PostgreSQL(需要主从固定角色)
- 消息队列:Kafka(Broker ID 与 Pod 序号绑定)
- 分布式协调:ZooKeeper、etcd(需要固定网络标识)
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: "mysql" # 必须指定 Headless Service 名称
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: password
volumeMounts:
- name: data
mountPath: /var/lib/mysql
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: fast
resources:
requests:
storage: 20Gi
ConfigMap/Secret 热更新
Volume 挂载方式(自动更新)
以 Volume 方式挂载的 ConfigMap,Kubernetes 会在 ConfigMap 内容变更后自动同步到容器内的文件(默认同步周期约为 1 分钟,取决于 kubelet 的 --sync-frequency)。
volumeMounts:
- name: config
mountPath: /etc/app/config.yaml
subPath: config.yaml # 注意:使用 subPath 时不会自动更新
volumes:
- name: config
configMap:
name: my-config
注意:使用 subPath 挂载单个文件时,自动更新失效,需重启 Pod。
环境变量方式(不自动更新)
通过 env.valueFrom.configMapKeyRef 或 envFrom 注入的环境变量,在 ConfigMap 更新后不会刷新。必须重启 Pod 才能生效。
强制触发 Pod 重启的常用方式:
# 方式一:滚动重启 Deployment(零停机)
kubectl rollout restart deployment/my-app
# 方式二:修改 Deployment 的 annotation 触发更新
kubectl patch deployment my-app -p \
'{"spec":{"template":{"metadata":{"annotations":{"restarted-at":"2026-04-11"}}}}}'
RBAC(基于角色的访问控制)
核心对象关系
ServiceAccount(身份)
│
│ RoleBinding / ClusterRoleBinding
▼
Role(命名空间级权限)/ ClusterRole(集群级权限)
| 对象 | 作用范围 | 说明 |
|---|---|---|
ServiceAccount |
Namespace | Pod 的身份标识,用于与 API Server 交互 |
Role |
Namespace | 定义在特定命名空间内的权限规则 |
ClusterRole |
集群 | 定义集群范围的权限,或跨命名空间复用 |
RoleBinding |
Namespace | 将 Role 或 ClusterRole 绑定到 ServiceAccount/User/Group |
ClusterRoleBinding |
集群 | 将 ClusterRole 绑定到集群范围的主体 |
# ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-sa
namespace: production
---
# Role:允许读取特定命名空间的 Pod 和 ConfigMap
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader
namespace: production
rules:
- apiGroups: [""]
resources: ["pods", "configmaps"]
verbs: ["get", "list", "watch"]
---
# RoleBinding:将 Role 绑定到 ServiceAccount
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: production
subjects:
- kind: ServiceAccount
name: app-sa
namespace: production
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
最小权限原则
- 每个应用创建专用 ServiceAccount,不使用
default - 优先使用
Role+RoleBinding而非ClusterRole+ClusterRoleBinding - verbs 只授予必要的操作:
get、list、watch(只读),避免授予*通配符 - 定期审查并清理不再使用的 Role
Helm
Helm 是 Kubernetes 的包管理工具,通过 Chart 封装一组 Kubernetes 资源模板,支持版本管理和参数化配置。
常用命令
helm install
helm install <release-name> <chart> [flags]
| 参数 | 说明 |
|---|---|
<release-name> |
发布名称,集群内唯一 |
<chart> |
Chart 名称(repo/chart)或本地路径 |
-n / --namespace |
部署到指定命名空间 |
-f / --values |
指定自定义 values 文件 |
--set |
命令行覆盖单个值,如 --set replicas=3 |
--version |
指定 Chart 版本 |
--dry-run |
模拟安装,输出渲染后的 YAML |
--create-namespace |
命名空间不存在时自动创建 |
helm upgrade
helm upgrade <release-name> <chart> [flags]
| 参数 | 说明 |
|---|---|
--install |
release 不存在时自动安装(等同于 install + upgrade 二合一) |
--atomic |
升级失败时自动回滚 |
--wait |
等待所有资源就绪后再返回 |
--timeout |
等待超时时间,默认 5m0s |
--reuse-values |
复用上次的 values,只覆盖本次指定的参数 |
helm rollback
helm rollback <release-name> [revision]
| 参数 | 说明 |
|---|---|
[revision] |
目标版本号,省略则回滚到上一版本 |
--wait |
等待回滚完成 |
helm uninstall
helm uninstall <release-name> [-n namespace]
| 参数 | 说明 |
|---|---|
--keep-history |
保留 release 历史记录(默认删除) |
其他常用命令
# 添加仓库
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
# 查看已安装的 release
helm list -A
# 查看 release 历史
helm history my-release
# 查看渲染后的 YAML(不实际部署)
helm template my-release ingress-nginx/ingress-nginx -f values.yaml
# 查看 Chart 的默认 values
helm show values ingress-nginx/ingress-nginx
values.yaml 覆盖
优先级从低到高:Chart 默认 values → -f values.yaml → --set。
# 使用自定义 values 文件安装
helm install ingress-nginx ingress-nginx/ingress-nginx \
-n ingress-nginx \
--create-namespace \
-f custom-values.yaml
# 多个 values 文件叠加(后者覆盖前者)
helm install my-app ./my-chart -f base.yaml -f prod.yaml
# 命令行覆盖嵌套字段
helm install my-app ./my-chart --set image.tag=1.2.3 --set service.type=LoadBalancer
常用 Chart
| Chart | 仓库 | 用途 |
|---|---|---|
ingress-nginx/ingress-nginx |
https://kubernetes.github.io/ingress-nginx |
Nginx Ingress Controller |
cert-manager/cert-manager |
https://charts.jetstack.io |
自动申请/续签 TLS 证书(支持 Let's Encrypt) |
prometheus-community/kube-prometheus-stack |
https://prometheus-community.github.io/helm-charts |
Prometheus + Grafana 监控套件 |
踩坑与注意事项
PVC 数据残留问题
删除 StatefulSet 时,volumeClaimTemplates 创建的 PVC 不会自动删除(设计如此,防止数据误删)。
# 查看残留 PVC
kubectl get pvc -n <namespace>
# 手动删除 PVC(确认数据已备份)
kubectl delete pvc data-mysql-0 data-mysql-1 data-mysql-2
如果 PV 的回收策略是 Retain,删除 PVC 后 PV 仍保留,需要手动删除或重新绑定。
ConfigMap 更新后 Pod 不重启
环境变量方式注入的 ConfigMap 不会自动刷新,必须触发 Pod 重启。推荐做法:
- 在 CI/CD 流水线中将 ConfigMap 的 hash 作为 Deployment annotation,ConfigMap 变更时 annotation 值变化,自动触发滚动更新:
annotations: checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} - 或使用
kubectl rollout restart deployment/<name>手动触发。
Ingress 404 排查步骤
- 确认 Ingress Controller 正常运行:
kubectl get pods -n ingress-nginx kubectl logs -n ingress-nginx <ingress-nginx-pod> - 确认 Ingress 资源已创建:
kubectl get ingress -A kubectl describe ingress <name> -n <namespace> - 确认 ingressClassName 匹配:Ingress 的
spec.ingressClassName必须与 Controller 的 class 一致(通常为nginx)。 - 确认后端 Service 和 Pod 正常:
Endpoints 列表为空说明 selector 未匹配到 Pod。kubectl get svc,endpoints -n <namespace> - 确认路径规则:
pathType: Prefix下/api能匹配/api/v1;pathType: Exact则只能精确匹配。 - 检查 annotation:
nginx.ingress.kubernetes.io/rewrite-target配置不当会导致路径重写错误。
其他常见坑
- HPA 不生效:检查 metrics-server 是否安装,
kubectl top pods能否正常输出。Deployment 必须设置resources.requests.cpu,否则 HPA 无法计算利用率百分比。 - StatefulSet 滚动更新卡住:某个 Pod 一直 Pending 或 CrashLoopBackOff 会阻塞后续 Pod 的更新(有序策略)。用
kubectl rollout status statefulset/<name>监控,出现问题需先修复该 Pod。 - Helm release 状态为 failed:执行
helm rollback或helm uninstall后重新安装。--atomic参数可以在失败时自动回滚,适合 CI/CD 场景。 - Secret 更新后 Volume 挂载不刷新:虽然理论上 Secret Volume 也会自动同步,但使用
subPath挂载时同样不会更新。避免对频繁变更的 Secret 使用subPath。
最佳实践
HPA 与 VPA 不要同时作用于同一资源维度:HPA 基于 CPU/内存自动扩缩副本数,VPA 自动调整单 Pod 的 resource requests;两者同时控制 CPU 会互相干扰。可以让 HPA 管 CPU 水平扩展,VPA 只管内存的垂直调整(updateMode: "Initial" 避免重建 Pod)。
StatefulSet 使用 podManagementPolicy: Parallel 加速批量操作:默认有序(OrderedReady)策略在扩缩容时依次等待每个 Pod ready,并发场景下改为 Parallel 可大幅缩短时间(注意应用本身需支持并发启动)。
Helm Chart 用 --atomic 防止残留失败 release:CI/CD 中加 --atomic 标志,升级失败时自动回滚到上一个成功版本,避免留下 failed 状态的 release 阻塞后续部署。
网络策略(NetworkPolicy)默认拒绝,按需开放:新命名空间默认无网络策略,所有 Pod 互通。生产环境先应用 deny-all 策略,再逐步开放需要的端口和方向,减少横向移动风险。
用 PodDisruptionBudget 保障滚动更新期间最小可用副本:minAvailable: 1 确保即使在节点维护或滚动更新时,始终至少有 1 个副本处于 Ready 状态对外服务。
常见陷阱
陷阱:HPA 不生效,副本数不变
现象: 负载升高但 kubectl get hpa 显示 <unknown>/50%,副本数不增加。
原因: metrics-server 未安装或 Pod 未设置 resources.requests.cpu,HPA 无法获取利用率百分比。
解决: 安装 metrics-server(kubectl top pods 可用),确保 Deployment 的 Pod spec 中有 resources.requests.cpu。
陷阱:StatefulSet 滚动更新卡在某个 Pod
现象: kubectl rollout status statefulset/<name> 长时间等待,某个 Pod 一直 CrashLoopBackOff。
原因: 有序策略下,前一个 Pod 未 Ready 则后续 Pod 不会更新,问题 Pod 阻塞整个滚动更新。
解决: kubectl describe pod <stucked-pod> 查看错误原因修复应用问题,或 kubectl rollout undo statefulset/<name> 回滚。
陷阱:Helm upgrade 后旧版本 ConfigMap/Secret 残留
现象: Helm upgrade 后应用读取到旧配置,kubectl describe pod 显示挂载的仍是旧 ConfigMap。
原因: Helm 不重启未变更的 Deployment,即使 ConfigMap 已更新,已运行的 Pod 不会自动感知变化。
解决: 在 Deployment 的 Pod template annotations 中加入 ConfigMap 的 checksum,ConfigMap 变更时 annotation 变化触发滚动重启:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
参见
Kubernetes基础
Docker高级指南
GitHub Actions完全指南
Prometheus与Grafana监控