☰
K8s Controller深度解析:从Pod失联到自愈,掌握Deployment、StatefulSet核心原理
2026/10/21 5:15:16 网站建设 项目流程

1. 从一次Pod“失联”说起:为什么我们需要Controller

刚接触K8s那会儿,我踩过一个特别典型的坑。当时手动创建了一个Pod跑业务,跑得好好的,结果某天节点资源紧张,Pod被驱逐了,业务直接挂掉。我第一反应是“谁把我的Pod删了”,查了半天日志才发现,根本没人删它,是节点自己扛不住压力把它踢走了。更让我崩溃的是,我以为Pod会自己“复活”,结果它就这么没了,再也没起来。

这件事让我彻底明白了一个道理:在K8s里,Pod本身是“易碎品”。它可能因为节点故障、资源不足、健康检查失败等各种原因消失,而K8s默认不会帮你把它拉回来。那谁来保证“我期望有3个副本在跑,实际就得有3个”这件事?答案就是Controller(控制器)。

Controller是K8s集群的“大脑皮层”,它负责持续观察集群的实际状态,并不断把它调整成你期望的状态。这个“期望状态”和“实际状态”之间的差距,就是Controller要填的坑。你告诉K8s“我要3个Nginx副本”,Controller就会盯着,少一个补一个,多一个删一个,节点挂了就把Pod调度到别的节点上重建。没有Controller,K8s就是一个只会跑容器的“裸机”,有了Controller,它才真正具备了自愈、扩缩容、滚动更新这些能力。

这篇文章适合谁看?如果你是刚入门K8s、被各种Controller名字绕晕的新手,或者你已经会用kubectl但说不清楚Deployment、ReplicaSet、StatefulSet到底啥关系,再或者你在生产环境遇到过Pod莫名其妙重启、滚动更新卡住、有状态服务数据丢失这些问题,那这篇内容就是写给你的。我会从核心概念讲起,把每个Controller的设计意图、适用场景、实操要点和踩坑经验都掰开揉碎说清楚,让你不仅知道怎么用,更知道为什么这么设计。

2. Controller核心设计思路拆解:声明式API与控制循环

2.1 声明式API:你只管说“要什么”,不管“怎么做”

K8s最核心的设计哲学之一就是声明式API。什么意思?你写一个YAML文件,里面写清楚“我要3个Nginx Pod,镜像用nginx:1.24,端口80”,然后kubectl apply一下,剩下的事K8s自己搞定。你不需要告诉它“先创建Pod A,再创建Pod B,然后检查A是否健康,如果不健康就删掉重建B”这种过程式指令。

这就像你去餐厅点菜,你只说“我要一份宫保鸡丁”,不需要告诉厨师“先热锅,再倒油,油温七成热下鸡丁”。厨师(Controller)会根据当前厨房的状态(有没有鸡丁、花生米够不够、灶台空不空)自动决定怎么做。如果鸡丁用完了,他会去冰箱拿;如果灶台被占了,他会等或者换一个。你只需要关心“我要宫保鸡丁”这个期望状态。

声明式API的好处是幂等性。你apply同一个YAML一百次,结果都一样。这让你可以用GitOps的方式管理集群,把YAML放在Git仓库里,每次变更都走PR流程,审计和回滚都变得极其简单。

2.2 控制循环:Controller的“心跳”

Controller的核心工作机制是一个无限循环,专业术语叫Reconcile Loop(调谐循环)。这个循环的逻辑可以用伪代码表示:

while True: desired_state = get_desired_state() # 从API Server读取期望状态 actual_state = get_actual_state() # 从集群读取实际状态 if desired_state != actual_state: take_action_to_align() # 执行操作,让实际状态向期望状态靠拢 sleep(interval) # 等待下一次循环

这个循环不是由某个中央控制器统一调度的,而是每个Controller各自独立运行。Deployment Controller管Deployment,ReplicaSet Controller管ReplicaSet,Node Controller管节点状态,Endpoint Controller管Service和Pod的映射关系。它们之间通过API Server通信,互相协作,但彼此不直接调用。

这种设计的好处是解耦和可扩展。你想加一个新的Controller?写一个独立的进程,监听你关心的资源,实现自己的Reconcile逻辑就行。K8s内置了几十种Controller,你还可以用Operator SDK写自定义Controller,这就是K8s生态如此繁荣的原因。

2.3 期望状态从哪来:API Server里的etcd

期望状态存在哪?存在etcd里。你kubectl apply一个YAML,API Server会把它写入etcd,然后各个Controller通过Watch机制监听API Server的变化。一旦发现你创建了一个新的Deployment,Deployment Controller就会触发Reconcile,开始干活。

这里有个关键点:Controller不直接操作etcd。所有读写都通过API Server进行,API Server负责认证、授权、准入控制、数据校验和持久化。Controller只是一个“客户端”,它Watch API Server,发现变化后执行操作,操作结果又写回API Server。这种架构保证了数据的一致性和安全性。

2.4 为什么不用过程式脚本:一个对比

假设你用Shell脚本管理Pod,逻辑可能是这样的:

#!/bin/bash # 检查Pod数量 count=$(kubectl get pods -l app=nginx --no-headers | wc -l) if [ $count -lt 3 ]; then # 创建缺少的Pod for i in $(seq 1 $((3 - count))); do kubectl run nginx-$RANDOM --image=nginx:1.24 done fi

这个脚本看起来能工作,但问题很多:如果脚本执行到一半挂了怎么办?如果两个脚本同时跑,会不会创建出6个Pod?如果节点挂了,脚本怎么知道要把Pod调度到别的节点?如果Pod处于Pending状态,脚本怎么判断它是“正常”还是“卡住”?

Controller模式通过持续调谐和状态机解决了这些问题。它不依赖一次性脚本执行,而是不断观察、不断调整。即使Controller自己挂了,重启后它会重新Watch,继续调谐。这种“永远在线、持续纠偏”的机制,才是K8s自愈能力的根基。

3. 核心Controller逐个拆解:从无状态到有状态

3.1 Deployment:无状态服务的“总指挥”

Deployment是日常用得最多的Controller,没有之一。它管理的是无状态服务,比如Web前端、API网关、无状态微服务。你创建一个Deployment,它会自动创建一个ReplicaSet,ReplicaSet再创建和管理Pod。

为什么要有ReplicaSet这一层?直接让Deployment管Pod不行吗?这里的设计意图是版本管理。每次你更新Deployment的镜像版本,Deployment会创建一个新的ReplicaSet,然后逐步把流量从旧ReplicaSet切到新ReplicaSet。旧ReplicaSet不会被删除,而是保留下来(副本数缩到0),这样你可以随时回滚到上一个版本。

一个典型的Deployment YAML长这样:

apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment labels: app: nginx spec: replicas: 3 selector: matchLabels: app: nginx strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 0 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.24 ports: - containerPort: 80 resources: requests: cpu: 100m memory: 128Mi limits: cpu: 500m memory: 256Mi

这里有几个关键参数需要解释:

  • replicas: 3:期望的Pod副本数。Deployment Controller会确保任何时候都有3个Pod在跑。
  • selector:Deployment通过这个标签选择器找到它管理的ReplicaSet和Pod。注意,selector一旦创建就不能修改,因为修改会导致Deployment“认不出”自己原来的Pod。
  • strategy.type: RollingUpdate:滚动更新策略。另一个选项是Recreate,先删光旧Pod再创建新Pod,会导致服务中断,生产环境基本不用。
  • maxSurge: 1:滚动更新时,最多可以比期望副本数多出1个Pod。比如期望3个,更新过程中最多同时存在4个。
  • maxUnavailable: 0:滚动更新时,最多允许0个Pod不可用。这意味着更新过程中始终有3个Pod在提供服务,适合对可用性要求极高的场景。

注意:maxSurge和maxUnavailable不能同时为0,否则更新会卡死。如果你设置maxSurge=0,maxUnavailable必须大于0;反之亦然。

3.2 ReplicaSet:Deployment背后的“执行者”

ReplicaSet的职责非常单一:确保指定数量的Pod副本在运行。它通过标签选择器找到自己管理的Pod,然后对比实际数量和期望数量,少了就创建,多了就删除。

你通常不需要直接创建ReplicaSet,因为Deployment已经帮你管了。但在排查问题时,你需要知道ReplicaSet的存在。比如你发现Pod数量不对,可以先看Deployment的状态,再看它关联的ReplicaSet,最后看ReplicaSet创建的Pod。

# 查看Deployment状态 kubectl get deployment nginx-deployment # 查看Deployment关联的ReplicaSet kubectl get rs -l app=nginx # 查看ReplicaSet创建的Pod kubectl get pods -l app=nginx

ReplicaSet的标签选择器支持matchExpressions,比单纯的matchLabels更灵活。比如你可以选择“environment in (production, staging)”的Pod。但日常使用中,matchLabels已经够用了。

3.3 StatefulSet:有状态服务的“定海神针”

StatefulSet是专门为有状态服务设计的,比如数据库、消息队列、分布式存储。它和Deployment最大的区别在于:

  • 稳定的网络标识:每个Pod有固定的主机名,格式是<statefulset-name>-<ordinal>,比如mysql-0、mysql-1、mysql-2。这些主机名在Pod重建后保持不变。
  • 稳定的存储:每个Pod绑定自己的PVC(PersistentVolumeClaim),Pod重建后还是挂载原来的PV,数据不丢。
  • 有序的部署和扩缩容:Pod按顺序创建(0→1→2),按逆序删除(2→1→0)。扩容时先创建低序号的,缩容时先删除高序号的。
  • 有序的滚动更新:更新时从高序号到低序号逐个进行,确保集群始终有大多数节点可用。

一个StatefulSet的YAML示例:

apiVersion: apps/v1 kind: StatefulSet metadata: name: mysql spec: serviceName: mysql-headless replicas: 3 selector: matchLabels: app: mysql template: metadata: labels: app: mysql spec: containers: - name: mysql image: mysql:8.0 ports: - containerPort: 3306 volumeMounts: - name: data mountPath: /var/lib/mysql volumeClaimTemplates: - metadata: name: data spec: accessModes: ["ReadWriteOnce"] resources: requests: storage: 10Gi

这里的关键是volumeClaimTemplates。它会为每个Pod自动创建一个PVC,命名规则是<volume-name>-<statefulset-name>-<ordinal>,比如>apiVersion: apps/v1 kind: DaemonSet metadata: name: log-collector spec: selector: matchLabels: app: log-collector template: metadata: labels: app: log-collector spec: containers: - name: fluentd image: fluentd:v1.16 volumeMounts: - name: varlog mountPath: /var/log volumes: - name: varlog hostPath: path: /var/log

DaemonSet默认会在所有节点上创建Pod,包括Master节点。如果你不希望Master节点跑业务Pod,可以通过nodeSelector或tolerations来控制。比如Master节点通常有Taint,DaemonSet需要添加对应的Toleration才能调度上去。

3.5 Job与CronJob:一次性任务和定时任务

Job用于一次性任务,比如数据库迁移、批处理计算、备份脚本。它确保指定数量的Pod成功完成,然后停止。如果Pod失败,Job会根据backoffLimit重试,直到成功或达到重试上限。

apiVersion: batch/v1 kind: Job metadata: name: db-migration spec: template: spec: containers: - name: migrate image: myapp/migrate:v1.0 command: ["python", "migrate.py"] restartPolicy: Never backoffLimit: 4

CronJob则是定时任务,基于Cron表达式调度。比如每天凌晨2点跑备份:

apiVersion: batch/v1 kind: CronJob metadata: name: daily-backup spec: schedule: "0 2 * * *" jobTemplate: spec: template: spec: containers: - name: backup image: myapp/backup:v1.0 restartPolicy: OnFailure

注意:CronJob的schedule使用UTC时间,不是本地时间。如果你在中国,凌晨2点备份需要写成0 18 * * *(UTC 18点=北京时间凌晨2点)。这个坑我踩过,备份任务跑在了下午,差点把生产库拖垮。

4. 实操过程:从零搭建一个完整的Controller管理示例

4.1 环境准备与基础检查

假设你已经有一个可用的K8s集群,版本在1.24以上。先确认集群状态:

# 查看节点状态 kubectl get nodes # 查看集群版本 kubectl version --short # 查看所有命名空间 kubectl get namespaces

创建一个专门的命名空间来隔离实验环境:

kubectl create namespace controller-demo kubectl config set-context --current --namespace=controller-demo

4.2 部署一个Deployment并观察ReplicaSet的创建

先创建一个简单的Deployment:

kubectl create deployment web --image=nginx:1.24 --replicas=3

然后观察Deployment、ReplicaSet和Pod的层级关系:

# 查看Deployment kubectl get deployment web -o wide # 查看ReplicaSet kubectl get rs -l app=web # 查看Pod kubectl get pods -l app=web -o wide

你会看到类似这样的输出:

NAME READY UP-TO-DATE AVAILABLE AGE web 3/3 3 3 30s NAME DESIRED CURRENT READY AGE web-7d8f9c6b5 3 3 3 30s NAME READY STATUS RESTARTS AGE web-7d8f9c6b5-2k4m7 1/1 Running 0 30s web-7d8f9c6b5-8nq2p 1/1 Running 0 30s web-7d8f9c6b5-x5j9r 1/1 Running 0 30s

Deployment的名字是web,ReplicaSet的名字是web-7d8f9c6b5(后面那串是Pod模板的哈希值),Pod的名字是web-7d8f9c6b5-2k4m7(后面那串是随机字符串)。

4.3 模拟Pod故障,验证Controller的自愈能力

现在手动删除一个Pod,看看会发生什么:

# 删除一个Pod kubectl delete pod web-7d8f9c6b5-2k4m7 # 立即查看Pod列表 kubectl get pods -l app=web -w

你会看到被删除的Pod进入Terminating状态,同时一个新的Pod被创建出来。几秒钟后,Pod数量恢复到3个。这就是ReplicaSet Controller在起作用:它发现实际Pod数量(2个)小于期望数量(3个),于是创建了一个新的。

再模拟节点故障。如果你有多节点集群,可以cordon一个节点,然后驱逐上面的Pod:

# 标记节点不可调度 kubectl cordon <node-name> # 驱逐节点上的Pod kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data

你会看到该节点上的Pod被驱逐,Deployment Controller会在其他可用节点上重建它们。这就是K8s的自愈能力:节点挂了,业务不挂。

4.4 执行滚动更新并观察版本切换

修改Deployment的镜像版本,触发滚动更新:

kubectl set image deployment/web nginx=nginx:1.25

然后观察更新过程:

# 查看滚动更新状态 kubectl rollout status deployment/web # 查看ReplicaSet变化 kubectl get rs -l app=web -w

你会看到一个新的ReplicaSet被创建,副本数从0逐步增加到3,同时旧的ReplicaSet副本数从3逐步减少到0。这就是滚动更新的过程:新Pod创建一个,就绪后旧Pod删除一个,直到全部替换完成。

如果更新过程中发现问题,可以立即回滚:

# 查看更新历史 kubectl rollout history deployment/web # 回滚到上一个版本 kubectl rollout undo deployment/web # 回滚到指定版本 kubectl rollout undo deployment/web --to-revision=1

4.5 部署StatefulSet并验证存储稳定性

创建一个Headless Service和StatefulSet:

# mysql-headless.yaml apiVersion: v1 kind: Service metadata: name: mysql-headless spec: clusterIP: None selector: app: mysql ports: - port: 3306 name: mysql --- apiVersion: apps/v1 kind: StatefulSet metadata: name: mysql spec: serviceName: mysql-headless replicas: 3 selector: matchLabels: app: mysql template: metadata: labels: app: mysql spec: containers: - name: mysql image: mysql:8.0 env: - name: MYSQL_ROOT_PASSWORD value: "demo-password" ports: - containerPort: 3306 volumeMounts: - name: data mountPath: /var/lib/mysql volumeClaimTemplates: - metadata: name: data spec: accessModes: ["ReadWriteOnce"] resources: requests: storage: 1Gi

应用后观察Pod的创建顺序:

kubectl apply -f mysql-headless.yaml kubectl get pods -l app=mysql -w

你会看到mysql-0先创建,等它Running且Ready后,mysql-1才开始创建,然后是mysql-2。这就是StatefulSet的有序部署。

验证存储稳定性:删除mysql-0,观察它重建后是否挂载了原来的PVC:

kubectl delete pod mysql-0 kubectl get pvc -l app=mysql kubectl get pod mysql-0 -o jsonpath='{.spec.volumes[?(@.name=="data")].persistentVolumeClaim.claimName}'

你会发现重建后的mysql-0仍然挂载>apiVersion: apps/v1 kind: DaemonSet metadata: name: node-agent spec: selector: matchLabels: app: node-agent template: metadata: labels: app: node-agent spec: containers: - name: agent image: busybox:1.36 command: ["sh", "-c", "while true; do echo agent running; sleep 60; done"]

应用后查看Pod分布:

kubectl apply -f daemonset.yaml kubectl get pods -l app=node-agent -o wide

你会看到每个节点上都有一个node-agent Pod。如果你添加一个新节点,DaemonSet会自动在新节点上创建Pod。

5. 常见问题与排查技巧实录

5.1 Pod一直Pending,Controller在干什么?

Pod处于Pending状态,说明它没有被调度到任何节点。常见原因和排查方法:

现象可能原因排查命令解决方法
Pod Pending资源不足kubectl describe pod <pod>扩容节点或降低资源请求
Pod Pending节点选择器不匹配kubectl get nodes --show-labels调整nodeSelector或给节点打标签
Pod Pending污点不容忍kubectl describe node <node>添加tolerations
Pod PendingPVC未绑定kubectl get pvc检查StorageClass和PV可用性

实操心得:kubectl describe pod的Events部分是最有用的排查入口。90%的调度问题都能从这里找到线索。

5.2 滚动更新卡住不动,怎么破?

滚动更新卡住通常是因为新Pod一直无法就绪。排查步骤:

# 查看Deployment状态 kubectl rollout status deployment/web # 查看新ReplicaSet的Pod状态 kubectl get pods -l app=web # 查看Pod事件 kubectl describe pod <new-pod> # 查看Pod日志 kubectl logs <new-pod>

常见原因包括:镜像拉取失败、健康检查配置错误、资源不足、配置挂载失败。如果是健康检查问题,可以临时调整探针参数:

livenessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 30 periodSeconds: 10 failureThreshold: 3

注意:initialDelaySeconds太短会导致Pod还没启动完就被判定为不健康,然后被反复重启。对于启动慢的应用,可以配合startupProbe使用。

5.3 StatefulSet的Pod无法启动,数据会丢吗?

不会。StatefulSet的Pod重建后仍然挂载原来的PVC,数据不会丢失。但如果PVC本身有问题(比如StorageClass不支持动态供给、PV容量不足),Pod会一直Pending。排查方法:

# 查看PVC状态 kubectl get pvc # 查看PV状态 kubectl get pv # 查看PVC详情 kubectl describe pvc>kubectl get storageclass kubectl get storageclass -o jsonpath='{.items[?(@.metadata.annotations.storageclass\.kubernetes\.io/is-default-class=="true")].metadata.name}'

5.4 DaemonSet没有覆盖所有节点,怎么回事?

DaemonSet默认会在所有节点上创建Pod,但以下情况会导致某些节点被跳过:

  • 节点有Taint,DaemonSet没有对应的Toleration
  • 节点被cordon(不可调度)
  • 节点资源不足
  • DaemonSet配置了nodeSelector或affinity

排查命令:

# 查看DaemonSet状态 kubectl get daemonset node-agent -o wide # 查看节点Taint kubectl describe node <node> | grep Taints # 查看DaemonSet的Toleration kubectl get daemonset node-agent -o jsonpath='{.spec.template.spec.tolerations}'

5.5 Job执行失败,如何快速定位?

Job失败时,先看Pod日志和事件:

# 查看Job状态 kubectl get job db-migration # 查看Job创建的Pod kubectl get pods -l job-name=db-migration # 查看Pod日志 kubectl logs <pod> # 查看Pod事件 kubectl describe pod <pod>

如果Job因为backoffLimit达到上限而失败,可以调整重试策略:

spec: backoffLimit: 6 activeDeadlineSeconds: 600

实操心得:对于数据库迁移这类任务,建议设置activeDeadlineSeconds,防止Job卡死无限重试。同时把restartPolicy设为Never,这样失败的Pod会保留下来,方便排查。

5.6 Controller Manager挂了,集群会怎样?

kube-controller-manager是运行所有内置Controller的组件。如果它挂了,集群的“自愈”能力会暂时失效:Pod挂了不会重建,滚动更新会暂停,节点状态不会更新。但已经运行的Pod不受影响,业务流量也不会中断。

排查Controller Manager状态:

# 如果是kubeadm部署的集群 kubectl get pods -n kube-system | grep controller-manager # 查看日志 kubectl logs -n kube-system kube-controller-manager-<node-name>

常见问题包括:证书过期、API Server连接失败、etcd不可达。如果是证书问题,需要更新证书后重启Controller Manager。

6. 进阶话题:自定义Controller与Operator模式

6.1 什么时候需要写自定义Controller?

K8s内置的Controller覆盖了大部分通用场景,但如果你有特定的业务逻辑,比如“当数据库主节点故障时自动切换”、“当配置变更时自动重启相关服务”,就需要自定义Controller。

自定义Controller的核心是CRD(Custom Resource Definition)+ Controller。CRD定义你的自定义资源类型,Controller监听这个资源的变化并执行调谐逻辑。这套模式就是Operator模式。

6.2 Operator模式的核心组件

一个典型的Operator包含:

  • CRD:定义自定义资源的结构,比如MySQLCluster、RedisFailover。
  • Controller:监听CRD实例的变化,执行调谐逻辑。
  • Reconcile函数:核心业务逻辑,对比期望状态和实际状态,执行操作。

用Operator SDK(基于Go)或Kopf(基于Python)可以快速开发Operator。核心逻辑是:

@kopf.on.create('mycompany.com', 'v1', 'mysqlclusters') def create_mysql_cluster(spec, **kwargs): # 读取期望状态 replicas = spec.get('replicas', 3) version = spec.get('version', '8.0') # 创建StatefulSet create_statefulset(replicas, version) # 创建Service create_service() # 更新状态 return {'status': 'created'}

6.3 自定义Controller的注意事项

  • 幂等性:Reconcile函数可能被多次调用,必须保证多次执行结果一致。
  • 错误处理:不要直接panic,而是返回错误让Controller重试。
  • 状态更新:及时更新CRD的status字段,方便用户查看进度。
  • 资源清理:删除CRD实例时,要清理关联的K8s资源,避免资源泄漏。
  • 权限控制:Controller需要RBAC权限,最小化授权原则。

实操心得:写自定义Controller时,建议先用kubectl get和kubectl describe手动模拟一遍调谐逻辑,确认每一步操作都正确,再写成代码。这样能避免很多逻辑错误。

7. 生产环境Controller调优经验

7.1 合理设置资源请求和限制

Controller管理的Pod一定要设置resources.requests和resources.limits。不设置requests会导致调度器无法准确判断节点资源,可能把Pod调度到资源不足的节点上。不设置limits会导致Pod可能耗尽节点资源,影响其他Pod。

resources: requests: cpu: 100m memory: 128Mi limits: cpu: 500m memory: 256Mi

注意:requests和limits的比值建议在1:2到1:4之间。比值太小(比如1:1)会导致资源浪费,比值太大(比如1:10)会导致Pod被OOM Kill的风险增加。

7.2 配置合理的健康检查

健康检查是Controller判断Pod是否就绪的依据。配置不当会导致Pod被误杀或流量被转发到未就绪的Pod。

livenessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 15 periodSeconds: 10 timeoutSeconds: 3 failureThreshold: 3 readinessProbe: httpGet: path: /ready port: 8080 initialDelaySeconds: 5 periodSeconds: 5 timeoutSeconds: 2 failureThreshold: 2
  • livenessProbe:判断容器是否存活。失败超过failureThreshold次后,容器会被重启。
  • readinessProbe:判断容器是否就绪。失败后,Pod会从Service的Endpoints中移除,不接收流量。
  • startupProbe:判断容器是否启动完成。启动期间,livenessProbe和readinessProbe不生效。

7.3 滚动更新参数调优

对于高可用服务,建议设置:

strategy: type: RollingUpdate rollingUpdate: maxSurge: 25% maxUnavailable: 0

这样更新过程中始终有100%的Pod可用,但会临时多出25%的Pod。如果资源紧张,可以设置maxUnavailable: 25%,maxSurge: 0,这样不会多占资源,但可用性会暂时降到75%。

7.4 使用PodDisruptionBudget保护关键服务

PDB(PodDisruptionBudget)用于限制自愿 disruptions(如节点维护、驱逐)对Pod的影响。比如你希望任何时候至少有2个Pod可用:

apiVersion: policy/v1 kind: PodDisruptionBudget metadata: name: web-pdb spec: minAvailable: 2 selector: matchLabels: app: web

这样在执行kubectl drain时,如果会导致可用Pod少于2个,驱逐会被阻止,直到有新的Pod就绪。

7.5 监控Controller的关键指标

生产环境建议监控以下指标:

指标说明告警阈值
kube_deployment_status_replicas_unavailable不可用副本数> 0 持续5分钟
kube_replicaset_status_ready_replicas就绪副本数< 期望值
kube_statefulset_status_replicas_readyStatefulSet就绪副本数< 期望值
kube_daemonset_status_number_unavailableDaemonSet不可用Pod数> 0
kube_job_status_failedJob失败数> 0

这些指标可以通过Prometheus采集,配合Grafana展示和Alertmanager告警。

8. 个人实操体会与建议

Controller是K8s的灵魂,理解了Controller,才算真正理解了K8s的运作方式。我刚开始学的时候,总是把Deployment、ReplicaSet、Pod的关系搞混,后来画了一张图贴在显示器旁边,每天看几遍,慢慢就记住了。现在回头看,其实核心就一句话:Deployment管版本,ReplicaSet管副本,Pod管运行。

在实际操作中,我踩过最大的坑是StatefulSet的存储问题。有一次测试环境用emptyDir做存储,Pod重建后数据全丢了,我还以为是StatefulSet的bug。后来才明白,emptyDir是节点本地临时存储,Pod删除就没了。StatefulSet要配合PVC才能保证数据持久化,这个点新手特别容易忽略。

另一个坑是滚动更新的maxUnavailable参数。有一次我设置了maxUnavailable: 0,maxSurge: 0,结果更新直接卡死,因为K8s既不能多创建Pod,也不能少Pod,滚动更新无法进行。后来改成maxSurge: 1,maxUnavailable: 0,更新就顺畅了。

最后分享一个小技巧:排查Controller问题时,养成“从下往上”看的习惯。先看Pod状态和事件,再看ReplicaSet,最后看Deployment。因为Pod是最底层的执行单元,问题往往最先在这里暴露。kubectl describe和kubectl logs是你最好的朋友,90%的问题都能通过这两个命令找到答案。

如果你正在学习K8s,建议你亲手把Deployment、StatefulSet、DaemonSet、Job都部署一遍,然后故意制造一些故障(删Pod、改镜像、改资源限制),观察Controller的反应。这种“破坏性实验”比看十篇文档都管用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询