如果让我用一个词来形容2014到2019年之间的容器编排器之争我会说这是一场还没有真正拉开大幕就已经结束了的战斗。Kubernetes、Docker Swarm、Apache Mesos这三条技术路线同时在容器编排器这条赛道上竞速当年很多人都在等着看一场旗鼓相当的硬仗但实际的发展轨迹几乎是一边倒。Swarm 和 Mesos 并不是输在某个功能点上而是输在接口策略、社区结构和技术理念的底层差异上。这篇文章想以我这些年在生产环境中摸爬滚打的视角把这场战斗从头复盘一遍也顺便聊聊我在切换技术栈时踩过的坑以及这场竞争留给普通技术人的经验教训。无论你当年是站 Swarm 的老运维还是从一开始就押注 Kubernetes我觉得这篇文章都不妨一读。它不只是讲谁赢了谁输了更想讲清楚一个问题为什么有些技术还没有开打就已经分出了胜负。1. 容器编排器这条赛道为什么会在2014年前后突然拥挤1.1 容器从“跑起来”到“管起来”编排器的诞生逻辑容器技术刚火起来那两年大家最兴奋的是“一次构建到处运行”。一个镜像打出来无论在开发机还是生产服务器上docker run一下就能跑起来这比传统虚拟机快太多。我在2015年前后接手公司容器化改造时最初的思路也很单纯把每个服务打包成镜像写个脚本批量启动就完事了。但规模一上来问题就来了。假设你只有三五台机器、二三十个容器确实可以靠脚本和自制表格管理。可当你面对几十台服务器、几百个容器时就会遇到一连串绕不开的问题容器应该调度到哪台机器上某台节点宕机了容器会不会自动在其他机器恢复A 服务怎么通过服务名找到 B 服务滚动更新期间如何保证访问不中断配置文件改了之后怎么让所有实例同步感知这些问题加在一起就是容器编排器要解决的核心命题大规模容器的生命周期管理。它至少涵盖调度、服务发现、负载均衡、自愈、滚动更新、配置管理这几件事。我常拿物流调度中心来类比容器像货物每台服务器像仓库只招几个人的时候你自己开着叉车就能搞定配送可货物上千件、仓库几十个之后没有调度中心就只能靠运气。编排器就是这个调度中心。1.2 赛前的三张入场券Swarm、Mesos、Kubernetes2014年到2015年是容器编排器最热闹的窗口期三张最有分量的入场券分别握在 Docker、Mesos 和 Kubernetes 手里。Docker Swarm 的出身最“根正苗红”它由 Docker 官方推出核心理念是“让编排像 Docker 本身一样简单”。在 Docker 1.12 之前Swarm 还是一套独立集群工具到了 Docker 1.12Docker 直接把 Swarm Mode 内置进引擎你只要用docker swarm init和docker service create就能拉起一个集群学习路径几乎为零。它最大的优点是复用 Docker API操作习惯和单机保持一致缺点则是抽象能力偏弱集群扩展有上限生态也比较封闭。Apache Mesos 则来自大数据圈子。它本来是加州大学伯克利分校 AMPLab 为了解决 Hadoop、Spark 等计算框架的资源共享问题而做的资源管理器。Mesos 把 CPU、内存、磁盘等资源池化再通过框架Framework机制分配到不同上层应用。跑在 Mesos 上的 Marathon 负责管理长时间运行的服务所以也可以当成容器编排器来用。Mesos 的资源调度效率很高集群规模能到万级节点但架构极重组件多概念多一般团队很难驾驭。Kubernetes 是 Google 基于内部 Borg 和 Omega 系统的经验打造的2014年开源。它不像 Swarm 那样讨好“老 Docker 用户”而是从一开始就引入了 Pod、Service、Label、ReplicaSet 这一整套新概念还是以 API 为核心来驱动系统。早期学习曲线比较陡但设计体系相当完整也为后续大量生态项目预留了空间。后来我也慢慢理解这种“陡峭”其实是用一次学习成本换来了更强大的表达力。三者对比下来Swarm 像电动车好上手城市通勤没问题但跑长途货运就力不从心Mesos 像重型卡车能装很多货但你需要配备专门的司机团队来伺候它Kubernetes 则更像一整套物流系统初看复杂但它能用标准接口容纳各种车辆、仓库和配送方式。1.3 被忽视的旁观者Nomad、Rancher 和云厂商平台除了这三家旁观席上其实还有几个角色。HashiCorp 在2015年发布了 Nomad主打单二进制部署支持 Docker、QEMU、Java 等多种工作负载调度能力也非常强。但 Nomad 的定位始终更偏向“调度器”在服务发现、配置管理、应用发布这些“应用平台”能力上它选择依赖 Consul、Vault 等自己生态里的其他产品而不是做成一套统一的编排体系。我觉得这是 Nomad 当时没进入核心战局的重要原因。Rancher 也是一个很有意思的变量。它起家的时候不是自己造编排器而是做“容器管理平台”早期对 Swarm、Kubernetes、Mesos 都有支持。但在2018年发布的 Rancher 2.0它全面转向 Kubernetes 底座。这个选择本身就说明问题连那些想“中立地管多种编排器”的平台最后都意识到只能梭哈其中一家。云厂商也一样各大公有云在2017年前后陆续推出托管的 Kubernetes 服务由此彻底终结了“编排器仍在争论期”的局面。2. 决定胜负的三个关键节点2.1 接口标准Kubernetes 如何用 CRI/CNI/CSI 打开生态容器编排器之战里Kubernetes 做得最聪明的一件事就是定义接口而不是绑定具体实现。它自己并不关心你用的是哪个容器运行时、哪家网络方案、哪种存储插件只要你实现了它约定的标准接口就能无缝接入。容器运行时层面有 CRIContainer Runtime Interface网络层面有 CNIContainer Network Interface存储层面有 CSIContainer Storage Interface。这三个接口的威力在于它们把“编排器”和“底层组件”彻底解耦。你用 Flannel、Calico 还是 Cilium只取决于你的网络需求而不是被编排器锁死。你用 containerd、CRI-O 还是其他运行时只要满足 CRI 就能被调度。这种“可插拔”的设计让所有人的创新都能长在 Kubernetes 身上。反观 Swarm它绑定在 Docker Engine 里网络和存储方案虽然也能用插件但整体设计是以 Docker 为中心的。用起来方便是方便可一旦你把问题从“跑容器”提升到“搭一套容器平台”这种绑定就开始显得碍手碍脚。生态位的差距就是从这里拉开的当所有人的目光从“用 Docker 跑容器”转向“用 Kubernetes 搭建平台”时谁的边界更开放谁就能吸收最多的生态力量。后来 Kubernetes 甚至通过 CRI 把 Docker 从核心运行时变成了“可选项”这一步对整个战局来说是决定性的。2.2 设计哲学声明式 API 为什么比命令式服务抽象更有生命力Kubernetes 还有一个让我后来非常着迷的设计就是声明式 API。它不是告诉系统“去创建5个副本”而是告诉系统“我希望有5个副本”。Controller 会持续观察当前状态一旦发现实际状态和期望状态不一致就会自动触发动作直到把它修正回来。这种控制循环模型在分布式系统里非常优雅因为它天然具备自愈能力。节点挂了控制器看到副本数不足就立刻在别的节点上补起来。Swarm 虽然是服务抽象但从 API 模型看仍然偏命令式。你执行的是一条创建服务、扩容副本的命令系统也会努力维持副本数量但它的资源模型相对单薄缺少像 Deployment、Service、ConfigMap、Ingress、NetworkPolicy 这样面向不同场景的资源表达。我举个具体的例子Kubernetes 做金丝雀发布可以通过 Deployment 和 Service 的 label selector 控制流量走向配合 Ingress 的流量权重调整整个过程都在 API 对象里显式表达Swarm 的滚动更新也能做但你要做到更复杂的灰度、分批暂停、精细回滚就得自己写很多外围脚本。API 的抽象层级决定了上层生态能长多高。Kubernetes 的声明式模型和丰富的资源类型让 Helm、Operator、GitOps 这些实践都有了发挥空间。一个技术如果能承载“更复杂的表达”它就能吸引更多工具在它上面生长工具越多用户的迁移成本就越高后来者想要弯道超车的难度就越大。2.3 社区与厂商从“多家押注”到“全体站队”技术竞争到后期拼的往往不是代码而是社区和厂商的密度。Kubernetes 在开源之后就捐给了 CNCFCloud Native Computing Foundation这意味着它不再属于 Google 一家而是由基金会治理。Red Hat 基于它做 OpenShiftIBM 收编 Red Hat 之后继续大举押注VMware、CoreOS 等企业也都投入了资源各大云厂商更是把托管 Kubernetes 作为标准产品去推。社区形成了一种“复利效应”每一个新的开源项目比如 Helm、Prometheus、Istio、Knative都优先支持 Kubernetes支持得越好用户越愿意迁移过来用户越多反过来又吸引了更多项目。而 Swarm 几乎只有 Docker 一家在维护社区热度和代码演进速度在2018年开始明显掉队Mesos 虽然也有一批铁粉但核心维护方太少生态丰富度完全跟不上。我到现在还记得2017年 Docker 官方宣布在自己的企业版里支持 Kubernetes 时那种说不出话的心情。连“对手”自己都在拥抱 Kubernetes这已经说明问题——不是 Docker 不想打而是生态的吸引力和商业的现实压力让它根本没法继续打下去。到2018年 Kubernetes 从 CNCF 毕业时市场上再谈 Swarm 和 Mesos基本就只剩下“怀旧”和“迁移”两个话题了。3. “还没拉开大幕就结束了”到底发生在哪一刻3.1 一张时间表看 Kubernetes 如何从开源走向定局我这两年复盘这场战争时习惯用时间线把关键节点列出来它比很多抽象讨论都更直观2013年Docker 开源并迅速引爆容器化概念人人都在讨论镜像和容器。2014年6月Google 开源 Kubernetes背后是 Borg/Omega 多年的内部实践。2014年底Docker Swarm 发布Mesos 也开始进入容器调度领域。2015年CNCF 成立Kubernetes 作为首个项目被捐入基金会Docker 1.12 内置 Swarm Mode 则是在2016年。2017年Docker 企业版宣布支持 KubernetesRancher 等平台陆续转向 Kubernetes 底座各大云厂商托管 Kubernetes 服务成标配。2018年3月Kubernetes 成为 CNCF 第一个毕业项目生态版图基本定稿。2019年起Mesosphere 转向 DC/OS 企业市场Docker 将容器运行时相关技术逐步移交给社区通用容器编排之争进入尾声。如果你认真看这份时间表会发现一个残酷的事实2016年到2017年胜负实际已经落定。所谓“战斗”其实只有短短两三年。很多团队可能还在办公室里争论 Swarm 简单还是 Kubernetes 复杂外围的生态、厂商、社区已经用脚投票完毕。这就是我说“还没有拉开大幕就结束了”的含义大家本来以为会有一场势均力敌的冠军赛结果裁判还没来得及吹开场哨赢家就已经穿好冠军外套了。3.2 Docker 的失焦被自己的成功和商业路径困住Docker 的失败并不是“技术不行”而是它的注意力和商业模式被分散了。Docker 公司那几年不仅要维护开源引擎还要运营 Docker Hub、推 Docker Enterprise同时还得应付容器行业快速变化带来的商业模式转型。公司精力是有限的当 Swarm 需要持续投入来对抗 Kubernetes 时Docker 却要同时打多场仗。更关键的是Kubernetes 最初其实非常依赖 Docker Runtime可后来 CRI 接口一出Docker 的位置就变得“可以替换”了。我记得 Kubernetes 在 v1.20 宣布弃用 dockershim、并在 v1.24 正式移除的时候很多人第一反应是“以后还能不能直接用 docker 命令了”。事实上Docker 命令只是一个客户端真正运行容器的是 containerd 这类运行时。Kubernetes 通过 CRI 把运行时标准握在自己手里等于是顺手把 Docker 从“不可替代的核心”变成了“可以替换的一环”。Docker 在编排器之战里输掉就算了连运行时这个老家都有点站不住脚这是我当时完全没想到的。这段历史给我的感觉是一个公司如果被自己的既有优势捆住手脚往往很难在下一场技术浪潮里继续领先。Docker 在“让容器跑起来”这件事上做到了极致但“让容器在平台上跑好”这件事它有太多包袱。3.3 Mesos 的错位资源调度不等于业务编排Mesos 没赢下通用容器编排市场我觉得核心问题是“错位”。Mesos 最擅长的是资源调度也就是把一堆机器的 CPU、内存、磁盘统一抠出来按需分配给上层框架。这个模型适合大数据作业因为一个 Spark 任务可能需要数百台机器同时并行用完资源马上释放Mesos 能把碎片资源集中再利用效率很高。但微服务应用平台需要的是另一套东西服务发现、配置下发、滚动发布、网络策略、多租户隔离、运维可观测。这些能力在 Mesos 的世界里都要靠 Marathon 和一堆外围组件拼凑体验就像是自己安装操作系统里的每一个驱动。另外社区话语权也决定了技术传播的路径。Mesos 的话语权主要在大数据和分布式计算圈而容器编排器的用户群是更广泛的运维和基础架构团队。当这些人想找一个“用 YAML 描述应用状态”的工具时Kubernetes 给出的答案显然更符合直觉。后来我也跟一些用 Mesos 的老前辈聊过他们承认 Mesos 在超大规模调度上至今依然有优势但通用场景里没人为它的复杂性和社区规模买单。资源调度能力和业务编排能力是两回事这是 Mesos 给所有后来者留下的一课。4. 亲历者视角三个编排器在生产环境里的真实手感4.1 Swarm 的上手体验简单是真的简单天花板也是真的低2016年我第一次用 Swarm Mode 时感觉确实舒爽。docker swarm init --advertise-addr 10.0.0.10初始化集群然后一条docker service create --name web --replicas 3 --publish published80,target80 nginx:1.16就把服务拉起来了。整个操作过程不需要学新概念不需要写 YAML几分钟就能建出一个像模像样的集群。对小团队、小项目来说这种体验非常友好。但随着服务数量增长我开始感觉到 Swarm 的表达力不够。比如我想对不同的服务做不同的滚动更新策略或者按自定义指标做水平伸缩Swarm 的支持都比较有限。它的服务抽象太简单上层工具很难在它身上做二次开发。更麻烦的是网络策略细化、多集群管理这些进阶需求Swarm 基本只能靠外部方案硬凑。我后来开玩笑说Swarm 适合“服务数量不超过二三十个”的场景再往上走你就要开始跟它笨拙的抽象搏斗了。4.2 Kubernetes 的复杂度组件多但表达力强Kubernetes 刚上手时我被它那套组件吓到了。API Server、Controller Manager、Scheduler、etcd、kubelet、kube-proxy每个都是独立的可执行文件还需要搭配 CNI 插件第一次部署难免手足无措。早期的 kubeadm 也不像现在这么成熟我踩过很多环境初始化的坑。但一旦你理解了它的模型写一个 Deployment 并把它跑起来你立刻会感受到声明式 API 带来的清晰感。比如我经常会用一个很简单的 YAML 部署服务apiVersion: apps/v1 kind: Deployment metadata: name: web spec: replicas: 3 selector: matchLabels: app: web template: metadata: labels: app: web spec: containers: - name: nginx image: nginx:1.16 ports: - containerPort: 80配合kubectl get pods、kubectl rollout status deployment/web、kubectl set image这些命令你能很清楚地看到整个系统如何在一个控制循环里运作。想更新镜像修改 YAML 后kubectl apply想回滚kubectl rollout undo deployment/web。整个操作过程有状态、有记录、可审计这对生产环境和团队协作太重要了。Swarm 的docker service update虽然也能发布但它的可观测性、扩展性和生态工具链跟 Kubernetes 完全不在一个量级。4.3 Mesos 的硬核适合大数据不适合绝大多数微服务团队我接触 Mesos 的时候第一感受是“这玩意不是给普通运维用的”。你要先部署 Zookeeper、Mesos Master、Mesos Agent再装 Marathon还得理解 Framework 的机制。为了清理故障状态我经常要看日志找某个 Agent 的状态是不是“卡死”了。在几千台节点下Mesos 的调度效率确实强资源利用率和任务隔离能力都是当时顶尖的。但对于一个主要跑微服务的小团队来说这些优势体现不出来反而要承担高得多的维护成本。Mesos 更适合那种拥有专门基础架构团队、以大数据批处理任务为主的大型企业。如果你就是“用容器跑一个订单服务”这种场景Mesos 给你的更多的是负担而不是收益。它和 Kubernetes 之间的关系有点像高端工具和通用工具的区别。问题在于市场最终选择了通用因为通用意味着更低的使用门槛、更厚的生态、更容易招到人。4.4 从 Swarm 迁移到 Kubernetes 的踩坑实录对我来说最痛的实战经历就是把一个已经跑在小规模 Swarm 集群上的业务系统迁移到 Kubernetes。表面看起来都是容器实际坑非常多。首先是网络策略差异。Swarm 的 ingress 网络默认把流量负载均衡到所有节点你只要暴露端口就能访问Kubernetes 里则需要自己定义 Service如果要对外提供 HTTP 访问还要配 Ingress。有一天我为了图方便直接把 Swarm 的端口映射翻译成了 NodePort结果服务是通了但绕过了 Ingress 那层路由很多基于域名的转发规则全部失效。排查了一下午才意识到通信模型完全不同。其次是服务发现。Swarm 里服务名就是 DNS 名服务之间直接靠服务名调用Kubernetes 则必须为每个工作负载创建对应的 Service 对象DNS 才会生效。我当时迁移了一批微服务只搬了 Deployment没建 Service结果服务之间连域名解析都失败。这个认知差异在初期不知道坑了多少人Kubernetes 的最小部署单元是 Pod但在集群内部的访问入口是 Service这两者必须一起规划。还有状态守护的问题。Swarm 的 service 自带重启策略节点挂了会自动在其他节点拉起任务Kubernetes 里这个职责落在 Deployment、StatefulSet 这类控制器上如果你图省事直接kubectl run跑一个裸 Pod节点宕机后这个 Pod 不会自动恢复。很多从 Swarm 迁移过来的同事最初都踩过这个坑以为 Pod 和容器一样挂了就重启一下没意识到“控制器”和“实例”是两层概念。存储迁移也值得单独说。Swarm 的 volume 基于 Docker volume 的概念而 Kubernetes 里是 PV 和 PVC 的两层抽象需要先准备好 StorageClass 和 CSI 驱动。我们有一个用本地盘的历史服务迁移时直接写 hostPath 硬用结果 Pod 被重新调度到其他节点后数据丢了。这个坑让我现在无论做什么有状态服务都先把存储方案想清楚再动手。5. 关于容器编排器的选型与迁移高频问题速查5.1 三选一常见疑问与我的回答这些年我被问得最多的问题就是“当年如果选了 Swarm/Mesos 会怎样”。我的回答通常很直接短期可能没事长期会很痛。这不是马后炮而是生态的存续周期直接影响技术栈的寿命。选 Swarm你确实省了学习成本但几年后服务规模上涨时你还是要面对网络策略、多集群管理、生态工具缺失这些问题而且那时候已经没有大团队帮你解决这些问题了。还有人会问“Kubernetes 会不会太重了我一个小项目有必要吗”我的看法是如果你的项目就是一台服务器上的几个容器用docker-compose完全够用没必要上 K8s。但只要你计划做多节点部署、有自动扩缩容需求、或者想统一管理多个环境那从第一天就上 Kubernetes 反而能少走很多弯路。所谓“重”在今天的托管 Kubernetes 服务面前已经不是大问题了。5.2 迁移期间的典型报错和排查思路我整理了迁移过程中最常遇到的几个问题做成速查表现象可能原因排查思路服务域名解析失败只创建 Deployment没创建 Service检查 Service 是否创建DNS 策略是否正常Ingress 规则不生效Service 的 targetPort 与容器端口不一致比对 YAML 中 containerPort、Service port、targetPortPod 一直处于 Pending资源不足或调度约束不满足kubectl describe pod看事件检查资源请求和节点亲和性Pod 频繁重启健康检查探针配置过严或启动时间过长调大 initialDelaySeconds 和 timeoutSeconds查看容器日志节点挂了 Pod 没恢复使用了裸 Pod没有控制器管理确认工作负载由 Deployment/StatefulSet 管理存储数据丢失用了 hostPath 且 Pod 被重新调度改用 PVC StorageClass必要时限制节点亲和性始终记住一条经验遇到问题先kubectl describe而不是猜事件里通常已经告诉你原因。这条经验帮我在迁移排障时省掉大量时间。5.3 几个“如果早点知道就好了”的小技巧如果让我穿越回去给当年的自己提建议我会说三件事。第一迁移时先梳理清楚应用之间的调用关系再设计 Service 和 DNS 命名顺序反了会引发大量调用链故障。第二不要把 Docker Compose 文件直接机械翻译成 Kubernetes YAML要先理解控制器、Service、Ingress 之间的分工再按 Kubernetes 的模型重构。第三做好标签Label设计从一开始就用统一的标签规范管理应用、版本、环境后面做灰度发布和故障定位都会轻松很多。还有一个容易被忽略的点etcd 是 Kubernetes 的脑子备份非常重要。我见过不止一次集群挂了结果发现 etcd 备份是坏的只能从头重建。不管在哪个环境都应该把 etcd 快照作为最高优先级运维任务之一。6. 这场战斗给技术从业者留下的真正教训6.1 赢的从来不是功能最多的那一个容器编排器之战最值得琢磨的一点是Swarm 简单Mesos 能扛超大规模Kubernetes 早期被吐槽复杂但最后赢的是 Kubernetes。原因不在于它每个功能都比别人强而在于它成为了一套“标准底座”能够承载各种上层创新。可插拔的接口、声明式 API、开放治理这些才是真正的胜负手。功能可以被追赶标准却很难被绕过。技术选型的时候我一直提醒自己你选择的不是一个工具而是一个生态位。Swarm 的生态位太窄Mesos 的生态位太垂直只有 Kubernetes 站在了“通用容器平台”这个最宽阔的位置上于是所有周边资源都向它汇聚。市场最终会奖励那些能让别人的能力长在自己身上的技术。6.2 Kubernetes 胜出后我们又面临哪些新问题不过 Kubernetes 赢得太彻底也带来了一些“幸福的烦恼”。控制面不轻etcd、API Server 都要资源几十个 Pod 的小集群也要养着这几个组件文化变重很多团队为了上 K8s 而上 K8s把简单应用塞进复杂架构里运维成本反而上升。生态内卷也明显光服务网格就有 Istio、Linkerd、Consul Connect 一堆方案网关又有 Gateway API 和各类 Ingress Controller概念越来越多新手学习路径越来越长。这些反思并不是否定 Kubernetes而是提醒我们任何技术一旦成为绝对标准就会出现“大而全”的惯性。也正因为这样后面才有了 K3s、k0s 这类轻量实现也有了 Wasm 工作负载、Serverless 容器这类新方向。但要注意这些新东西大多不是“推翻 Kubernetes”而是在它的生态位上做加法或减法。赢家不会轻易被取代这一点至少在未来几年里不会变。6.3 新一轮“容器编排器之战”还会发生吗有人会问未来还会不会出现下一个“Kubernetes 干掉 Swarm”这样的翻盘我的看法是真正的变量不在编排器本身而在工作负载模型。当主流工作负载从“容器”变成“WebAssembly 模块”或者其他更轻量的沙箱形态时底层基础设施的选择就可能发生变化。正因如此containerd-wasm-shim、KWasm 这类工作才有意义——它们不是推翻 Kubernetes而是让 Kubernetes 去适配新的工作负载。作为普通工程师我觉得最值得做的不是赌哪家赢而是把那些稍微底层一点、通用一点的原理吃透调度是资源匹配的过程控制循环是系统自愈的灵魂接口解耦是生态繁荣的前提。把这几件事想明白不管未来底层技术怎么换你都能快速切换而不慌。我个人的体会是技术在变但技术竞争的规律变来变去就那么几条开放接口往往战胜封闭实现社区生态往往战胜单一厂商表达力强的模型往往战胜上手舒服但上限低的模型。容器编排器之战是一场还没拉开大幕就结束的战斗但它给后来者留下的思考题足够我们做很多轮技术选型时慢慢回味。
阅读完成 · 觉得有帮助?