Docker数据管理:持久化存储最佳实践

引言:为何持久化存储是Docker应用的生命线?

在容器化应用中,数据持久化是保障业务连续性的核心环节。Docker容器默认的临时存储特性虽适合无状态应用,但对于数据库、配置中心、日志服务等有状态服务,必须通过持久化存储确保数据在容器重启、迁移或崩溃后不丢失。结合生产环境案例与代码示例,助您构建安全可靠的数据管理体系。

Docker 数据管理

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

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

如果将正在运行中的容器修改生成了新的数据,那么新产生的数据将会被复制到读写层,进行持久化保存,这个读写层也就是容器的工作目录,也为写时复制(COW) 机制。COW机制节约空间,但会导致性低下,虽然关闭重启容器,数据不受影响,但会随着容器的删除,其对应的可写层也会随之而删除,即数据也会丢失。如果容器需要持久保存数据,并不影响性能可以用数据卷技术实现。

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

容器的数据管理介绍

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

Docker容器的分层

容器的数据分层目录

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

查看指定容器数据分层

bash 复制代码
[root@ubuntu2404 ~]#docker inspect nginx
   "GraphDriver": {
            "Data": {
                "ID": "660581654f6ffea0f212d0a21a36cbd3922671a774ca70c0a7297db0c0a9b52c",
                "LowerDir": "/var/lib/docker/overlay2/f48612e5f634574856956b172dba0081a1260ada85145f20d41107bb506a9fce-init/diff:/var/lib/docker/overlay2/607e85ed7f725d1b01ed6009ce54f1e65e61f08998ec9242476709a770e9aa96/diff:/var/lib/docker/overlay2/e222bfa7d82bb3a7e45c75d01b3356aadf699c9355797e1daf8294fad83bdea3/diff:/var/lib/docker/overlay2/a691a846c3d41fcdcdb4a60f696f47c9633a7d35ea48023e4051e664e3d0870c/diff:/var/lib/docker/overlay2/1ae4dbe4c4ab9425102b23595002d317d2b272e8b1772d157e58d9d2b8c03a8e/diff:/var/lib/docker/overlay2/15df0f71eb95878219824a12d8d71983832263fda8e168178d9f4d1e236fed5f/diff:/var/lib/docker/overlay2/2a69175cf068a1af9d6036137c402558ed85c632198280f473e992ae3c212c49/diff:/var/lib/docker/overlay2/2b7f5cb956e775221f563178bcde0ca160368f476699c51745265d0a6995d1ef/diff",
                "MergedDir": "/var/lib/docker/overlay2/f48612e5f634574856956b172dba0081a1260ada85145f20d41107bb506a9fce/merged",
                "UpperDir": "/var/lib/docker/overlay2/f48612e5f634574856956b172dba0081a1260ada85145f20d41107bb506a9fce/diff",
                "WorkDir": "/var/lib/docker/overlay2/f48612e5f634574856956b172dba0081a1260ada85145f20d41107bb506a9fce/work"
            },
            "Name": "overlay2"
[root@ubuntu2404 ~]#ll -i /var/lib/docker/overlay2/f48612e5f634574856956b172dba0081a1260ada85145f20d41107bb506a9fce
total 28
5253677 drwx--x---  5 root root 4096 Aug  7 21:27 ./
5246322 drwx--x--- 13 root root 4096 Aug  7 21:27 ../
5253690 drwxr-xr-x  5 root root 4096 Aug  7 21:27 diff/
5253692 -rw-r--r--  1 root root   26 Aug  7 21:27 link
5253695 -rw-r--r--  1 root root  231 Aug  7 21:27 lower
5253690 drwxr-xr-x  1 root root 4096 Aug  7 21:27 merged/
5253693 drwx------  3 root root 4096 Aug  7 21:27 work/
[root@ubuntu2404 ~]#tree /var/lib/docker/overlay2/f48612e5f634574856956b172dba0081a1260ada85145f20d41107bb506a9fce|head -n 50
/var/lib/docker/overlay2/f48612e5f634574856956b172dba0081a1260ada85145f20d41107bb506a9fce
├── diff
│   ├── etc
│   │   └── nginx
│   │       └── conf.d
│   │           └── default.conf
│   ├── run
│   │   └── nginx.pid
│   └── var
│       └── cache
│           └── nginx
│               ├── client_temp
│               ├── fastcgi_temp
│               ├── proxy_temp
│               ├── scgi_temp
│               └── uwsgi_temp
├── link
├── lower
├── merged
│   ├── bin -> usr/bin
│   ├── boot
│   ├── dev
│   │   ├── console
│   │   ├── pts
│   │   └── shm
│   ├── docker-entrypoint.d
│   │   ├── 10-listen-on-ipv6-by-default.sh
│   │   ├── 15-local-resolvers.envsh
│   │   ├── 20-envsubst-on-templates.sh
│   │   └── 30-tune-worker-processes.sh
│   ├── docker-entrypoint.sh
│   ├── etc
│   │   ├── adduser.conf
│   │   ├── alternatives
│   │   │   ├── awk -> /usr/bin/mawk
│   │   │   ├── awk.1.gz -> /usr/share/man/man1/mawk.1.gz
......

[root@ubuntu2404 ~]#docker run -it alpine:3.3 sh
/ # dd if=/dev/zero of=/root/test.img bs=1M count=10
10+0 records in
10+0 records out
/ #

[root@ubuntu2404 ~]#find /var/lib/docker/overlay2/c3085a747c41b3254c0dd7f18d55af94997e995bdeb3e2f1287b2593d787bf14/ -name test.img -ls
  5253773  10240 -rw-r--r--   1 root     root     10485760 Aug  7 21:37 /var/lib/docker/overlay2/c3085a747c41b3254c0dd7f18d55af94997e995bdeb3e2f1287b2593d787bf14/merged/root/test.img
  5253773  10240 -rw-r--r--   1 root     root     10485760 Aug  7 21:37 /var/lib/docker/overlay2/c3085a747c41b3254c0dd7f18d55af94997e995bdeb3e2f1287b2593d787bf14/diff/root/test.img
[root@ubuntu2404 ~]#mount
overlay on /var/lib/docker/overlay2/f48612e5f634574856956b172dba0081a1260ada85145f20d41107bb506a9fce/merged type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/USTJFL335JN3Q4BAN53KOGJL6Q:/var/lib/docker/overlay2/l/6UF7GTJ5W5UEWP3E7KQ5ELA4EN:/var/lib/docker/overlay2/l/Z43FX27QMTNF5OXX3J4AG2OKAO:/var/lib/docker/overlay2/l/5TPFU53RY5EA6FVXJPEHXESDJV:/var/lib/docker/overlay2/l/ZPIGD7KO2TXQ3BNK4PISSR4YKB:/var/lib/docker/overlay2/l/XFMKVWYYU3PEE5J7WT4OL54LZQ:/var/lib/docker/overlay2/l/WRAXF4ASIXY4TVGP2ZRSFI3SE2:/var/lib/docker/overlay2/l/N2VYVGZG546MSVAKB4DNHM2CNY,upperdir=/var/lib/docker/overlay2/f48612e5f634574856956b172dba0081a1260ada85145f20d41107bb506a9fce/diff,workdir=/var/lib/docker/overlay2/f48612e5f634574856956b172dba0081a1260ada85145f20d41107bb506a9fce/work,nouserxattr)
nsfs on /run/docker/netns/4bdd1076075c type nsfs (rw)
overlay on /var/lib/docker/overlay2/c3085a747c41b3254c0dd7f18d55af94997e995bdeb3e2f1287b2593d787bf14/merged type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/5J33RFRVKFDA43PRA2HZOHRU6K:/var/lib/docker/overlay2/l/D3HUNLXCPOTQYQXGTXL6HDSFTO,upperdir=/var/lib/docker/overlay2/c3085a747c41b3254c0dd7f18d55af94997e995bdeb3e2f1287b2593d787bf14/diff,workdir=/var/lib/docker/overlay2/c3085a747c41b3254c0dd7f18d55af94997e995bdeb3e2f1287b2593d787bf14/work,nouserxattr)
nsfs on /run/docker/netns/88a706c41efc type nsfs (rw)

[root@ubuntu2404 ~]#tree /var/lib/docker/overlay2/c3085a747c41b3254c0dd7f18d55af94997e995bdeb3e2f1287b2593d787bf14/diff/root/
/var/lib/docker/overlay2/c3085a747c41b3254c0dd7f18d55af94997e995bdeb3e2f1287b2593d787bf14/diff/root/
└── test.img

1 directory, 1 file

[root@ubuntu2404 ~]#ls /var/lib/docker/overlay2/c3085a747c41b3254c0dd7f18d55af94997e995bdeb3e2f1287b2593d787bf14/merged/
bin  dev  etc  home  lib  linuxrc  media  mnt  proc  root  run  sbin  sys  tmp  usr  var

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

welcome to docker
/ #

[root@ubuntu2404 ~]#tree /var/lib/docker/overlay2/c3085a747c41b3254c0dd7f18d55af94997e995bdeb3e2f1287b2593d787bf14/diff/
/var/lib/docker/overlay2/c3085a747c41b3254c0dd7f18d55af94997e995bdeb3e2f1287b2593d787bf14/diff/
├── etc
│   └── issue
└── root
    └── test.img

3 directories, 2 files

#删除容器后,所有容器数据目录都随之而删除
[root@ubuntu2404 ~]#docker rm nervous_brahmagupta 
nervous_brahmagupta
[root@ubuntu2404 ~]#ls /var/lib/docker/overlay2/c3085a747c41b3254c0dd7f18d55af94997e995bdeb3e2f1287b2593d787bf14
ls: cannot access '/var/lib/docker/overlay2/c3085a747c41b3254c0dd7f18d55af94997e995bdeb3e2f1287b2593d787bf14': No such file or directory

哪些数据需要持久化

有状态的协议

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

左侧是无状态的http请求服务,右侧为有状态。

下层为不需要存储的服务,上层为需要存储的部分服务。

容器数据持久保存方式

如果要将写入到容器的数据永久保存,则需要将容器中的数据保存到宿主机的指定目录 Docker的数据类型分为两种:

数据卷(Data Volume):直接将宿主机目录挂载至容器的指定的目录 ,推荐使用此种方式,此方式较常用。

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

数据卷(data volume)

数据卷特点和使用

数据卷实际上就是宿主机上的目录或者是文件,可以被直接mount到容器当中使用。实际生成环境中,需要针对不同类型的服务、不同类型的数据存储要求做相应的规划,最终保证服务的可扩展性、稳定性以及数据的安全性。

数据卷使用场景

数据库

日志输出

静态web页面

应用配置文件

多容器间目录或文件共享

数据卷的特点

数据卷是目录或者文件,并且可以在多个容器之间共同使用,实现容器之间共享和重用,对数据卷更改数据在所有容器里面会立即更新。

数据卷的数据可以持久保存,即使删除使用使用该容器卷的容器也不影响。 在容器里面的写入数据不会影响到镜像本身,即数据卷的变化不会影响镜像的更新。

依赖于宿主机目录,宿主机出问题,上面容器会受影响,当宿主机较多时,不方便统一管理。

匿名和命名数据卷在容器启动时初始化,如果容器使用的镜像在挂载点包含了数据,会拷贝到新初始化的数据卷中。

数据卷分类

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

指定宿主机目录或文件:指定宿主机的具体路径和容器路径的挂载关系,此方式不会创建数据卷。

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

命名卷:指定数据卷的名称和容器路径的挂载关系,此方式会创建命名数据卷。

数据卷使用方法

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

bash 复制代码
-v, --volume list                      Bind mount a volume

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

方式1

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

方式2

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

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

方式3

bash 复制代码
#命名卷将固定的存放在/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 选项可以删除容器时,同时删除相关联的匿名卷

bash 复制代码
-v, --volumes  Remove the volumes associated with the container

管理数据卷命令

bash 复制代码
Usage:  docker volume COMMAND

Manage volumes

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

Run 'docker volume COMMAND --help' for more information on a command.

查看数据卷的挂载关系

bash 复制代码
docker inspect --format="{{.Mounts}}"  <容器ID>

删除所有数据卷

bash 复制代码
[root@ubuntu2404 ~]# docker volume rm `docker volume ls -q`

创建命令卷并删除

bash 复制代码
[root@ubuntu2404 ~]#docker volume create mysql
[root@ubuntu2404 ~]#docker volume ls
DRIVER    VOLUME NAME
local     56f3dc469c3efac03cca72571968d38319cf5e8442147439dfa51cf13d06213c
local     69934bfa590a55f64566894e5839e5e1c3e20a7d13309ea4ce62be5a22eb11ab
local     a1a11dcd7957d57c5811b0831d5d8738940076f3fcb206b3ce64dabc466662ee
local     f402cae908ad3478caff992e80f15b2ace77d675a355d2694fd2379c54a62f5a
local     mysql
[root@ubuntu2404 ~]#tree /var/lib/docker/volumes/
/var/lib/docker/volumes/
├── 56f3dc469c3efac03cca72571968d38319cf5e8442147439dfa51cf13d06213c
│   └── _data
│       └── f1
├── 69934bfa590a55f64566894e5839e5e1c3e20a7d13309ea4ce62be5a22eb11ab
│   └── _data
├── a1a11dcd7957d57c5811b0831d5d8738940076f3fcb206b3ce64dabc466662ee
│   └── _data
├── backingFsBlockDev
├── f402cae908ad3478caff992e80f15b2ace77d675a355d2694fd2379c54a62f5a
│   └── _data
│       ├── auto.cnf
│       ├── binlog.000001
│       ├── binlog.000002
│       ├── binlog.index
│       ├── ca-key.pem
│       ├── ca.pem
│       ├── client-cert.pem
│       ├── client-key.pem
│       ├── #ib_16384_0.dblwr
│       ├── #ib_16384_1.dblwr
[root@ubuntu2404 ~]#docker volume rm mysql 
mysql
[root@ubuntu2404 ~]#docker volume ls
DRIVER    VOLUME NAME
local     56f3dc469c3efac03cca72571968d38319cf5e8442147439dfa51cf13d06213c
local     69934bfa590a55f64566894e5839e5e1c3e20a7d13309ea4ce62be5a22eb11ab
local     a1a11dcd7957d57c5811b0831d5d8738940076f3fcb206b3ce64dabc466662ee
local     f402cae908ad3478caff992e80f15b2ace77d675a355d2694fd2379c54a62f5a
[root@ubuntu2404 ~]#docker volume rm `docker volume ls -q`

删除不再使用的数据卷

bash 复制代码
[root@ubuntu2404 1.26]#docker volume ls
DRIVER    VOLUME NAME
local     749a8bb98586cb3fa09660135f32e08c5d8403d34e8fc845722533ac2c1ebbe7
[root@ubuntu2404 1.26]#docker volume prune -f
Deleted Volumes:
749a8bb98586cb3fa09660135f32e08c5d8403d34e8fc845722533ac2c1ebbe7

Total reclaimed space: 0B
[root@ubuntu2404 1.26]#docker volume ls
DRIVER    VOLUME NAME

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

bash 复制代码
命名卷就是有名字的卷,使用 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,不过一般不需要、也不应该访问这个位置

实战案例:目录数据卷

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

bash 复制代码
[root@ubuntu2404 ~]#mkdir /data/testdir
[root@ubuntu2404 ~]#echo "web ubuntu-nginx">/data/testdir/index.html

查看容器相关目录路径

bash 复制代码
[root@ubuntu2404 ~]#docker images 
REPOSITORY      TAG             IMAGE ID       CREATED        SIZE
nginx-ubuntu    v1.1            99951e989855   4 hours ago    525MB
nginx-alpin     v1.2            153846168892   7 hours ago    258MB
[root@ubuntu2404 ~]#docker run -it --rm nginx-alpin:v1.2 sh
/usr/local/src/nginx-1.26.3 # cat /data/nginx/html/index.html 
hello nginx-alpine
/usr/local/src/nginx-1.26.3 # exit

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

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

bash 复制代码
[root@ubuntu2404 ~]#docker run -d -v /data/testdir:/data/nginx/html/ -p 8080:80 nginx-alpin:v1.2
c6d2b90ea32488496ec76309b61f46311fa934cf8df7e7416a620d316ac9788e
[root@ubuntu2404 ~]#docker run -d -v /data/testdir:/data/nginx/html/ -p 8181:80 nginx-alpin:v1.2
47b0db70cf672391e5915662fb83f17d359be5c4bf13649244e933f2a931751b
[root@ubuntu2404 ~]#docker ps
CONTAINER ID   IMAGE              COMMAND   CREATED             STATUS             PORTS                                              NAMES
47b0db70cf67   nginx-alpin:v1.2   "nginx"   4 seconds ago       Up 3 seconds       443/tcp, 0.0.0.0:8181->80/tcp, [::]:8181->80/tcp   stoic_tu
c6d2b90ea324   nginx-alpin:v1.2   "nginx"   About an hour ago   Up About an hour   443/tcp, 0.0.0.0:8080->80/tcp, [::]:8080->80/tcp   optimistic_grothendieck
[root@ubuntu2404 ~]#curl 127.0.0.1:8181
web ubuntu-nginx
[root@ubuntu2404 ~]#curl 127.0.0.1:8080
web ubuntu-nginx

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

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

bash 复制代码
[root@ubuntu2404 ~]#docker exec -it 47b0db70cf67 sh
/usr/local/src/nginx-1.26.3 # df
Filesystem           1K-blocks      Used Available Use% Mounted on
overlay              101590008  12667924  83715456  13% /
tmpfs                    65536         0     65536   0% /dev
shm                      65536         0     65536   0% /dev/shm
/dev/mapper/ubuntu--vg-ubuntu--lv
                     101590008  12667924  83715456  13% /etc/resolv.conf
/dev/mapper/ubuntu--vg-ubuntu--lv
                     101590008  12667924  83715456  13% /etc/hostname
/dev/mapper/ubuntu--vg-ubuntu--lv
                     101590008  12667924  83715456  13% /etc/hosts
/dev/mapper/ubuntu--vg-ubuntu--lv
                     101590008  12667924  83715456  13% /data/nginx/html
tmpfs                  1980504         0   1980504   0% /proc/asound
tmpfs                  1980504         0   1980504   0% /proc/acpi
tmpfs                    65536         0     65536   0% /proc/kcore
tmpfs                    65536         0     65536   0% /proc/keys
tmpfs                    65536         0     65536   0% /proc/latency_stats
tmpfs                    65536         0     65536   0% /proc/timer_list
tmpfs                  1980504         0   1980504   0% /proc/scsi
tmpfs                  1980504         0   1980504   0% /sys/firmware
/usr/local/src/nginx-1.26.3 # cat /data/nginx/html/index.html 
web02 nginx-ubuntu
/usr/local/src/nginx-1.26.3 # echo web03 ubuntu-nginx>/data/nginx/html/index.html 
/usr/local/src/nginx-1.26.3 # exit
#进入另一个容器看到数据变化
[root@ubuntu2404 ~]#docker exec -it c6d2b90ea324 sh
/usr/local/src/nginx-1.26.3 # cat /data/nginx/html/index.html 
web03 ubuntu-nginx
/usr/local/src/nginx-1.26.3 # exit

[root@ubuntu2404 ~]#curl 127.0.0.1:8080
web03 ubuntu-nginx
[root@ubuntu2404 ~]#curl 127.0.0.1:8181
web03 ubuntu-nginx

在宿主机修改数据

bash 复制代码
[root@ubuntu2404 ~]#echo web05 nginx-ubuntu-2404 >/data/testdir/index.html 
[root@ubuntu2404 ~]#cat /data/testdir/index.html 
web05 nginx-ubuntu-2404
[root@ubuntu2404 ~]#curl 127.0.0.1:8181
web05 nginx-ubuntu-2404
[root@ubuntu2404 ~]#curl 127.0.0.1:8080
web05 nginx-ubuntu-2404               
[root@ubuntu2404 ~]#docker exec -it optimistic_grothendieck sh
/usr/local/src/nginx-1.26.3 # cat /data/nginx/html/index.html 
web05 nginx-ubuntu-2404
/usr/local/src/nginx-1.26.3 # exit
[root@ubuntu2404 ~]#docker exec -it stoic_tu sh
/usr/local/src/nginx-1.26.3 # cat /data/nginx/html/index.html 
web05 nginx-ubuntu-2404
/usr/local/src/nginx-1.26.3 # exit

只读方法挂载数据卷

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

bash 复制代码
[root@ubuntu2404 ~]#docker run -d -p 8080:80 -v /data/testdir:/data/nginx/html/:ro --name nginx nginx-alpin:v1.2
f5a07691b0459cd73d686d9fec0bff4754e91f6c880a126a9bf8a00013b91e1d
[root@ubuntu2404 ~]#docker exec -it nginx sh
/usr/local/src/nginx-1.26.3 # cat /data/nginx/html/index.html 
web05 nginx-ubuntu-2404
/usr/local/src/nginx-1.26.3 # echo hello>/data/nginx/html/index.html 
sh: can't create /data/nginx/html/index.html: Read-only file system
/usr/local/src/nginx-1.26.3 # exit

[root@ubuntu2404 ~]#docker ps -a
CONTAINER ID   IMAGE              COMMAND   CREATED              STATUS              PORTS                                              NAMES
f5a07691b045   nginx-alpin:v1.2   "nginx"   About a minute ago   Up About a minute   443/tcp, 0.0.0.0:8080->80/tcp, [::]:8080->80/tcp   nginx
[root@ubuntu2404 ~]#curl 127.0.0.1:8080
web05 nginx-ubuntu-2404

删除容器

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

bash 复制代码
root@ubuntu2404 ~]#docker ps -a
CONTAINER ID   IMAGE              COMMAND   CREATED          STATUS          PORTS                                              NAMES
f5a07691b045   nginx-alpin:v1.2   "nginx"   45 minutes ago   Up 45 minutes   443/tcp, 0.0.0.0:8080->80/tcp, [::]:8080->80/tcp   nginx
[root@ubuntu2404 ~]#docker rm -f nginx 
nginx
[root@ubuntu2404 ~]#cat /data/testdir/index.html 
web05 nginx-ubuntu-2404
[root@ubuntu2404 ~]#docker run -d -p 80:80 -v /data/testdir:/data/nginx/html/ nginx-alpin:v1.2
00d907069643d1015df30e597d56c04a85d24502d1e06f7e66bd5a601468d36e
[root@ubuntu2404 ~]#curl 127.0.0.1:80
web05 nginx-ubuntu-2404

通过遵循上述最佳实践,您可构建高可用、易维护的Docker数据管理体系。数据持久化不仅是技术实现,更是业务连续性的保障。希望本文能为您提供实战指导,助您在容器化道路上走得更稳、更远。

相关推荐
强子感冒了9 小时前
Java 学习笔记:File类核心API详解与使用指南
java·笔记·学习
spencer_tseng9 小时前
eclipse ALT+SHIFT+A
java·ide·eclipse
vyuvyucd9 小时前
C++排序算法全解析
java·数据结构·算法
BD_Marathon9 小时前
SpringBoot程序快速启动
java·spring boot·后端
stillaliveQEJ9 小时前
【JavaEE】Spring IoC(二)
java·开发语言·spring
寻星探路9 小时前
【Python 全栈测开之路】Python 基础语法精讲(一):常量、变量与运算符
java·开发语言·c++·python·http·ai·c#
行百里er9 小时前
代码跑得慢?让Spring的StopWatch告诉你真相!
java·后端·github
又是忙碌的一天9 小时前
SpringMVC响应
java·服务器·数据库
万物皆字节9 小时前
Spring Cloud Gateway 启动流程源码分析
java·开发语言·spring boot