docker存储
docker存储
一、docker volume类型
1.1 docker分层结构
容器是由最上面可读可写的容器层,以及若干个只读镜像层组成,创建容器时,容器中的数据都来自镜像层。这样的分层结构最大的特点是写时复制:
-
容器中新生成的数据会直接存放在容器层,也可以称之为可写层。
-
修改容器中现有的数据会先从镜像层将数据复制到容器层,修改后的数据就会直接保存在容器层,而镜像层数据不会有任何变化。
-
如果多个镜像层中有相同的命名文件,在容器层只会看到最上面镜像层的内容。
1.2 联合挂载技术
Docker镜像采用这种分层构建设计,能使镜像结构和容器的创建,共享和分发变得非常高效,每个镜像层可以称之为layer,这些layer被存放在了/var/lib/docker/volume/<storage-driver>/目录下,这里的storage-driver可以有很多种,比如AUFS、OverlayFS、VFS、Brtfs等。可以通过docker info命令查看存储驱动,通常Ubuntu类的系统默认采用的是AUFS;CentOS7.1+系列采用的是OverlayFS。
OverlayFS是一种堆叠文件系统,它依赖并建立在其他的文件系统之上(例如ext4和xfs等待),并不直接参与磁盘空间结构的划分,仅仅将原来底层文件系统中不同的目录进行合并,然后向用户呈现,这也就是联合挂载技术。
1.3 volume类型
默认情况下,在容器内创建的所有文件都存储在可写容器层上。这意味着,当该容器不再存在时,数据将不会持久保存,并且如果另一个进程需要它,则可能很难从容器中取出数据。为了能够保存持久化数据,Docker提出了volume的概念。
简单来说,volume就是目录或者文件,它可以绕过默认的联合文件系统,而以正常的文件或者目录的形式存在于Docker Host文件系统上。Docker为容器提供了两个选项来将文件存储在主机中,以便即使容器停止后文件也可以持久存储。
Volume类型:
-
Bind mounts 宿主机---->容器
-
Docker managed volume 容器---->宿主机
1.3.1 bind mounts
Bind mounts只需要在创建容器时,使用-v参数指明docker主机目录或文件和容器目录或文件映射的对应关系,将本地的数据映射到容器内,可以使用多个-v映射多个目录或文件,还需要主义的是目录只能映射目录,文件只能映射文件,不然会报错。示例如下
[root@ldh ~]# mkdir /html -v mkdir: created directory ‘/html’ [root@ldh ~]# echo "Hello World" > /html/index.html [root@ldh ~]# cat /html/index.html Hello World
[root@ldh ~]# docker run -itd --name nginx -p 80:80 -v /html:/usr/share/nginx/html nginx:latest d77a21cf75515e7340c11b43cd6d8803512948b584807e59c8fc60b6d556e483 # 验证 [root@ldh ~]# curl http://192.168.100.5 Hello World
当docker主机上的数据发生了变化,容器中的数据也会随着发生变化,示例如下
[root@ldh ~]# echo "Hello Hello" > /html/index.html [root@ldh ~]# curl http://192.168.100.5 Hello Hello
就算把容器删除,也不会影响到docker主机上的数据,示例如下
[root@ldh ~]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES d77a21cf7551 nginx:latest "/docker-entrypoint.…" 3 minutes ago Up 3 minutes 0.0.0.0:80->80/tcp, :::80->80/tcp nginx [root@ldh ~]# docker rm nginx -f nginx [root@ldh ~]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES [root@ldh ~]# ls /html/ index.html [root@ldh ~]# cat /html/index.html Hello Hello
此外,Bind mounts还可以指定数据的读写权限,默认权限为可读可写。在映射volume时,指定容器中的文件权限为只读,那么在容器那日该文件只能被读取,无法修改,这样也提高了容器的安全性。示例如下
[root@ldh ~]# docker run -itd --name nginx -p 80:80 -v /html:/usr/share/nginx/html:ro nginx:latest f443b371fa208e9be39c3cca301d43f0a37d05d65f324aea3aca28679e446b32 [root@ldh ~]# docker exec -it nginx /bin/bash root@f443b371fa20:/# cd /usr/share/nginx/html/ root@f443b371fa20:/usr/share/nginx/html# ls index.html root@f443b371fa20:/usr/share/nginx/html# echo 123 > index.html bash: index.html: Read-only file system
1.3.2 docker managed volume
第二种volume类型是由Docker管理的volume,与Bind mounts相比,Docker管理的volume在使用时不需要指定docker主机路径,Docker管理的volume具有以下几个优点:
-
与Bind mounts相比,Docker管理的volume更易于备份或迁移。
-
用户可以使用Docker CLI命令或Docker API管理volume。
-
volume在Linux和Windows容器上均可工作。
-
可以在多个容器之间更安全地共享volume,提供volume加密功能。
-
可以通过容器序言填充新volume的内容。
示例:通过-v告诉Docker需要一个volume,并将其mount到/usr/share/nginx/html目录,那么docker会在/var/lib/docker/volumes/目录下创建一个volume
[root@ldh ~]# docker run -itd --name nginx -p 80:80 -v /usr/share/nginx/html nginx:latest 3f85b8b07489140500a19a51776d5d3538f643ffac1d6559a5ed7735174eea18
[root@ldh ~]# docker container inspect nginx
......
"Mounts": [
{
"Type": "volume",
"Name": "c717c635bb52251d3921eddd42a5d17bacb8b823bbb39d7c5df0e772c1987e35",
"Source": "/var/lib/docker/volumes/c717c635bb52251d3921eddd42a5d17bacb8b823bbb39d7c5df0e772c1987e35/_data",
"Destination": "/usr/share/nginx/html",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
......
[root@ldh ~]# cd /var/lib/docker/volumes/c717c635bb52251d3921eddd42a5d17bacb8b823bbb39d7c5df0e772c1987e35/_data [root@ldh _data]# ls 50x.html index.html
可以看到docker主机上的/var/lib/docker/volumes/c717c635bb52251d3921eddd42a5d17bacb8b823bbb39d7c5df0e772c1987e35/_data目录以及挂在到容器中/usr/share/nginx/html目录下,但是查看docker主机的volume中的内容,是容器中的数据。也就是说使用Docker管理的volume,在做映射时,会把容器中的数据映射到docker主机中。
修改volume中的数据,再访问,发现volume中修改的数据会同步到容器中。
[root@ldh _data]# echo "Hello World" > index.html [root@ldh _data]# curl http://192.168.100.5 Hello World
我们还可以使用docker volume命令查看docker管理的有哪些volume,并且还可以创建volume,示例如下
# 查看docker管理的volume [root@ldh ~]# docker volume ls DRIVER VOLUME NAME local c717c635bb52251d3921eddd42a5d17bacb8b823bbb39d7c5df0e772c1987e35
# 创建一个volume,名为test,在创建新的容器时,可以直接指定使用该volume
# 创建test卷
[root@ldh ~]# docker volume create test
test
[root@ldh ~]# docker volume ls
DRIVER VOLUME NAME
local c717c635bb52251d3921eddd42a5d17bacb8b823bbb39d7c5df0e772c1987e35
local test
# 创建新容器使用test卷
[root@ldh ~]# docker run -itd --name testnginx -p 8081:80 -v test:/usr/share/nginx/html nginx:latest
0d66ec2c2ede5ff5943f856b12129458d5e181b692038dc4a39284671f44eb65
[root@ldh ~]# docker container inspect testnginx
......
"Mounts": [
{
"Type": "volume",
"Name": "test",
"Source": "/var/lib/docker/volumes/test/_data",
"Destination": "/usr/share/nginx/html",
"Driver": "local",
"Mode": "z",
"RW": true,
"Propagation": ""
}
],
......
1.3.3 两种volume的不同点
在使用docker volume命令创建volume时,volume默认都是存放在本地的,实际上本地的volume就是一个目录。除此之外Docker还支持在外部存储系统上创建volume,比如:NFS、Ceph等。
两种volume的相同点都是使用Docker主机文件系统中的某个路径。
两种volume的不同点如下表所示:
| 不同点 | Bind mounts | Docker managed volume |
|---|---|---|
| volume位置 | 可以任意指定 | /var/lib/docker/volumes/... |
| 对已有mount point影响 | 隐藏并替换为volume | 原有数据复制到volume |
| 是否支持单个文件 | 支持 | 不支持 |
| 权限控制 | 可设置为只读,默认为读写 | 无控制,均为读写权限 |
| 移植性 | 移植性弱,与主机路径绑定 | 移植性弱,无需指定主机目录 |
二、容器之间数据共享
实现容器间数据共享的方法如下:
-
第一种方法是将共享数据放在volume中,然后将其mount到多个容器。
-
第二种方法是使用volume container,volume container是用来为其他容器提供volume的容器。这样可以提高容器的移植性。
-
第三种方法是将数据打包到镜像中,然后通过--volumes-from共享
2.1 方法一
第一种方法是将共享数据放在volume中,然后将其mount到多个容器。示例如下,创建三个nginx容器组成的web集群,使用相同的html文件。在使用-v指定volume时,可以是docker主机的路径或者是docker创建的volume。
[root@ldh ~]# cat /html/index.html Hello Hello
[root@ldh ~]# docker run -itd --name web1 -p 8081:80 -v /html:/usr/share/nginx/html nginx:latest 09f4bdacdf40b7c2d5da11ee0ef89f9bae152a7fdcfe6f49e43383ce31fa11bc [root@ldh ~]# docker run -itd --name web2 -p 8082:80 -v /html:/usr/share/nginx/html nginx:latest b2356ab39801ee25ce9458b7b1160211933c31b52905252c45e522a311691067 [root@ldh ~]# docker run -itd --name web3 -p 8083:80 -v /html:/usr/share/nginx/html nginx:latest 407653d82ec1d9ddd0818b84dcf608cd6dbb3e38ee8d9529c86e7f935157c2d0 [root@ldh ~]# curl http://192.168.100.5:8081 Hello Hello [root@ldh ~]# curl http://192.168.100.5:8082 Hello Hello [root@ldh ~]# curl http://192.168.100.5:8083 Hello Hello
2.2 方法二
第二种方法是使用volume container,volume container是用来为其他容器提供volume的容器。这样可以提高容器的可移植性。
创建volume container,容器名为ldh_data,使用-v指定了volume,提供html。需要注意的是,这里使用的是docker create创建的容器,也就是说容器只是创建了,并未启动,因为volume container只是提供数据,自身并不需要运行。
[root@ldh ~]# cat /html/index.html Hello Hello
[root@ldh ~]# docker create --name ldh_data -v /html:/usr/share/nginx/html busybox:latest 33da6312c5bf2143c0dc441f1ca6235a703b8edf784d8e207cbd7e9a12de8ea1 [root@ldh ~]# docker run -itd --name nginx1 -p 8081:80 --volumes-from ldh_data nginx:latest 4b8dd9db236ad4e2ac0439ecaa08dc6fa0351f46078b8f571aa2f6d709e8fd08 [root@ldh ~]# docker run -itd --name nginx2 -p 8082:80 --volumes-from ldh_data nginx:latest 9942d50a85875b189a1d9933206147b97656a20d6a2c368733770aa5c52c39f4 [root@ldh ~]# docker run -itd --name nginx3 -p 8083:80 --volumes-from ldh_data nginx:latest c63e422676c0cd86519919d423bcd06de9cecfc2cbf339efe89d724128ee0c2c
[root@ldh ~]# curl http://192.168.100.5:8081 Hello Hello [root@ldh ~]# curl http://192.168.100.5:8082 Hello Hello [root@ldh ~]# curl http://192.168.100.5:8083 Hello Hello
与第一种实现数据共享的方法相比,不必再为每个容器指定volume,所有volume都在volume container中定义好了,容器只需与volume container关联,实现了容器与Docker主机的解耦。
2.3 方法三
volume container的数据归根到底还是在docker主机里,对容器的可移植性有一定的限制。这里可以使用data-packed volume container。其原理是将数据打包到镜像中,然后通过--volumes-from共享。
示例如下,使用Dockerfile构建镜像,将docker主机中的数据进行打包。ADD指令是将html目录中的数据添加到容器目录/usr/share/nginx/html中。VOLUME指令的作用与-v效果相同,用来创建volume,并且会把/usr/share/nginx/html目录中的数据复制到volume中。
[root@ldh ~]# mkdir html -v mkdir: created directory ‘html’ [root@ldh ~]# echo "Hello World" > html/index.html
[root@ldh ~]# vim Dockerfile FROM busybox:latest ADD html /usr/share/nginx/html VOLUME /usr/share/nginx/html CMD ["/bin.bash"]
[root@ldh ~]# docker build -t ldhtest . [+] Building 0.1s (7/7) FINISHED docker:default => [internal] load build definition from Do 0.0s => => transferring dockerfile: 135B 0.0s => [internal] load metadata for docker.io/l 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [internal] load build context 0.0s => => transferring context: 82B 0.0s => [1/2] FROM docker.io/library/busybox:lat 0.0s => [2/2] ADD html /usr/share/nginx/html 0.0s => exporting to image 0.0s => => exporting layers 0.0s => => writing image sha256:50935064363accc5 0.0s => => naming to docker.io/library/ldhtest 0.0s
使用新构建的镜像创建data-packer volume container,创建新的nginx容器,使用参数--volumes-from指定使用data-packer volume提供的volume。
[root@ldh ~]# docker create --name ldh_data ldhtest:latest 9b0a15166b137f3e878272b1b2bbc5ad2d9e2ae89ca9a9a564cc78bb83f87787 [root@ldh ~]# docker run -itd --name nginx -p 80:80 --volumes-from ldh_data nginx:latest 7389b4c757269518661d67b516367f519ec3e18116df4ca0aaa8c7c3f2144d64
[root@ldh ~]# curl http://192.168.100.5 Hello World
容器能够正确读取volume中的数据。data-packed volume container是自包含的,不依赖docker主机提供数据,具有很强的移植性,非常适合使用静态数据的场景,比如应用的配置信息、web server的静态文件等。
更多推荐



所有评论(0)