Docker存储

前提条件

  1. 拥有docker环境,可参考:Docker的安装
  2. 掌握容器的使用,可参考:Docker容器的使用
  3. 掌握镜像的使用,可参考:Docker镜像的使用

Docker存储的问题

容器是隔离环境,容器内程序的文件、配置、运行时产生的数据都存储在容器内部,思考如下问题:

  • 如果要升级MySQL版本,需要销毁旧容器,那么数据岂不是跟着被销毁了?

  • MySQL、Nginx容器运行后,如果要修改其中的某些配置该怎么办?需要先进入容器内部才能再修改,不方便。

Docker存储概述

默认情况下,在容器内部创建的所有文件都存储在可写的容器层上。这意味着:

  • 当容器不再存在时,数据不会持久化,并且如果其他进程需要这些数据,可能很难从容器中取出数据。

  • 容器的可写层与容器正在运行的主机紧密耦合,无法轻易地将数据移动到其他地方。

  • 写入容器的可写层需要一个存储驱动程序来管理文件系统。该存储驱动程序使用 Linux 内核提供联合文件系统。与直接写入主机文件系统的数据卷相比,这种额外的抽象会降低性能。

Docker解决存储问题的方式:

Docker 为容器在主机上存储文件提供了两种选择,以便在容器停止后文件仍然持久化:卷(volume )和绑定挂载(bind mount)

对于敏感数据不希望被持久化的情况下,Docker 还支持容器在主机的内存中存储文件,这样的文件不会被持久化,**tmpfs 挂载(tmpfs mount)**用于将文件存储在主机的系统内存中。

一种直观理解卷、绑定挂载和 tmpfs 挂载之间差异的简单方法是数据在 Docker 主机上的存储位置。挂载类型及其在 Docker 主机上的存储位置,如下图所示:

  • 绑定挂载可以存储在主机系统的任何位置,甚至可能是重要的系统文件或目录。Docker 主机上的非 Docker 进程或 Docker 容器可以随时修改它们。

  • 卷存储在主机文件系统中由 Docker 管理的一部分(在 Linux 上为 /var/lib/docker/volumes/)。非 Docker 进程不应修改文件系统的这一部分。卷是在 Docker 中持久化数据的最佳方式。

  • tmpfs 挂载仅存储在主机系统的内存中,并且永远不会写入主机系统的文件系统。

绑定挂载和卷都可以使用 -v--volume 标志挂载到容器中,但每种的语法略有不同。对于 tmpfs 挂载,可以使用 --tmpfs 标志。对于容器和服务,建议使用 --mount 标志来进行绑定挂载、卷或 tmpfs 挂载,因为其语法更清晰。

绑定挂载

绑定挂载就是将容器内部的目录/文件与宿主机的任意目录进行映射,主机目录由用户决定,目录不存在将系统自动创建。

案例:实现nginx的html目录绑定挂载

复制代码
#运行容器
[root@localhost ~]# docker run -d --name my-nginx -p 90:80 -v /root/html:/usr/share/nginx/html nginx
​
#挂载目录会自动创建
[root@localhost ~]# ls html
​
#访问页面,403错误,因为挂载目录/root/html下没有任何内容,所以403
[root@localhost ~]# curl localhost:90
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.21.5</center>
</body>
</html>
​
#添加index.html
[root@localhost ~]# echo "hello docker" > html/index.html
​
#再次访问
[root@localhost ~]# curl localhost:90
hello docker
​

从案例中看到,使用-v实现绑定挂载,将主机的/root/html与容器中的/usr/share/nginx/html做映射。当主机/root/html目录没有内容时,容器内部的/usr/share/nginx/html也没有内容。当主机目录内容改变,容器内部目录内容也跟着改变。

什么是数据卷

卷由 Docker 创建和管理。你可以使用 docker volume create 命令显式地创建一个卷,或者在容器或服务创建期间,Docker 可以创建一个卷。

当创建一个卷时,它存储在 Docker 主机上的一个目录中,目录由Docker决定。当你将卷挂载到容器中时,这个目录会被挂载到容器中。这与绑定挂载的工作方式类似,不同之处在于卷由 Docker 管理并且与主机的核心功能隔离。

一个给定的卷可以同时挂载到多个容器中。当没有正在运行的容器使用卷时,该卷仍然可供 Docker 使用,并且不会自动删除。你可以使用 docker volume prune 来删除未使用的卷。

当你挂载一个卷时,它可以是命名的或匿名的。匿名卷会被赋予一个随机名称,在给定的 Docker 主机中保证是唯一的。就像命名卷一样,即使你删除了使用它们的容器,匿名卷也会持久化,除非你在创建容器时使用 --rm 标志,在这种情况下,匿名卷将被销毁。如果你依次创建多个使用匿名卷的容器,每个容器都会创建自己的卷。匿名卷不会在容器之间自动重用或共享。要在两个或多个容器之间共享一个匿名卷,必须使用随机卷 ID 挂载匿名卷。卷还支持使用卷驱动程序,这使你可以将数据存储在远程主机或云提供商等地方。

数据卷命令

数据卷的相关命令有:

命令 说明
docker volume create 创建数据卷
docker volume ls 查看所有数据卷
docker volume rm 删除指定数据卷
docker volume inspect 查看某个数据卷的详情
docker volume prune 清除数据卷

注意:容器的卷挂载要在创建容器时配置,对于创建好的容器,是不能设置数据卷的。而且 创建容器的过程中,数据卷会自动创建 。

案例1:nginx html目录的卷挂载

复制代码
# 1.首先创建容器并指定数据卷,注意通过 -v 参数来指定数据卷
[root@localhost ~]# docker run -d --name nginx -p 80:80 -v html:/usr/share/nginx/html nginx
​
# 2.然后查看数据卷
[root@localhost ~]# docker volume ls
DRIVER    VOLUME NAME
local     html
​
# 3.查看数据卷详情
[root@localhost ~]# docker volume inspect html
[
    {
        "CreatedAt": "2024-09-28T00:07:39+08:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/html/_data",
        "Name": "html",
        "Options": null,
        "Scope": "local"
    }
]
​
# 4.查看/var/lib/docker/volumes/html/_data目录
[root@localhost ~]# ll /var/lib/docker/volumes/html/_data
total 8
-rw-r--r-- 1 root root 497 Dec 28  2021 50x.html
-rw-r--r-- 1 root root 615 Dec 28  2021 index.html
​
# 5.访问页面
[root@localhost ~]# curl localhost
有默认内容
...
<title>Welcome to nginx!</title>
...
​
# 6.进入该目录,并随意修改index.html内容
[root@localhost ~]# cd /var/lib/docker/volumes/html/_data/
​
[root@localhost _data]# ls
50x.html  index.html
​
直接查看index.html
[root@localhost _data]# cat index.html 
...
<title>Welcome to nginx!</title>
...
​
修改index.html内容
[root@localhost _data]# echo "hello world" > index.html 
​
# 7.打开页面,查看效果
[root@localhost _data]# curl localhost
hello world
​
# 8.进入容器内部,查看/usr/share/nginx/html目录内的文件是否变化
[root@localhost _data]# docker exec -it nginx bash
root@6a5ddf625373:/# cat /usr/share/nginx/html/index.html 
hello world
​

看到绑定挂载和卷挂载的区别在于:

  • 绑定挂载:目录是绝对路径或相对路径(以 /./开头),目录挂载的目录不存在会自动创建,不会把容器里面的文件映射到挂载目录,以主机目录为准。

  • 卷挂载:直接以卷名(目录)名字开头,卷和目录不存在也会自动创建,通过 docker volume ls查看卷,通过 docker volume inspect查看目录,把容器里面的文件复制到映射的目录。

案例2:nginx配置的卷挂载

显然启动ngin需要用到nginx.conf内容,所以不能使用绑定挂载(除非主机目录已经准备好相关配置文件),而是使用卷挂载方式。

查看nginx容器内部的配置,如下

复制代码
进入nginx容器内部
[root@localhost _data]# docker exec -it nginx bash
​
查看配置目录
root@6a5ddf625373:/# ls /etc/nginx/
conf.d  fastcgi_params  mime.types  modules  nginx.conf  scgi_params  uwsgi_params
​
查看nginx.conf配置内容
root@6a5ddf625373:/# cat /etc/nginx/nginx.conf 
​
user  nginx;
worker_processes  auto;
​
error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;
​
​
events {
    worker_connections  1024;
}
​
​
http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
​
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
​
    access_log  /var/log/nginx/access.log  main;
​
    sendfile        on;
    #tcp_nopush     on;
​
    keepalive_timeout  65;
​
    #gzip  on;
​
    include /etc/nginx/conf.d/*.conf;
}
​

再启动一个容器进行卷映射

复制代码
# 启动容器,进行html和ngconf卷映射
[root@localhost ~]# docker run -d --name nginx1 -p 81:80 -v html:/usr/share/nginx/html -v ngconf:/etc/nginx nginx
​
# 访问页面,因为此前进行卷映射修改了html内容index.html,所以看到此前html卷映射的内容
[root@localhost ~]# curl localhost:81
hello world
​
# 查看卷
[root@localhost ~]# docker volume ls
DRIVER    VOLUME NAME
local     html
local     ngconf
​
# 查看卷信息
[root@localhost ~]# docker volume inspect ngconf
[
    {
        "CreatedAt": "2024-09-28T00:51:26+08:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/ngconf/_data",
        "Name": "ngconf",
        "Options": null,
        "Scope": "local"
    }
]
​
# 进入ngconf卷,发现存在相关配置文件,说明卷挂载可以将容器内部的配置映射出来。
[root@localhost ~]# cd /var/lib/docker/volumes/ngconf/_data
[root@localhost _data]# ls
conf.d  fastcgi_params  mime.types  modules  nginx.conf  scgi_params  uwsgi_params
​
# 在宿主机修改配置内容,在第一行添加注释内容 # hhh
[root@localhost _data]# vi nginx.conf
# hhh
...
​
# 查看容器内部的nginx.conf,配置内容也改变了
[root@localhost _data]# docker exec -it nginx1 bash
root@88c3de4ddc3e:/# cat /etc/nginx/nginx.conf 
# hhh
...
​

tmpfs

tmpfs 挂载,将数据放在内存中,不会在磁盘上持久化(无论是在 Docker 主机上还是在容器内都不会持久化),它可以在容器的生命周期内被容器使用。用于存储非持久化状态或敏感信息。例如,在内部,Swarm 服务使用 tmpfs 挂载将机密信息挂载到服务的容器中。

复制代码
# 使用tmpfs挂载:将容器内部的/tmp/container_tmp_data目录挂载到主机内存中
[root@localhost ~]# docker run -it --tmpfs /tmp/container_tmp_data busybox sh
/ # echo "This is a test" > /tmp/container_tmp_data/test.txt
/ # cat /tmp/container_tmp_data/test.txt
This is a test
/ # exit
[root@localhost ~]#
​
# 查看运行的容器,说明容器的不再运行,容器的本次生命周期已经结束,tmpfs挂载的内容在内存中应该被释放,挂载的内容不会持久化存储在容器中。
[root@localhost ~]# docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
​
# 查看所有容器
[root@localhost ~]# docker ps -a
CONTAINER ID   IMAGE     COMMAND   CREATED          STATUS                      PORTS     NAMES
8229d76fc9fd   busybox   "sh"      47 seconds ago   Exited (0) 13 seconds ago             relaxed_gould
​
# 再次进入到容器
[root@localhost ~]# docker exec -it 822 sh
​
# 查看容器内部的数据,发现容器内部的container_tmp_data目录是空的,说明数据不持久化到容器内部
/ # ls /tmp/
container_tmp_data
/ # ls /tmp/container_tmp_data/
/ # cat /tmp/container_tmp_data/test.txt
cat: can't open '/tmp/container_tmp_data/test.txt': No such file or directory
/ # exit
[root@localhost ~]# 
​
# 查看宿主机也没有数据,说明数据也不持久化到主机
[root@localhost ~]# ls /tmp/ | grep con
[root@localhost ~]#

完成!enjoy it!

相关推荐
程序猿小三1 小时前
Linux下基于关键词文件搜索
linux·运维·服务器
虚拟指尖2 小时前
Ubuntu编译安装COLMAP【实测编译成功】
linux·运维·ubuntu
椎4953 小时前
苍穹外卖前端nginx错误之一解决
运维·前端·nginx
刘某的Cloud3 小时前
parted磁盘管理
linux·运维·系统·parted
极验3 小时前
iPhone17实体卡槽消失?eSIM 普及下的安全挑战与应对
大数据·运维·安全
爱倒腾的老唐3 小时前
24、Linux 路由管理
linux·运维·网络
yannan201903133 小时前
Docker容器
运维·docker·容器
_清浅3 小时前
计算机网络【第六章-应用层】
运维·服务器·计算机网络
正在努力的小河3 小时前
Linux 自带的 LED 灯驱动实验
linux·运维·服务器
李子圆圆3 小时前
电力专用多功能微气象监测装置在电网安全运维中的核心价值是什么?
运维·安全