Skip to content

Commit 7f96f9f

Browse files
committed
fix typo
1 parent 9475ba0 commit 7f96f9f

File tree

7 files changed

+58
-41
lines changed

7 files changed

+58
-41
lines changed

ServiceMesh/summary.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
Kubernetes 的崛起意味着虚拟化的基础设施,开始解决分布式系统软件层面的问题。但 Kubernetes 解决问题的最细粒度只能到达容器层次,在此粒度之下的技术问题仍然需要工程师亲自解决。历经 SOA、微服务架构、云原生架构洗礼的工程师,想必深谙服务发现、容错、路由、限流、加密等服务间通信管控问题在分布式系统中是不可回避的。
1111

12-
在传统分布式时代,解决这些问题通常依赖于微服务治理框架(如 Spring Cloud 和 Apache Dubbo),将解决方案侵入业务逻辑之中。而在云原生时代,解决上述细粒度技术问题时,在 Pod 内注入辅助功能的 Sidecar 显然是最“Kubernetes Native”的方式。Sidecar 设计模式将非业务逻辑从应用中彻底剥离,服务间的通信治理由此开启了全新的进化,并最终演化出一层全新基础设施层 —— 服务网格(ServiceMesh)。
12+
在传统分布式时代,解决这些问题通常依赖于微服务治理框架(如 Spring Cloud 和 Apache Dubbo),将解决方案侵入业务逻辑之中。而在云原生时代,解决容器之内细粒度技术问题时,在 Pod 内注入辅助功能的 Sidecar 显然是最“Kubernetes Native”的方式。Sidecar 设计模式将非业务逻辑从应用中彻底剥离,服务间的通信治理由此开启了全新的进化,并最终演化出一层全新基础设施层 —— 服务网格(ServiceMesh)。
1313

1414
本章,我们将根据服务间通信的演化历程,深刻理解服务网格出现的背景,以及它所解决的问题。然后,了解当前服务网格领域的产品生态(主要介绍 Linkerd 和 Istio)。最后,我们将直面服务网格实践中的问题(网络延迟和资源占用问题),讨论如何解决,并展望服务网格的未来。本章内容组织如图 8-0 所示。
1515

container/conclusion.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# 7.9 小结
22

3-
本章,我们以 Google 内部容器系统演进作为开篇,深入讨论了 Kubernetes 网络、计算、存储、调度等方面的设计原理和应用。希望能让读者在 Kubernetes 这个复杂而庞大的项目中抓到主线,领悟到操作 YAML 文件背后的设计理念
3+
本章,我们以 Google 内部容器系统演进作为开篇,深入讨论了 Kubernetes 网络、计算、存储、调度等方面的设计原理和应用。希望能让读者在 Kubernetes 这个复杂而庞大的项目中抓到主线,领悟到操作 YAML 文件背后的核心设计理念
44

5-
Kubernetes 的核心设计理念在于,从 API 到容器运行时的每一层,都为开发者暴露可供扩展的插件机制。通过 CNI 插件,把网络功能解耦,让外部参与容器网络的实现;通过 CSI 插件机制,建立了一套庞大的存储生态;通过 Device Plugin(设备插件)把资源的支持扩展到 GPU、FPGA、RDMA 等各类异构设备。依托这种开放性的设计,Kubernetes 社区出现了成千上万的插件,让工程师可以轻松构建出各个各样的技术平台
5+
在笔者看来,Kubernetes 的核心设计理念有两点。其一,从 API 到容器运行时的每一层,都为开发者暴露可供扩展的插件机制。通过 CNI 插件,把网络功能解耦,让外部参与容器网络的实现;通过 CSI 插件机制,建立了一套庞大的存储生态;通过 Device Plugin(设备插件)把资源的支持扩展到 GPU、FPGA、DPDK、RDMA 等各类异构设备。依托这种开放性的设计,Kubernetes 社区出现了成千上万的插件,让运维工程师可以轻松构建出各个各样的技术平台
66

7-
至关重要的是,Kubernetes 将所有接触的方方面面统一抽象为“资源”。所有的“资源”使用 YAML 文件描述,一个 YAML 文件即可表达出一个复杂基础设施的最终状态,并且自动地对应用进行运维和管理。这种设计隐藏了底层细节,为工程师提供了一种友好、一致且跨平台的管理和部署应用的方式。让云原生技术产生真正价值,正是 Kubernetes 设计哲学的精髓所在。
7+
其二,在开放性的底层之上,Kubernetes 将所有接触的方方面面统一抽象为“资源”。所有的“资源”使用 YAML 文件描述,一个 YAML 文件即可表达出一个复杂基础设施的最终状态,并且自动地对应用进行运维和管理。这种设计隐藏了底层细节,为业务工程师提供了一种友好、一致且跨平台的管理和部署应用的方式。让云原生技术产生真正价值,正是 Kubernetes 设计哲学的精髓所在。
88

99
接下来,笔者将继续介绍基于“容器设计模式”的二次创新,也就是近几年热度极高的服务网格(ServiceMesh)。

container/orchestration.md

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22

33
字面上,“容器”这个词难以让人形象地理解其真正含义,Kubernetes 中最核心的概念“Pod”也是如此。
44

5-
仅靠几句简单的解释并不足以让人充分理解这些概念,甚至可能引发误解。例如,业内常常将容器与轻量级虚拟机混为一谈,如果容器类似于虚拟机,那么应该存在一种通用的方法,能够无缝地将虚拟机内的应用迁移至容器中,但现实中并不存在这种方法。
5+
仅靠几句简单的解释并不足以让人充分理解这些概念,甚至可能引发误解,如业内常常将容器与轻量级虚拟机混为一谈。如果容器类似虚拟机,那么应该存在一种普适的方法,能够无缝地将虚拟机内的应用迁移至容器中,但现实中并不存在这种方法。
66

7-
本节,笔者将从最初的文件系统隔离开始,逐步介绍容器在不同历史阶段的作用,深入理解容器技术的演进,以及 Kubernetes 中最核心的概念 Pod 的设计背景与应用
7+
本节,笔者将从最初的文件系统隔离开始,逐步介绍容器在不同历史阶段的作用,深入理解容器技术的演进,以及 Kubernetes 中最核心的概念 Pod 的设计背景和应用
88

99
## 7.2.1 文件系统隔离
1010

11-
容器的起源可以追溯到 1979 年 UNIX 系统中引入的 chroot 命令[^1]chroot 是“change root”的缩写,它允许管理员将进程的根目录锁定在特定位置,从而限制进程对文件系统的访问范围。
11+
容器的起源可以追溯到 1979 年 UNIX 系统中引入的 chroot 命令[^1]
1212

13-
chroot 的隔离功能对安全性至关重要,例如可以创建一个“蜜罐”,用来安全地运行和监控可疑代码或程序。因此,chroot 环境也被形象地称为“jail”(监狱),而突破 chroot 的过程则被称为“越狱”。
13+
chroot 是“change root”的缩写,它允许管理员将进程的根目录锁定在特定位置,从而限制进程对文件系统的访问范围。chroot 的隔离功能对安全性至关重要。例如,可以创建一个“蜜罐”,用来安全地运行和监控可疑代码或程序。因此,chroot 环境也被形象地称为“jail”(监狱),而突破 chroot 的过程则被称为“越狱”。
1414

1515
时至今日,chroot 命令仍然活跃于主流的 Linux 系统中。在绝大部分 Linux 系统中,只需几步操作,就可以为进程创建一个文件隔离环境。
1616

@@ -41,7 +41,9 @@ root@028f46a5b7db:/# cd bin
4141
root@028f46a5b7db:/bin# pwd
4242
/bin
4343
```
44-
虽然 chroot 看起来与容器相似,都是创建与宿主机隔离的文件系统环境,但这并不意味着 chroot 就是容器。chroot 只是改变了进程的根目录,并未创建真正独立、安全的隔离环境。在 Linux 系统中,从低层次的资源(如网络、磁盘、内存、处理器)到操作系统控制的高层次资源(如 UNIX 分时、进程 ID、用户 ID、进程间通信),都存在大量非文件暴露的操作入口。
44+
虽然 chroot 看起来与容器相似,都是创建与宿主机隔离的文件系统环境,但这并不意味着 chroot 就是容器。
45+
46+
chroot 只是改变了进程的根目录,并未创建真正独立、安全的隔离环境。在 Linux 系统中,从低层次的资源(如网络、磁盘、内存、处理器)到操作系统控制的高层次资源(如 UNIX 分时、进程 ID、用户 ID、进程间通信),都存在大量非文件暴露的操作入口。
4547

4648
因此,无论是 chroot,还是针对 chroot 安全问题改进后的 pivot_root,都无法实现对资源的完美隔离。
4749

@@ -90,7 +92,7 @@ int pid = clone(main_function, stack_size, flags | SIGCHLD, NULL);
9092

9193
cgroups 是 Linux 内核中用于隔离、分配并限制进程组使用资源配额的机制。例如用来控制进程 CPU 占用时间、内存的大小、磁盘 I/O 速度等。该项目由 Google 工程师(主要是 Paul Menage 和 Rohit Seth)在 2000 年发起,当时取名字叫“进程容器”(Process container)。不过,在 Linux 内核中,容器(container)这个名词有许多不同的意义。为了避免与其他“容器”相关概念混淆,于是被重命名为 cgroups 。
9294

93-
2008 年,cgroups 合并到 Linux 内核 2.6.24 版本 后正式对外发布,这一阶段的 cgroups 被称为第一代 cgroups。2016 年 3 月发布的 Linux 内核 4.5 中引入了由 Facebook 工程师 Tejun Heo 重新编写的“第二代 cgroups”。相较于 v1 版本,第二代 cgroups 提供了更加统一的资源控制接口,使得对 CPU、内存、I/O 等资源的限制更加一致和统一。不过由于兼容性和稳定性原因,目前多数容器运行时(container runtime)默认使用的仍然是第一代 cgroups。
95+
2008 年,cgroups 合并到 Linux 内核 2.6.24 版本 后正式对外发布,这一阶段的 cgroups 被称为第一代 cgroups。2016 年 3 月发布的 Linux 内核 4.5 中引入了由 Facebook 工程师 Tejun Heo 重新编写的“第二代 cgroups”。相较于 v1 版本,第二代 cgroups 提供了更加统一的资源控制接口,使得对 CPU、内存、I/O 等资源的限制更加一致和统一。不过,由于兼容性和稳定性原因,目前多数容器运行时(container runtime)默认使用的仍然是第一代 cgroups。
9496

9597
Linux 系统通过文件系统向用户暴露 cgroups 的操作接口,这些接口以文件和目录的形式组织在 /sys/fs/cgroup 路径下。在 Linux 中执行 ls /sys/fs/cgroup 命令,可以看到在该路径下有许多子目录,如 blkio、cpu、memory 等。
9698

@@ -113,7 +115,7 @@ cgroup.sane_behavior memory.memsw.usage_in_bytes
113115
```
114116
这些文件各自具有不同的作用。例如,memory.kmem.limit_in_bytes 文件用于限制应用的总内存使用;memory.stat 用于统计内存使用情况;memory.failcnt 文件报告内存使用达到 memory.limit_in_bytes 设定的限制值的次数,等等。
115117

116-
目前,主流的 Linux 系统支持的控制组群子系统如表 7-2 所示。
118+
目前,主流的 Linux 系统支持的控制组子系统如表 7-2 所示。
117119

118120
:::center
119121
表 7-2 cgroups 控制组群子系统
@@ -133,7 +135,7 @@ cgroup.sane_behavior memory.memsw.usage_in_bytes
133135

134136
Linux cgroups 的设计简单易用。对于 Docker 等容器系统,它们只需在每个子系统下为每个容器创建一个控制组(通过新建目录的方式),然后在容器进程启动后,将进程的 PID 写入对应控制组的 tasks 文件即可。
135137

136-
如下代码所示,我们创建了一个新的控制组(目录名为 $hostname),将进程(PID 为 3892)的内存限制为 1 GB,并限制其 CPU 使用时间为 1/4。
138+
如下代码所示,我们创建了一个内存控制组子系统(目录名为 $hostname),将进程(PID 为 3892)的内存限制为 1 GB,并限制其 CPU 使用时间为 1/4。
137139

138140
```bash
139141
/sys/fs/cgroup/memory/$hostname/memory.limit_in_bytes=1GB // 容器进程及其子进程使用的总内存不超过 1GB
@@ -142,11 +144,13 @@ Linux cgroups 的设计简单易用。对于 Docker 等容器系统,它们只
142144
echo 3892 > /sys/fs/cgroup/cpu/$hostname/tasks
143145
```
144146

145-
最后,笔者需要补充一点,实际上 cgroups 对资源的限制也存在不完善之处,最常提到的问题是 /proc 文件系统的问题。/proc 文件系统记录了 Linux 系统中一些特殊状态,如 CPU 使用情况和内存占用情况,这些数据也是 top 命令查看系统信息的主要来源。
147+
最后,笔者需要补充一点,实际上 cgroups 对资源的限制也存在不完善之处。最常提到的问题是 /proc 文件系统的问题,/proc 文件系统记录了 Linux 系统中一些特殊状态,如 CPU 使用情况和内存占用情况,这些数据也是 top 命令查看系统信息的主要来源。
148+
149+
问题在于,/proc 文件系统并不反映通过 cgroups 对进程施加的限制。因此,在容器内部执行 top 命令时,显示的信息是宿主机的数据,而不是容器内部的数据。现在,业内一般使用 LXCFS(FUSE filesystem for LXC)技术维护一套专用于容器的 /proc 文件系统,解决这个问题。
146150

147-
问题在于,/proc 文件系统并不反映通过 cgroups 对进程施加的限制。因此,在容器内部执行 top 命令时,显示的信息是宿主机的数据,而不是容器内部的数据。在生产环境中,这个问题必须得到解决,不然会给系统带来很大的问题。现在,业内一般使用 LXCFS(FUSE filesystem for LXC)来维护一套专用于容器的 /proc 文件系统,解决这个问题
151+
至此,相信读者们一定理解容器是什么
148152

149-
至此,相信读者们一定理解容器是什么。容器并不是轻量化的虚拟机,也没有创建出真正的沙盒(容器之间共享系统内核,这也是为什么出现了如 kata 和 gVisor 等内核隔离的沙盒容器,7.4.5 节详细介绍)。容器只是利用命名空间、cgroups 等技术进行资源隔离和限制,并拥有独立的根目录(rootfs)的特殊进程。
153+
容器并不是轻量化的虚拟机,也没有创建出真正的沙盒(容器之间共享系统内核,这也是为什么出现了如 kata 和 gVisor 等内核隔离的沙盒容器,7.4.5 节详细介绍)。说白了,容器只是利用命名空间、cgroups 等技术进行资源隔离和限制,并拥有独立的根目录(rootfs)的特殊进程。
150154

151155

152156
## 7.2.4 设计容器协作的方式
@@ -165,19 +169,25 @@ $ pstree -g
165169

166170
对于操作系统而言,这种进程组管理更加方便。比如,Linux 操作系统可以通过向一个进程组发送信号(如 SIGKILL),使该进程组中的所有进程同时终止运行。
167171

168-
那么,现在思考一个问题:“如果把上面的进程用容器改造,该如何设计?”。如果是使用 Docker,自然会想到在 Docker 容器内运行两个进程:
172+
那么,现在思考一个问题:“如果把上面的进程用容器改造,该如何设计?”。
173+
174+
如果是使用 Docker,自然会想到在 Docker 容器内运行两个进程:
169175
- rsyslogd 进程执行具体的业务;
170176
- imklog 进程处理业务日志。
171177

172-
但这种设计会遇到一个问题:“容器中的 PID=1 进程应该是谁?”。在 Linux 系统中,PID 为 1 的进程是 init,它作为所有其他进程的祖先进程,负责监控进程状态,并处理孤儿进程。因此,容器中的第一个进程也需要具备类似的功能,能够处理 SIGTERM、SIGINT 等信号,优雅地终止容器内的其他进程。
178+
但这种设计会遇到一个问题:“容器中的 PID=1 进程应该是谁?”。
179+
180+
在 Linux 系统中,PID 为 1 的进程是 init,它作为所有其他进程的祖先进程,负责监控进程状态,并处理孤儿进程。因此,容器中的第一个进程也需要具备类似的功能,能够处理 SIGTERM、SIGINT 等信号,优雅地终止容器内的其他进程。
173181

174182
Docker 的设计核心在于 Docker 容器采用的是“单进程”模型。Docker 通过监控 PID 为 1 的进程的状态来判断容器的健康状态(在 Dockerfile 中用 ENTRYPOINT 指定启动的进程)。如果确实需要在一个 Docker 容器中运行多个进程,首个启动的进程应该具备资源监控和管理能力,例如使用专为容器开发的 tinit 程序。
175183

176184
通过 Docker,虽然可以勉强实现容器内运行多个进程,但进程间的协作远不止于资源回收那么简单。要让容器像操作系统中的进程组一样进行协作,下一步的演进是找到一个类似于“进程组”的概念。这是实现容器从隔离到协作的第一步。
177185

178186
## 7.2.5 超亲密容器组 Pod
179187

180-
在 Kubernetes 中,与“进程组”对应的设计概念是 Pod。Pod 是一组紧密关联的容器集合,它们共享 IPC、Network 和 UTS 等命名空间,是 Kubernetes 的最基本单位。
188+
在 Kubernetes 中,与“进程组”对应的设计概念是 Pod。
189+
190+
Pod 是一组紧密关联的容器集合,它们共享 IPC、Network 和 UTS 等命名空间,是 Kubernetes 的最基本单位。
181191

182192
容器之间原本是通过命名空间和 cgroups 进行隔离的。Pod 首要解决的问题是如何打破这种隔离,使 Pod 内的容器能够像进程组一样自然地共享资源和数据。为了解决这个问题,Kubernetes 引入了一个特殊的容器 —— Infra Container。
183193

@@ -250,13 +260,13 @@ Pod 承担的另一个重要职责是作为调度的原子单位。
250260
- Node1:1.25G 可用内存;
251261
- Node2:2G 可用内存。
252262

253-
如果这两个 Pod 需要协作并运行在同一台机器上,调度器可能首先将 Nginx 调度到 Node1。但由于 Node1 上只剩下 1.25G 的内存,而 Nginx 占用 1G 内存,LogCollector 将无法在 Node1 上运行,因为资源不足,从而导致调度流程被阻塞。尽管可以通过重新调度来解决这个问题,但考虑到如果需要解决数以万计的容器协同调度问题呢?
263+
如果这两个 Pod 需要协作并运行在同一台机器上,调度器可能首先将 Nginx 调度到 Node1。但由于 Node1 上只剩下 1.25G 的内存,而 Nginx 占用 1G 内存,LogCollector 将无法在 Node1 上运行,从而导致调度流程被阻塞。
254264

255-
以下为业内两种典型的解决方案:
265+
尽管可以通过重新调度来解决这个问题,但考虑到如果需要解决数以万计的容器协同调度问题呢?以下为业内两种典型的解决方案:
256266

257267
- **成组调度**:可以在集群中等待足够的空余资源以满足亲和性约束的容器需求后,再进行统一调度。这是一种典型的成组调度方式,但会导致调度效率降低、资源利用不足,并可能出现互相等待而导致死锁的问题;
258268
- **提高单个调度效率**
259-
通过提高单任务调度的效率来解决这一问题。例如,Google 的 Omega 系统引入了一种基于共享状态的乐观绑定(Optimistic Binding)方式,以提高大规模系统调度的效率,但这种方案无疑非常复杂。笔者将在本章 7.7.3 节“调度器及扩展设计”中详细介绍该方案
269+
通过提高单任务调度的效率来解决这一问题。Google 的 Omega 系统引入了一种基于共享状态的乐观绑定(Optimistic Binding)方式,以提高大规模系统调度的效率,但这种方案无疑非常复杂。笔者将在本章 7.7.3 节“调度器及扩展设计”中详细介绍该方案。
260270

261271

262272
将资源需求声明直接定义在 Pod 上,并以 Pod 作为最小的原子单位来实现调度。Pod 与 Pod 之间不存在超亲密的关系,如果有关系,就通过网络通信实现关联。复杂的协同调度问题在 Kubernetes 中直接消失了。

0 commit comments

Comments
 (0)