11.Docker存储管理
Docker Data Volume
Docker镜像由多个只读层叠加,启动容器时,Docker会加载只读镜像层并在镜像栈顶部添加一个读写层
如果运行中的容器修改了现有一个已存在的文件,那该文件将会从读写层下面的只读层复制到读写层,该文件的只读版本仍然存在,只是已经被读写层中该文件的副本所隐藏;即“写时复制(COW)”机制
关闭并重启容器,其数据不受影响;但删除Docker容器,则其更改将会全部丢失
存在的问题
- 存储于联合文件系统中,不易于宿主机访问;
- 容器间数据不方便共享
- 删除容器其数据会丢失
解决方案:”volume“
卷是容器上的一个或多个目录,此类目录可绕过联合文件系统(AUFS),与宿主机上的某目录绑定
volume的初衷是独立于容器生命周期实现数据持久化,因此删除容器不会删除卷
Docker的数据持久化即使数据不随着container的结束而结束,数据存在于host机器上——要么存在于host的某个指定目录中(使用bind mount),要么使用docker自己管理的volume(/var/lib/docker/volumes下)。
bind mount
bind mount自docker早期便开始为人们使用了,用于将host机器的目录mount到container中。需明确指定宿主机路径与容器内部路径,进行绑定;具有高耦合性,强自由行;
bind mount在不同的宿主机系统时不可移植的,比如Windows和Linux的目录结构是不一样的,bind mount所指向的host目录也不能一样。这也是为什么bind mount不能出现在Dockerfile中的原因,因为这样Dockerfile就不可移植了。
docker run -it -v HOSTDIR:VOLUMEDIR --name bbox2 busybox
#查看bbox2容器的卷、卷标识符及挂载的主机目录(-f 指定Go模板)
docker inspect -f {{.mounts}} bbox2
#绑定当前路径下host-dava到容器/container-data下
docker run -it -v $(pwd)/host-dava:/container-data alpine sh
有几点需要注意:
- host机器的目录路径必须为全路径(准确的说需要以
/
或~/
开始的路径),不然docker会将其当做volume而不是volume处理- 如果host机器上的目录不存在,docker会自动创建该目录
- 如果container中的目录不存在,docker会自动创建该目录
- 如果container中的目录已经有内容,那么docker会使用host上的目录将其覆盖掉
volume
只需指定容器内需要挂载路径,由docker daemon自动创建维护特定目录下;低耦合性,但无法指定特定宿主机路径
volume也是绕过container的文件系统,直接将数据写到host机器上,只是volume是被docker管理的,docker下所有的volume都在host机器上的指定目录下/var/lib/docker/volumes。
docker run -it -name bbox1 -v /data busybox
#查看bbox1容器的卷、卷标识符及挂载的主机目录
docker inspect -f {{.mounts}} bbox1
docker run -it -v my-volume:/mydata alpine sh
然后可以查看到给my-volume的volume:
docker volume inspect my-volume
[
{
"CreatedAt": "2019-08-28T19:42:49Z",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/my-volume/_data",
"Name": "my-volume",
"Options": {},
"Scope": "local"
}
]
LowerDir: image镜像层(镜像本身,只读)
UpperDir:容器的上层(读写)
MergedDir:容器的文件系统,使用UnionFS(联合文件系统)将lowerdir和upperdir合并给容器使用
WorkDir:容器在宿主机的工作目录
可以看到,volume在host机器的目录为/var/lib/docker/volumes/my-volume/_data
。此时,如果my-volume不存在,那么docker会自动创建my-volume,然后再挂载。
也可以不指定host上的volume:
docker run -it -v /mydata alpine sh
此时docker将自动创建一个匿名的volume,并将其挂载到container中的/mydata目录。匿名volume在host机器上的目录路径类似于:/var/lib/docker/volumes/30b32264cd0acfe8625e07edf156eb197716c20f69e7e9a053c87c2182b2e7d8/_data
。
创建volume
除了让docker帮我们自动创建volume,我们也可以自行创建:
docker volume create my-volume-2
然后将这个已有的my-volume-2挂载到container中:
docker run -it -v my-volume-2:/mydata alpine sh
需要注意的是,与bind mount不同的是,如果volume是空的而container中的目录有内容,那么docker会将container目录中的内容拷贝到volume中,但是如果volume中已经有内容,则会将container中的目录覆盖。
创建数据容器(一个不运行的容器)
docker run -it -v /father/path:/child/path --name data ubuntu
exit
上面两步是创建号了数据container,接下来就用 --volumes--from 来挂载数据到新的容器中
docker run -it --volumes--from data ubuntu
复制容器volume(共享容器存储)
单个容器的卷使用同一个主机目录
docker run -it --name c1 -v /docker/volume/v1:/data busybox
docker run -it --name c2 -v /docker/volume/v1:/data busybox
复制使用其它容器的卷,为docker run命令使用--volumes-from
选项
docker run -it --name bbox1 -v /docker/volumes/v1:/data busybox
docker run -it --name bbox2 --volumes-from bbox1 busybox
tmpfs
tmpfs:挂载存储在主机系统的内存中,而不会写入主机的文件系统。如果不希望将数据持久存储在任何位置,可以使用
tmpfs,同时避免写入容器可写层提高性能。
# 使用--tmpfs
docker run -dit --name tmpfs-test --tmpfs /app busybox