内容更新于 2026-09-29。保留原发布日期,正文与示例已按更新时的官方文档修订。

很多人把云计算理解成“把服务器放到别人的机房”,把容器理解成“更轻量的虚拟机”。这样的理解能帮助入门,但在做架构设计时还不够:应用打成镜像后为什么仍然不能随意迁移?买了云服务器为什么还需要平台工程?用了 Kubernetes,为什么一次数据库故障还是能让整个系统不可用?

要回答这些问题,需要把计算资源、应用交付和系统运行分开来看。这组文章从一台物理机开始,沿着虚拟化、容器和集群编排的路径,讨论这些技术分别解决了什么问题,又把哪些责任留给了应用团队。

一、从一个订单服务说起

假设我们要部署一个订单服务:Java 应用对外提供 HTTP 接口,数据库保存订单,后台任务发送通知,上传附件保存在磁盘上。业务刚开始时,一台服务器就能容纳这些进程。

问题通常出现在变化发生以后。促销活动需要扩容,应用和数据库却在争抢内存;升级 Java 版本会影响同机的其他应用;服务器损坏后,没人能确认安装过哪些软件、改过哪些配置。即使备份了应用代码,也不代表能够恢复完整的运行环境。

这些问题涉及三个不同层次:

  • 资源层:谁占用了多少 CPU、内存、磁盘和网络,彼此能否隔离?
  • 交付层:一个版本究竟包含哪些文件和依赖,能否重复部署?
  • 运行层:实例故障后谁来恢复,流量如何切换,数据是否仍然正确?

虚拟化主要改善资源层的分配与隔离,容器镜像改善交付的一致性,编排平台管理大量实例的运行状态。它们存在重叠,但没有哪一项能够单独包办所有问题。

二、虚拟化怎样把机器变成资源

2.1 虚拟机为什么需要独立内核

虚拟机向来宾操作系统提供一套虚拟硬件,包括 CPU、内存、磁盘和网卡。来宾系统运行自己的内核,再由内核管理自己的应用。不同虚拟机可以运行不同的操作系统,不必共享同一个用户空间和内核版本。

Hypervisor 负责协调这些虚拟硬件与物理资源的关系。以 KVM 为例,Linux 内核中的 KVM 提供硬件辅助虚拟化能力,QEMU 在常见部署中负责虚拟机进程和设备模型,libvirt 提供管理接口。这三个名字对应不同职责,不能把它们简单看成同一个组件。

例如,为订单服务分配 4 个 vCPU 和 8 GiB 内存,首先是在定义它能够使用的资源和隔离范围。4 个 vCPU 通常不意味着独占 4 个物理核心;是否共享、是否绑核、如何进行超分,取决于基础设施配置。

2.2 隔离不等于没有争抢

多台虚拟机仍然共享物理 CPU、内存带宽、磁盘和网络。某个邻居持续写盘,可能影响同一存储后端上的其他实例;同时把过多 vCPU 分给不同租户,也会让调度等待变长。

因此,虚拟化提高的是资源分配的灵活性,而非创造额外算力。评估性能时需要区分实例规格、实际可用资源和底层共享瓶颈。对延迟敏感的服务,还要考虑独占实例、CPU 绑定或更明确的 IO 保障。

2.3 虚拟机、容器和普通进程的区别

维度 普通进程 Linux 容器 虚拟机
内核 使用宿主机内核 通常共享宿主机内核 每个来宾系统有自己的内核
环境组织 依靠目录、包管理器和配置 镜像与运行时配置 系统镜像与虚拟硬件配置
隔离方式 用户权限等操作系统机制 Namespace、cgroup 等组合 虚拟硬件与 Hypervisor 边界
启动工作 启动应用进程 准备隔离环境后启动进程 通常包含来宾系统启动
运维重点 主机环境与进程管理 镜像、运行时、主机内核 来宾系统与虚拟化平台

这个比较针对常规 Linux 容器。使用虚拟机或沙箱内核承载容器的运行方式,会改变隔离和性能取舍,不能一概套用“所有容器都一样”的结论。

三、容器把应用交付变成什么样

容器镜像把应用程序和用户空间依赖组织成一个可分发的制品。订单服务需要的 JRE、依赖库和启动命令,都可以在构建时明确下来;开发、测试和生产部署相同的镜像摘要,就能减少环境漂移。

但镜像不包含一切。Linux 镜像依赖宿主提供兼容的内核能力;CPU 架构需要匹配;数据库地址、证书和访问权限通常由部署环境提供。在 macOS 或 Windows 上运行 Linux 容器,常见方式是在 Linux 虚拟机中执行,不能理解为 Linux 程序直接使用另一种操作系统的内核。

订单服务容器化以后,还必须处理本地文件。若上传附件仍保存在容器可写层,实例替换后就会丢失;若登录会话只保存在进程内存里,扩容后的请求落到另一实例就可能失效。把附件迁到对象存储、把必要的共享状态放到外部系统,才能让实例具备可替换性。

无状态实例不代表业务没有状态,而是业务状态不依赖某一个可随时替换的实例。 这往往比“写一个 Dockerfile”更难,也是容器化改造中真正需要调整的架构部分。

四、云计算比远程租机器多了什么

NIST 对云计算的定义强调按需自助服务、广泛网络访问、资源池化、快速弹性和可计量服务。按照这个定义,能够通过 SSH 登录远程服务器,并不足以说明系统已经具备完整的云服务能力。

对业务团队而言,这些特征体现为一组具体操作:通过 API 申请计算资源,选择镜像和网络,绑定访问身份,根据负载调整容量,在任务结束后释放资源,并能知道资源由谁使用、花了多少钱。

4.1 控制面与数据面

云平台通常把管理资源的控制面与承载实际业务的数据面分开理解。申请虚拟机、配置网络策略、查询资源状态属于控制面;应用运行、读写磁盘、传输业务流量属于数据面。

控制面暂时不可用时,已有实例有可能继续运行,但无法创建新实例或完成扩容。数据面故障时,管理控制台仍可能显示正常,却不代表业务请求可达。排障时必须明确:出问题的是管理操作,还是实际业务路径?

4.2 一次创建虚拟机请求的背后

以 OpenStack 的常见组件分工为例:Keystone 负责身份认证,Nova 管理计算实例,Glance 管理镜像,Neutron 提供网络能力,Cinder 管理块存储。它们把一个“创建实例”的请求,拆成身份校验、计算调度、镜像准备、网络连接和卷挂载等步骤。

这些步骤可能跨越多个服务和物理节点,因此创建过程通常具有异步性,也可能部分成功。平台需要记录资源状态、重试失败操作、处理超时和清理遗留资源。理解这一点,就能看出云平台的复杂度主要来自资源生命周期管理,而不仅是启动虚拟机。

OpenStack 只是实现方式之一。公有云可以用不同的内部组件提供相似能力,而业务团队通常只接触服务 API,无须了解其内部实现。

五、IaaS、PaaS、SaaS 是责任边界

模型 使用者主要管理 服务商主要管理 订单服务中的例子
IaaS 操作系统、运行时、应用和数据 底层计算、存储与网络设施 在云虚拟机上自行安装和维护 Java
PaaS 应用代码、配置、数据使用方式 约定范围内的运行时和平台 上传应用,由平台部署并维护运行环境
SaaS 账号权限、业务配置和数据使用 软件服务及其运行设施 使用在线工单或客服系统

实际产品的责任划分要看服务约定。例如托管 Kubernetes 可能管理控制面,却仍要求用户维护工作节点、工作负载权限和应用升级。购买托管数据库,也不会自动解决业务层误删、权限过宽和错误的数据保留策略。

选择服务时,应把“由谁备份、由谁升级、由谁响应告警、由谁验证恢复”写清楚。抽象层提高以后,责任常常转移了位置,并没有完全消失。

六、弹性扩容为什么没有想象中简单

假设订单服务只有 2 个实例,促销时扩到 10 个。应用层处理能力提高以后,数据库连接数也可能从 100 增加到 500。如果数据库没有余量,扩容反而加剧拥塞。

再比如队列任务积压,增加消费者通常有帮助;但如果任务都在调用同一个限流的外部接口,消费者增加只会带来更多失败和重试。因此扩容指标应反映真正的瓶颈:HTTP 服务可以看吞吐和延迟,异步任务可以看积压量及最老任务等待时间,不能一律只看 CPU。

弹性还需要时间。新实例可能要拉取镜像、初始化连接池和预热缓存;缩容时需要停止接收新任务,等待在途工作完成。流量瞬时增长超过系统反应速度时,仍然需要容量余量、限流和排队来保护系统。

七、怎样判断是否需要 Kubernetes

应用数量少、发布频率低、团队已有成熟的虚拟机运维流程时,可以先改进镜像构建、自动部署和监控。一个简单系统完全可能通过虚拟机、单机 Compose 或托管应用平台获得足够的可靠性。

当多个团队共享资源、服务数量增长、发布频繁,且需要统一调度、访问控制和声明式管理时,Kubernetes 的收益才更容易体现。与此同时,团队需要承担集群升级、网络、存储、容量和排障能力建设。

比较成本时也不要只看机器费用。镜像仓库、网络流量、日志保留、备用容量,以及值班和平台维护的人力,都会进入总成本。采购更高级的产品不一定更省钱,自建更灵活的平台也不一定更便宜。

一个可执行的演进方式是:先让订单服务能够重复构建和替换实例,再自动化发布与回滚,接着补齐故障监测和容量管理,最后根据团队规模决定是否引入更复杂的平台。每一步都应该对应具体问题,而不是为了凑齐技术栈。

八、把云原生落实到故障处理

云原生的工程价值可以通过几个问题检验:删除一个应用实例,业务能否继续?丢失一台宿主机,副本是否真的位于其他故障域?发布新版本出错,是否有已验证的旧镜像和兼容的数据结构?从备份恢复后,能否确认数据完整?

这些问题把“弹性”“自动化”“可观测性”落到了具体行为上。容器和编排平台可以提供机制,但业务是否具备重试、幂等、超时和降级能力,仍然取决于应用设计。

下一篇:容器隔离的四个 Linux 基础,会从进程、资源、文件系统和网络解释容器的工作方式。

参考资料