1
+ [ toc]
1
2
2
3
本文转自互联网,侵删
3
4
本系列文章将整理到我在GitHub上的《Java面试指南》仓库,更多精彩内容请到我的仓库里查看
33
34
34
35
首先,Docker 的出现一定是因为目前的后端在开发和运维阶段确实需要一种虚拟化技术解决开发环境和生产环境环境一致的问题,通过 Docker 我们可以将程序运行的环境也纳入到版本控制中,排除因为环境造成不同运行结果的可能。但是上述需求虽然推动了虚拟化技术的产生,但是如果没有合适的底层技术支撑,那么我们仍然得不到一个完美的产品。本文剩下的内容会介绍几种 Docker 使用的核心技术,如果我们了解它们的使用方法和原理,就能清楚 Docker 的实现原理。
35
36
36
- ## [ ] ( https://draveness.me/docker?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io#namespaces ) Namespaces
37
+ ## Namespaces
37
38
38
39
命名空间 (namespaces) 是 Linux 为我们提供的用于分离进程树、网络接口、挂载点以及进程间通信等资源的方法。在日常使用 Linux 或者 macOS 时,我们并没有运行多个完全分离的服务器的需要,但是如果我们在服务器上启动了多个服务,这些服务其实会相互影响的,每一个服务都能看到其他服务的进程,也可以访问宿主机器上的任意文件,这是很多时候我们都不愿意看到的,我们更希望运行在同一台机器上的不同服务能做到完全隔离,就像运行在多台不同的机器上一样。
39
40
43
44
44
45
Linux 的命名空间机制提供了以下七种不同的命名空间,包括` CLONE_NEWCGROUP ` 、` CLONE_NEWIPC ` 、` CLONE_NEWNET ` 、` CLONE_NEWNS ` 、` CLONE_NEWPID ` 、` CLONE_NEWUSER ` 和` CLONE_NEWUTS ` ,通过这七个选项我们能在创建新的进程时设置新进程应该在哪些资源上与宿主机器进行隔离。
45
46
46
- ## [ ] ( https://draveness.me/docker?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io#%E8%BF%9B%E7%A8%8B ) 进程
47
+ ## 进程
47
48
48
49
进程是 Linux 以及现在操作系统中非常重要的概念,它表示一个正在执行的程序,也是在现代分时系统中的一个任务单元。在每一个 * nix 的操作系统上,我们都能够通过` ps ` 命令打印出当前操作系统中正在执行的进程,比如在 Ubuntu 上,使用该命令就能得到以下的结果:
49
50
@@ -100,7 +101,7 @@ daemon.containerd.Create(context.Background(), container.ID, spec, createOptions
100
101
101
102
所有与命名空间的相关的设置都是在上述的两个函数中完成的,Docker 通过命名空间成功完成了与宿主机进程和网络的隔离。
102
103
103
- ## [ ] ( https://draveness.me/docker?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io#%E7%BD%91%E7%BB%9C ) 网络
104
+ ## 网络
104
105
105
106
如果 Docker 的容器通过 Linux 的命名空间完成了与宿主机进程的网络隔离,但是却有没有办法通过宿主机的网络与整个互联网相连,就会产生很多限制,所以 Docker 虽然可以通过命名空间创建一个隔离的网络环境,但是 Docker 中的服务仍然需要与外界相连才能发挥作用。
106
107
@@ -151,7 +152,7 @@ $ redis-cli -h 127.0.0.1 -p 6379 pingPONG
151
152
152
153
Docker 通过 Linux 的命名空间实现了网络的隔离,又通过 iptables 进行数据包转发,让 Docker 容器能够优雅地为宿主机器或者其他容器提供服务。
153
154
154
- ### [ ] ( https://draveness.me/docker?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io#libnetwork ) libnetwork
155
+ ### libnetwork
155
156
156
157
整个网络部分的功能都是通过 Docker 拆分出来的 libnetwork 实现的,它提供了一个连接不同容器的实现,同时也能够为应用给出一个能够提供一致的编程接口和网络层抽象的容器网络模型。
157
158
@@ -165,7 +166,7 @@ libnetwork 中最重要的概念,容器网络模型由以下的几个主要组
165
166
166
167
> 想要获得更多与 libnetwork 或者容器网络模型相关的信息,可以阅读[ Design · libnetwork] ( https://github.com/docker/libnetwork/blob/master/docs/design.md ) 了解更多信息,当然也可以阅读源代码了解不同 OS 对容器网络模型的不同实现。
167
168
168
- ## [ ] ( https://draveness.me/docker?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io#%E6%8C%82%E8%BD%BD%E7%82%B9 ) 挂载点
169
+ ## 挂载点
169
170
170
171
虽然我们已经通过 Linux 的命名空间解决了进程和网络隔离的问题,在 Docker 进程中我们已经没有办法访问宿主机器上的其他进程并且限制了网络的访问,但是 Docker 容器中的进程仍然能够访问或者修改宿主机器上的其他目录,这是我们不希望看到的。
171
172
@@ -189,12 +190,12 @@ libnetwork 中最重要的概念,容器网络模型由以下的几个主要组
189
190
190
191
> 这一部分的内容是作者在 libcontainer 中的[ SPEC.md] ( https://github.com/opencontainers/runc/blob/master/libcontainer/SPEC.md ) 文件中找到的,其中包含了 Docker 使用的文件系统的说明,对于 Docker 是否真的使用` chroot ` 来确保当前的进程无法访问宿主机器的目录,作者其实也没有确切的答案,一是 Docker 项目的代码太多庞大,不知道该从何入手,作者尝试通过 Google 查找相关的结果,但是既找到了无人回答的[ 问题] ( https://forums.docker.com/t/does-the-docker-engine-use-chroot/25429 ) ,也得到了与 SPEC 中的描述有冲突的[ 答案] ( https://www.quora.com/Do-Docker-containers-use-a-chroot-environment ) ,如果各位读者有明确的答案可以在博客下面留言,非常感谢。
191
192
192
- ## [ ] ( https://draveness.me/docker?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io# chroot) chroot
193
+ ## chroot
193
194
在这里不得不简单介绍一下` chroot ` (change root),在 Linux 系统中,系统默认的目录就都是以` / ` 也就是根目录开头的,` chroot ` 的使用能够改变当前的系统根目录结构,通过改变当前系统的根目录,我们能够限制用户的权利,在新的根目录下并不能够访问旧系统根目录的结构个文件,也就建立了一个与原系统完全隔离的目录结构。
194
195
195
196
> 与 chroot 的相关内容部分来自[ 理解 chroot] ( https://www.ibm.com/developerworks/cn/linux/l-cn-chroot/index.html ) 一文,各位读者可以阅读这篇文章获得更详细的信息。
196
197
197
- ## [ ] ( https://draveness.me/docker?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io#cgroups ) CGroups
198
+ ## CGroups
198
199
199
200
我们通过 Linux 的命名空间为新创建的进程隔离了文件系统、网络并与宿主机器之间的进程相互隔离,但是命名空间并不能够为我们提供物理资源上的隔离,比如 CPU 或者内存,如果在同一台机器上运行了多个对彼此以及宿主机器一无所知的『容器』,这些容器却共同占用了宿主机器的物理资源。
200
201
@@ -241,7 +242,7 @@ $ docker run -it -d --cpu-quota=50000 busybox53861305258ecdd7f5d2a3240af694aec9a
241
242
242
243
当我们使用 Docker 关闭掉正在运行的容器时,Docker 的子控制组对应的文件夹也会被 Docker 进程移除,Docker 在使用 CGroup 时其实也只是做了一些创建文件夹改变文件内容的文件操作,不过 CGroup 的使用也确实解决了我们限制子容器资源占用的问题,系统管理员能够为多个容器合理的分配资源并且不会出现多个容器互相抢占资源的问题。
243
244
244
- ## [ ] ( https://draveness.me/docker?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io#unionfs ) UnionFS
245
+ ## UnionFS
245
246
246
247
Linux 的命名空间和控制组分别解决了不同资源隔离的问题,前者解决了进程、网络以及文件系统的隔离,后者实现了 CPU、内存等资源的隔离,但是在 Docker 中还有另一个非常重要的问题需要解决 - 也就是镜像。
247
248
@@ -255,7 +256,7 @@ $ docker export $(docker create busybox) | tar -C rootfs -xvf -$ lsbin dev etc
255
256
256
257
你可以看到这个 busybox 镜像中的目录结构与 Linux 操作系统的根目录中的内容并没有太多的区别,可以说Docker 镜像就是一个文件。
257
258
258
- ## [ ] ( https://draveness.me/docker?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io#%E5%AD%98%E5%82%A8%E9%A9%B1%E5%8A%A8 ) 存储驱动
259
+ ## 存储驱动
259
260
260
261
Docker 使用了一系列不同的存储驱动管理镜像内的文件系统并运行容器,这些存储驱动与 Docker 卷(volume)有些不同,存储引擎管理着能够在多个容器之间共享的存储。
261
262
@@ -275,7 +276,7 @@ FROM ubuntu:15.04COPY . /appRUN make /appCMD python /app/app.py
275
276
276
277
![ docker-images-and-container] ( https://java-tutorial.oss-cn-shanghai.aliyuncs.com/2017-12-06-docker-images-and-container.png )
277
278
278
- ## [ ] ( https://draveness.me/docker?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io#aufs ) AUFS
279
+ ## AUFS
279
280
280
281
UnionFS 其实是一种为 Linux 操作系统设计的用于把多个文件系统『联合』到同一个挂载点的文件系统服务。而 AUFS 即 Advanced UnionFS 其实就是 UnionFS 的升级版,它能够提供更优秀的性能和效率。
281
282
@@ -295,7 +296,7 @@ $ ls /var/lib/docker/aufs/diff/00adcccc1a55a36a610a6ebb3e07cc35577f2f5a3b671be3d
295
296
296
297
上面的这张图片非常好的展示了组装的过程,每一个镜像层都是建立在另一个镜像层之上的,同时所有的镜像层都是只读的,只有每个容器最顶层的容器层才可以被用户直接读写,所有的容器都建立在一些底层服务(Kernel)上,包括命名空间、控制组、rootfs 等等,这种容器的组装方式提供了非常大的灵活性,只读的镜像层通过共享也能够减少磁盘的占用。
297
298
298
- ## [ ] ( https://draveness.me/docker?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io#%E5%85%B6%E4%BB%96%E5%AD%98%E5%82%A8%E9%A9%B1%E5%8A%A8 ) 其他存储驱动
299
+ ## 其他存储驱动
299
300
300
301
AUFS 只是 Docker 使用的存储驱动的一种,除了 AUFS 之外,Docker 还支持了不同的存储驱动,包括` aufs ` 、` devicemapper ` 、` overlay2 ` 、` zfs ` 和` vfs ` 等等,在最新的 Docker 中,` overlay2 ` 取代了` aufs ` 成为了推荐的存储驱动,但是在没有` overlay2 ` 驱动的机器上仍然会使用` aufs ` 作为 Docker 的默认驱动。
301
302
@@ -311,7 +312,7 @@ $ docker info | grep StorageStorage Driver: aufs
311
312
312
313
作者的这台 Ubuntu 上由于没有` overlay2 ` 存储驱动,所以使用` aufs ` 作为 Docker 的默认存储驱动。
313
314
314
- ## [ ] ( https://draveness.me/docker?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io#%E6%80%BB%E7%BB%93 ) 总结
315
+ ## 总结
315
316
316
317
Docker 目前已经成为了非常主流的技术,已经在很多成熟公司的生产环境中使用,但是 Docker 的核心技术其实已经有很多年的历史了,Linux 命名空间、控制组和 UnionFS 三大技术支撑了目前 Docker 的实现,也是 Docker 能够出现的最重要原因。
317
318
0 commit comments