一、Docker文件系统
1.1、概述
分层镜像
rootfs 的基础上,Docker 公司结合UnionFS创新性地提出了使用多个增量 rootfs 联合挂载一个完整 rootfs 的方案,这就是容器镜像中“层”的概念。
分层镜像的好处
通过“分层镜像”的设计,以 Docker 镜像为核心,来自不同公司、不同团队的技术人员被紧密地联系在了一起。而且,由于容器镜像的操作是增量式的,这样每次镜像拉取、推送的内容,比原本多个完整的操作系统的大小要小得多;而共享层的存在,可以使得所有这些容器镜像需要的总空间,也比每个镜像的总和要小。这样就使得基于容器镜像的团队协作,要比基于动则几个 GB 的虚拟机磁盘镜像的协作要敏捷得多。
强一致性
一旦这个镜像被发布,那么你在全世界的任何一个地方下载这个镜像,得到的内容都完全一致,可以完全复现这个镜像制作者当初的完整环境。这,就是容器技术“强一致性”的重要体现。
1.2、Docker如何应用UnionFS
镜像文件系统
我们拉取一个ubuntu的镜像,通过image inspect查看他的文件系统
$ docker image inspect ubuntu:latest
...
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:f49017d4d5ce9c0f544c...",
"sha256:8f2b771487e9d6354080...",
"sha256:ccd4d61916aaa2159429...",
"sha256:c01d74f99de40e097c73...",
"sha256:268a067217b5fe78e000..."
]
}
这五个层就是五个增量 rootfs,每一层都是 Ubuntu 操作系统文件与目录的一部分。
如何应用UnionFS
在使用镜像时,Docker 会把这些增量联合挂载在一个统一的挂载点上(等价于前面例子里的“/C”目录),挂在成一个完整的Ubuntu文件系统。
(1)这个信息记录在 AuFS 的系统目录 /sys/fs/aufs 下面。首先,通过查看 AuFS 的挂载信息,我们可以找到这个目录对应的 AuFS 的内部 ID(也叫:si):
# si=972c6d361e6b32ba
$ cat /proc/mounts| grep aufs
none /var/lib/docker/aufs/mnt/6e3be5d2ecccae7cc0fc... aufs rw,relatime,si=972c6d361e6b32ba,dio,dirperm1 0 0
(2)使用这个 ID,你就可以在 /sys/fs/aufs 下查看被联合挂载在一起的各个层的信息:
$ cat /sys/fs/aufs/si_972c6d361e6b32ba/br[0-9]* /var/lib/docker/aufs/diff/6e3be5d2ecccae7cc...=rw /var/lib/docker/aufs/diff/6e3be5d2ecccae7cc...-init=ro+wh /var/lib/docker/aufs/diff/32e8e20064858c0f2...=ro+wh /var/lib/docker/aufs/diff/2b8858809bce62e62...=ro+wh /var/lib/docker/aufs/diff/20707dce8efc0d267...=ro+wh /var/lib/docker/aufs/diff/72b0744e06247c7d0...=ro+wh /var/lib/docker/aufs/diff/a524a729adadedb90...=ro+wh
从这里我们可以看到:镜像的层都放置在 /var/lib/docker/aufs/diff 目录下,然后被联合挂载在 /var/lib/docker/aufs/mnt 里面。最终,这 7 个层都被联合挂载到 /var/lib/docker/aufs/mnt 目录下,表现为一个完整的 Ubuntu 操作系统供容器使用。
1.3、Docker文件系统结构图
从上面目录可以看出Docker的文件系统如下图所示:
从这个结构可以看出,容器的 rootfs 由如下图所示的三部分组成:只读层、Init层、可读写层
-
只读层(存储基础镜像文件)
这层对应的正是 ubuntu:latest 镜像的五层,这些层,都以增量的方式分别包含了 Ubuntu 操作系统的一部分。这层在容器中是不会被改动的。
-
Init层
用户在启动容器时,写入一些指定的值比如hostname等,这些值只对当前容器有效,因此,我们并不希望再执行docker commit时,把这些数据连同读写层一起提交,因此形成一个单独的层。
所以,docker commit 只会提交可读写层,不包含这一层的内容。
-
可读写层
用户在容器中做得增删改的操作,会被增量的记录在可读写层。
1.3、Docker容器全景
我们先编写有个DockerFile构建出一个镜像,看看他启动后的全景
FROM python:3.7
WORKDIR /app
ADD . /app
RUN pip install --trusted-host pypi.python.org -r requirements.txt
EXPOSE 80
ENV NAME World
# CMD,意思是 Dockerfile 指定 python app.py 为这个容器的进程。
CMD ["python", "app.py"]
Dockerfile 中的每个原语执行后,都会生成一个对应的镜像层。即使原语本身并没有明显地修改文件的操作(比如,ENV 原语),它对应的层也会存在。只不过在外界看来,这个层是空的。
我们可以看到:
(1)这个容器进程“python app.py”,运行在由 Linux Namespace 和 Cgroups 构成的隔离环境里;
(2)而它运行所需要的各种文件,比如 python,app.py,以及整个操作系统文件,则由多个联合挂载在一起的 rootfs 层提供。
(3)DockerFile中的每一步,都是一层,组成了只读层;
(4)只读层 + Init层 + 可读写层 = rootfs;
二、Docker网络
2.1、Docker的三种网络模式
安装 Docker 以后,会默认创建三种网络,可以通过 docker network ls
查看。
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
688d1970f72e bridge bridge local
885da101da7d host host local
f4f1b3cf1b7f none null local
bridge
- 为容器创建一个隔离的网络环境(network namespace)
- 容器端口号可以与主机端口号重合
- 网络互通
host
- 并没有为容器创建一个隔离的网络环境
- 容器的 IP 地址同 Docker host 的 IP 地址,因此容器中端口号不能与主机的端口号重合
- 网络互通
none
- 不为Docker容器构造任何网络环境
- 容器只能使用127.0.0.1的本机网络(无法ping www.baidu.com)
2.2、如何设置网络
当你运行一个容器的时候,可以使用–network参数来指定
docker run -it -d --network none --name test2 centos7:new002 /bin/bash