运维知识
悠悠
2026年10月9日

容器跑起来以后,谁来兜住故障?K8s、Swarm、Mesos、Nomad怎么选

发布完成,容器全部显示“运行中”,业务监控却开始报错。旧实例正在退出,新实例还没准备好,流量已经打了进去。

另一种情况更直接:承载服务的主机故障了。镜像还在、配置也在,但服务能否自动恢复,取决于是否有人替你调度新的实例,以及剩余节点有没有容量。

这时再去比较“哪个工具功能更多”,很难解决眼前的问题。真正需要回答的是:谁判断实例可用,谁接住流量,发布失败以后怎样退回?

我看容器编排,通常先看这三件事。Kubernetes、Docker Swarm、Apache Mesos、Nomad都值得理解,但它们的适用边界并不相同。

容器启动成功,离业务可用还有多远?

Docker解决了应用怎样被打包、运行的问题。多台主机上的服务持续运行,还需要处理副本分布、节点故障、服务发现、流量接入、滚动更新和资源分配。

例如一个订单查询服务有三个副本:进程退出,需要按重启策略恢复容器;副本缺失,控制器需要补足数量;节点失联,需要按故障判定与调度规则在可用节点上恢复工作负载;新版本上线,需要根据健康信号逐步替换实例。

但“具备这些机制”不等于“业务必然恢复”。没有多余容量,新副本可能一直等待调度;探针写错,运行中的坏实例仍可能接流量;数据库结构已经发生不兼容变化,退回应用镜像也可能继续报错。

需要特别分清自动恢复和自动扩容。维持期望副本数,是控制器的常见职责;根据负载增加副本,则需要配置指标、扩缩容策略和容量。Kubernetes的HPA不会在创建Deployment时自动替你设置好。

编排选型的核心,是故障恢复、发布治理与团队能力能否匹配。

我会先写下三个业务条件:允许中断多久,哪些服务带有持久状态,团队能投入多少平台维护时间。这比先罗列功能更有价值。

四种工具,要放回各自的使用场景

Kubernetes:适合需要持续建设平台能力的团队。

它通过声明式资源和控制器管理应用,提供工作负载调度、Service、健康探针、滚动更新等机制,并有丰富的扩展生态。多服务、多团队、跨环境交付,或者需要细致的权限、网络和配置治理时,Kubernetes通常更容易形成统一平台。

代价也很明确:控制平面、网络、存储、证书、升级和可观测性,都要有人负责。使用托管集群可以减少部分维护工作,但应用探针、容量、发布策略和数据恢复仍属于团队自己的责任。

还要澄清一个容易混淆的地方:Docker构建的兼容镜像,可以运行在符合要求的Kubernetes环境中;这并不意味着节点必须使用Docker Engine作为容器运行时。选型时要确认实际CRI运行时,以及镜像架构、操作系统和版本兼容性。

Docker Swarm:适合以Docker为主、需求较集中且希望降低上手成本的团队。

这里说的是当前Docker Engine内置的Swarm mode。它通过service管理任务、副本和更新,团队可以沿用熟悉的Docker CLI。对于服务数量有限、复杂扩展需求较少的场景,它有务实价值。

简单不代表没有集群运维。manager节点数量、仲裁可用性、网络连通、镜像拉取、持久数据和更新失败策略,都需要规划。通常应使用奇数个manager并分散故障域;失去多数manager的仲裁,会影响集群管理和调度,不能只检查worker上的容器是否还在运行。

也不要把单机Compose文件直接当成完整的生产方案。迁入Swarm前,应验证所用配置项、数据卷行为以及服务更新策略是否满足集群部署需求。尤其是本地数据卷,任务换到另一台主机,并不意味着数据会一起迁过去。

Apache Mesos:重点已转向存量维护与迁移。

Mesos将集群资源提供给上层框架使用,历史上可以结合Marathon管理长期运行的应用,也支持多类工作负载。

Apache官方已将Mesos标记为退役项目:2025年8月退役,2025年10月转入Apache Attic。这改变了它在选型表中的位置,不能继续与活跃维护的工具等量推荐。

退役不会让现有系统立即停止运行,但持续维护责任需要重新确认。已有Mesos系统应盘点实际版本、上层框架、依赖组件、补丁来源以及迁移成本,再按业务依赖逐步制定替换计划。

对新项目,不宜仅凭旧资料中的“大规模、高可用”标签立项。历史能力仍值得理解,持续维护能力却必须单独判断。

Nomad:适合希望统一调度不同工作负载、同时接受自行组合周边能力的团队。

Nomad使用job描述工作负载,支持Docker等任务驱动,也可以通过相应驱动运行其他类型任务。对既有容器服务,又有批处理或普通进程任务的环境,它提供了另一条路线。

“支持多种工作负载”有前提:取决于任务驱动、操作系统、权限和插件兼容性,隔离能力也随驱动变化。Docker驱动需要客户端主机上的Docker daemon及相应socket访问权限;QEMU等驱动的存在,也不意味着能直接调度任意VMware虚拟机。

额外驱动可能需要另装插件。服务发现、密钥管理、入口流量和监控如何接入,同样需要单独设计。

如果只想在一台机器上启动几个内部服务,暂时没有跨节点恢复需求,未必需要立即引入集群。明确单机故障的影响并准备恢复方案,也是合理的阶段性选择。

先拿一个无状态服务,把恢复路径走通

下面以Kubernetes上的无状态HTTP服务为例。命令适用于支持Deployment apps/v1与startupProbe的集群;执行者需要相应资源的读取权限,发布时还需要更新Deployment的权限。

代码中的<namespace>等内容都是占位符,执行前必须替换为实际值。服务涉及数据库变更时,需要额外审核数据兼容性。

先进行只读确认,避免在错误集群里排查或发布:

kubectl config current-context
kubectl version
kubectl get nodes -o wide
kubectl -n <namespace> get deployment <deployment> -o wide
kubectl -n <namespace> get hpa

检查客户端与服务端版本是否在官方支持的版本偏差范围内,节点是否Ready,Deployment的READY、UP-TO-DATE和AVAILABLE是否符合预期。

没有HPA,就不能假定存在HPA自动扩容;节点Ready也只说明节点状态,需要继续检查应用。

下面这组只读检查,将控制器状态与应用日志放在一起看:

kubectl -n <namespace> describe deployment <deployment>
kubectl -n <namespace> get pods -l app=<app> -o wide
kubectl -n <namespace> get events --sort-by=.metadata.creationTimestamp
kubectl -n <namespace> describe pod <pod>
kubectl -n <namespace> logs <pod> -c <container> --since=10m --tail=200 --timestamps

Deployment重点看Conditions和更新策略;Pod重点看Ready、重启次数、容器状态与Events。

Pending应结合调度事件检查资源、约束或存储;ImagePullBackOff检查镜像地址与拉取授权;运行中但未Ready,关联探针失败原因与应用初始化日志。

事件列表不是完整审计记录。排查时保存时间窗、请求标识和版本信息;若容器重启过,可给日志命令增加--previous,检查上一次容器实例的日志。日志可能包含用户信息和令牌,保存或共享前必须脱敏。

探针要回答不同的问题:

  • readiness判断当前能否承接请求,失败使Pod不就绪,默认Service不会继续向它分配正常流量,但不会因此重启容器。
  • liveness判断进程是否需要重启,连续失败达到阈值后触发恢复动作。
  • startup为初始化提供保护,在它成功前不会执行liveness和readiness。

不要让liveness直接绑定所有下游依赖,否则数据库短暂抖动可能触发大量容器重启。

下面是Deployment配置片段,需要合并到完整清单。路径由应用实现,数值仅用于示例,必须根据启动耗时和负载验证后调整:

spec:
  replicas: 3
  revisionHistoryLimit: 5
  minReadySeconds: 10
  progressDeadlineSeconds: 300
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0
      maxSurge: 1
  template:
    spec:
      containers:
        - name: api
          image: <registry>/<image>:<tested-tag>
          ports:
            - containerPort: 8080
          resources:
            requests:
              cpu: 200m
              memory: 256Mi
            limits:
              memory: 512Mi
          startupProbe:
            httpGet:
              path: /startup
              port: 8080
            periodSeconds: 5
            failureThreshold: 24
          readinessProbe:
            httpGet:
              path: /ready
              port: 8080
            periodSeconds: 5
            failureThreshold: 2
          livenessProbe:
            httpGet:
              path: /live
              port: 8080
            periodSeconds: 10
            failureThreshold: 3

requests影响调度,不能随意压低来“塞进更多副本”。内存limit不足可能导致OOMKilled;是否设置CPU limit,应结合资源治理策略决定。

采用CPU利用率HPA时,需要正确配置相关容器的CPU requests及资源指标API,后者通常由Metrics Server提供;按QPS扩缩容则需要相应的指标适配。HPA调整工作负载副本,不会直接增加节点,下游承载能力也必须同步评估。

maxUnavailable: 0要求滚动更新按保留可用副本的策略推进,但会需要额外容量,资源不足时更新可能停滞。终止中的旧Pod还可能继续占用资源,实际资源峰值不能以replicas + maxSurge为硬上限。

minReadySeconds只能要求持续Ready一段时间,不能识别“探针通过但业务报错”。progressDeadlineSeconds提供进展失败信号,也不会自动替你回滚。

发布失败以后,怎样有依据地退回?

发布前保存配置,并记录可恢复的版本:

kubectl -n <namespace> get deployment <deployment> -o yaml > deployment-before.yaml
kubectl -n <namespace> rollout history deployment/<deployment>
kubectl -n <namespace> diff -f <manifest.yaml>

YAML快照用于比对和取证,恢复应优先使用Git中的已验证声明式配置。diff退出码0表示无差异,1表示有差异,大于1应检查执行错误。

备份只包含Deployment也不够,关联的ConfigMap、Secret引用和版本应一并记录;敏感内容须保存到受控位置。

更改镜像、探针或资源都属于变更。先在预发布环境验证,再用独立Deployment与实际流量路由实现小范围验证。普通滚动更新会替换整个Deployment,不能因为一次只增加一个Pod,就把它称为按流量比例控制的灰度。

确认备用容量、探针、会话处理与数据库兼容性后,才执行已评审的清单:

kubectl -n <namespace> apply -f <manifest.yaml>
kubectl -n <namespace> rollout status deployment/<deployment> --timeout=5m
kubectl -n <namespace> get pods -l app=<app> -o wide

成功条件不能只有“successfully rolled out”。还要检查新副本是否稳定Ready,重启是否增加,以及请求成功率、延迟分位数、下游连接数和业务结果是否正常。

观察窗口应覆盖初始化、缓存预热和一次代表性负载周期;停止阈值按业务SLO与发布前基线确定。

超时表示客户端停止等待,当前Deployment可能仍在继续更新。出现异常后,先保存事件与日志,并停止后续批次发布。

对于本文的无状态示例,若已确认目标历史版本仍保留,且与当前配置和数据兼容,应尽快执行下面的明确修订号回滚:

kubectl -n <namespace> rollout undo deployment/<deployment> --to-revision=<revision>
kubectl -n <namespace> rollout status deployment/<deployment> --timeout=5m

这会回退Deployment的Pod模板,不会替你恢复数据库、外部配置或持久卷数据。

已经删除字段、修改数据语义或运行不可逆迁移时,不能把undo当成完整恢复方案。优先采用兼容旧版本的分阶段数据库变更,并单独验证备份与恢复路径。

“全部Running,却有错误”的证据怎样串起来?

下面用一个脱敏后的典型场景说明。时间、指标与日志均为示例,用于说明判断过程。某无状态查询API有三个副本,发布新版本后,查询接口短时间内出现5xx,其他服务没有同步异常。

示例时间线是:14:00开始更新;14:01第一个新Pod显示Ready;14:01至14:03查询接口错误率从发布前的0.1%升至6%,P95耗时从180毫秒升至900毫秒。异常请求主要落在新版本实例,旧版本同一接口表现正常。

初判可能是数据库变慢或节点资源不足。检查同期数据库连接等待、慢查询与节点状态,没有发现同步变化;Pod没有重启,调度事件也没有资源不足。

这个结果缩小了范围,但单凭“数据库指标正常”还不能宣布数据库完全无关。

继续用请求标识对照新旧版本日志。失败请求在新实例中出现WARMUP_INCOMPLETE;该实例14:01:10已经通过readiness,而应用日志直到14:02:20才记录warmup complete。相同初始化间隔在另一个新副本上重复出现。

结合代码检查,发现/ready只判断HTTP进程启动,没有检查本地查询缓存是否初始化完成。

这些证据支持的根因是:新版本必须等待本地预热结束才能正确查询,但Ready提前变为true,流量进入了尚未具备处理能力的实例。

临时止损前,保存异常实例日志和部署版本,确认旧版本仍有足够承载能力,而且本次发布没有数据库结构变更。在小范围验证回退版本后,按已审核的回滚方案恢复,持续观察查询成功率、延迟与数据库负载。

最终修复让/ready在本地初始化未完成时返回503,完成后返回200;/live保持进程级健康判断。启动保护时间按实际初始化耗时调整,不能通过无限增加探针宽限掩盖初始化卡死。

修复后的验证包含冷启动、慢预热、初始化失败以及正常发布四类测试。示例验收结果是:预热期间实例不进入Ready,旧副本继续服务,初始化结束后新副本才接入;错误率回到发布前区间。

防复发动作则是将冷启动业务检查纳入发布门禁,并对“Pod Ready但业务成功率下降”增加监控。

选型之前,把这张清单贴到发布流程里

  • 务必确认版本、上下文和权限。错集群操作的后果,比选错一个参数更严重。
  • 不要把Running当成可用。健康信号必须覆盖应用实际接流量的必要条件,否则编排系统会忠实执行错误判断。
  • 先采证,再变更。日志、事件、版本与时间线丢失以后,恢复服务也难以解释根因。
  • 不要假定副本越多越可靠。副本需要分散故障域,还要有备用容量;扩容也可能压垮共享数据库。
  • 务必演练一次发布失败。验证超时、更新停滞和业务指标恶化时由谁处理,回退后怎样验收。
  • 禁止把应用回退等同于数据恢复。数据库迁移、外部配置和持久数据必须有独立的兼容与恢复方案。
  • 不要只比较安装难度。项目维护来源、升级路径、人员能力和夜间故障处理成本,决定方案能否长期运行。

Kubernetes适合平台能力的持续建设;Swarm强调Docker环境中的集群管理便利;Nomad为不同工作负载提供另一种调度选择;已退役的Mesos重点是存量维护与迁移,新建平台不建议作为常规候选。

工具名称只能给出方向,业务恢复演练才能验证选择。

今天就可以做一个动作:挑一个无状态服务,记录从“发布异常”到“确认恢复”的完整过程,检查探针、容量、回滚和业务验收是否有人负责。这个过程中的空白,就是下一轮平台建设应该补齐的地方。

如果这篇文章对你梳理容器平台有帮助,欢迎关注、点赞、点个在看,也可以转发给一起负责发布和稳定性的同事。

公众号:耕云躬行录

个人博客:躬行笔记

文章目录

博主介绍

热爱技术的云计算运维工程师,Python全栈工程师,分享开发经验与生活感悟。
欢迎关注我的微信公众号@运维躬行录,领取海量学习资料

微信二维码