1.1 什么是容器化:一只箱子的由来


1.1 什么是容器化:一只箱子的由来

本节摘要:容器化是把应用连同它的运行环境(依赖库、配置、运行时)打包成一个标准化交付单元的技术。本节是全册的起点,讲清容器化解决的核心矛盾——"在我机器上是好的",并带你跑通第一个容器。

凌晨两点的交付事故

承接章支柱页的主线:认识港区,得先看清没有箱子时的码头有多乱。想象一个真实的场景——

周五晚上十一点,版本准时发布。十分钟后,监控告警:订单接口全部超时。值班工程师登录生产服务器排查,日志里赫然写着某个依赖库的版本号和一行"undefined symbol"。他记得清清楚楚:测试环境用的是新版本,这个错误根本不存在。翻出生产环境的部署文档才发现,三个月前有位已离职的同事在这台服务器上手动升级过一个系统组件,文档没更新,测试环境和新环境从那天起就不是同一个世界了。

凌晨两点,事故复盘会上有人提议:能不能把生产环境"整台搬"到测试环境?可以,但生产环境是台运行着别的业务的共享服务器,搬不动。有人提议把所有环境全部重装一遍统一版本——重装期间业务停摆,没人敢签字。

这类事故的病根只有一个:应用和它的环境是分离的。代码是同一份代码,但两台机器上的依赖库版本、系统组件、环境变量、时区设置,任何一处差异都可能让程序行为完全不同。环境靠"人肉维护"来保持一致,而人肉维护必然漂移。

箱子如何救场

码头早就遇到过一模一样的问题。散装货时代,同一批瓷器用麻绳吊装会碎、用木板夹装会潮,每个港口的装卸队都有自己的习惯,货损率居高不下。集装箱的做法釜底抽薪:装卸的对象从"货物本身"变成"标准箱子"。装箱时货物连同保护措施一起封进箱子,此后无论经过多少只吊臂、多少个堆场,箱子本身不动,里面的世界就是恒定的。

容器化把同样的思路搬进软件世界:

  • 应用连同依赖一起封箱——程序、运行时、依赖库、配置文件全部打进一个交付单元;
  • 箱体标准化——不管里面是 Python 应用还是 Go 服务,对外暴露的接口(启动、停止、日志)完全一致;
  • 运输工具通用化——任何装了 Docker 的机器都能运行这个箱子,不需要理解箱子内部的构造。

环境从"装出来的"变成"打包出来的",这是一次范式的变化。运维人员不再维护"环境",而是运输"环境"。

概念总是抽象的,直接看效果。在你的机器上安装 Docker 之后(安装方法见第 2 章),敲下你人生中第一条容器命令:

# 拉取一个官方的极简镜像(hello-world 是 Docker 官方的入门镜像) docker pull hello-world # 用这个镜像启动一个容器,它会打印一段说明文字后自动退出 docker run hello-world

如果一切正常,终端会输出类似下面这段话:

Unable to find image 'hello-world:latest' locally latest: Pulling from library/hello-world 719385e32843: Pull complete Digest: sha256:926fac19233cac21e65d69bf7036a59ffefba6863bd41b57f4ba88e47c59f75 Status: Downloaded newer image for hello-world:latest Hello from Docker! This message shows that your installation appears to be working correctly. To generate this message, Docker took the following steps: 1. The Docker client contacted the Docker daemon. 2. The Docker daemon pulled the "hello-world" image from the Docker Hub. 3. The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading. 4. The Docker daemon streamed that output to the Docker client, which sent it to your terminal.

别小看这十几行输出,它把容器化的精髓全讲完了:Docker 发现本地没有这个镜像(堆场里没这批货),自动去公共仓库(远端堆场)提货拉回本地,然后装箱起吊——创建一个容器来运行它——最后把容器里程序的输出原样送到你的终端。整个过程没有任何一步"配置环境",因为环境已经封在镜像里了。

容器化的三个关键词

标准化交付单元。 容器的交付粒度不是"一份代码"也不是"一台机器",而是一个自包含的镜像。镜像可以打版本号、可以做校验、可以回滚,软件交付第一次有了"货物运单"级别的确定性。

进程级隔离。 容器里的进程被限制了视野:它有自己的文件系统(看不到宿主机的目录)、自己的进程空间(看不到别人的进程)、自己的网络栈(默认独立的一套网卡)。这种隔离来自操作系统内核的既有能力(Linux 的命名空间与控制组),Docker 的贡献是把这些底层机制包装成人人会用的命令。

中心化分发。 镜像做完要有人存、有人取,镜像仓库(Registry)承担码头堆场的角色。正是中心化的公共仓库让"别人造好的轮子"变成一条命令就能提走的现货,生态才滚起了雪球。

三者各司其职,用一个比喻收拢:标准化交付单元是箱子,进程级隔离是船舱里的隔舱,中心化分发是堆场。第 1.4 节的架构五大件会沿着这条线展开。

一个反例:容器化不是什么

新手最常见的误解是"容器就是小一点的虚拟机"。反例来了——在容器里执行 uname -r 查看内核版本,你会发现它和宿主机的内核版本一模一样

# 在宿主机上查看内核版本 uname -r # 输出示例:5.15.0-91-generic # 在 Ubuntu 容器里查看内核版本(-it 参数含义见第 2 章) docker run -it ubuntu uname -r # 输出示例:5.15.0-91-generic <- 与宿主机完全相同

虚拟机里绝不会出现这个现象——虚拟机有自己独立的内核,版本号与宿主机毫无关系。这个小小的实验说明:容器并没有虚拟出一台机器,它只是给进程戴上了"视野限制器"。第 1.2 节会把这组对比讲得更彻底。

还有一个常见误会:容器化等于微服务。两者确实常一起出现,但容器化只解决"怎么打包怎么跑",把单体应用整体装进一个容器完全合法,而且常常是迁移的第一步。

本节要点回顾

  • 容器化的核心:把应用连同环境打包成标准化交付单元,环境从"装出来的"变成"打包出来的"。
  • 病根:应用与环境分离导致的环境漂移,是一切"在我机器上是好的"故事的源头。
  • 三个关键词:标准化交付单元、进程级隔离、中心化分发,分别对应箱子、隔舱与堆场。
  • 第一个实验docker run hello-world 背后隐藏着拉取、创建、运行、输出的完整链路。
  • 边界:容器不是轻量级虚拟机(内核与宿主机相同),也不等于微服务(单体也能容器化)。

下一节把"箱子"和"整船出租"放到同一张对比表里,看看隔离的两种思路各自付出了什么代价、又各自换来了什么。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U