200字
Docker数据管理
2026-03-12
2026-03-12

Docker 数据管理

Docker镜像由多个只读层叠加而成,启动容器时,Docker会加载只读镜像层并在镜像栈顶部添加一个读写层

如果运行中的容器修改了现有的一个已经存在的文件,那该文件将会从读写层下面的只读层复制到读写层,该文件的只读版本仍然存在,只是已经被读写层中该文件的副本所隐藏,此即“写时复制(COW copy on write)"机制

如果将正在运行中的容器修改生成了新的数据,那么新产生的数据将会被复制到读写层,进行持久化保存,这个读写层也就是容器的工作目录,也为写时复制(COW) 机制。

COW机制节约空间,但会导致性低下,虽然关闭重启容器,数据不受影响,但会随着容器的删除,其对应的可写层也会随之而删除,即数据也会丢失.如果容器需要持久保存数据,并不影响性能可以用数据卷技术实现

如下图是将对根的数据写入到了容器的可写层,但是把/data 中的数据写入到了一个另外的volume 中用于数据持久化

image-ifeLhtZFmwUHbbKEZELLfewdZYzdBScw.png

容器的数据管理介绍

Docker镜像是分层设计的,镜像层是只读的,通过镜像启动的容器添加了一层可读写的文件系统,用户写入的数据都保存在这一层中。

Docker容器的分层

容器的数据分层目录

  • LowerDir: image 镜像层,即镜像本身,只读
  • UpperDir: 容器的上层,可读写 ,容器变化的数据存放在此处
  • MergedDir: 容器的文件系统,使用Union FS(联合文件系统)将lowerdir 和 upperdir 合并完成后给容器使用,最终呈现给用户的统一视图
  • WorkDir: 容器在宿主机的工作目录,挂载后内容会被清空,且在使用过程中其内容用户不可见

范例: 查看指定容器数据分层

[root@ubuntu1804 ~]#docker inspect 12959f2c152f
"GraphDriver": {
"Data": {
"LowerDir":
"/var/lib/docker/overlay2/848d77064091ba3ddd25a10ea6e0065af15ee701fed06f82804cf9
ed58751761-
init/diff:/var/lib/docker/overlay2/4a259e4cc9da105d17075e316c3e1f7c29abd6f7a37c5
c7c29251a7e5f0e7eb3/diff",
"MergedDir":
"/var/lib/docker/overlay2/848d77064091ba3ddd25a10ea6e0065af15ee701fed06f82804cf9
ed58751761/merged",
"UpperDir":
"/var/lib/docker/overlay2/848d77064091ba3ddd25a10ea6e0065af15ee701fed06f82804cf9
ed58751761/diff",
"WorkDir":
"/var/lib/docker/overlay2/848d77064091ba3ddd25a10ea6e0065af15ee701fed06f82804cf9
ed58751761/work"
},
"Name": "overlay2"

[root@ubuntu1804 ~]#ll -i /var/lib/docker/overlay2/848d77064091ba3ddd25a10ea6e0065af15ee701fed06f82804cf9ed58751761
total 28
920832 drwx------ 5 root root 4096 Jan 31 19:02 ./
917524 drwx------ 53 root root 4096 Jan 31 19:02 ../
920843 drwxr-xr-x 3 root root 4096 Jan 31 19:02 diff/
920846 -rw-r--r-- 1 root root 26 Jan 31 19:02 link
920851 -rw-r--r-- 1 root root 57 Jan 31 19:02 lower
920818 drwxr-xr-x 1 root root 4096 Jan 31 19:02 merged/
920847 drwx------ 3 root root 4096 Jan 31 19:02 work/


[root@ubuntu1804 ~]#tree -d /var/lib/docker/overlay2/848d77064091ba3ddd25a10ea6e0065af15ee701fed06f82804cf9ed58751761/
/var/lib/docker/overlay2/848d77064091ba3ddd25a10ea6e0065af15ee701fed06f82804cf9ed58751761/
├── diff
│ └── root
├── merged
│ ├── bin
│ ├── dev
│ │ ├── pts
│ │ └── shm
│ ├── etc
│ │ ├── apk
│ │ │ ├── keys
│ │ │ └── protected_paths.d
│ │ ├── conf.d
│ │ ├── crontabs
│ │ ├── init.d
│ │ ├── logrotate.d
│ │ ├── modprobe.d
│ │ ├── modules-load.d
│ │ ├── network
│ │ │ ├── if-down.d
│ │ │ ├── if-post-down.d
│ │ │ ├── if-post-up.d
│ │ │ ├── if-pre-down.d
│ │ │ ├── if-pre-up.d
│ │ │ └── if-up.d
│ │ ├── opt
│ │ ├── periodic
│ │ │ ├── 15min
│ │ │ ├── daily
│ │ │ ├── hourly
│ │ │ ├── monthly
│ │ │ └── weekly
│ │ ├── profile.d
│ │ ├── ssl
│ │ │ ├── certs
│ │ │ ├── misc
│ │ │ └── private
│ │ └── sysctl.d
│ ├── home
│ ├── lib
│ │ ├── apk
│ │ │ └── db
│ │ ├── firmware
│ │ └── mdev
│ ├── media
│ │ ├── cdrom
│ │ ├── floppy
│ │ └── usb
│ ├── mnt
│ ├── opt
│ ├── proc
│ ├── root
│ ├── run
│ ├── sbin
│ ├── srv
│ ├── sys
│ ├── tmp
│ ├── usr
│ │ ├── bin
│ │ ├── lib
│ │ │ └── engines-1.1
│ │ ├── local
│ │ │ ├── bin
│ │ │ ├── lib
│ │ │ └── share
│ │ ├── sbin
│ │ └── share
│ │ ├── apk
│ │ │ └── keys
│ │ │ ├── aarch64
│ │ │ ├── armhf
│ │ │ ├── ppc64le
│ │ │ ├── s390x
│ │ │ ├── x86
│ │ │ └── x86_64
│ │ ├── man
│ │ ├── misc
│ │ └── udhcpc
│ └── var
│ ├── cache
│ │ ├── apk
│ │ └── misc
│ ├── empty
│ ├── lib
│ │ ├── apk
│ │ ├── misc
│ │ └── udhcpd
│ ├── local
│ ├── lock
│ │ └── subsys
│ ├── log
│ ├── mail
│ ├── opt
│ ├── run -> /run
│ ├── spool
│ │ ├── cron
│ │ └── mail -> /var/mail
│ └── tmp
└── work
└── work
99 directories
[root@ubuntu1804 ~]#docker run -it alpine:3.11 sh
/ # dd if=/dev/zero of=/root/test.img bs=1M count=10
10+0 records in
10+0 records out
/ #

#每个镜像层目录中包含了一个文件link,文件内容则是当前层对应的短标识符,镜像层的内容则存放在diff目录

[root@ubuntu1804 ~]#find /var/lib/docker/overlay2/848d77064091ba3ddd25a10ea6e0065af15ee701fed06f82804cf9e d58751761 -name test.img -ls

920903 10240 -rw-r--r-- 1 root root 10485760 Jan 31 19:02
/var/lib/docker/overlay2/848d77064091ba3ddd25a10ea6e0065af15ee701fed06f82804cf9e
d58751761/merged/root/test.img
920903 10240 -rw-r--r-- 1 root root 10485760 Jan 31 19:02
/var/lib/docker/overlay2/848d77064091ba3ddd25a10ea6e0065af15ee701fed06f82804cf9e
d58751761/diff/root/test.img

[root@ubuntu1804 ~]#mount
overlay on
/var/lib/docker/overlay2/848d77064091ba3ddd25a10ea6e0065af15ee701fed06f82804cf9e
d58751761/merged type overlay
(rw,relatime,lowerdir=/var/lib/docker/overlay2/l/OIRNCV35BGPGWKRWEMCJD5SYUZ:/var/lib/docker/overlay2/l/VZ7N54CVY2JLDASJAZ6AASRQSJ,upperdir=/var/lib/docker/overlay2/848d77064091ba3ddd25a10ea6e0065af15ee701fed06f82804cf9ed58751761/diff,workdir=/var/lib/docker/overlay2/848d77064091ba3ddd25a10ea6e0065af15ee701fed06f82804cf9ed58751761/work)
nsfs on /run/docker/netns/6ce1d74bc9af type nsfs (rw)

[root@ubuntu1804 ~]#tree /var/lib/docker/overlay2/848d77064091ba3ddd25a10ea6e0065af15ee701fed06f82804cf9ed58751761/diff/
/var/lib/docker/overlay2/848d77064091ba3ddd25a10ea6e0065af15ee701fed06f82804cf9e
d58751761/diff/
└── root
    └── test.img
1 directory, 1 file

[root@ubuntu1804 ~]#ls /var/lib/docker/overlay2/848d77064091ba3ddd25a10ea6e0065af15ee701fed06f82804cf9ed58751761/merged/
bin dev etc home lib media mnt opt proc root run sbin srv sys tmp
usr var

[root@ubuntu1804 ~]#docker run -it alpine:3.11 sh
/ # echo welcome to magedu >> /etc/issue
/ # cat /etc/issue
Welcome to Alpine Linux 3.11
Kernel \r on an \m (\l)
welcome to magedu

[root@ubuntu1804 ~]#tree /var/lib/docker/overlay2/848d77064091ba3ddd25a10ea6e0065af15ee701fed06f82804cf9ed58751761/diff/
/var/lib/docker/overlay2/848d77064091ba3ddd25a10ea6e0065af15ee701fed06f82804cf9e
d58751761/diff/
├── etc
│ 	└── issue
└── root
	└── test.img
2 directories, 2 files

#删除容器后,所有容器数据目录都随之而删除
[root@ubuntu1804 ~]#docker rm -f 12959f2c152f
[root@ubuntu1804 ~]#ls
/var/lib/docker/overlay2/848d77064091ba3ddd25a10ea6e0065af15ee701fed06f82804cf9e
d58751761
ls: cannot access
'/var/lib/docker/overlay2/848d77064091ba3ddd25a10ea6e0065af15ee701fed06f82804cf9
ed58751761': No such file or directory

哪些数据需要持久化

有状态的协议

有状态协议就是就通信双方要记住双方,并且共享一些信息。而无状态协议的通信每次都是独立的,与上一次的通信没什么关系。
"状态”可以理解为“记忆”,有状态对应有记忆,无状态对应无记忆

容器数据持久保存方式

如果要将写入到容器的数据永久保存,则需要将容器中的数据保存到宿主机的指定目录

Docker的数据类型分为两种:

  • 数据卷(Data Volume): 直接将宿主机目录挂载至容器的指定的目录 ,推荐使用此种方式,此方式较常用
  • 数据卷容器(Data Volume Container): 间接使用宿主机空间,数据卷容器是将宿主机的目录挂载至个专门的数据卷容器,然后让其他容器通过数据卷容器读写宿主机的数据 ,此方式不常用

image-LnqGObGjLZflEwDmqHaacglCleIsivOa.png

数据卷(data volume)

数据卷特点和使用

数据卷实际上就是宿主机上的目录或者是文件,可以被直接mount到容器当中使用

实际生成环境中,需要针对不同类型的服务、不同类型的数据存储要求做相应的规划,最终保证服务的可扩展性、稳定性以及数据的安全性

数据卷使用场景

  • 数据库
  • 日志输出
  • 静态web页面
  • 应用配置文件
  • 多容器间目录或文件共享

数据卷的特点

  • 数据卷是目录或者文件,并且可以在多个容器之间共同使用,实现容器之间共享和重用
  • 对数据卷更改数据在所有容器里面会立即更新。
  • 数据卷的数据可以持久保存,即使删除使用使用该容器卷的容器也不影响。
  • 在容器里面的写入数据不会影响到镜像本身,即数据卷的变化不会影响镜像的更新
  • 依赖于宿主机目录,宿主机出问题,上面容器会受影响,当宿主机较多时,不方便统一管理
  • 匿名和命名数据卷在容器启动时初始化,如果容器使用的镜像在挂载点包含了数据,会拷贝到新初始化的数据卷中

数据卷分类

启动容器时,可以指定使用数据卷实现容器数据的持久化,数据卷有三种

  • 指定宿主机目录或文件: 指定宿主机的具体路径和容器路径的挂载关系,此方式不会创建数据卷
  • 匿名卷: 不指定数据名称,只指定容器内目录路径充当挂载点,docker自动指定宿主机的路径进行挂载,此方式会创建匿名数据卷,Dockerfile中VOLUME指定的卷即为此种
  • 命名卷: 指定数据卷的名称和容器路径的挂载关系,此方式会创建命名数据卷

数据卷使用方法

docker run 命令的以下格式可以实现数据卷

-v, --volume=[host-src:]container-dest[:<options>]

<options>
ro 从容器内对此数据卷是只读,不写此项默认为可读可写
rw 从容器内对此数据卷可读可写,此为默认值
host-src 宿主机目录如果不存在,会自动创建

方式1

#指定宿主机目录或文件格式:
-v <宿主机绝对路径的目录或文件>:<容器目录或文件>[:ro] #将宿主机目录挂载容器目录,两个目录都可自动创建
#注意:如果初始容器中有旧数据,将被宿主机目录覆盖

方式2

#匿名卷,只指定容器内路径,没有指定宿主机路径信息,宿主机自动生成/var/lib/docker/volumes/<卷ID>/_data目录,并挂载至容器指定路径
#注意:如果初始容器中有旧数据,将被复制到宿主机数据卷目录
-v <容器内路径>

#示例:
docker run --name nginx -v /etc/nginx nginx

方式3

#命名卷将固定的存放在/var/lib/docker/volumes/<卷名>/_data
#注意:如果初始容器中有旧数据,将被复制到宿主机数据卷目录


-v <卷名>:<容器目录路径>
#可以通过以下命令事先创建,如果没有事先创建卷名,docker run时也会自动创建卷
docker volume create <卷名>

#示例:
docker volume create vol1 #也可以事先不创建
docker run -d -p 80:80 --name nginx01 -v vol1:/usr/share/nginx/html nginx

docker rm 的 -v 选项可以删除容器时,同时删除相关联的匿名卷

-v, --volumes Remove the volumes associated with the container

管理数据卷命令

docker volume COMMAND

Commands:
    create 		Create a volume
    inspect 	Display detailed information on one or more volumes
    ls 			List volumes
    prune 		Remove all unused local volumes
    rm 			Remove one or more volumes

查看数据卷的挂载关系

ocker inspect --format="{{.Mounts}}" <容器ID>

范例: 删除所有数据卷

[root@ubuntu1804 ~]#docker volume rm `docker volume ls -q`

范例:创建命名卷并删除

[root@ubuntu1804 ~]#docker volume create mysql-vol
mysql-vol
[root@ubuntu1804 ~]#docker volume ls
DRIVER VOLUME NAME
local mysql-vol

[root@ubuntu1804 ~]#tree /var/lib/docker/volumes/
/var/lib/docker/volumes/
├── metadata.db
└── mysql-vol
	└── _data
	
[root@ubuntu1804 ~]#docker volume rm mysql-vol
mysql-vol
[root@ubuntu1804 ~]#docker volume ls
DRIVER VOLUME NAME

范例:删除不再使用的数据卷

[root@ubuntu1804 ~]#docker volume ls
DRIVER VOLUME NAME
local
897bd48c5c5e2067627d5c6d10dad17d4793132a638986c16f36820663728ee1

[root@ubuntu1804 ~]#docker volume prune -f
Deleted Volumes:
897bd48c5c5e2067627d5c6d10dad17d4793132a638986c16f36820663728ee1

[root@ubuntu1804 ~]#docker volume ls
DRIVER VOLUME NAME

关于匿名数据卷和命名数据卷

命名卷就是有名字的卷,使用 docker volume create <卷名> 形式创建并命名的卷;而匿名卷就是没名字的卷,一般是 docker run -v /data 这种不指定卷名的时候所产生,或者 Dockerfile 里面的定义直接使用的。

有名字的卷,在用过一次后,以后挂载容器的时候还可以使用,因为有名字可以指定。所以一般需要保存的数据使用命名卷保存。
而匿名卷则是随着容器建立而建立,随着容器消亡而淹没于卷列表中(对于 docker run 匿名卷不会被自动删除)。 因此匿名卷只存放无关紧要的临时数据,随着容器消亡,这些数据将失去存在的意义。

Dockerfile中指定VOLUME为匿名数据卷,其目的只是为了将某个路径确定为卷。

按照最佳实践的要求,不应该在容器存储层内进行数据写入操作,所有写入应该使用卷。如果定制镜像的时候,就可以确定某些目录会发生频繁大量的读写操作,那么为了避免在运行时由于用户疏忽而忘记指定卷,导致容器发生存储层写入的问题,就可以在 Dockerfile 中使用 VOLUME 来指定某些目录为匿名卷。这样即使用户忘记了指定卷,也不会产生不良的后果。

这个设置可以在运行时覆盖。通过 docker run 的 -v 参数或者 docker-compose.yml 的 volumes指定。使用命名卷的好处是可以复用,其它容器可以通过这个命名数据卷的名字来指定挂载,共享其内容(不过要注意并发访问的竞争问题)。

比如,Dockerfile 中说 VOLUME /data,那么如果直接 docker run,其 /data 就会被挂载为匿名卷,向 /data 写入的操作不会写入到容器存储层,而是写入到了匿名卷中。但是如果运行时 docker run -v mydata:/data,这就覆盖了 /data 的挂载设置,要求将 /data 挂载到名为 mydata 的命名卷中。所以说 Dockerfile 中的 VOLUME 实际上是一层保险,确保镜像运行可以更好的遵循最佳实践,不向容器存储层内进行写入操作。

数据卷默认可能会保存于 /var/lib/docker/volumes,不过一般不需要、也不应该访问这个位置。

实战案例: 目录数据卷

在宿主机创建容器所使用的目录

[root@ubuntu1804 ~]#mkdir /data/testdir
[root@ubuntu1804 ~]#echo Test page on host > /data/testdir/index.html

查看容器相关目录路径

[root@ubuntu1804 ~]#docker images "*nginx*"
REPOSITORY TAG IMAGE ID CREATED
SIZE
nginx-ubuntu1804 1.16.1 19efdd23ac87 2 days ago
378MB
nginx-alpine 1.16.1 978a43bbb61d 2 days ago
211MB

[root@ubuntu1804 ~]#docker run -it --rm nginx-alpine:1.16.1 sh
/ # cat /apps/nginx/conf/nginx.conf
...
	server {
...
		location / {
            root /data/nginx/html; #nginx存放网页文件的路径
            index index.html index.htm;
}
...

/ # cat /data/nginx/html/index.html
Test Page based nginx-alpine
/ # exit
[root@ubuntu1804 ~]#docker ps -a
CONTAINER ID IMAGE COMMAND CREATED
STATUS PORTS NAMES

引用宿主机的数据卷启动容器

引用同一个数据卷目录,开启多个容器,实现多个容器共享数据

[root@ubuntu1804 ~]#docker run -d -v /data/testdir:/data/nginx/html/ -p 8001:80 nginx-alpine:1.16.1
56a5460f584bd2de56040c4a1dff86ad8a9723cfd6bf21ed8a538b9629b0874c
[root@ubuntu1804 ~]#docker run -d -v /data/testdir:/data/nginx/html/ -p 8002:80 nginx-alpine:1.16.1
e7b5bff6ce56fa51ed6411175c9c9f1fb9bf8e7b1b9471080380b01692f89e58
[root@ubuntu1804 ~]#docker ps
CONTAINER ID IMAGE COMMAND CREATED
STATUS PORTS NAMES
e7b5bff6ce56 nginx-alpine:1.16.1 "nginx" 6 seconds ago
Up 5 seconds 443/tcp, 0.0.0.0:8002->80/tcp hungry_robinson
56a5460f584b nginx-alpine:1.16.1 "nginx" 33 seconds ago
Up 31 seconds 443/tcp, 0.0.0.0:8001->80/tcp stupefied_dubinsky
[root@ubuntu1804 ~]#curl 127.0.0.1:8001
Test page on host
[root@ubuntu1804 ~]#curl 127.0.0.1:8002
Test page on host

进入到容器内测试写入数据

进入其中一个容器写入数据,可以其它容器的数据也变化

[root@ubuntu1804 ~]#docker exec -it e7b5bff6ce56 sh
/ # df
Filesystem 1K-blocks Used Available Use% Mounted on
overlay 47799020 5294492 40046724 12% /
tmpfs 65536 0 65536 0% /dev
tmpfs 492552 0 492552 0% /sys/fs/cgroup
shm 65536 0 65536 0% /dev/shm
/dev/sda2 47799020 5294492 40046724 12% /etc/resolv.conf
/dev/sda2 47799020 5294492 40046724 12% /etc/hostname
/dev/sda2 47799020 5294492 40046724 12% /etc/hosts
/dev/sda3 19091540 3958732 14139940 22% /data/nginx/html
tmpfs 492552 0 492552 0% /proc/asound
tmpfs 492552 0 492552 0% /proc/acpi
tmpfs 65536 0 65536 0% /proc/kcore
tmpfs 65536 0 65536 0% /proc/keys
tmpfs 65536 0 65536 0% /proc/timer_list
tmpfs 65536 0 65536 0% /proc/sched_debug
tmpfs 492552 0 492552 0% /proc/scsi
tmpfs 492552 0 492552 0% /sys/firmware
/ # cat /data/nginx/html/index.html
Test page on host
/ # echo Test page v2 on host > /data/nginx/html/index.html

#进入另一个容器看到数据变化
[root@ubuntu1804 ~]#docker exec -it 56a5460f584b sh
/ # cat /data/nginx/html/index.html
Test page v2 on host

#访问应用
[root@ubuntu1804 ~]#curl 127.0.0.1:8001
Test page v2 on host
[root@ubuntu1804 ~]#curl 127.0.0.1:8002
Test page v2 on host

在宿主机修改数据

[root@ubuntu1804 ~]#echo Test page v3 on host > /data/testdir/index.html
[root@ubuntu1804 ~]#cat /data/testdir/index.html
Test page v3 on host
[root@ubuntu1804 ~]#curl 127.0.0.1:8001
Test page v3 on host
[root@ubuntu1804 ~]#curl 127.0.0.1:8002
Test page v3 on host

[root@ubuntu1804 ~]#docker exec -it e7b5bff6ce56 sh
/ # cat /data/nginx/html/index.html
Test page v3 on host
[root@ubuntu1804 ~]#docker exec -it 56a5460f584b sh
/ # cat /data/nginx/html/index.html
Test page v3 on host
只读方法挂载数据卷

默认数据卷为可读可写,加ro选项,可以实现只读挂载,对于不希望容器修改的数据,比如: 配置文件,脚本等,可以用此方式挂载

[root@ubuntu1804 ~]#docker run -d -v /data/testdir:/data/nginx/html/:ro -p 8004:80 nginx-alpine:1.16.1
727d3ecf65c5a79bd9a11033812dc01619c4c45bd25af5155f904016f5f0c45a

[root@ubuntu1804 ~]#docker exec -it 727d3ecf65c5a79bd9 sh
/ # cat /data/nginx/html/index.html
Test page v3 on host
/ # echo Test page v4 on host > /data/nginx/html/index.html
sh: can't create /data/nginx/html/index.html: Read-only file system
/ # cat /data/nginx/html/index.html
删除容器

删除容器后,宿主机的数据卷还存在,可继续给新的容器使用

[root@ubuntu1804 ~]#docker ps -a
CONTAINER ID IMAGE COMMAND CREATED
STATUS PORTS NAMES
e7b5bff6ce56 nginx-alpine:1.16.1 "nginx" 9 minutes ago
Up 9 minutes 443/tcp, 0.0.0.0:8002->80/tcp hungry_robinson
56a5460f584b nginx-alpine:1.16.1 "nginx" 9 minutes ago
Up 9 minutes 443/tcp, 0.0.0.0:8001->80/tcp stupefied_dubinsky

[root@ubuntu1804 ~]#docker rm -f `docker ps -aq`
e7b5bff6ce56
56a5460f584b

[root@ubuntu1804 ~]#cat /data/testdir/index.html
Test page v3 on host

#新建的容器还可以继续使用原有的数据卷
[root@ubuntu1804 ~]#docker run -d -v /data/testdir:/data/nginx/html/ -p 8003:80 nginx-alpine:1.16.1
ecd016506f6af3f4af61dbd869f8fce5f634ecdc0e3272f9e0402c981acd80a4
[root@ubuntu1804 ~]#curl 127.0.0.1:8003
Test page v3 on host

实战案例: MySQL使用的数据卷

[root@ubuntu1804 ~]#docker pull mysql:5.7.30
5.7.29: Pulling from library/mysql
Status: Downloaded newer image for mysql:5.7.30
docker.io/library/mysql:5.7.30

[root@ubuntu1804 ~]#docker images "mysql*"
REPOSITORY TAG IMAGE ID CREATED
SIZE
mysql 5.7.29 b598110d0fff 2 weeks ago
435MB

[root@ubuntu1804 ~]#docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456
mysql:5.7.30
[root@ubuntu1804 ~]#docker ps
CONTAINER ID IMAGE COMMAND CREATED
STATUS PORTS NAMES
c21ca6f8a7fe mysql:5.7.30 "docker-entrypoint.s…" 3 minutes ago
Up 3 minutes 0.0.0.0:3306->3306/tcp, 33060/tcp nifty_banach

[root@ubuntu1804 ~]#docker exec -it c21ca6f8a7fe bash
root@c21ca6f8a7fe:/# cat /etc/issue
Debian GNU/Linux 9 \n \l

root@c21ca6f8a7fe:/# cat /etc/mysql/my.cnf
......
!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mysql.conf.d/

root@c21ca6f8a7fe:/# cat /etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld]
pid-file 	= /var/run/mysqld/mysqld.pid
socket		 = /var/run/mysqld/mysqld.sock
datadir 	= /var/lib/mysql #数据存放路径

root@c21ca6f8a7fe:/# pstree -p
mysqld(1)-+-{mysqld}(130)
    |-{mysqld}(131)
    |-{mysqld}(132)
    |-{mysqld}(133)
    |-{mysqld}(134)
    |-{mysqld}(135)
    |-{mysqld}(136)
    |-{mysqld}(137)
    |-{mysqld}(138)
    |-{mysqld}(139)
    |-{mysqld}(140)
    |-{mysqld}(141)
    |-{mysqld}(143)
    |-{mysqld}(144)
    |-{mysqld}(145)
    |-{mysqld}(146)
    |-{mysqld}(147)
    |-{mysqld}(148)
    |-{mysqld}(149)
    |-{mysqld}(150)
    |-{mysqld}(151)
    |-{mysqld}(152)
    |-{mysqld}(153)
    |-{mysqld}(154)
    |-{mysqld}(155)
    `-{mysqld}(156)
    
[root@ubuntu1804 ~]#mysql -uroot -p123456 -h127.0.0.1
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.30 MySQL Community Server (GPL)
Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
4 rows in set (0.00 sec)

mysql> create database dockerdb;
Query OK, 1 row affected (0.00 sec)

mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| dockerdb |
| mysql |
 performance_schema |
| sys |
+--------------------+
5 rows in set (0.01 sec)
mysql>

#删除容器后,再创建新的容器,数据库信息丢失
[root@ubuntu1804 ~]#docker rm -f c21ca6f8a7fe
c21ca6f8a7fe
[root@ubuntu1804 ~]#docker run -d --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7.30
f52a50c7f80ee39d9a935a762eacb05db72dcfa5f0d02af8b4f23b5538080b67
[root@ubuntu1804 ~]#mysql -uroot -p123456 -h127.0.0.1
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.30 MySQL Community Server (GPL)
Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
4 rows in set (0.00 sec)

#利用数据卷创建容器
[root@ubuntu1804 ~]#mkdir /data/mysql
[root@ubuntu1804 ~]#docker run -d --name mysql -p 3306:3306 -v /data/mysql/:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7.30
d64ef3f9a64491061132020306fd3e97e1aa361b0fb9f6f644f2a1e3f334119c
[root@ubuntu1804 ~]#mysql -uroot -p123456 -h127.0.0.1
mysql> create database dockerdb;
Query OK, 1 row affected (0.00 sec)
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| dockerdb |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.00 sec)
mysql> exit

#删除容器,数据存放在挂载数据卷中,不会删除
[root@ubuntu1804 ~]#docker rm -fv mysql
mysql

[root@ubuntu1804 ~]#ls /data/mysql/
auto.cnf ca.pem client-key.pem ib_buffer_pool ib_logfile0 ibtmp1
performance_schema public_key.pem server-key.pem
ca-key.pem client-cert.pem dockerdb ibdata1 ib_logfile1 mysql
private_key.pem server-cert.pem sys

#重新创建新容器,之前数据还在
[root@ubuntu1804 ~]#docker run -d --name mysql -p 3306:3306 -v /data/mysql/:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7.30
8d6cd0822cc61db30c0401992d2dbfa5c33b525f9aead44c165e0e247c37e5df
[root@ubuntu1804 ~]#mysql -uroot -p123456 -h127.0.0.1
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| dockerdb |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.00 sec)
mysql> exit


#指定多个数据卷,创建MySQL
[root@ubuntu1804 ~]#docker run --name mysql-test1 -v /data/mysql:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_DATABASE=wordpress -e MYSQL_USER=wpuser -e MYSQL_PASSWORD=123456 -d -p 3306:3306 mysql:5.7.30

[root@ubuntu1804 ~]#docker run --name mysql-test2 -v /root/mysql/:/etc/mysql/conf.d -v /data/mysql2:/var/lib/mysql --env-file=env.list -d -p 3307:3306 mysql:5.7.30

[root@ubuntu1804 ~]#cat mysql/mysql-test.cnf
[mysqld]
server-id=100
log-bin=mysql-bin
[root@ubuntu1804 ~]#cat env.list
MYSQL_ROOT_PASSWORD=123456
MYSQL_DATABASE=wordpress
MYSQL_USER=wpuser
MYSQL_PASSWORD=wppass

实战案例: 文件数据卷

文件挂载用于很少更改文件内容的场景,比如: nginx 的配置文件、tomcat的配置文件等。

准备相关文件

[root@ubuntu1804 ~]#mkdir /data/{bin,testapp,logs}
[root@ubuntu1804 ~]#echo testapp v1 > /data/testapp/index.html
[root@ubuntu1804 ~]#cat /data/testapp/index.html
testapp v1
[root@ubuntu1804 ~]#cp /data/dockerfile/web/tomcat/tomcat-base-8.5.50/apache-tomcat-8.5.50/bin/catalina.sh /data/bin/
[root@ubuntu1804 ~]#vim /data/bin/catalina.sh
#加下面tomcat的优化参数行
# -----------------------------------------------------------------------------
JAVA_OPTS="-server -Xms4g -Xmx4g -Xss512k -Xmn1g -XX:CMSInitiatingOccupancyFraction=65 -XX:+UseFastAccessorMethods -XX:+AggressiveOpts-XX:+UseBiasedLocking -XX:+DisableExplicitGC -XX:MaxTenuringThreshold=10 -XX:NewSize=2048M -XX:MaxNewSize=2048M -XX:NewRatio=2 -XX:PermSize=128m -XX:MaxPermSize=512m -XX:CMSFullGCsBeforeCompaction=5 -XX:+ExplicitGCInvokesConcurrent -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods"

# OS specific support. $var _must_ be set to either true or false.

[root@ubuntu1804 ~]#chown 2019:2019 /data/bin/catalina.sh
[root@ubuntu1804 ~]#ll /data/bin/catalina.sh
-rwxr-x--- 1 2019 2019 24324 Jan 31 21:43 /data/bin/catalina.sh*
[root@ubuntu1804 ~]#chown 2019:2019 /data/logs/

引用文件数据卷启动容器

同时挂载可读可写方式的目录数据卷和只读方式的文件数据卷,实现三个数据卷的挂载,数据,日志和启动脚本

[root@ubuntu1804 ~]#docker run -d -v /data/bin/catalina.sh:/apps/tomcat/bin/catalina.sh:ro -v /data/testapp:/data/tomcat/webapps/testapp -v /data/logs:/apps/tomcat/logs -p 8080:8080 tomcat-web:app1

验证容器可以访问

[root@ubuntu1804 ~]#curl 127.0.0.1:8080/testapp/
testapp v1
[root@ubuntu1804 ~]#ls -l /data/logs/
total 36
drwxr-xr-x 2 2019 2019 4096 Jan 31 22:44 ./
drwxr-xr-x 7 root root 4096 Jan 31 22:43 ../
-rw-r----- 1 2019 2019 8336 Jan 31 22:44 catalina.2020-01-31.log
-rw-r----- 1 2019 2019 8808 Jan 31 22:44 catalina.out
-rw-r----- 1 2019 2019 0 Jan 31 22:44 host-manager.2020-01-31.log
-rw-r----- 1 2019 2019 0 Jan 31 22:44 localhost.2020-01-31.log
-rw-r----- 1 2019 2019 76 Jan 31 22:44 localhost_access_log.2020-01-31.txt
-rw-r----- 1 2019 2019 0 Jan 31 22:44 manager.2020-01-31.log

直接修改宿主机的数据

#宿主机修改目录数据卷
[root@ubuntu1804 ~]#echo testapp v2 > /data/testapp/index.html
[root@ubuntu1804 ~]#curl 127.0.0.1:8080/testapp/
testapp v2

[root@ubuntu1804 ~]#ll /data/bin/catalina.sh
-rwxr-x--- 1 2019 2019 24324 Jan 31 21:43 /data/bin/catalina.sh*
[root@ubuntu1804 ~]#echo >> /data/bin/catalina.sh
[root@ubuntu1804 ~]#ll /data/bin/catalina.sh
-rwxr-x--- 1 2019 2019 24325 Jan 31 22:21 /data/bin/catalina.sh*

进入容器修改数据

[root@ubuntu1804 ~]#docker exec -it 55de76261c5e bash
[root@55de76261c5e /]# netstat -ntl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 127.0.0.1:8005 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:8009 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN

#文件数据卷上的文件为只读
[root@55de76261c5e /]# echo >> /apps/tomcat/bin/catalina.sh
bash: /apps/tomcat/bin/catalina.sh: Read-only file system

#目录数据卷可读可写
[root@55de76261c5e /]# cat /data/tomcat/webapps/testapp/index.html
testapp v2
[root@55de76261c5e /]# echo testapp v3 > /data/tomcat/webapps/testapp/index.html

[root@55de76261c5e /]# cat /data/tomcat/webapps/testapp/index.html
testapp v3
[root@ubuntu1804 ~]#curl 127.0.0.1:8080/testapp/
testapp v3

查看容器中挂载和进程信息

[root@55de76261c5e /]# mount
......
/dev/sda3 on /apps/apache-tomcat-8.5.50/bin/catalina.sh type ext4
(ro,relatime,data=ordered)
/dev/sda3 on /data/tomcat/webapps/testapp type ext4 (rw,relatime,data=ordered)
......
[root@55de76261c5e /]# df
Filesystem 1K-blocks Used Available Use% Mounted on
overlay 47799020 5295032 40046184 12% /
tmpfs 65536 0 65536 0% /dev
tmpfs 492552 0 492552 0% /sys/fs/cgroup
shm 65536 0 65536 0% /dev/shm
/dev/sda2 47799020 5295032 40046184 12% /etc/hosts
/dev/sda3 19091540 3974908 14123764 22% /data/tomcat/webapps/testapp
tmpfs 492552 0 492552 0% /proc/asound
tmpfs 492552 0 492552 0% /proc/acpi
tmpfs 492552 0 492552 0% /proc/scsi
tmpfs 492552 0 492552 0% /sys/firmware

[root@55de76261c5e /]# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.3 15136 3040 ? Ss 22:09 0:00 /bin/bash
/apps/tomcat/bin/run_tomcat.sh
www 25 0.5 12.7 6253760 125268 ? Sl 22:09 0:04
/usr/local/jdk/bin/java -Djava.util.logging.config.file=/apps/tomcat
root 26 0.0 0.4 85428 4468 ? S 22:09 0:00 su - www -c
tail -f /etc/hosts
www 28 0.0 0.0 4416 716 ? Ss 22:09 0:00 tail -f
/etc/hosts
root 85 0.0 0.4 15800 3980 pts/0 Ss 22:16 0:00 bash
root 109 0.0 0.3 55196 3696 pts/0 R+ 22:25 0:00 ps aux

[root@55de76261c5e /]# ps aux|grep java
www 25 0.5 12.7 6253760 125268 ? Sl 22:09 0:04
/usr/local/jdk/bin/java -
Djava.util.logging.config.file=/apps/tomcat/conf/logging.properties -
Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -server -Xms4g
-Xmx4g -Xss512k -Xmn1g -XX:CMSInitiatingOccupancyFraction=65 -
XX:+UseFastAccessorMethods -XX:+AggressiveOpts -XX:+UseBiasedLocking -
XX:+DisableExplicitGC -XX:MaxTenuringThreshold=10 -XX:NewSize=2048M -
XX:MaxNewSize=2048M -XX:NewRatio=2 -XX:PermSize=128m -XX:MaxPermSize=512m -
XX:CMSFullGCsBeforeCompaction=5 -XX:+ExplicitGCInvokesConcurrent -
XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -
XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -
XX:+UseFastAccessorMethods -Djdk.tls.ephemeralDHKeySize=2048 -
Djava.protocol.handler.pkgs=org.apache.catalina.webresources -
Dorg.apache.catalina.security.SecurityListener.UMASK=0027 -
Dignore.endorsed.dirs= -classpath
/apps/tomcat/bin/bootstrap.jar:/apps/tomcat/bin/tomcat-juli.jar -
Dcatalina.base=/apps/tomcat -Dcatalina.home=/apps/tomcat -
Djava.io.tmpdir=/apps/tomcat/temp org.apache.catalina.startup.Bootstrap start
root 111 0.0 0.2 12536 2320 pts/0 S+ 22:25 0:00 grep --
color=auto java

实战案例: 匿名数据卷

[root@ubuntu1804 ~]#docker volume ls
DRIVER VOLUME NAME

[root@ubuntu1804 ~]#docker ps -a
CONTAINER ID IMAGE COMMAND CREATED
STATUS PORTS NAMES

#利用匿名数据卷创建容器
[root@ubuntu1804 ~]#docker run -d -p 80:80 --name nginx01 -v /usr/share/nginx/html nginx
914c622af7ebae68aab0b0075da39534fdb118cd5d4d7adf3054da94eb2a4952
[root@ubuntu1804 ~]#curl 127.0.0.1
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
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>

#查看自动生成的匿名数据卷
[root@ubuntu1804 ~]#docker volume ls
DRIVER VOLUME NAME
local
2a18aff09215e7830c6df56a80e0f8ace9b44f3384d512daf06d75502f8b40fe

#查看匿名数据卷的详细信息
[root@ubuntu1804 ~]#docker volume inspect 2a18aff09215e7830c6df56a80e0f8ace9b44f3384d512daf06d75502f8b40fe
[
{
"CreatedAt": "2020-07-21T19:17:07+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint":
"/var/lib/docker/volumes/2a18aff09215e7830c6df56a80e0f8ace9b44f3384d512daf06d755
02f8b40fe/_data",
"Name":
"2a18aff09215e7830c6df56a80e0f8ace9b44f3384d512daf06d75502f8b40fe",
"Options": null,
"Scope": "local"
}
]

[root@ubuntu1804 ~]#docker inspect --format="{{.Mounts}}" nginx01
[{volume 2a18aff09215e7830c6df56a80e0f8ace9b44f3384d512daf06d75502f8b40fe
/var/lib/docker/volumes/2a18aff09215e7830c6df56a80e0f8ace9b44f3384d512daf06d7550
2f8b40fe/_data /usr/share/nginx/html local true }]

#查看匿名数据卷的文件
[root@ubuntu1804 ~]#ls
/var/lib/docker/volumes/2a18aff09215e7830c6df56a80e0f8ace9b44f3384d512daf06d7550
2f8b40fe/_data
50x.html index.html

#修改宿主机中匿名数据卷的文件
[root@ubuntu1804 ~]#echo Anonymous Volume > /var/lib/docker/volumes/2a18aff09215e7830c6df56a80e0f8ace9b44f3384d512daf06d75502f8b40fe/_data/index.html

[root@ubuntu1804 ~]#curl 127.0.0.1
Anonymous Volume

#删除容器不会删除匿名数据卷
[root@ubuntu1804 ~]#docker rm -f nginx01
nginx01
[root@ubuntu1804 ~]#docker volume ls
DRIVER VOLUME NAME
local
2a18aff09215e7830c6df56a80e0f8ace9b44f3384d512daf06d75502f8b40fe

[root@ubuntu1804 ~]#docker run -d -p 80:80 --name nginx01 -v /usr/share/nginx/html nginx
40a99ae066cc60aa0ad7d073e46101a15446509acbd7971e677f3cbe6733aa79

[root@ubuntu1804 ~]#cat /var/lib/docker/volumes/2a18aff09215e7830c6df56a80e0f8ace9b44f3384d512daf06d75502f8b40fe/_data/index.html
Anonymous Volume

#删除匿名数据卷
[root@ubuntu1804 ~]#docker volume rm 2a18aff09215e7830c6df56a80e0f8ace9b44f3384d512daf06d75502f8b40fe
2a18aff09215e7830c6df56a80e0f8ace9b44f3384d512daf06d75502f8b40fe
[root@ubuntu1804 ~]#docker volume ls
DRIVER VOLUME NAME

实战案例: 命名数据卷

创建命名数据卷

[root@ubuntu1804 ~]#docker volume create vol1
vol1
[root@ubuntu1804 ~]#docker volume ls
DRIVER VOLUME NAME
local vol1
[root@ubuntu1804 ~]#docker inspect vol1
[
{
"CreatedAt": "2020-07-21T18:36:45+08:00",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/vol1/_data",
"Name": "vol1",
"Options": {},
"Scope": "local"
}
]

使用命名数据卷创建容器

[root@ubuntu1804 ~]#docker run -d -p 8001:80 --name nginx01 -v vol1:/usr/share/nginx/html nginx

[root@ubuntu1804 ~]#curl 127.0.0.1:8001
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
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>

#显示命名数据卷
[root@ubuntu1804 ~]#docker volume ls
DRIVER VOLUME NAME
local vol1

#查看命名数据卷详解信息
[root@ubuntu1804 ~]#docker volume inspect vol1
[
{
"CreatedAt": "2020-07-21T18:44:03+08:00",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/vol1/_data",
"Name": "vol1",
"Options": {},
"Scope": "local"
}
]

[root@ubuntu1804 ~]#docker inspect --format="{{.Mounts}}" nginx01
[{volume vol1 /var/lib/docker/volumes/vol1/_data /usr/share/nginx/html local z
true }]

#查看命名数据卷的文件
[root@ubuntu1804 ~]#ls /var/lib/docker/volumes/vol1/_data
50x.html index.html

#修改宿主机命名数据卷的文件
[root@ubuntu1804 ~]#echo nginx vol1 website > /var/lib/docker/volumes/vol1/_data/index.html
[root@ubuntu1804 ~]#curl 127.0.0.1:8001
nginx vol1 website

#利用现在的命名数据卷再创建新容器,可以和原有容器共享同一个命名数据卷的数据
[root@ubuntu1804 ~]#docker run -d -p 8002:80 --name nginx02 -v vol1:/usr/share/nginx/html nginx
fb8739438992b7797a3f23dc495d2fd43f513fd793114848171f376c4325675e
[root@ubuntu1804 ~]#curl 127.0.0.1:8002
nginx vol1 website

创建容器时自动创建命名数据卷

#创建容器自动创建命名数据卷
[root@ubuntu1804 ~]#docker run -d -p 8003:80 --name nginx03 -v vol2:/usr/share/nginx/html nginx
5d7497c04907babcb2084f8f7a16874223e07965410fd4da14bc2fe993277aed
[root@ubuntu1804 ~]#docker volume ls
DRIVER VOLUME NAME
local portainer_data
local vol1
local vol2

删除数据卷

#删除指定的命名数据卷
[root@ubuntu1804 ~]#docker volume rm vol1
#清理全部不再使用的卷
[root@ubuntu1804 ~]#docker volume prune -f

实战案例:实现 wordpress 持久化

[root@ubuntu2004 ~]#docker run -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_DATABASE=wordpress -e MYSQL_USER=wordpress -e MYSQL_PASSWORD=123456 --name mysql -d -v /data/mysql:/var/lib/mysql --restart=always mysql:8.0.29-oracle

[root@ubuntu2004 ~]#docker run -d -p 80:80 --name wordpress -v /data/wordpress:/var/www/html --restart=always wordpress:php7.4-apache

#注意:数据库服务器的地址要输入宿主机的IP

数据卷容器

image-MpnvoevEcaiHBAJLHOQcMUiCoKBatfaK.png

在Dockerfile中创建的是匿名数据卷,无法直接实现多个容器之间共享数据

数据卷容器主要的功能是可以让数据在多个docker容器之间共享

如下图所示: 即可以让B容器和C容器都可以访问A容器的内容,即可以实现A,B,C 三个容器之间的数据读写共享。

image-reNiZvYFBvCbRlyQZTUHKDsYBIkfgOeV.png

相当于先要创建一个后台运行的容器作为 Server,用于提供数据卷,这个卷可以为其他容器提供数据存储服务,其他使用此卷的容器作为client端 ,但此方法并不常使用

缺点: 因为依赖一个 Server 的容器,所以此 Server 容器出了问题,其它 Client容器都会受影响

使用数据卷容器

启动容器时,指定使用数据卷容器

docker run 命令的以下选项可以实现数据卷容器,格式如下:

--volumes-from <数据卷容器> Mount volumes from the specified container(s)

实战案例: 数据卷容器

创建一个数据卷容器 Server

先创建一个挂载宿主机的数据目录的容器,且可以无需启动

范例: 使用之前的镜像创建数据卷容器

#数据卷容器一般无需映射端口
[root@ubuntu1804 ~]#docker run -d --name volume-server -v /data/bin/catalina.sh:/apps/tomcat/bin/catalina.sh:ro -v /data/testapp:/data/tomcat/webapps/testapp tomcat-web:app1
109a34c5a83b23bc57b16fc8b1e19104d3d522a8c62e1e98d8e69577b01739b5
[root@ubuntu1804 ~]#docker ps
CONTAINER ID IMAGE COMMAND CREATED
STATUS PORTS NAMES
109a34c5a83b tomcat-web:app1 "/apps/tomcat/bin/ru…" 14 seconds ago
Up 13 seconds 8009/tcp, 8080/tcp volume-server

启动多个数据卷容器 Client

[root@ubuntu1804 ~]#docker run -d --name client1 --volumes-from volume-server -p 8081:8080 tomcat-web:app1
fe6ce0548dfee924cd39a8d86d5ed0e8ce9ea65323742f1336fa3b002c1b4a8c
[root@ubuntu1804 ~]#docker run -d --name client2 --volumes-from volume-server -p 8082:8080 tomcat-web:app1
10397838df9a489d7af2112850d4285bff5c2c262ea05cc9c5fb265af538f2c8

[root@ubuntu1804 ~]#docker ps
CONTAINER ID IMAGE COMMAND CREATED
STATUS PORTS NAMES
10397838df9a tomcat-web:app1 "/apps/tomcat/bin/ru…" 30 seconds ago
Up 27 seconds 8009/tcp, 0.0.0.0:8082->8080/tcp client2
fe6ce0548dfe tomcat-web:app1 "/apps/tomcat/bin/ru…" 40 seconds ago
Up 38 seconds 8009/tcp, 0.0.0.0:8081->8080/tcp client1
109a34c5a83b tomcat-web:app1 "/apps/tomcat/bin/ru…" 2 minutes ago
Up 2 minutes 8009/tcp, 8080/tcp volume-server
[root@ubuntu1804 ~]#

验证访问

[root@ubuntu1804 ~]#curl 127.0.0.1:8081/testapp/
testapp v3
[root@ubuntu1804 ~]#curl 127.0.0.1:8082/testapp/
testapp v3

进入容器测试读写

读写权限依赖于源数据卷Server容器

#进入 Server 容器修改数据
[root@ubuntu1804 ~]#docker exec -it volume-server bash
[root@109a34c5a83b /]# cat /data/tomcat/webapps/testapp/index.html
testapp v3
[root@109a34c5a83b /]# echo testapp v4 > /data/tomcat/webapps/testapp/index.html
[root@109a34c5a83b /]#
[root@ubuntu1804 ~]#curl 127.0.0.1:8081/testapp/
testapp v4
[root@ubuntu1804 ~]#curl 127.0.0.1:8082/testapp/
testapp v4

#进入 Client 容器修改数据
[root@ubuntu1804 ~]#docker exec -it client1 bash
[root@fe6ce0548dfe /]# cat /data/tomcat/webapps/testapp/index.html
testapp v4
[root@fe6ce0548dfe /]# echo testapp v5 > /data/tomcat/webapps/testapp/index.html
[root@fe6ce0548dfe /]# cat /data/tomcat/webapps/testapp/index.html
testapp v5
[root@ubuntu1804 ~]#curl 127.0.0.1:8081/testapp/
testapp v5
[root@ubuntu1804 ~]#curl 127.0.0.1:8082/testapp/
testapp v5

在宿主机直接修改

[root@ubuntu1804 ~]#cat /data/testapp/index.html
testapp v5
[root@ubuntu1804 ~]#echo testapp v6 > /data/testapp/index.html
[root@ubuntu1804 ~]#cat /data/testapp/index.html
testapp v6
[root@ubuntu1804 ~]#curl 127.0.0.1:8081/testapp/
testapp v6
[root@ubuntu1804 ~]#curl 127.0.0.1:8082/testapp/
testapp v6
[root@ubuntu1804 ~]#
[root@ubuntu1804 ~]#docker exec -it volume-server bash
[root@109a34c5a83b /]# cat /data/tomcat/webapps/testapp/index.html
testapp v6
[root@109a34c5a83b /]#

关闭卷容器Server测试能否启动新容器

关闭卷容器Server,仍然可以创建新的client容器及访问旧的client容器

[root@ubuntu1804 ~]#docker stop volume-server
volume-server
[root@ubuntu1804 ~]#docker ps
CONTAINER ID IMAGE COMMAND CREATED
STATUS PORTS NAMES
10397838df9a tomcat-web:app1 "/apps/tomcat/bin/ru…" 9 minutes ago
Up 9 minutes 8009/tcp, 0.0.0.0:8082->8080/tcp client2
fe6ce0548dfe tomcat-web:app1 "/apps/tomcat/bin/ru…" 10 minutes ago
Up 10 minutes 8009/tcp, 0.0.0.0:8081->8080/tcp client1
[root@ubuntu1804 ~]#docker run -d --name client3 --volumes-from volume-server -p 8083:8080 tomcat-web:app1
458df991688c3dbc69d824f889616e0a7534ce18543c8559162f3609fbb26b53

[root@ubuntu1804 ~]#docker ps -a
CONTAINER ID IMAGE COMMAND CREATED
STATUS PORTS NAMES
458df991688c tomcat-web:app1 "/apps/tomcat/bin/ru…" About a minute
ago Up About a minute 8009/tcp, 0.0.0.0:8083->8080/tcp
client3
10397838df9a tomcat-web:app1 "/apps/tomcat/bin/ru…" 11 minutes ago
Up 11 minutes 8009/tcp, 0.0.0.0:8082->8080/tcp
client2
fe6ce0548dfe tomcat-web:app1 "/apps/tomcat/bin/ru…" 11 minutes ago
Up 11 minutes 8009/tcp, 0.0.0.0:8081->8080/tcp
client1
109a34c5a83b tomcat-web:app1 "/apps/tomcat/bin/ru…" 13 minutes ago
Exited (137) About a minute ago
volume-server

[root@ubuntu1804 ~]#curl 127.0.0.1:8081/testapp/
testapp v6
[root@ubuntu1804 ~]#curl 127.0.0.1:8082/testapp/
testapp v6
[root@ubuntu1804 ~]#curl 127.0.0.1:8083/testapp/
testapp v6

删除源卷容器Server,访问client和创建新的client容器

删除数据卷容器后,旧的client 容器仍能访问,但无法再创建新的client容器

删除数据卷容器后,旧的client 容器仍能访问,但无法再创建基于数据卷容器的新的client容器,但可以创建基于已创建的Client容器的Client容器

[root@ubuntu1804 ~]#docker rm -fv volume-server
volume-server
[root@ubuntu1804 ~]#docker ps -a
CONTAINER ID IMAGE COMMAND CREATED
STATUS PORTS NAMES
458df991688c tomcat-web:app1 "/apps/tomcat/bin/ru…" 4 minutes ago
Up 4 minutes 8009/tcp, 0.0.0.0:8083->8080/tcp client3
10397838df9a tomcat-web:app1 "/apps/tomcat/bin/ru…" 14 minutes ago
Up 14 minutes 8009/tcp, 0.0.0.0:8082->8080/tcp client2
fe6ce0548dfe tomcat-web:app1 "/apps/tomcat/bin/ru…" 14 minutes ago
Up 14 minutes 8009/tcp, 0.0.0.0:8081->8080/tcp client1

[root@ubuntu1804 ~]#docker run -d --name client4 --volumes-from volume-server -p 8084:8080 tomcat-web:app1
Unable to find image 'tomcat-web:app1' locally
docker: Error response from daemon: pull access denied for tomcat-web,
repository does not exist or may require 'docker login': denied: requested
access to the resource is denied.
See 'docker run --help'.
[root@ubuntu1804 ~]#curl 127.0.0.1:8081/testapp/
testapp v6
[root@ubuntu1804 ~]#curl 127.0.0.1:8082/testapp/
testapp v6
[root@ubuntu1804 ~]#curl 127.0.0.1:8083/testapp/
testapp v6

重新创建容器卷 Server

重新创建容器卷容器后,还可继续创建新client 容器

[root@ubuntu1804 ~]#docker run -d --name volume-server -v /data/bin/catalina.sh:/apps/tomcat/bin/catalina.sh:ro -v /data/testapp:/data/tomcat/webapps/testapp tomcat-web:app1
ee0dacff6a4531dc8ecc1d416b449e087d8bf27dad2d6a6e515faf10455a1cc0

[root@ubuntu1804 ~]#docker run -d --name client4 --volumes-from volume-server -p 8084:8080 tomcat-web:app1
dcd002fe87887a0c5d1f62b24cd7665e42ee864c975d1a2953c383af0cb65a70

[root@ubuntu1804 ~]#curl 127.0.0.1:8084/testapp/
testapp v6
[root@ubuntu1804 ~]#curl 127.0.0.1:8081/testapp/
testapp v6
[root@ubuntu1804 ~]#curl 127.0.0.1:8082/testapp/
testapp v6
[root@ubuntu1804 ~]#curl 127.0.0.1:8083/testapp/
testapp v6
[root@ubuntu1804 ~]#curl 127.0.0.1:8084/testapp/
testapp v6

实战案例: 利用数据卷容器备份指定容器的数据卷实现

由于匿名数据卷在宿主机中的存储位置不确定,所以为了方便的备份匿名数据卷,可以利用数据卷容器实现数据卷的备份

#在执行备份命令容器上执行备份方式
docker run -it --rm --volumes-from [container name] -v $(pwd):/backup ubuntu 
root@ca5bb2c1f877:/#tar cvf /backup/backup.tar [container data volume]

#说明
[container name] #表示需要备份的匿名数据卷的容器
[container data volume] #表示容器内的需要备份的匿名数据卷对应的目录

#还原方式
docker run -it --rm --volumes-from [container name] -v $(pwd):/backup ubuntu
root@ca5bb2c1f877:/#tar xvf /backup/backup.tar -C [container data volume]

范例:

#创建需要备份的匿名数据卷容器
[root@ubuntu1804 ~]#docker run -it -v /datavolume1 --name volume-server centos bash
[root@88bbc22a3072 /]# ls
bin dev home lib64 media opt root sbin sys usr
datavolume1 etc lib lost+found mnt proc run srv tmp var

[root@88bbc22a3072 /]# touch /datavolume1/centos.txt
[root@88bbc22a3072 /]# exit
exit
[root@ubuntu1804 ~]#docker ps
CONTAINER ID IMAGE COMMAND CREATED
STATUS PORTS NAMES
#可以看到数据卷对应的宿主机目录
[root@ubuntu1804 ~]#docker inspect --format="{{.Mounts}}" volume-server
[{volume 21199be9bff7ce98926f673d2250d64d75aaa00c5d375a2528b2b47f0ec4cb93
/var/lib/docker/volumes/21199be9bff7ce98926f673d2250d64d75aaa00c5d375a2528b2b47f
0ec4cb93/_data /datavolume1 local true }]

#基于前面的匿名数据卷容器创建执行备份操作的容器
[root@ubuntu1804 ~]#docker run -it --rm --volumes-from volume-server -v ~/backup:/backup --name backup-server ubuntu
root@9ad8be4b5810:/# ls
backup boot dev home lib32 libx32 mnt proc run srv tmp var
bin datavolume1 etc lib lib64 media opt root sbin sys usr
root@9ad8be4b5810:/# ls /backup/
root@9ad8be4b5810:/# ls /datavolume1/
centos.txt
root@9ad8be4b5810:/datavolume1# exit
exit
[root@ubuntu1804 ~]#ls ~/backup/
data.tar

#删除容器的数据
[root@ubuntu1804 ~]#docker start -i volume-server
[root@88bbc22a3072 /]# rm -rf /datavolume1/*
[root@88bbc22a3072 /]# ls /datavolume1/
[root@88bbc22a3072 /]# exit
exit
#进行还原
[root@ubuntu1804 ~]#docker run --rm --volumes-from volume-server -v ~/backup:/backup --name backup-server ubuntu tar xvf /backup/data.tar -C /datavolume1/
./
./centos.txt
#验证是否还原
[root@ubuntu1804 ~]#docker start -i volume-server
[root@88bbc22a3072 /]# ls /datavolume1/
centos.txt

范例: 利用数据卷容器备份MySQL数据库

#MySQL容器默认使用了匿名卷
[root@ubuntu1804 ~]#docker run -d --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7.30

[root@ubuntu1804 ~]#docker volume ls
DRIVER VOLUME NAME
local
897bd48c5c5e2067627d5c6d10dad17d4793132a638986c16f36820663728ee1

#备份数据库
[root@ubuntu1804 ~]#docker run -it --rm --volumes-from mysql -v $(pwd):/backup centos tar cvf /backup/mysql.tar -C /var/lib/mysql
#删除数据库文件
[root@ubuntu1804 ~]#rm -rf
/var/lib/docker/volumes/897bd48c5c5e2067627d5c6d10dad17d4793132a638986c16f368206
63728ee1/_data/
#还原数据库
[root@ubuntu1804 ~]#docker run -it --rm --volumes-from mysql -v $(pwd):/backup centos tar xvf /backup/mysql.tar -C /var/lib/mysql

数据卷容器总结

将提供卷的容器Server 删除,已经运行的容器Client依然可以使用挂载的卷,因为容器是通过挂载访问数据的,但是无法创建新的卷容器客户端,但是再把卷容器Server创建后即可正常创建卷容器Client此方式可以用于线上共享数据目录等环境,因为即使数据卷容器被删除了,其他已经运行的容器依然可以挂载使用

由此可知, 数据卷容器的功能只是将数据挂载信息传递给了其它使用数据卷容器的容器,而数据卷容器本身并不提供数据存储功能

数据卷容器可以作为共享的方式为其他容器提供文件共享,类似于NFS共享,可以在生产中启动一个实例挂载本地的目录,然后其他的容器分别挂载此容器的目录,即可保证各容器之间的数据一致性

数据卷容器的 Server 和 Client 可以不使用同一个镜像生成

当创建Client容器时,会复制Server容器的数据卷信息,后续Server容器状态和存在与否,都不会影响Client容器使用的数据卷

当Server容器删除后,不能再基于Server容器创建新的Client容器,但可以基于已存在的Client容器来创建新的Client容器

最终实现了多个客户端容器共享相同的持久化宿主机的存储方案

评论