介绍
镜像本身是只读的,保持不变。从镜像启动一个容器的时候,会在镜像的最上一层创建一个可写层。容器对于文件系统的写操作只会修改可写层,不会修改镜像。
容器运行的过程中产生的数据全都存储到可写层中,当我们删除掉这个容器的时候,同时也会删除掉可写层上的文件。如果有些数据你想一直保存,比如 Web 服务器的日志、数据库管理系统里的数据,怎么办?
我们可以创建一个数据卷,然后将数据卷挂载到容器上指定的目录,这样容器的这个目录便不属于容器的镜像文件系统了,而属于数据卷都对应着的主机上的一个物理目录 /var/lib/docker/volumes/数据卷名/_data。因此将容器的数据保存在数据卷上,就相当于保存到了对应的主机目录上,即便容器被删除了,数据卷中的数据也会一直保存。
命令
docker volume create
docker volume inspect
docker volume ls
docker volume prune
docker volume rm
docker create -v
docker create --volumes-from
docker run -v
docker run --volumes-from
docker volume create
使用 docker volume create
命令创建一个数据卷,该数据卷并没有挂载到任何容器。以下代码创建了一个名称为 foo 的数据卷,该数据卷对应着主机上的 /var/lib/docker/volumes/foo/_data 物理目录。
$ docker volume create foo
docker create -v / docker run -v
使用 docker create -v
或 docker run -v
命令创建容器时,可以使用 -v
参数将容器的指定目录挂载到指定的数据卷。以下代码创建了一个容器 db1 ,然后将容器的 /data 目录挂载到已经存在的名为 foo 的数据卷。
$ docker create -v foo:/data --name db1 centos
以下代码创建了一个名为 db2 的容器,然后将容器的 /data 目录挂载到名为 bar 的数据卷,由于 bar 数据卷不存在,所以在创建容器的时候会先创建一个名为 bar 的数据卷,然后将 /data 目录挂载到 bar 数据卷,该数据卷对应着主机上的 /var/lib/docker/volumes/bar/_data 物理目录。
$ docker create -v bar:/data --name db2 centos
以下代码创建了一个名为 db3 的容器,然后将容器的 /data 目录挂载到匿名的数据卷。创建容器的时候会先创建一个名为随机字符串的数据卷,然后将 /data 目录挂载到该数据卷,该数据卷对应着主机上的 /var/lib/docker/volumes/随机字符串/_data 物理目录。
$ docker create -v /data --name db3 centos
以下代码创建了一个名为 db4 的容器,然后将容器的 /data 目录挂载到指定的主机目录的绝对路径,这个主机目录并不是数据卷,因此通过数据卷命令 docker volume
无法管理该主机目录。
$ docker create -v /Users/yumanli/Desktop/data:/data --name db4 centos
可以使用 :ro
来绑定只读的数据卷,只能绑定具名数据卷或者主机目录。
$ docker create -v foo:/data:ro --name db1 centos
$ docker create -v /Users/yumanli/Desktop/data:/data:ro --name db4 centos
不能使用 :ro
来绑定匿名数据卷,以下代码会报错。
$ docker create -v /data:ro --name db3 centos
docker volume ls
使用 docker volume ls
命令查看所有的数据卷。
$ docker volume ls
DRIVER VOLUME NAME
local 6cda7f4532a18c226ae419cee91c7d08ca6fe6e409914b25f368b8edea641a09
local bar
local foo
使用 docker volume ls -f dangling=true
命令查看没有被挂载到容器的数据卷。
$ docker volume create baz
$ docker volume ls -f dangling=true
DRIVER VOLUME NAME
local baz
docker volume inspect
使用 docker volume inspect
命令查看数据卷的信息,Name
字段为数据卷的名字,Mountpoint
字段为数据卷挂载的主机目录。
$ docker volume inspect foo bar baz 6cda7f4532a18c226ae419cee91c7d08ca6fe6e409914b25f368b8edea641a09
[
{
"CreatedAt": "2020-01-28T02:29:32Z",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/foo/_data",
"Name": "foo",
"Options": {},
"Scope": "local"
},
{
"CreatedAt": "2020-01-28T02:29:46Z",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/bar/_data",
"Name": "bar",
"Options": null,
"Scope": "local"
},
{
"CreatedAt": "2020-01-28T03:02:30Z",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/baz/_data",
"Name": "baz",
"Options": {},
"Scope": "local"
},
{
"CreatedAt": "2020-01-28T02:29:53Z",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/6cda7f4532a18c226ae419cee91c7d08ca6fe6e409914b25f368b8edea641a09/_data",
"Name": "6cda7f4532a18c226ae419cee91c7d08ca6fe6e409914b25f368b8edea641a09",
"Options": null,
"Scope": "local"
}
]
docker volume rm
使用 docker volume rm
命令删除数据卷,该命令只能删除没有被挂载到容器的数据卷。
$ docker volume rm baz
对于已经被挂载到容器的数据卷,必须先删除相关容器,再删除数据卷。db1 容器挂载了 foo 数据卷,要想删除 foo 数据卷,必须先删除 db1 容器。
$ docker rm db1
$ docker volume rm foo
可以使用 docker rm -v
命令,在删除容器的同时删除该容器挂载的数据卷。该命令只能删除匿名数据卷(名字为一串随机字符串的数据卷),不会删除具名数据卷。db2 容器挂载了 bar 数据卷,db3 容器挂载了匿名数据卷 6cda7f4532a18c226ae419cee91c7d08ca6fe6e409914b25f368b8edea641a09,使用以下命令不会删除 bar 数据卷。
$ docker rm -v db2 db3
$ docker volume ls
local bar
docker volume prune
使用 docker volume prune
命令删除所有没有被挂载到容器的数据卷,不管该数据卷是具名的还是匿名的。
$ docker volume prune
WARNING! This will remove all local volumes not used by at least one container.
Are you sure you want to continue? [y/N] y
Deleted Volumes:
bar
docker create --volumes-from / docker run --volumes-from
使用 docker create --volumes-from
或 docker run --volumes-from
命令创建容器时,可以使用 --volumes-from
参数使得新创建的容器与已有的容器共享数据卷。
以下代码将 db2 容器的 /data 目录与 db1 容器的 /data 目录绑定到同一个具名数据卷 foo (对应着主机上的同一个物理目录 /var/lib/docker/volumes/foo/_data),因此 db1 和 db2 容器可以共享数据。
$ docker create -v foo:/data --name db1 centos
$ docker create --volumes-from db1 --name db2 centos
以下代码可以通过另一种方式实现同样的功能。首先创建一个具名数据卷 foo,然后分别创建两个容器 db1 和 db2,并绑定到这个数据卷 foo,这样也可以实现多个容器共享数据卷。
$ docker volume create foo
$ docker create -v foo:/data --name db1 centos
$ docker create -v foo:/data --name db2 centos
如果一些数据,比如配置文件、数据文件等,要在多个容器之间共享,一种常见的做法是创建一个数据容器,其他容器与之共享 volume。
以下代码先创建了一个名为 db 的容器作为数据容器,将该容器 /data 目录挂载到一个匿名数据卷(也可以挂载到具名数据卷或主机目录)。然后又创建了 db1 和 db2 两个容器,并通过 --volumes-from
参数指定 db1 和 db2 容器与 db 容器共享数据卷,这样 db 容器的 /data 目录、db1 容器的 /data 目录和 db2 容器的 /data 目录都挂载到一个相同的数据卷(对应着主机上的同一个物理目录),因此他们可以共享数据。
$ docker create -v /data --name db centos
$ docker create --volumes-from db --name db1 centos
$ docker create --volumes-from db --name db2 centos
Dockerfile VOLUME
使用 Dockerfile 文件的 VOLUME 参数可以事先指定某个镜像挂载的数据卷。
FROM centos
VOLUME ["/data"]
使用 docker build
命令基于该 Dockerfile 创建一个镜像。
$ docker build -t yumanli/centos .
Sending build context to Docker daemon 2.048kB
Step 1/2 : FROM centos
---> 470671670cac
Step 2/2 : VOLUME ["/data"]
---> Running in 89f3f2cf040f
Removing intermediate container 89f3f2cf040f
---> d2cf56c8d1bd
Successfully built d2cf56c8d1bd
Successfully tagged yumanli/centos:latest
通过该镜像创建容器的时候,就不需要通过 -v
参数挂载数据卷了,该容器会使用 Dockerfile 文件的 VOLUME 参数挂载数据卷。
$ docker run --name db yumanli/centos
$ docker inspect db
"Mounts": [
{
"Type": "volume",
"Name": "740bba034eb3e28d0f7fd4227651c0364ec15011db73abdfcd299fce51aa2cbd",
"Source": "/var/lib/docker/volumes/740bba034eb3e28d0f7fd4227651c0364ec15011db73abdfcd299fce51aa2cbd/_data",
"Destination": "/data",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
]
使用 docker run -v
命令创建容器可以在 Dockerfile 文件的 VOLUME 参数基础上挂载新的数据卷。
$ docker run -v /foo --name db2 yumanli/centos
$ docker inspect db2
"Mounts": [
{
"Type": "volume",
"Name": "9b159b8b28e99424937b54b205daa7708db3839e5baa23557cc03bcdc72e60ac",
"Source": "/var/lib/docker/volumes/9b159b8b28e99424937b54b205daa7708db3839e5baa23557cc03bcdc72e60ac/_data",
"Destination": "/foo",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
},
{
"Type": "volume",
"Name": "b5cd187a31c03f175f4d576f45b74f8d04f552a5d00d85efe149f0cfa720a9c0",
"Source": "/var/lib/docker/volumes/b5cd187a31c03f175f4d576f45b74f8d04f552a5d00d85efe149f0cfa720a9c0/_data",
"Destination": "/data",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
]