云原生部署(一):从物理机到容器的演进之路

你有没有想过,为什么十年前部署一个应用要折腾好几天,而现在几分钟就能上线?我们今天熟悉的容器、Kubernetes、云原生,其实不是凭空出现的,它们是一步步演化而来的。要理解云原生部署,得先知道它从哪里来、解决了什么问题。

1、部署方式的四次跃迁

软件部署方式的变化,大致可以分为四个阶段。

第一阶段:物理机时代

最早我们部署应用,就是买一台服务器,装好操作系统,把应用丢上去跑。

一台物理机上可能跑着 Web 服务、数据库、缓存,所有东西混在一起。这种方式问题很多:

  • 资源争抢:一个应用把 CPU 或内存打满了,其他应用都受影响。
  • 环境不一致:开发环境是一台机器,测试环境是另一台,配置不一样,代码在开发机跑得好好的,到了服务器上就崩,这种故事每个程序员都经历过。
  • 部署慢:新加一台机器?采购审批走一周,上架配网又几天,等机器到手黄花菜都凉了。

有人会说,那我一个应用一台物理机不就行了?这个想法很好,就是钱包有点疼。大部分时候机器资源利用率不到 20%,剩下的 80% 都在白烧电。

第二阶段:虚拟机时代

VMware、KVM 这类虚拟化技术出来以后,一台物理机可以切分成多个虚拟机,每个 VM 有自己的操作系统。这样一来:

  • 应用之间资源隔离了,一个应用不会拖垮整台机器。
  • 利用率上去了,一台物理机可以跑多个 VM。
  • 扩容也快了不少,几分钟就能创建一台新 VM。

但虚拟机也有它的痛点。每个 VM 都要跑一个完整的操作系统,本身就很重。一台 VM 光操作系统就要占几 GB 磁盘、几百 MB 内存,这些开销都属于“为了隔离而付出的代价”。

更重要的是,虚拟机的镜像动辄几 GB,传输起来很慢,启动也要几十秒甚至几分钟。这对快速扩缩容、弹性伸缩的场景来说,还是太慢了。

第三阶段:容器时代

2013 年 Docker 横空出世,让“容器”这个词走进了每个开发者的生活。

容器本质上还是跑在宿主机上的一个进程,但它利用了 Linux 内核的 namespace(命名空间)和 cgroup(控制组)这两个特性,在进程级别实现了轻量级的资源隔离和限制。

和虚拟机相比,容器的优势很明显:

  虚拟机 容器
启动速度 分钟级 秒级
镜像大小 GB 级 MB 级
资源开销 每 VM 一个完整 OS 共享宿主机内核
部署密度 每台物理机几十个 每台物理机成百上千个

Docker 还带来了一个更重要的东西:镜像(Image)。镜像是一个只读的模板,包含了运行应用所需的一切——代码、运行时、系统工具、库、配置。而且镜像是分层的,这意味着不同的镜像可以共享相同的底层,大大节省了存储和传输成本。

从此,“在我机器上能跑” 这个问题基本上消失了。因为开发环境、测试环境、生产环境用的是同一个镜像。

第四阶段:Kubernetes 编排时代

容器虽然好,但生产环境不是跑一两个容器那么简单。

如果一个应用要跑几十个容器实例,它们怎么调度?哪个节点有空闲资源?某个节点挂了怎么办?容器之间怎么通信?存储怎么挂载?配置怎么管理?

这些“编排”问题,容器本身解决不了。

Kubernetes(简称 K8s)应运而生。它源自 Google 内部的 Borg 系统,2015 年发布了 1.0 版本,很快成为了容器编排的事实标准。

K8s 做的最核心的一件事:你告诉它“我想要什么”,它自己想办法去实现。你说“我要 3 个副本”,K8s 就去调度;你说“负载太高了”,它就自动扩容;某个节点宕了,它自动把容器迁移到健康节点上。这种声明式的管理方式,就是云原生部署的核心。

2、什么是云原生?

“云原生”(Cloud Native)这个词现在满天飞,但很多人说不清楚它到底是什么。

简单说,云原生不是某个具体的技术,而是一套理念和方法论——让应用从设计之初就以云环境为基础,充分利用云平台的弹性、分布式、按需交付等能力

CNCF(云原生计算基金会)对云原生的定义中,包含了几个关键元素:

  • 容器化:应用和依赖打包在一起,环境一致性。
  • 动态编排:由 K8s 这样的平台负责调度和管理。
  • 微服务:应用拆分为松耦合的独立服务,各自独立部署和演化。
  • 声明式 API:描述期望状态,由系统自动达成。
  • 可观测性:日志、指标、追踪,知道系统在发生什么。

其中,12-Factor App(十二要素应用)方法论是云原生应用的经典实践指南。虽然它提出得比“云原生”这个词还早,但思想完全契合。

12-Factor 核心要点速览

12-Factor 的内容网上很多,这里只挑几个对部署最关键的说说:

Factor I: 代码库 —— 一份代码库,多次部署。同一个代码仓库可以部署到开发、预发、生产环境,差异由配置和环境变量控制,而不是用不同分支。

Factor III: 配置 —— 配置和代码分离。不要把你的数据库密码写在代码里、打进镜像里。配置应该通过环境变量注入,随环境变化。

Factor V: 构建、发布、运行 —— 严格分离三个阶段。构建阶段产出镜像,发布阶段组合镜像+配置,运行阶段启动实例。这三个阶段是单向的,不能从运行阶段往回改镜像。

Factor VI: 进程 —— 应用应该是无状态的。状态(如 Session、文件、数据库)要外挂到独立的服务上(如 Redis、MySQL)。这样应用实例才能随时被销毁和重建。

Factor VIII: 并发 —— 通过进程模型进行扩展。想扛更多流量?多起几个实例就行。这就是所谓的“水平扩展”。

理解了这些原则,再看 Docker 和 K8s 的设计,你会觉得一切都那么自然——它们就是为了支撑这些原则而生的。

3、本系列的路线图

了解完演进史和核心概念,接下来我们会分步深入云原生部署的实践。本系列共 10 篇文章:

日期 主题 内容要点
1 03-07 云原生部署概述 演进史、云原生概念、12-Factor、系列介绍
2 03-14 Docker 基础 镜像、容器、分层原理
3 03-21 Dockerfile 最佳实践 指令详解、多阶段构建、镜像瘦身
4 03-28 容器镜像仓库 Docker Hub、Harbor 私服搭建
5 04-04 K8s 架构核心 Master/Node/etcd 组件原理
6 04-11 K8s 核心概念 Pod/Deployment/Service 实战
7 04-18 K8s 网络模型 CNI、Service 发现
8 04-25 K8s 存储 Volume/PV/PVC/StorageClass
9 05-02 Helm 包管理 Chart 结构、模板化部署
10 05-09 健康检查与零停机部署 Liveness/Readiness/滚动更新

这个系列不追求面面俱到,而是聚焦于一枚开发者做云原生部署时需要掌握的核心知识。每篇文章都会有实际的代码和操作示例,帮助你把概念落地。

小结

从物理机到虚拟机,从虚拟机到容器,从容器的野蛮生长到 K8s 的秩序编排——每一次跃迁都在解决上一阶段的痛点。云原生不是凭空造出来的新名词,它是这个演进过程的自然产物。

下一篇,我们从 Docker 基础开始,聊聊镜像、容器和分层原理,看看 Docker 里到底发生了什么。

每天前进一小步,就是一个新的高度!