一、Docker存储驱动
(1)Docker的分层特性
- Docker容器的启动是依赖于image镜像的,在Docker容器启动前需要先拉取镜像,然后再启动容器,多个容器可以使用同一个镜像动,这些容器都是共用一个镜像,而不是各自将镜像复制一份然后各自独立运行
- Docker镜像不管是在pull拉取和load加载的时候都是分层拉取或者加载的,从拉取或者加载的过程就可以看出来:
[root@docker ~]# ll 总用量 134228 -rw-------. 1 root root 1264 1月 12 2021 anaconda-ks.cfg drwxr-xr-x 3 root root 4096 7月 26 21:58 docker -rw-r--r-- 1 root root 137441280 7月 26 21:58 nginx [root@docker ~]# docker load -i nginx #可以看到加载的时候都是一步一步加载,并不是一次性加载的 764055ebc9a7: Loading layer [==================================================>] 72.53MB/72.53MB ace9ed9bcfaf: Loading layer [==================================================>] 64.86MB/64.86MB 48b4a40de359: Loading layer [==================================================>] 3.072kB/3.072kB c553c6ba5f13: Loading layer [==================================================>] 4.096kB/4.096kB d97733c0a3b6: Loading layer [==================================================>] 3.584kB/3.584kB 9d1af766c818: Loading layer [==================================================>] 7.168kB/7.168kB Loaded image: nginx:latest #可以看到在加载镜像的时候是一层一层加载的,而镜像其实也是这么一层一层存储在磁盘上的
- 通常一个镜像包含多层:
- 首先必须明确镜像是只读的。每一层都只读。
- 在上图上,我们可以看到,在内核之上,最底层首先是一个基础镜像层,这里是一个ubuntu的基础镜像,因为镜像的只读特性,如果我们想要在这个ubuntu的基础镜像上安装一个emacs编辑器(相当于centos的vi编辑器),则只能在基础镜像之上,在构建一层新的镜像层。同样的道理,如果想要在当前的emacs镜像层之上添加一个apache,则只能在其上再构建一个新的镜像层。而这即是镜像的分层特性。
(2)容器读写层的工作原理
上面说了镜像的分层特性,镜像是只读的,包括镜像的每一层都是只读的,而事实上当我们使用镜像启动一个容器的时候,我们是可以在容器里随意读写的,从结果上来看,似乎是与镜像的只读特性相悖。
在上面的图里,在最上层可以看到还有一个读写层。每一个容器在启动的时候,都会基于当前镜像的基础上在镜像的最上层挂载一层读写层,用户对容器的所有操作都是在读写层中完成,一旦容器销毁,这个读写层也会销毁,数据也就丢失了
容器=镜像+读写层。针对读写层的操作,主要基于两种方式,写时复制和用时分配。
-写时复制:
写时复制是所有驱动都用到的技术。COW(Copy On Write的缩写),表示只有在写的时候才去复制,这是针对已有文件的修改场景。例如:基于一个镜像启动多个容器,如果为每个容器都去分配一个镜像一样的文件系统,那么会占用大量的磁盘空间,那么容器的轻量级优势也就没有了,而COW技术可以让所有的容器共享镜像的文件系统,所有的数据都从镜像中读取,只有容器要对文件进行写操作时,容器才会从镜像里把要写的文件复制到自己的文件系统中进行修改,所以无论有多少个容器共享一个镜像,所做的写操作都是从镜像中复制到自己的文件系统中的副本上进行,并不会修改镜像中的源文件,并且多个容器操作一个相同的文件,就会在多个容器的文件系统中生成一个对应的文件副本,每个容器修改的都是自己的文件副本,相互不影响。使用COW可以有效的提高磁盘的利用率
-用时分配:
用时分配是用在原本没有这个文件的场景,只有在要新写入一个文件时才会分配空间,这样可以提高存储资源的利用率。例如:启动一个容器,并不会为这个容器预分配一些磁盘空间,而是在有新的文件写入时,才会按需分配新空间
(3)Docker存储驱动
- Docker提供了多种存储驱动来实现不同的方式存储镜像,常用的有:
- AUFS
- OverlayFS
- Devicemapper
- Btrfs
- ZFS
- 现在最常用的就是OverlayFS存储驱动,现在已经更新到了Overlay2
-AUFS
AUFS(AnotherUnionFS)是一种Union FS,是文件级的存储驱动。AUFS是一个能透明覆盖一个或多个现有文件系统的层状文件系统,把多层合并成文件系统的单层表示。简单来说就是支持将不同目录挂载到同一个虚拟文件系统下的文件系统。这种文件系统可以一层一层地叠加修改文件。无论底下有多少层都是只读的,只有最上层的文件系统是可写的。当需要修改一个文件时,AUFS创建该文件的一个副本,使用CoW将文件从只读层复制到可写层进行修改,结果也保存在可写层。在Docker中,底下的只读层就是image镜像,可写层就是Container容器。结构如下图所示:
-OverlayFS
Overlay是Linux内核3.18后支持的,也是一种Union FS,和AUFS的多层不同的是Overlay只有两层:一个upper文件系统和一个lower文件系统,分别代表Docker的镜像层和容器层。当需要修改一个文件时,使用CoW将文件从只读的lower复制到可写的upper进行修改,结果也保存在upper层。在Docker中,底下的只读层就是image,可写层就是Container。目前最新的OverlayFS为Overlay2。
-Devicemapper
Device mapper是Linux内核2.6.9后支持的,提供的一种从逻辑设备到物理设备的映射框架机制,在该机制下,用户可以很方便的根据自己的需要制定实现存储资源的管理策略。前面讲的AUFS和OverlayFS都是文件级存储,而Device mapper是块级存储,所有的操作都是直接对块进行操作,而不是文件。Device mapper驱动会先在块设备上创建一个资源池,然后在资源池上创建一个带有文件系统的基本设备,所有镜像都是这个基本设备的快照,而容器则是镜像的快照。所以在容器里看到文件系统是资源池上基本设备的文件系统的快照,并没有为容器分配空间。当要写入一个新文件时,在容器的镜像内为其分配新的块并写入数据,这个叫用时分配。当要修改已有文件时,再使用CoW为容器快照分配块空间,将要修改的数据复制到在容器快照中新的块里再进行修改。Device mapper 驱动默认会创建一个100G的文件包含镜像和容器。每一个容器被限制在10G大小的卷内,可以自己配置调整。结构如下图所示:
-常用存储驱动对比
-AUFS VS OverlayFS
AUFS和Overlay都是联合文件系统,但AUFS有多层,而Overlay只有两层,所以在做写时复制操作时,如果文件比较大且存在比较低的层,则AUSF可能会慢一些。而且Overlay并入了linux kernel mainline,AUFS没有。目前AUFS已基本被淘汰
OverlayFS VS Device mapper
OverlayFS是文件级存储,Device mapper是块级存储,当文件特别大而修改的内容很小,Overlay不管修改的内容大小都会复制整个文件,对大文件进行修改显然要比小文件要消耗更多的时间,而块级无论是大文件还是小文件都只复制需要修改的块,并不是整个文件,在
这种场景下,显然device mapper要快一些。因为块级的是直接访问逻辑盘,适合IO密集的场景。而对于程序内部复杂,大并发但少IO的场景,Overlay的性能相对要强一些。
二、Docker套接字介绍
作用: 可以使不同主机连接(使用)不同主机的docker
******查看Docker状态 [root@docker ~]# systemctl status docker ● docker.service - Docker Application Container Engine Loaded: loaded (/usr/lib/systemd/system/docker.service; disabled; vendor preset: disabled) Active: active (running) since 一 2021-07-26 22:00:11 CST; 37min ago Docs: https://docs.docker.com Main PID: 1326 (dockerd) Memory: 168.9M CGroup: /system.slice/docker.service └─1326 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock 7月 26 22:00:10 docker dockerd[1326]: time="2021-07-26T22:00:10.860859981+08:00" level=info msg="Loading containers: start." 7月 26 22:00:11 docker dockerd[1326]: time="2021-07-26T22:00:11.038497116+08:00" level=info msg="Default bridge (docker0) is a...ddress" 7月 26 22:00:11 docker dockerd[1326]: time="2021-07-26T22:00:11.073211819+08:00" level=info msg="Loading containers: done." 7月 26 22:00:11 docker dockerd[1326]: time="2021-07-26T22:00:11.084423373+08:00" level=info msg="Docker daemon" commit=481bc77...18.09.6 7月 26 22:00:11 docker dockerd[1326]: time="2021-07-26T22:00:11.084469997+08:00" level=info msg="Daemon has completed initialization" 7月 26 22:00:11 docker dockerd[1326]: time="2021-07-26T22:00:11.101030654+08:00" level=info msg="API listen on /var/run/docker.sock" 7月 26 22:00:11 docker systemd[1]: Started Docker Application Container Engine. 7月 26 22:03:07 docker dockerd[1326]: time="2021-07-26T22:03:07.581931947+08:00" level=warning msg="Error getting v2 registry:...aders)" 7月 26 22:03:07 docker dockerd[1326]: time="2021-07-26T22:03:07.581957043+08:00" level=error msg="Not continuing with pull aft...aders)" 7月 26 22:03:07 docker dockerd[1326]: time="2021-07-26T22:03:07.581994482+08:00" level=error msg="Handler for POST /v1.39/imag...aders)" Hint: Some lines were ellipsized, use -l to show in full.
******查看docker版本 [root@docker ~]# docker version Client: #客户端引擎社区版 Version: 18.09.6 #版本 API version: 1.39 Go version: go1.10.8 Git commit: 481bc77156 Built: Sat May 4 02:34:58 2019 OS/Arch: linux/amd64 Experimental: false Server: Docker Engine - Community #服务端引擎 Engine: Version: 18.09.6 #版本 API version: 1.39 (minimum version 1.12) Go version: go1.10.8 Git commit: 481bc77 Built: Sat May 4 02:02:43 2019 OS/Arch: linux/amd64 Experimental: false
#docker是一个C/S架构,在执行docker的指令的时候,会默认连接到自己本机的docker -deamon进程 ******停止掉docker进程 [root@docker ~]# systemctl stop docker [root@docker ~]# systemctl status docker ● docker.service - Docker Application Container Engine Loaded: loaded (/usr/lib/systemd/system/docker.service; disabled; vendor preset: disabled) Active: inactive (dead) since 一 2021-07-26 22:42:49 CST; 4s ago Docs: https://docs.docker.com Process: 1326 ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock (code=exited, status=0/SUCCESS) Main PID: 1326 (code=exited, status=0/SUCCESS) 7月 26 22:00:11 docker dockerd[1326]: time="2021-07-26T22:00:11.084423373+08:00" level=info msg="Docker daemon" commit=481bc77...18.09.6 7月 26 22:00:11 docker dockerd[1326]: time="2021-07-26T22:00:11.084469997+08:00" level=info msg="Daemon has completed initialization" 7月 26 22:00:11 docker dockerd[1326]: time="2021-07-26T22:00:11.101030654+08:00" level=info msg="API listen on /var/run/docker.sock" 7月 26 22:00:11 docker systemd[1]: Started Docker Application Container Engine. 7月 26 22:03:07 docker dockerd[1326]: time="2021-07-26T22:03:07.581931947+08:00" level=warning msg="Error getting v2 registry:...aders)" 7月 26 22:03:07 docker dockerd[1326]: time="2021-07-26T22:03:07.581957043+08:00" level=error msg="Not continuing with pull aft...aders)" 7月 26 22:03:07 docker dockerd[1326]: time="2021-07-26T22:03:07.581994482+08:00" level=error msg="Handler for POST /v1.39/imag...aders)" 7月 26 22:42:49 docker systemd[1]: Stopping Docker Application Container Engine... 7月 26 22:42:49 docker dockerd[1326]: time="2021-07-26T22:42:49.280932423+08:00" level=info msg="Processing signal 'terminated'" 7月 26 22:42:49 docker systemd[1]: Stopped Docker Application Container Engine. Hint: Some lines were ellipsized, use -l to show in full. [root@docker ~]# docker version #再次查看docker版本号,发现server端没有了 Client: Version: 18.09.6 API version: 1.39 Go version: go1.10.8 Git commit: 481bc77156 Built: Sat May 4 02:34:58 2019 OS/Arch: linux/amd64 Experimental: false Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running? #显示无法连接Docker daemon,连接的方式是基于文件套接字连接 #客户端使用套接字连接,不需要监听任何端口,只需要读取/var/run/docker.sock这个文件 [root@docker ~]# ll /var/run/docker.sock srw-rw---- 1 root docker 0 7月 26 22:00 /var/run/docker.sock #默认是监听本地的套接字文件,也可以使用网络套接字,需要修改启动文件 [root@docker ~]# vim /lib/systemd/system/docker.service 。。。。。。 13 # for containers run by docker 14 ExecStart=/usr/bin/dockerd -H fd:// -H 0.0.0.0:2375 --containerd=/run/containerd/containerd.sock #配置成监听网络接口 15 ExecReload=/bin/kill -s HUP $MAINPID 16 TimeoutSec=0 17 RestartSec=2 。。。。。。 #保存退出 [root@docker ~]# systemctl daemon-reload [root@docker ~]# systemctl start docker [root@docker ~]# netstat -anpt | grep 2375 #查看端口,成功监听 tcp6 0 0 :::2375 :::* LISTEN 16356/dockerd #docker的网络套接字就配置完成,客户端就可以连接2375端口,连接docker-daemon,服务端就是开启端口,等着客户端进行访问 [root@docker ~]# docker -H 192.168.100.202 version #使用本机或者另外一台安装docker的机器上运行,version是命令,这个命令就是docker命令,例如:docker images 就可以写成docker -H 192.168.100.202:2375 images,实现不同主机访问docker Client: Version: 18.09.6 API version: 1.39 Go version: go1.10.8 Git commit: 481bc77156 Built: Sat May 4 02:34:58 2019 OS/Arch: linux/amd64 Experimental: false Server: Docker Engine - Community Engine: Version: 18.09.6 API version: 1.39 (minimum version 1.12) Go version: go1.10.8 Git commit: 481bc77 Built: Sat May 4 02:02:43 2019 OS/Arch: linux/amd64 Experimental: false [root@docker ~]# docker -H 192.168.100.202:2375 version Client: Version: 18.09.6 API version: 1.39 Go version: go1.10.8 Git commit: 481bc77156 Built: Sat May 4 02:34:58 2019 OS/Arch: linux/amd64 Experimental: false Server: Docker Engine - Community Engine: Version: 18.09.6 API version: 1.39 (minimum version 1.12) Go version: go1.10.8 Git commit: 481bc77 Built: Sat May 4 02:02:43 2019 OS/Arch: linux/amd64 Experimental: false [root@docker ~]# docker -H 192.168.100.202:2375 images REPOSITORY TAG IMAGE ID CREATED SIZE nginx latest 4cdc5dd7eaad 2 weeks ago 133MB #docker在开启网络套接字,默认是没有任何验证的,需要安全配置,否则会很危险,生产中也不会使用网络套接字来管理所有的docker客户端,默认使用本地的文件套接字管理自己的docker服务端,如果需要管理所有的docker,可以借助K8S平台进行管理