资讯中心

K8S Deployment实战:Pod管理、滚动更新与高可用运维指南

📅 2026/9/26 11:44:38
K8S Deployment实战:Pod管理、滚动更新与高可用运维指南
1. 为什么K8S要引入Deployment这个东西1.1 Pod的局限性与Deployment的定位先聊个基本问题K8S里最小调度单位是Pod但你在生产环境里几乎不会直接创建Pod。为啥因为Pod太“脆”了。它是有生命周期的节点挂了Pod就没了节点上的容器不会自动在其他机器上重启Pod被删除就是真的被删了。你手动创建一个Pod它挂了就是挂了没人管你。Deployment就是来解决这个问题的。它的核心定位是一个控制器负责声明“我想要多少个Pod副本”然后由K8S的控制器循环Control Loop去保证实际状态无限逼近你的期望状态。比如你写replicas: 3Deployment就会确保集群里始终有3个Pod在跑少了一个它马上重新拉起一个多了一个它会帮你回收掉。我早期刚接触K8S的时候总觉得Deployment就是个“批量建Pod的工具”后来才明白这个想法错得离谱。Deployment的本质是一套“状态管理机制”它和Pod的关系有点像Kubernetes里的“项目经理”和“一线员工”——员工干活经理负责盯着人数、盯版本、盯健康度员工栽了立刻补位版本不对了安排滚动替换。也常有朋友问“K8S和Docker到底啥区别”其实两者不是一个层面的东西。Docker解决的是“单个容器怎么打包、怎么跑”K8S解决的是“成百上千个容器怎么编排、怎么调度、怎么自愈”。Deployment就是这一整套调度体系里最常用的一张牌。1.2 Deployment能干什么副本、滚动更新、自愈Deployment的能力可以拆成三块来看。第一块是副本管理。你声明replicas: 5它就把Pod数量维持到5。这里的“维持”不是创建完就完事了而是一直盯着的。有Pod被Evicted驱离、被误删、所在节点宕机Deployment都会在别的可用节点上重建直到数量回到5。第二块是滚动更新Rolling Update。你要升级镜像版本不需要手动一个个删Pod。只需要修改镜像标签apply一下Deployment就会按照你设定的策略一批一批替换旧Pod期间服务不中断。这就是线上应用发布的基础能力。第三块是自愈与故障转移。Pod里的容器如果一直崩溃CrashLoopBackOffDeployment会按照restartPolicy配合kubelet做重启尝试节点故障时Pod会被重新调度到健康节点。把这三块能力展开来讲其实就是Deployment作为“无状态应用部署标准方案”的全部底气。1.3 Deployment和StatefulSet、DaemonSet怎么选很多人看K8S的控制器列表会眼花。Deployment之外还有StatefulSet、DaemonSet、Job、CronJob它们到底怎么分工这里分享一个最简单的判断逻辑应用无状态谁都能替代谁数据不落在本地盘用Deployment。应用有状态每个实例有固定身份、需要稳定网络标识、数据要持久化用StatefulSet。需要在每台节点上都跑一个实例比如日志采集、监控Agent用DaemonSet。跑一次性任务或定时任务用Job或CronJob。我在实际项目里见过不少把MySQL这类有状态应用塞进Deployment的结果Pod漂移后数据全乱套。这种场景应该用StatefulSet配合持久化存储或者干脆用托管数据库。Deployment不是万能的它的设计前提是“副本之间完全对等谁走了都能立刻补位”。也就是说Deployment是K8S里部署无状态应用的默认选项。你写微服务、Web应用、API网关第一反应就应该是Deployment。这句话值得刻在工位上。2. 动手编写一个Deployment从命名到字段拆解2.1 清单文件结构apiVersion、kind、metadata、specK8S所有资源都是声明式的写YAML就是和集群“签合同”。一个标准的Deployment清单长这样apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deploy namespace: production labels: app: nginx spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.25 ports: - containerPort: 80四个顶级字段一个都不能想当然apiVersionDeployment在apps/v1这个稳定版本里。早期K8S用过apps/v1beta1和apps/v1beta2新集群不要再写老版本。kind资源类型这里就是Deployment。metadata名字、命名空间、标签。注意name必须符合DNS子域名规范只能用小写字母、数字和中划线。spec核心期望状态下面展开。2.2 核心字段逐项解读replicas、selector、templatespec里的三个核心配置是Deployment的灵魂。replicas副本数。它表示期望运行的Pod数量。但这里有个很多人一开始容易忽略的点Deployment管理Pod不是直接对着Pod下手的而是通过一个中间层——ReplicaSet。你每次修改Pod模板比如换镜像Deployment都会创建一个新的ReplicaSet然后控制新旧ReplicaSet的Pod数量此消彼长。所以你kubectl get rs时能看到一串历史ReplicaSet这是回滚的“存档点”。selector这是Deployment识别自己管辖Pod的规则。它用matchLabels去匹配Pod模板里的标签。这里我必须强调selector一旦创建就不可修改想改只能删掉重建Deployment。而且selector的标签必须和template里Pod的标签对得上否则Deployment会报错。我见过同事手滑改了个标签结果Deployment找不到Pod傻等半天。template这是Pod的“蓝本”。Deployment创建Pod完全是按照template里定义的来。包括Pod的标签、容器镜像、端口、环境变量、资源限制、探针等等。你的容器配置基本都写在template下面的spec.containers里。2.3 副本数怎么定从N1到HPAreplicas不是拍脑袋定的。它决定了你的服务能扛多大流量、能容忍几台机器同时宕机。这里分享我自己的推导方法。先考虑冗余能力。比如你单副本能扛1000 QPS线上业务高峰期大概2000 QPS那至少2个副本。但如果要考虑单节点故障假设集群里3台节点同时只能坏1台而副本分布在节点上是不均衡的K8S默认调度并不保证均匀分布那最极端情况下2个副本可能都在同一台节点上节点挂了服务就全挂。所以更稳妥的是N1冗余即满足流量所需副本数再加1。再考虑滚动更新期间的资源占用。默认滚动策略maxSurge25%意味着更新时最多会额外创建25%的Pod。如果你replicas: 4更新时集群里最多可能有5个Pod同时运行这些额外Pod也要占节点资源。所以给副本数留点余量不只是给流量留余量也是给发布过程留余量。如果业务流量波动大建议直接上HPAHorizontal Pod Autoscaler。比如生产环境一个Deployment副本数是5但配置了HPA根据CPU使用率在2到10之间自动伸缩。这种弹性能力是K8S相比传统运维方式最大的魅力。2.4 给Deployment起中文名字劝你算了吧我之前看到社区里有个挺有意思的讨论“如果给K8S Deployment一个中文名字会是什么”顺着这个问题我去翻了K8S的源码和文档结论很明确Deployment的name字段必须符合DNS子域名规范只支持小写字母、数字、中划线不支持中文。这是硬性限制不是配置问题。所以如果你是做国际化项目Deployment的名字最好和业务域、环境、组件挂钩比如order-service-prod、payment-api-staging这样。我踩过最深的坑是命名太随意比如test、aaa到了排查问题的时候根本不知道哪个Deployment是哪个服务还要一个个去describe翻镜像地址效率极低。一个可参考的命名规范是应用名-环境比如gateway-prod。如果同一个应用有多个版本共存可以再加版本标识比如gateway-prod-v2。然后配合labels标注更细的维度比如app: gateway、env: prod、version: v2。这样后续做灰度发布、标签路由都非常方便。3. 部署实操盘点从apply到状态确认3.1 先准备什么Namespace和镜像部署一个Deployment之前有三件事我建议先确认好。第一Namespace。如果集群规模不大用default当然也行。但一旦团队人多、项目多建议每个项目或每条产品线一个独立的Namespace用ResourceQuota控制资源上限用NetworkPolicy做隔离。我见过最乱的情况是所有人都在default里折腾某天一个测试服务抢占大量CPU把生产服务给拖死了。没有隔离事故只是时间问题。第二镜像。镜像必须能被集群里的节点拉取到。私有镜像仓库需要提前配置imagePullSecrets或者把镜像推到K8S集群各节点能访问的仓库里。镜像标签强烈建议用具体版本或commit哈希不要用latest。理由很简单latest是动态的你昨天部署的是A版本今天集群按需扩容新Pod时拉到的可能是B版本同一Deployment下新旧Pod镜像不一致出问题很难查。第三资源配额。在template里给容器设置requests和limits这是生产环境的基本素养。没有资源限制的容器就像不设预算的项目跑着跑着就开始抢占节点内存直到把整台机器搞挂。Kubelet发现节点内存不足时会开始Evict Pod往往先杀掉的是那些没设limits的应用。3.2 kubectl apply与kubectl create别混着用部署命令一般就两种kubectl create和kubectl apply。这两个命令的区别对刚接触K8S的人来说容易混淆。kubectl create是“创建”语义。它要求目标资源不存在存在就报错。它也不保留你上一次apply的配置记录。kubectl apply是“声明式更新”语义。它会做三方合并上次配置、当前配置、线上配置资源不存在就创建存在就按需更新。我强烈建议一律使用kubectl apply这样才能配合kubectl rollout等命令做版本管理。实际部署命令kubectl apply -f deployment.yaml -n production如果配置文件在远程URL上也可以直接apply但生产环境我更倾向把YAML放进Git仓库配合CI/CD流水线执行。这样每一次部署都有记录出问题可以回溯。3.3 怎么确认Deployment真的部署好了apply命令执行完输出显示deployment.apps/nginx-deploy created这时候远没结束。需要从三个层级确认状态。第一层Deployment状态。kubectl get deployment nginx-deploy -n production输出里的READY列显示类似3/3表示3个副本中3个可用。如果显示1/3说明还有副本没就绪需要进一步排查。UP-TO-DATE列表示有几个副本已经更新到最新模板AVAILABLE表示几个副本可供服务。第二层ReplicaSet状态。kubectl get rs -l appnginx -n production这里能看到当前以及历史的ReplicaSet列表DESIRED和CURRENT应该一致。第三层Pod状态。kubectl get pods -l appnginx -n production重点看STATUS列正常是Running。如果是Pending、ContainerCreating、CrashLoopBackOff、ImagePullBackOff都是异常状态需要kubectl describe pod pod-name -n production看详细事件。3.4 Deployment状态值里藏着的门道kubectl get deployment输出的状态列看起来只是几个数字但背后的判断逻辑很关键。K8S对Deployment的可用性有明确定义Deployment处于可用状态需要同时满足三个条件——最新ReplicaSet已经达到期望副本数、最新ReplicaSet已经完成滚动更新且新旧ReplicaSet副本数符合预期、Deployment的Progressing状态没有超时。有个很常见的坑Deployment的AVAILABLE显示3/3但滚动更新后老ReplicaSet的Pod迟迟不缩容比如kubectl get rs看到新旧两个ReplicaSet都有Pod在跑那说明滚动更新可能卡在了终止Pod的阶段。这时候要看Pod是不是卡在Terminating通常是PreStop钩子执行太久或者节点资源不够导致新Pod无法被调度。想快速看Deployment的整体状态可以用kubectl rollout status deployment/nginx-deploy -n production这条命令会持续输出滚动更新的过程直到全部完成或者报错。发布脚本里加上它比干等yaml文件apply完要靠谱得多。4. 发布与回滚滚动更新机制拆解4.1 滚动更新策略maxSurge和maxUnavailable怎么配Deployment最爽的功能之一就是无损发布。这依赖的是strategy字段。默认策略是RollingUpdate还有一种是Recreate先把旧Pod全删了再建新的会导致短暂停机适合不能新旧共存的场景。RollingUpdate有两个关键参数strategy: type: RollingUpdate rollingUpdate: maxSurge: 25% maxUnavailable: 25%maxSurge更新时最多允许超出期望副本数的Pod数量可以是绝对数值或百分比。默认25%副本数为4时更新期间最多5个Pod。maxUnavailable更新时最多允许不可用的Pod数量。默认25%副本数为4时最多允许1个Pod短暂不可用。这两个参数一起决定了发布的速度和风险。我的建议是核心业务、流量敏感的maxUnavailable: 0maxSurge: 1。先多起一个副本确认就绪后再杀旧的保证整个发布过程没有容量缺口代价是发布慢一点、资源占用多一点。资源紧张、发布频次高maxSurge: 25%maxUnavailable: 25%。发布快但高峰期可能造成短暂容量下降。4.2 更新触发与Pod重建的完整流程更新一个Deployment很简单改镜像版本后apply即可kubectl set image deployment/nginx-deploy nginxnginx:1.26 -n production也可以用直接修改YAML的方式改完apply。触发更新后Deployment的完整动作是这样的生成一个新的ReplicaSet期望副本数先按照maxSurge比例增加。新ReplicaSet的Pod启动并等待readinessProbe通过。新Pod就绪后旧ReplicaSet的Pod开始按maxUnavailable比例缩容。重复上面步骤直到新ReplicaSet达到期望副本数旧ReplicaSet缩容到0。这里我强烈建议给容器配置就绪探针readinessProbe尤其是HTTP服务readinessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 3 periodSeconds: 5没有就绪探针K8S默认Pod里容器启动就算就绪新Pod还没准备好接收流量就被拉进Service端点列表线上会出现发布瞬间大量5xx的情况。我早期踩过这个坑后来所有生产Deployment都强制加readinessProbe。4.3 回滚操作rollout undo与修订版本管理发布出问题怎么办Deployment的另一个优势就是回滚速度极快。K8S会把每次Deployment的模板变化记录为一次“修订版本”revision可以随时回滚到任意历史版本。查看历史版本kubectl rollout history deployment/nginx-deploy -n production回滚到上一个版本kubectl rollout undo deployment/nginx-deploy -n production回滚到指定版本kubectl rollout undo deployment/nginx-deploy -n production --to-revision2回滚的操作本质上是触发一次新的滚动更新把Pod模板改回历史版本。所以回滚过程也是无损的不会停机。需要说明的是回滚也会生成新的revision也就是说回滚后你会有一份对应旧模板的新“存档”。rollout history里每个revision对应一次模板变化但如果你只是改了副本数没有改template这不会产生新revision。这个细节搞清楚回滚时就不会错乱。我还习惯在每次发布前手动给Deployment打一个注解标记发布原因kubectl annotate deployment/nginx-deploy kubernetes.io/change-causeupgrade nginx to 1.26 for CVE fixes -n production这样rollout history输出里就可以看到每次发布的原因多人协作时特别有用。5. 日常运维排坑实录5.1 ImagePullBackOff镜像拉不下来kubectl get pods看到ImagePullBackOff最直接的原因是节点拉取镜像失败。排查步骤kubectl describe pod pod-name -n productionEvents里一般会写明原因常见几类401 Unauthorized私有仓库认证失败检查imagePullSecrets有没有配置。manifest unknown镜像标签不存在去仓库确认这个tag有没有推送。connection refused节点访问不到仓库需要排查网络和防火墙规则。没有imagePullSecretsPod启动用的是默认的docker config如果集群和仓库不在同一环境需要额外配置。我作为一个习惯写YAML镜像地址时一定写全地址比如registry.example.com/library/nginx:1.26不要写nginx:1.26这种简写省得在不同环境里产生歧义。5.2 CrashLoopBackOff容器起来就挂CrashLoopBackOff表示容器启动后立刻崩溃kubelet反复重启间隔时间指数退避10秒、20秒、40秒、80秒...。排查思路kubectl logs pod-name -n production --tail100 --previous--previous能看容器上一次崩溃前的日志这个很关键因为当前容器可能已经退出直接kubectl logs只能拿到空输出或者只拿到崩溃后的启动日志。常见原因有启动命令依赖的配置不存在、环境变量缺失、数据库连接失败、端口冲突、启动参数错误。还有一个容易被忽视的问题容器镜像的entrypoint在变更工作目录后需要重新调整否则容器一起就退出。如果日志看着没问题但还是一直重启可以进容器手动执行启动命令kubectl exec -it pod-name -n production -- /bin/sh手动启动进程看它报什么错。比对着日志猜要快得多。5.3 Pending状态Pod卡在排队Pod一直Pending说明调度器还没把它分配到节点上。描述Pod后常见事件是0/3 nodes are available: 1 Insufficient memory, 2 Insufficient cpu节点资源不够要么扩容节点要么给Deployment的requests调低。0/3 nodes are available: 1 node(s) had untolerated taint节点有污点taintPod没有对应的容忍toleration。比如Master节点通常有node-role.kubernetes.io/master污点普通Pod默认不会调度上去。0/3 nodes are available: 1 node(s) had volume node affinity conflict使用了节点亲和性或者存储卷绑定了特定节点但调度条件不满足。Pending还有一个隐蔽原因节点上有大量已终止的Pod和堆积的镜像导致磁盘压力。可以用kubectl describe node node-name看Allocated resources和Conditions如果DiskPressure为True先清理节点上的无用镜像和日志文件。5.4 证书过期这回事别等报警了才处理群里总有人问K8S集群的证书过期怎么办。这块虽然不是Deployment本身的问题但集群证书过期直接会导致kube-apiserver不可用所有Deployment操作全部失效。如果集群是用kubeadm搭建的证书有效期默认只有一年。kubeadm提供了自动续签命令kubeadm certs renew all如果每年自动续签太麻烦有两条路一是用kubeadm init时指定--certificate-ttl参数默认是8760小时一年你可以改成--certificate-ttl87600h让有效期变成十年也可以把它接入cron等定时任务定期执行续签。如果你用的是托管K8S这部分完全由云厂商负责基本不用操心。我自己踩过一次证书过期的坑那真是“整个集群什么都干不了”kubectl命令全线超时。后来就在监控里专门加了证书到期时间检查项提前30天提醒续签。这个检查比部署本身重要得多集群没了Deployment再多也是白搭。6. 高可用与资源管理生产环境必须考虑的事6.1 多Master集群高可用和Deployment的关系很多朋友问过“K8S三台Master怎么保证高可用”Master节点跑的是kube-apiserver、etcd、kube-controller-manager、kube-scheduler这几个核心组件。高可用的关键在etcd和数据一致性一般会用奇数台比如3台Master组成etcd集群通过Raft协议保证多数派可用。只要不是同时挂掉2台以上集群的控制面就能持续工作。那么Deployment在这个架构里扮演什么角色它跑在Worker节点上其可用性由集群整体健康度决定。如果Master的kube-controller-manager挂了Deployment的“期望状态不断校正”也就停了——已经跑着的Pod不会立刻挂掉但副本数的调整、滚动更新、故障Pod重建都会停摆。所以Master高可用是Deployment可用性的前提。如果你用kubekey这类工具搭建高可用集群建议保持3个Master。这样哪怕规整维护一台集群仍能正常调度Deployment的滚动更新也不会中断。6.2 资源限制与HPA别让Deployment把集群吃垮给Deployment里的容器设置resources这是生产环境的基础操作。我自己常用的写法resources: requests: cpu: 100m memory: 128Mi limits: cpu: 500m memory: 512Mirequests给调度器提供参考limits限制容器使用上限。这里有个细节值得提醒limits的CPU可以超卖但memory不能超卖。CPU是可压缩资源超了只是被限流内存是不可压缩资源超了内核会触发OOM Kill直接把容器干掉。所以内存的limits一定要根据容器实际使用情况来设千万不要拍脑袋写个大数值。副本数不想固定在3个可以配合HPA自动伸缩apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: nginx-deploy-hpa namespace: production spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: nginx-deploy minReplicas: 2 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 60这个HPA会监控Deployment所有Pod的CPU平均使用率超过60%就扩容低于60%并且持续一段时间就缩容。注意HPA调的是replicas但不改YAML里的replicas数值这是两套逻辑别混在一起看。6.3 K8S常用命令速查表存一份少踩坑Deployment相关的日常操作命令整理成一张速查表操作命令创建/更新Deploymentkubectl apply -f deployment.yaml查看Deployment列表kubectl get deployment -A查看Deployment详情kubectl describe deployment name -n ns查看滚动更新状态kubectl rollout status deployment/name -n ns修改镜像版本kubectl set image deployment/name containerimage:tag -n ns查看修订历史kubectl rollout history deployment/name -n ns回滚到上一版kubectl rollout undo deployment/name -n ns回滚到指定版本kubectl rollout undo deployment/name --to-revisionver -n ns查看Pod详情kubectl describe pod pod-name -n ns实时查看Pod日志kubectl logs -f pod-name -n ns进入Pod执行命令kubectl exec -it pod-name -n ns -- /bin/sh临时扩容kubectl scale deployment/name --replicas5 -n ns这些命令覆盖了Deployment生命周期里90%的操作需求。我不建议死记硬背把这份表存到自己的笔记里用到哪查哪比强行记命令更高效。我自己平时还有个习惯部署前先跑一句kubectl diff -f deployment.yaml它会输出本次改动和集群现状的差异。这条命令不用真的改动集群非常适合在apply之前自查一遍我靠着它避免过好几次误改镜像版本的事故。

看完文章,想为自己的企业也做一次专业网站诊断?

尧图顾问免费为您评估现有网站,并给出建站/改版建议与报价方案。

免费获取方案