Docker存储管理
一、Docker存储概念
1、容器本地存储与Docke存储驱动
容器本地存储:每个容器都被自动分配了内部存储,即容器本地存储。采用的是联合文件系统。通过存储驱动进行管理。
容器本地存储空间:分层结构构成,由一个可写容器层和若干只读的镜像层组成。
联合文件系统:Docker的一种底层技术,由存储驱动(Storage Driver)实现。相应的存储驱动有aufs、overlay、overlay2、devicemapper、btrfs、zfs、vfs等。
存储驱动:控制镜像和容器在 docker 主机上的存储和管理方式。
每个Docker主机只能选择一种存储驱动,不能为每个容器选择不同的存储驱动。
(1)查看主机支持的存储驱动
[root@hqs ~]# docker info
# 找到如下信息
Storage Driver: overlay2 # 存储驱动是overlay2
Backing Filesystem: xfs # 底层文件系统是xfs
Supports d_type: true # 支持d_type
Native Overlay Diff: true
userxattr: false
(2)Linux发行版支持的存储驱动
您的操作系统和内核可能不支持每个存储驱动程序。
例如,aufs仅在Ubuntu和Debian上受支持,并且可能需要安装额外的软件包,而btrfs仅在您的系统使用btrfs作为存储时受支持。通常,以下配置适用于最新版本的Linux发行版:
Linux发行版 | 推荐存储驱动 | 可用驱动 |
---|---|---|
Ubuntu | overlay2 | overlay,devicemapper,aufs,zfs,vfs |
Debian | overlay2 | overlay,devicemapper,aufs,vfs |
CentOS | overlay2 | overlay, devicemapper, zfs, vfs |
Fedora | overlay2 | overlay, devicemapper, zfs, vfs |
SLES 15 | overlay2 | overlay, devicemapper, vfs |
RHEL | overlay2 | overlay, devicemapper, vfs |
(3)可支持的文件系统
关于Docker,备份文件系统是/var/lib/doker/所在的文件系统。某些存储驱动程序仅适用于特定的备份文件系统。
存储驱动 | 可支持的后台文件系统 |
---|---|
overlay2, overlay | xfs with ftype=1, ext4 |
fuse-overlayfs | any filesystem |
aufs | xfs, ext4 |
devicemapper | direct-lvm |
btrfs | btrfs |
zfs | zfs |
vfs | any filesystem |
(4)更改现有的存储驱动
可以根据需要修改现有的存储驱动。
注意:
- 一个主机只能选择一个存储驱动。
- 系统版本和存储驱动的兼容问题。
- 更改存储驱动会使得现有的容器和镜像不可访问(原因:每种存储驱动存储镜像层的位置是不同的)
- 但恢复存储驱动,可以再次访问之前的镜像和容器
# 修改daemon.json文件来更改存储引擎配置
[root@hqs ~]# vi /etc/docker/daemon.json
{
"registry-mirror":["https://registry.docker-cn.com"],
"storage-driver":"overlay" 《————————添加这个信息,23.01版本似乎只能改vfs
}
# 重启docker生效
[root@hqs ~]# systemctl daemon-reload
[root@hqs ~]# systemctl restart docker
[root@hqs ~]# docker info
# 找到如下信息
Storage Driver: overlay 《————————修改生效
Backing Filesystem: xfs
Supports d_type: true
# 更改存储驱动会使得现有的容器和镜像不可访问
[root@hqs ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
# 恢复存储驱动,可以再次访问之前的镜像和容器
[root@hqs ~]# vi /etc/docker/daemon.json
{
"registry-mirror":["https://registry.docker-cn.com"]
}
[root@hqs ~]# systemctl daemon-reload
[root@hqs ~]# systemctl restart docker
[root@hqs ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest 825d55fb6340 2 weeks ago 72.8MB
centos latest 5d0da3dc9764 7 months ago 231MB
2、容器和非持久化数据
非持久化数据:不需要保存的数据。容器本地存储中的数据属于非持久化数据。
容器创建时会创建非持久化存储,非持久化存储自动创建,从属于容器,生命周期与容器相同。删除容器也会删除全部非持久化数据。
非持久化数据存在问题:
- 这类数据从属于容器,生命周期与容器相同,会随着容器的删除而被删除。(易丢失)
- 当该容器不再运行时,数据不会持久保存,如果另一个进程需要,则可能很难从该容器中获取数据。(难获取)
- 容器的可写层与运行容器的Docker主机紧密耦合,无法轻松地将数据转移到其他位置。(难转移)
- 写入容器的可写层需要Docker存储驱动管理文件系统。存储驱动使用Linux内核提供的联合文件系统,其性能不如直接写入主机文件系统的Docker卷。(读写性能差)
3、容器和持久化数据
持久化数据:需要保存的数据。例如:日志、业务数据、客户信息等有用的数据。
外部存储:Docker通过将主机中的文件系统挂载到容器中供容器存取,从而实现持久化数据存储。
容器持久化数据存储方式:Docker目前支持卷、绑定挂载,这两种挂载类型实现容器的持久化。
卷(数据卷):Docker中进行持久化数据存储的最佳方式。本质是Docker主机文件系统中的目录或文件直接挂载到容器的文件系统中。
卷和容器是解耦的,可以独立地创建并管理卷
卷不与容器的生命周期绑定(容器的停止删除和卷无关)
可以将任意数量的卷装入容器,多个容器也可以共享一个或多个卷(多对多关系)
4、挂载类型
往容器中挂载的外部文件系统主要有:卷、绑定挂载、tmpfs挂载。无论哪种挂载,对容器内部来说是一样的,都会显示为文件或目录。
(1)卷
卷存储在主机文件系统中由Docker管理的位置,在Linux主机上该位置默认就是/var/lib/docker/volumes
目录。
卷是Docker持久化存储数据的最佳方式。卷支持使用卷驱动,可以让用户将数据存储在远程主机或云提供商处等。
可以以命名方式或匿名方式挂载卷:
- 匿名卷(Anonymous Volumes):首次挂载容器未指定名称,Docker为其随机指定一个唯一名称。
- 命名卷(Named Volumes):指定明确名称,和匿名卷其他特性相同。
卷由Docker创建并管理,卷适合以下应用场景。
- 在多个正在运行的容器之间共享数据。(数据共享)
- 当Docker主机不能保证具有特定目录结构时,卷有助于将Docker主机的配置与容器运行时解耦。(构建新目录与主机不同)
- 当需要将容器的数据存储到远程主机或云提供商处,而不是本地时。(可以远程挂载卷,公有云、灾备等场景)
- 当需要在两个Docker主机之间备份、恢复或迁移数据时。(主机间备份迁移)
(2)绑定挂载
绑定挂载可以存储到主机系统的任意位置,甚至会存储到一些重要的系统文件或目录中。
特点:
- 主机上进程或容器可以随时修改。
- 相比卷,功能更受限、性能更高。
- 绑定挂载运行访问敏感文件。
绑定挂载适合以下应用场景。
- 在主机和容器之间共享配置文件。
- 在Docker主机上的开发环境和容器之间共享源代码或构建工件。
- 当Docker主机上的目录结构保证与容器要求的绑定挂载一致时。
(3)tmpfs挂载
tmpfs挂载仅限于运行Linux操作系统的Docker主机使用,它只存储在主机的内存中,不会被写到主机的文件系统中,因此不能持久保存容器的应用数据。
在不需要将数据持久保存到主机或容器中时,tmpfs挂载最合适。
如果容器产生了非持久化数据,那么可以考虑使用tmpfs挂载避免将数据永久存储到任何位置,并且通过避免写入容器的可写层来提高容器的性能。
二、Docker卷管理
docker volume
是Docker卷的管理命令。
创建卷的时候主机上的Docker根目录(linux默认是/var/lib/docker)下的volumes子目录内会生成一个以卷名命名的子目录,这个子目录里面再创建一个_data的子目录作为卷的数据存储路径。
卷名必须唯一,不同的卷驱动不能使用相同的卷名。
# 语法
[root@localhost volumes]# docker volume
Usage: docker volume COMMAND
Manage volumes
Commands:
create Create a volume # 创建卷
inspect Display detailed information on one or more volumes # 查看卷的详细信息
ls List volumes # 列出本地docker主机上的卷
prune Remove all unused local volumes # 删除未被使用的卷
rm Remove one or more volumes # 删除指定卷
1、创建卷
使用 docker volume create
命令创建卷。
# 语法
[root@localhost volumes]# docker volume create --help
Usage: docker volume create [OPTIONS] [VOLUME]
Create a volume
Options:
-d, --driver string Specify volume driver name (default "local") # 说明卷驱动名称(默认是'local')
--label list Set metadata for a volume # 设置卷元数据
-o, --opt map Set driver specific options (default map[]) # 设置驱动特殊选项
# 案例1:创建普通卷
[root@localhost volumes]# docker volume create test-vol01
test-vol01
[root@localhost volumes]# docker volume inspect test-vol01
[
{
"CreatedAt": "2022-04-27T17:02:27+08:00",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/test-vol01/_data",
"Name": "test-vol01",
"Options": {},
"Scope": "local"
}
]
# 案例2:创建tmpfs卷,大小100m,uid为1000
[root@localhost volumes]# docker volume create --driver local \
> --opt type=tmpfs \
> --opt device=tmpfs \
> --opt o=size=100m,uid=1000 \
> test-vol02
test-vol02
[root@localhost volumes]# docker volume inspect test-vol02
[
{
"CreatedAt": "2022-04-27T17:10:21+08:00",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/test-vol02/_data",
"Name": "test-vol02",
"Options": {
"device": "tmpfs",
"o": "size=100m,uid=1000",
"type": "tmpfs"
},
"Scope": "local"
}
]
# 案例3:设置元数据
[root@localhost volumes]# docker volume create --label city=wuhan test-vol03
test-vol03
[root@localhost volumes]# docker volume inspect test-vol03
[
{
"CreatedAt": "2022-04-27T17:07:44+08:00",
"Driver": "local",
"Labels": {
"city": "wuhan"
},
"Mountpoint": "/var/lib/docker/volumes/test-vol03/_data",
"Name": "test-vol03",
"Options": {},
"Scope": "local"
}
]
# 案例4:创建匿名卷
[root@localhost volumes]# docker volume create
597f094e886b533e0a826b4e99ec01dad64e25b039ff84574869da5e26df1b3f
[root@localhost volumes]# docker volume ls
DRIVER VOLUME NAME
local 597f094e886b533e0a826b4e99ec01dad64e25b039ff84574869da5e26df1b3f
local test-vol01
local test-vol02
local test-vol03
2、查看卷列表
使用 docker volume ls
命令列出当前的卷。
format关键词:
Placeholder | Description |
---|---|
.Name | Volume name |
.Driver | Volume driver |
.Scope | Volume scope (local, global) |
.Mountpoint | The mount point of the volume on the host |
.Labels | All labels assigned to the volume |
.Label | Value of a specific label for this volume. For example {{.Label "project.version"}} |
# 语法
[root@localhost volumes]# docker volume ls --help
Usage: docker volume ls [OPTIONS] 《——————注意选项不能写在ls之前
List volumes
Aliases:
ls, list
Options:
-f, --filter filter Provide filter values (e.g. 'dangling=true') # 条件筛选
--format string Pretty-print volumes using a Go template # 内容过滤输出
-q, --quiet Only display volume names # 只打印卷名
# 案例1:普通查看
[root@localhost volumes]# docker volume ls
卷驱动 卷名称
DRIVER VOLUME NAME
local test-vol01
local test-vol02
local test-vol03
# 案例2:条件筛选
# 没有被使用的卷
[root@localhost volumes]# docker volume ls -f dangling=true
DRIVER VOLUME NAME
local test-vol01
local test-vol02
local test-vol03
# 驱动为local的卷
[root@localhost volumes]# docker volume ls -f driver=local
DRIVER VOLUME NAME
local test-vol01
local test-vol02
local test-vol03
# 根据label元数据筛选
[root@localhost volumes]# docker volume ls --filter label=city
DRIVER VOLUME NAME
local test-vol03
# 根据名称筛选
[root@localhost volumes]# docker volume ls -f name=03
DRIVER VOLUME NAME
local test-vol03
[root@localhost volumes]# docker volume ls -f name=vol
DRIVER VOLUME NAME
local test-vol01
local test-vol02
local test-vol03
# 案例3:只打印卷名
[root@localhost volumes]# docker volume ls -q
test-vol01
test-vol02
test-vol03
# 案例4:内容过滤输出
[root@localhost volumes]# docker volume ls --format "{{.Name}}: {{.Driver}}"
test-vol01: local
test-vol02: local
test-vol03: local
[root@localhost volumes]# docker volume ls --format "{{.Name}}: {{.Mountpoint}}: {{.Labels}}"
test-vol01: /var/lib/docker/volumes/test-vol01/_data:
test-vol02: /var/lib/docker/volumes/test-vol02/_data:
test-vol03: /var/lib/docker/volumes/test-vol03/_data: city=wuhan
3、查看卷详细信息
使用 docker volume inspect
命令查看卷详细信息。包含卷在Docker主机文件系统中的具体位置。
# 语法
[root@hqs ~]# docker volume inspect --help
Usage: docker volume inspect [OPTIONS] VOLUME [VOLUME...]
Display detailed information on one or more volumes
Options:
-f, --format string Format the output using the given Go template
# 案例
[root@hqs ~]# docker volume create --driver local \
> --opt type=tmpfs \
> --opt device=tmpfs \
> --opt o=size=100m,uid=1000 \
> --label city=wuhan \
> --label type=tmpfs \
> test-vol
70eefb6835df2daafcc3e678395af214dae4a98241fd6886e5822bbc9e7de920
[root@hqs ~]# docker volume inspect test-vol
[
{
"CreatedAt": "2022-04-28T00:36:35+08:00",
"Driver": "local", # 卷驱动
"Labels": {
"city": "wuhan",
"type": "tmpfs"
},
"Mountpoint": "/var/lib/docker/volumes/test-vol/_data", # 卷挂载点
"Name": "test-vol", # 卷名称
"Options": {
"device": "tmpfs",
"o": "size=100m,uid=1000",
"type": "tmpfs"
},
"Scope": "local" # 卷作用域
}
]
[root@hqs ~]# docker volume inspect --format '{{ .Mountpoint }}' test-vol
/var/lib/docker/volumes/test-vol/_data
[root@hqs ~]# docker volume inspect --format '{{ .Options }}' test-vol
map[device:tmpfs o:size=100m,uid=1000 type:tmpfs]
[root@hqs ~]# docker volume inspect --format '{{ .Options.o }}' test-vol
size=100m,uid=1000
[root@hqs ~]# docker volume inspect --format '{{ .Labels.city }}' test-vol
wuhan
4、删除卷
使用 docker volume rm
删除指定卷(不能删除正在被容器使用的卷)。
# 语法
[root@hqs ~]# docker volume rm --help
Usage: docker volume rm [OPTIONS] VOLUME [VOLUME...]
Remove one or more volumes. You cannot remove a volume that is in use by a container.
Aliases:
rm, remove
Options:
-f, --force Force the removal of one or more volumes 强制删除
# 案例
[root@hqs ~]# docker volume rm test-vol01
test-vol01
[root@hqs ~]# docker volume rm $(docker volume ls -q)
test-vol
test-vol02
test-vol03
使用 docker volume prune
删除未被容器或服务使用的所有卷。
# 语法
[root@hqs ~]# docker volume prune --help
Usage: docker volume prune [OPTIONS]
Remove all unused local volumes
Options:
--filter filter Provide filter values (e.g. 'label=<label>')
-f, --force Do not prompt for confirmation
# 案例
[root@hqs ~]# 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:
test-vol01
test-vol02
test-vol03
Total reclaimed space: 0B
三、容器文件系统挂载
使用 docker run
或 docker create
命令的相关选项将外部的文件系统挂载到容器中。
早期版本:
- 卷和绑定挂载都可以通过
-v
和--mount
选项挂载到容器中,主要是两者的语法的细微区别。 -v
用于独立容器,--mount
用于集群服务。- tmpfs挂载可以使用
--tmpfs
选项。
docker 17.06或更高版本:
- 建议所有的容器或服务,绑定挂载、卷、tmpfs都使用
--mount
选项。(语法清晰、定制详细) --mount
选项也可以用于独立容器。
-v
和--mount
的区别:
-v
语法是将选项组合在一个字段中,--mount
的语法是将它们分开。--mount
采用键值对写法支持更多的设置选项,-v
写法更加简洁。
1、-v(--volume)选项
选项包括冒号分隔的三个字段。必须按照正确顺序排序。
# 语法
[root@hqs ~]# docker run(create)
-v, --volume=[host-src:]container-dest[:<options>]: Bind mount a volume.
第一个host-src字段表示挂载源,来自主机的文件系统,为绝对路径或名称变量。
第二个container-dest字段表示挂载目标,即容器的挂载点,可以是目录或文件路径(绝对路径)
第三个options字段可选,以逗号分隔选项列表,如:[rw|ro], [z|Z], [[r]shared|[r]slave|[r]private], and [nocopy].
rw:读写模式??
ro:只读模式
nocopy:禁止自动复制到卷存储地址
命名卷,copy是默认模式,copy模式不支持绑定挂载的卷。
如果既没有指定rw又没有指定ro,卷挂载为读写模式。
# 案例:卷类型
[root@hqs ~]# docker volume create test-vol
test-vol
[root@hqs ~]# docker run -ti -v test-vol:/world ubuntu /bin/bash
# 案例:绑定挂载类型
[root@hqs ~]# docker run -d \
> -it \
> --name devtest \
> -v "$(pwd)"/target:/app \
> nginx:latest
9c9bd6c4b7898f272b162683bf7370fe2fde2d04b30a361bda7443bc6a2d2bf4
[root@hqs ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9c9bd6c4b789 nginx:latest "/docker-entrypoint.…" 10 seconds ago Up 9 seconds 80/tcp devtest
[root@hqs ~]# docker exec -ti 9c9bd6c4b789 /bin/bash
root@9c9bd6c4b789:/# ls
app boot docker-entrypoint.d etc lib media opt root sbin sys usr
bin dev docker-entrypoint.sh home lib64 mnt proc run srv tmp var
root@9c9bd6c4b789:/# df -H
Filesystem Size Used Avail Use% Mounted on
overlay 19G 5.5G 13G 30% /
tmpfs 68M 0 68M 0% /dev
tmpfs 1.1G 0 1.1G 0% /sys/fs/cgroup
shm 68M 0 68M 0% /dev/shm
/dev/mapper/centos-root 19G 5.5G 13G 30% /app
# 案例:只读卷
[root@localhost volumes]# docker run -tid --name test-onlyread \
> -v test-vol:/world:ro \
> centos
f0fe5ba41e18321d261fb0c4bbdd3f2a423ac6cd965f013cd5d70736ae3ad9ef
# 登录容器
[root@localhost volumes]# docker exec -ti test-onlyread /bin/bash
# 尝试向卷中文件写入信息:报错只读文件
[root@f0fe5ba41e18 world]# echo 'test' >> /world/test.txt
bash: /world/test.txt: Read-only file system
2、--mount选项
绑定挂载、卷、tmpfs类型都使用--mount
选项。
--mount
选项的参数由多个由逗号分隔的键值对组成。
特殊键如下:
- type:指定要挂载的类型,值可以是bind(绑定挂载)、volume(卷)或tmpfs。
- source(或src):指定挂载源
- destination(或dst、target):指定挂载目标,即容器中的挂载点,必须采用绝对路径的形式
- readonly:指定只读选项,表示源以只读方式挂载到容器中。
- 其他键:可以被多次指定,由若干键值对组成。卷和绑定挂载有不同的键。
(1)卷案例
[root@hqs ~]# docker run(create)
--mount <键>=<值>,<键>=<值>,... Attach a filesystem mount to the container
# 案例1
[root@hqs ~]# docker run -d --name devtest01 --mount source=myvol2,target=/app nginx:latest
65e5132a4a64d40f688d2bcb80fe2f6d9a2fb504e092ca628b59398dd207b2a3
[root@hqs ~]# docker volume ls
DRIVER VOLUME NAME
local myvol2
[root@hqs ~]# docker volume inspect myvol2
[
{
"CreatedAt": "2022-04-28T02:45:45+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/myvol2/_data",
"Name": "myvol2",
"Options": null,
"Scope": "local"
}
]
[root@hqs ~]# docker exec -ti devtest01 /bin/bash
root@65e5132a4a64:/# cd /app/
# 案例2:只读卷
[root@localhost volumes]# docker run -tid --name devtest02 \
> --mount source=myvol3,target=/app,readonly \
> centos:latest
e021d47368bb127504af60cb3d01b6ef3060f148d23a290cdac0ce6865bb26ec
# 查看只读卷
[root@localhost volumes]# docker volume ls
DRIVER VOLUME NAME
local myvol3
# 在宿主机创建文件
[root@localhost _data]# pwd
/var/lib/docker/volumes/myvol3/_data
[root@localhost _data]# cp /etc/resolv.conf ./
# 登录容器尝试修改
[root@localhost volumes]# docker exec -ti devtest02 /bin/bash
[root@e021d47368bb app]# echo 'asdadad' >> /app/resolv.conf
bash: /app/resolv.conf: Read-only file system
(2)绑定挂载示例
[root@localhost ~]# docker run -tid --name test01 --mount type=bind,source=/etc/localtime,target=/etc/localtime centos
c660c8241a5a5c6ba78998288aaa6c5e8c797a4df35b9ff31cd38a2d1b0046cf
[root@localhost ~]# docker exec -ti test01 /bin/bash
[root@c660c8241a5a /]# date +%z
+0800
(3)tmpfs挂载示例
四、卷应用示例
1、创建一个卷并让容器挂载
# 1.创建一个卷、查看当前卷列表、查看卷详细信息
[root@hqs ~]# docker volume create test-vol
test-vol
[root@hqs ~]# docker volume ls
DRIVER VOLUME NAME
local test-vol
[root@hqs ~]# docker volume inspect test-vol
[
{
"CreatedAt": "2022-04-28T03:06:05+08:00",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/test-vol/_data",
"Name": "test-vol",
"Options": {},
"Scope": "local"
}
]
# 2.启动容器,将卷挂载到容器/world目录,在容器中查看根目录、退出容器
[root@hqs ~]# docker run -ti -v test-vol:/world ubuntu /bin/bash
root@e6535eebe726:/# ls
bin dev home lib32 libx32 mnt proc run srv tmp var
boot etc lib lib64 media opt root sbin sys usr world
root@e6535eebe726:/# ls /world/
root@e6535eebe726:/# exit
exit
# 3.查看容器信息,验证卷是否正确挂载
[root@hqs ~]# docker inspect --format='{{json .Mounts}}' e6535
[{"Type":"volume","Name":"test-vol","Source":"/var/lib/docker/volumes/test-vol/_data","Destination":"/world","Driver":"local","Mode":"z","RW":true,"Propagation":""}]
# 4.删除卷
[root@hqs ~]# docker volume rm test-vol
Error response from daemon: remove test-vol: volume is in use - [e6535eebe726069b355567be0a562131844ad04b175b2a0ee870d990f72dca95]
# 使用中的卷不能直接删除,第二次是先删除容器,再删除卷
[root@hqs ~]# docker rm e6535
e6535
[root@hqs ~]# docker volume rm test-vol
test-vol
2、启动容器时自动创建卷
启动带有卷的容器时,如果卷尚不存在,则Docker会自动创建这个卷。
示例:将myvol卷挂载到容器testnovol的/app目录下。
--mount
选项实现:
[root@hqs ~]# docker run -d --name testnovol01 --mount source=myvol,target=/app nginx
beafe21868168531d1eeb69c7b29e234f0ceb60cabc60ed8e5deae0c4c99f28d
[root@hqs ~]# docker inspect --format='{{json .Mounts}}' testnovol01
[{"Type":"volume","Name":"myvol","Source":"/var/lib/docker/volumes/myvol/_data","Destination":"/app","Driver":"local","Mode":"z","RW":true,"Propagation":""}]
-v
选项实现:
[root@hqs ~]# docker run -d --name testnovol02 -v myvol02:/app nginx
d7728830916fd11dec8487ea9aaf7e6f1b306b3c4c072683cf8155f430f57887
[root@hqs ~]# docker inspect --format='{{json .Mounts}}' testnovol02
[{"Type":"volume","Name":"myvol02","Source":"/var/lib/docker/volumes/myvol02/_data","Destination":"/app","Driver":"local","Mode":"z","RW":true,"Propagation":""}]
3、使用容器填充卷
容器启动时挂载已经存在并包含数据的卷,则容器不会将其挂载点目录的数据复制到该卷,而是直接使用该卷的数据。
如果容器启动时挂载空白卷或自动创建新卷,而容器的挂载点目录有文件,则该挂载点的内容会复制到卷中。
# 1.执行以下命令启动一个nginx容器,并使用容器的/usr/share/nginx/html目录的内容填充新的卷nginx-vol。
[root@hqs ~]# docker run -d --name=nginxtest --mount source=nginx-vol,destination=/usr/share/nginx/html nginx
3f208b4cecc9bc98c0424dc891ae39314b9dab61985e1e957b294a799589fafb
[root@hqs ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3f208b4cecc9 nginx "/docker-entrypoint.…" 20 seconds ago Up 19 seconds 80/tcp nginxtest
# 2.执行docker volume inspect nginx-vol命令查看该卷的详细信息,其中挂载点设置如下。
[root@hqs ~]# docker volume ls
DRIVER VOLUME NAME
local nginx-vol
[root@hqs ~]# docker volume inspect --format='{{json .Mountpoint}}' nginx-vol
"/var/lib/docker/volumes/nginx-vol/_data"
# 3.查看主机上该卷所在目录的内容,发现容器已经填充了卷。
[root@hqs ~]# cd /var/lib/docker/volumes/nginx-vol/_data/
[root@hqs _data]# ls
50x.html index.html
[root@hqs _data]# cat index.html
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
# 4.启动另一个容器挂载该卷,以使用其中预先填充的内容。
[root@hqs _data]# docker run -ti --name=othercntr --mount source=nginx-vol,destination=/nginx ubuntu /bin/bash
root@a68b8352663b:/# ls /nginx/
50x.html index.html
root@a68b8352663b:/# cd /nginx/
root@a68b8352663b:/nginx# cat 50x.html
<!DOCTYPE html>
<html>
<head>
<title>Error</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>An error occurred.</h1>
<p>Sorry, the page you are looking for is currently unavailable.<br/>
Please try again later.</p>
<p>If you are the system administrator of this resource then you should check
the error log for details.</p>
<p><em>Faithfully yours, nginx.</em></p>
</body>
</html>
# 5.删除容器和卷
[root@hqs _data]# docker stop $(docker ps -aq)
a68b8352663b
3f208b4cecc9
d7728830916f
beafe2186816
65e5132a4a64
9c9bd6c4b789
[root@hqs _data]# docker rm $(docker ps -aq)
a68b8352663b
3f208b4cecc9
d7728830916f
beafe2186816
65e5132a4a64
9c9bd6c4b789
[root@hqs _data]# docker volume rm $(docker volume ls -q)
myvol
myvol2
myvol02
nginx-vol
启动容器时指定一个不存在的卷,则自动创建一个空白卷。
如果是把空白卷挂载到容器已有文件的目录,则将这些文件复制到卷中。(常用于预填充容器所需数据)
4、使用只读卷
同一个卷可以由多个容器挂载,并且让容器执行读写操作。设置只读权限后,在容器中是无法对卷中数据修改的,可以提升安全性。
# --mount
[root@localhost _data]# docker run -dti --name=nginx-test --mount source=nginx-vol01,destination=/usr/share/nginx/html,readonly nginx
44bb1ee56819a71a4fafda36ab0ea05952d51b5d3c49dbfcc14a27fd2c84f740
[root@localhost _data]# docker exec -ti nginx-test /bin/bash
root@44bb1ee56819:/# cd /usr/share/nginx/html/
# 在路径中既不能添加文件,也不能修改文件
root@44bb1ee56819:/usr/share/nginx/html# touch sss
touch: cannot touch 'sss': Read-only file system
root@44bb1ee56819:/usr/share/nginx/html# echo 'adadadd' >> 50x.html
bash: 50x.html: Read-only file system
[root@localhost _data]# docker inspect --format='{{json .Mounts}}' nginx-test
[{"Type":"volume","Name":"nginx-vol01","Source":"/var/lib/docker/volumes/nginx-vol01/_data","Destination":"/usr/share/nginx/html","Driver":"local","Mode":"z","RW":false,"Propagation":""}]
# -v
[root@localhost _data]# docker run -dti --name=nginx-test02 -v nginx-vol02:/usr/share/nginx/html:ro nginx
5b909ace1f2533ecd637bcf5779baf8df5a90aafb9f18a21fcf1fe3573103641
[root@localhost _data]# docker exec -ti nginx-test02 /bin/bash
root@5b909ace1f25:/# cd /usr/share/nginx/html/
root@5b909ace1f25:/usr/share/nginx/html# echo 'sadad' >> index.html
bash: index.html: Read-only file system
root@5b909ace1f25:/usr/share/nginx/html# touch asdad
touch: cannot touch 'asdad': Read-only file system
[root@localhost _data]# docker inspect --format='{{json .Mounts}}' nginx-test02
[{"Type":"volume","Name":"nginx-vol02","Source":"/var/lib/docker/volumes/nginx-vol02/_data","Destination":"/usr/share/nginx/html","Driver":"local","Mode":"ro","RW":false,"Propagation":""}]
注意:-v
和 --mount
选项,inspect查看输出的Mounts部分的信息有差异,主要是'Mode'显示的内容不同,一个表示读写模式,一个表示selinux的标签设置。
5、使用匿名卷
定义:匿名卷没有指定明确的名称。在创建或启动容器时可以创建匿名卷。
# --mount:启动容器时不定义source
[root@localhost ~]# docker run -it --mount destination=/app ubuntu /bin/bash
root@eacbc9629f0f:/# ls
app boot etc lib lib64 media opt root sbin sys usr
bin dev home lib32 libx32 mnt proc run srv tmp var
root@eacbc9629f0f:/# exit
exit
[root@localhost ~]# docker volume ls
DRIVER VOLUME NAME
local baa55a4c1cfc3aa117630efd47b2986fb9ad775be95d4f08735fc0495340ab0e
[root@localhost ~]# docker volume rm baa55a4c
Error: No such volume: baa55a4c 《————指定匿名卷必须使用完整的UUID
[root@localhost ~]# docker rm eacbc9629f0f
eacbc9629f0f
[root@localhost ~]# docker volume rm baa55a4c1cfc3aa117630efd47b2986fb9ad775be95d4f08735fc0495340ab0e
baa55a4c1cfc3aa117630efd47b2986fb9ad775be95d4f08735fc0495340ab0e
# -v:启动容器时省略第一个字段[host-src:]
[root@localhost ~]# docker run -ti -v /app ubuntu /bin/bash
root@3673f14ae07a:/# ls
app boot etc lib lib64 media opt root sbin sys usr
bin dev home lib32 libx32 mnt proc run srv tmp var
root@3673f14ae07a:/# exit
exit
[root@localhost ~]# docker volume ls
DRIVER VOLUME NAME
local db8dfec8d223d5eb3662c73249a9545ee1d982f00b52ca8b1a5e6314d843495d
# 删容器
[root@localhost ~]# docker container prune
WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] y
Deleted Containers:
3673f14ae07a660a437d615eaca1aeb012673233010d5e7e0cbc0d30c7e601f0
Total reclaimed space: 8B
# 删空白卷
[root@localhost ~]# 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:
db8dfec8d223d5eb3662c73249a9545ee1d982f00b52ca8b1a5e6314d843495d
Total reclaimed space: 0B
6、绑定挂载主机上的目录
可以通过绑定挂载将 Docker主机上现有的目录 挂载到容器的目录中,需要挂载的目录可以由 主机的绝对路径 引用。
$(pwd)
命令可以表示linux主机的当前目录,注意要用双引号包起来,否则报错。
绑定挂载的容器,不能强制删除,应该先停止后删除,否则会有报警。
(1) --mount选项
# 1.准备目录,切换到新目录的父目录
[root@localhost ~]# mkdir -p source/target
[root@localhost ~]# cd source/
[root@localhost source]# pwd
/root/source
# 2.启动容器,将主机的source/target目录挂载到容器的/app目录下
[root@localhost source]# docker run -dti --name devtest --mount type=bind,source="$(pwd)/target",target=/app nginx
497a3e9b17926b428e22c8b424a5d566a2edd8cc1211bbadd6416d65cbe13b86
# 失败的案例:绑定挂载必须使用已经存在的目录:
[root@localhost source]# docker run -dti --name devtest01 --mount type=bind,source="$(pwd)/test",target=/app nginx
docker: Error response from daemon: invalid mount config for type "bind": bind source path does not exist: /root/source/test.
# 3.查看容器的信息,验证绑定挂载是否创建
[root@localhost source]# docker inspect --format='{{json .Mounts}}' devtest
[{"Type":"bind","Source":"/root/source/target","Destination":"/app","Mode":"","RW":true,"Propagation":"rprivate"}]
# type说明挂载方式为绑定挂载,源和目标路径都是正确的,Propagation(传播)方式是:rprivate(私有的)。
# Mode:表示与selinux标签有关的选项。
# z选项表示绑定挂载的内容在多个容器之间共享。
# Z选项表示绑定挂载的内容是私有的。
# 4.停止并删除容器
[root@localhost source]# docker rm -f devtest
devtest
# 5.启动只读绑定挂载容器
[root@localhost source]# docker run -dti --name devtest --mount type=bind,source="$(pwd)"/target,target=/app,readonly nginx
586c51f2b909903e4a755a85685bb4b1d9a7b5f2bb226f2bcc4c2f8fae1cf270
# 6.查看容器的信息
[root@localhost source]# docker inspect --format='{{json .Mounts}}' devtest
[{"Type":"bind","Source":"/root/source/target","Destination":"/app","Mode":"","RW":false,"Propagation":"rprivate"}]
# RW为false,说明为只读模式
(2)-v选项
# 1.准备目录,切换到新目录的父目录
[root@localhost ~]# mkdir -p source/target
[root@localhost ~]# cd source/
[root@localhost source]# pwd
/root/source
# 2.启动容器,将主机的source/target目录挂载到容器的/app目录下
[root@localhost source]# docker run -dti --name devtest -v "$(pwd)/target":/app nginx
9efef939c123a1b2e7f4b053008d4699c0dc4f1f879c40ea81614f24e9acec04
# 3.查看容器的信息,验证绑定挂载是否创建
[root@localhost source]# docker inspect --format='{{json .Mounts}}' devtest
[{"Type":"bind","Source":"/root/source/target","Destination":"/app","Mode":"","RW":true,"Propagation":"rprivate"}]
# type说明挂载方式为绑定挂载,源和目标路径都是正确的,Propagation(传播)方式是:rprivate(私有的)。
# 4.停止并删除容器
[root@localhost source]# docker stop devtest
devtest
[root@localhost source]# docker rm devtest
devtest
# 5.启动只读绑定挂载容器
[root@localhost source]# docker run -dti --name devtest -v "$(pwd)/target":/app:ro nginx
c56f97fd97247eaa3fe02edeef6e298944944575d023e16a60365edb53911aa3
# 6.查看容器的信息
[root@localhost source]# docker inspect --format='{{json .Mounts}}' devtest
[{"Type":"bind","Source":"/root/source/target","Destination":"/app","Mode":"ro","RW":false,"Propagation":"rprivate"}]
# Mode为"ro",为只读模式
7、绑定挂载主机上的文件
绑定挂载文件主要用于主机与容器之间共享配置文件。
配置文件置于Docker主机中,并挂载到容器,可以随时更改配置,将配置管理变简单。
# 1.查看主机的时区
[root@localhost source]# date +%z
+0800
# 2.启动一个ubuntu容器查看时区
[root@localhost source]# docker run -tid --rm --name test ubuntu /bin/bash
a688f13a3d1eb1e2d252b68df657517f3325145823c9ce2c83cdb7e0fb6c889d
[root@localhost source]# docker exec -ti test /bin/bash
root@a688f13a3d1e:/# date +%z
+0000
# 3.启动一个绑定挂载文件的容器查看时区
[root@localhost source]# docker run -tid --rm --name test01 -v /etc/localtime:/etc/localtime ubuntu /bin/bash
bda7b3deca99bd39985df06a0c875114c3867f7411477ccbe220dcf08260e4a6
[root@localhost source]# docker exec -ti test01 /bin/bash
root@bda7b3deca99:/# date +%z
+0800
8、绑定挂载主机上不存在的目录或文件
绑定挂载Docker主机上不存在的目录或文件,--mount
和-v
选项的表现不同。
(1)-v选项:自动创建目录
-v
选项,对不存在的目录或文件,主机都会自动创建一个目录。
# 启动容器,查看路径,并退出
[root@localhost ~]# docker run -ti --rm -v /doestnt/exist:/foo ubuntu bash
root@8fcdd361f44f:/# cd foo/
root@8fcdd361f44f:/foo# pwd
/foo
root@8fcdd361f44f:/foo# exit
exit
# 在docker主机查看新创建的路径
[root@localhost /]# ls
bin dev etc lib media opt root sbin sys usr
boot doestnt home lib64 mnt proc run srv tmp var
[root@localhost /]# ls /doestnt/exist/
(2)--mount选项:报错
--mount
选项,对不存在的目录或文件,主机不会自动创建目录,会报错。
[root@localhost /]# docker run -ti --rm --mount type=bind,source=/doesnt/exist,destination=/foo ubuntu bash
docker: Error response from daemon: invalid mount config for type "bind": bind source path does not exist: /doesnt/exist.
9、绑定挂载到容器中的非空目录
如果将主机上的目录绑定挂载到容器上的非空目录,则容器挂载的目录中的现有内容会被绑定挂载(主机上的目录)所遮盖。
- 被遮盖的目录和文件不会被删除或更改,但在使用绑定挂载时不可访问。
- 无论主机上的目录是否为空,绑定挂载到容器中的非空目录都会发生被遮盖的情况。
# 案例1:主机目录有内容,会遮盖容器目录内容
[root@localhost /]# docker run -tid --name broken-test --mount type=bind,source=/tmp,target=/etc/yum.repos.d centos
cb7c523f56bd7ae4293bb83a27074f35f9abd319c9d08a94d9b17d072ddffad1
[root@localhost /]# docker exec -ti broken-test /bin/bash
[root@cb7c523f56bd /]# cd /etc/yum.repos.d/
[root@cb7c523f56bd yum.repos.d]# ls
vmware-root 《————遮盖了容器之前的内容
# 案例2:主机目录没有内容,也会遮盖容器目录内容
[root@localhost /]# docker run -tid --name broken-test02 --mount type=bind,source=/home,target=/etc/yum.repos.d centos
76b23ec347b54f32789d2883ef5c0fd5ef37d1af3aac54d4b6a52858db2f3892
[root@localhost /]# docker exec -ti 76b23ec347b5 /bin/bash
[root@76b23ec347b5 /]# cd /etc/yum.repos.d/
[root@76b23ec347b5 yum.repos.d]# ls 《————————内容和/home一样为空
# 案例3:-v案例
[root@localhost /]# docker run -tid --name broken-test03 \
> -v /etc/yum.repos.d/:/etc/yum.repos.d/ centos
2c3a8cf94b79db1c78551c4e63606aadc9dbe3c6010b995da1ba0962f2a8a739
[root@localhost /]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2c3a8cf94b79 centos "/bin/bash" 2 seconds ago Up 2 seconds broken-test03
76b23ec347b5 centos "/bin/bash" 8 minutes ago Up 8 minutes broken-test02
1e6f1d84a0f6 centos "/bin/bash" 9 minutes ago Up 9 minutes broken-test
[root@localhost /]# docker exec -ti broken-test03 /bin/bash
[root@2c3a8cf94b79 /]# cd /etc/yum.repos.d/
[root@2c3a8cf94b79 yum.repos.d]# ls 《————————内容和宿主机目录内容一致
CentOS-Base.repo docker-ce.repo elrepo.repo repo.bak
10、备份、恢复和迁移数据卷
可以通过卷容器(Volume Container)实现卷的备份、恢复和迁移。
卷容器(数据卷容器):一种特殊的容器,专门用来将卷(或绑定挂载)提供给其他容器挂载。
--volumes-from
选项,创建容器时使用这个选项:
- 可基于
卷容器
创建一个新的容器; - 新容器挂载
卷容器
提供的卷。
(1)创建卷容器
# 创建卷容器dbstore,该卷容器挂载一个匿名卷
[root@hqs tmp]# docker create -v /dbdata --name dbstore busybox /bin/sh
e0bf42be22e492c1377112967dd113d6cfc6b0bd2483adef84f9f0da962c2806
# 查看新创建的匿名卷
[root@hqs tmp]# docker volume ls
DRIVER VOLUME NAME
local e3440f88e29fdb974db84419090554c2d61bd78819a86650446f3f69a5030783
# 查看卷容器详情中,卷信息
[root@hqs tmp]# docker inspect --format='{{json .Mounts}}' dbstore
[{"Type":"volume","Name":"e3440f88e29fdb974db84419090554c2d61bd78819a86650446f3f69a5030783","Source":"/var/lib/docker/volumes/e3440f88e29fdb974db84419090554c2d61bd78819a86650446f3f69a5030783/_data","Destination":"/dbdata","Driver":"local","Mode":"","RW":true,"Propagation":""}]
(2)备份卷容器
# 基于卷容器dbstore,创建新容器
# 此时新容器会挂载卷容器dbstore的匿名卷
# 另外还用-v绑定挂载了本地/tmp目录到容器的/backup目录
# 运行容器后,将匿名卷挂载目录/dbdata内容打包压缩存放在/backup/目录,也就是宿主机的/tmp目录
[root@hqs tmp]# docker run --rm --volumes-from dbstore -v $(pwd):/backup ubuntu tar cvfP /backup/backup.tar /dbdata
/dbdata/
# 查看宿主机/tmp目录内容
[root@hqs tmp]# ls
backup.tar vmware-root
[root@hqs tmp]# du -sh *
12K backup.tar
0 vmware-root
# 执行命令会完成一下操作:
# 1. 启动一个新的容器并从dbstore容器中挂载卷。
# 2. 将本地主机的当前目录挂载为/backup。
# 3. 传送一个命令将dbdata卷的内容打包为/backup/backup.tar文件。
(3)从备份恢复卷容器
创建备份后,可以将它恢复到同一个容器,或另一个容器。
# 1. 创建卷容器dbstore2,也挂载了一个匿名卷
[root@hqs tmp]# docker run -v /dbdata --name dbstore2 ubuntu /bin/bash
# 2. 在新容器的数据卷中将备份文件解压
# 创建新容器,挂载卷容器dbstore2的匿名卷
# 绑定挂载宿主机的当前目录/tmp到容器的/backup目录
# 宿主机/tmp中包含dbstore卷中内容的压缩文件,运行容器时将其解压
[root@hqs tmp]# docker run -tid --volumes-from dbstore2 -v $(pwd):/backup ubuntu bash -c "cd /backup && tar xvf backup.tar && /bin/bash"
e4a269b16d16e03e8e8fa4bd94940d03e7425c2671573bf570a3f42a2f2f29a2
# 登录容器查看/backup路径中内容
[root@hqs tmp]# docker exec -ti e4a269b16d16 /bin/bash
root@e4a269b16d16:/# cd /backup/
root@e4a269b16d16:/backup# ls
backup.tar dbdata vmware-root 《————dbdata为解压出的文件夹