# Docker数据共享与持久化

容器运行的 文件系统 处于沙盒环境中,与外界其实是隔离的。

Docker 容器中的文件系统系统的隔离性,虽然有很多优势,但也有很多弊端:

  • 由于 沙盒文件系统 会跟随容器生命周期所创建和移除的,所以容器内的数据是无法 持久化存储
  • 由于 容器隔离,我们很难从容器外部获得或操作容器内部文件中的数据。

Docker 容器 文件系统 是基于 UnionFS。由于 UnionFS 支持 挂载不同类型文件系统统一的目录 结构中,所以我们只需要将宿主操作系统中,文件或目录挂载到容器中,便能够让容器内外共享这个文件。

由于通过这种方式可以互通容器内外的文件,那么文件数据持久化和操作容器内文件的问题就自然而然的解决了。

同时,UnionFS 带来的 读写性能 损失是可以忽略不计的,所以这种实现可以说是相当优秀的。

# 数据共享与持久化

在容器中管理数据,实现 数据共享持久化 主要有两种⽅式:

  • 数据卷(Data Volumes)
  • 挂载主机⽬录 (Bind mounts)

# 数据卷

数据卷 是⼀个可供⼀个或多个容器使⽤的特殊⽬录,它绕过 UFS ,可以提供很多有⽤的特性:

  • 数据卷 可以在容器之间共享和重⽤
  • 对 数据卷 的修改会⽴⻢⽣效
  • 对 数据卷 的更新,不会影响镜像
  • 数据卷 默认会⼀直存在,即使容器被删除
注意:数据卷 的使⽤,类似于 Linux 下对⽬录或⽂件进⾏ mount,镜像中的被指定为挂载点的⽬录中的⽂件会隐藏掉,能显示看的是挂载的 数据卷。

数据卷的本质其实依然是宿主操作系统上的一个目录,只不过这个目录存放在 Docker 内部,接受 Docker 的管理

# 创建⼀个数据卷

$ docker volume create volTest1

# 挂载数据卷

在⽤ docker run 命令的时候,可以使用 -v--volume 选项来定义数据卷的挂载。来将 数据卷 挂载到容器⾥。

使用 -v 选项挂载数据卷时,如果数据卷不存在,Docker 会为我们自动创建和分配宿主操作系统的目录,而如果同名数据卷已经存在,则会直接引用。

# 格式

-v <container-path>-v <volume-name>:<container-path>

如果没有指定 <volume-name>,容器后台会自动创建挂载好。

# 删除数据卷

在删除数据卷之前,我们必须保证数据卷没有被任何容器所使用 ( 也就是之前引用过这个数据卷的容器都已经删除 ),否则 Docker 不会允许我们删除这个数据卷。

我们可以直接通过 docker volume rm 来删除指定的数据卷。

docker volume rm volTest1

数据卷 ⽣命周期独⽴于容器,Docker 不会在容器被删除后 ⾃动删除 数据卷,并且也不存在 垃圾回收 这样的机制来处理没有任何容器 引⽤数据卷

如果需要在删除容器的同时移除数据卷。可以在删除容器的时候使⽤ docker rm -v 这个命令。

没有任何容器 引⽤ 的数据卷可能会占据很多空间,删除那些没有被容器引用的数据卷:

docker volume prune

# 查看数据卷信息

使用 docker volume ls 查看所有的数据卷:

[root@localhost ~]# docker volume ls
DRIVER              VOLUME NAME
local               volTest1

我们可以通过 docker inspect 看到容器中数据卷挂载的详细信息。

[root@localhost ~]# docker inspect volTest1
[
    {
        "CreatedAt": "2020-07-30T09:21:43+08:00",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/volTest1/_data",
        "Name": "volTest1",
        "Options": {},
        "Scope": "local"
    }
]

# 挂载主机⽬录

要将容器中目录挂载到宿主操作系统中,我们可以在容器创建的时候通过传递 -v--volume 选项来指定内外挂载的对应目录或文件。

定义绑定挂载时必须使用 绝对路径

# 格式

-v 宿主机目录:容器目录

# 实例

启动一个 Nginx 容器,将资源文件,日志文件挂载到宿主机的 /home/nginx

docker run -itd -p 80:80 --name jenkins-test \
  -v /home/nginx/html:/usr/share/nginx/html \
  -v /home/nginx/logs:/var/log/nginx \
  --restart always \
  nginx

查看容器数据挂载的详细信息:

docker inspect jenkins-test
"Mounts": [
    {
        "Type": "bind",
        "Source": "/home/nginx/html",
        "Destination": "/usr/share/nginx/html",
        "Mode": "",
        "RW": true,
        "Propagation": "rprivate"
    },
    {
        "Type": "bind",
        "Source": "/home/nginx/logs",
        "Destination": "/var/log/nginx",
        "Mode": "",
        "RW": true,
        "Propagation": "rprivate"
    }
],

在上面的挂载信息中,我们可以看到一个 RW 字段,这表示挂载目录或文件的读写性 ( Read and Write)。

实际操作中,Docker 还支持以只读的方式挂载,通过只读方式挂载的目录和文件,只能被容器中的程序读取,但不接受容器中程序修改它们的请求。

在挂载选项 -v 后再接上 :ro 就可以只读挂载了。

# 临时性挂载存储

Tmpfs Mount 是一种特殊的挂载方式,它主要利用 内存 来存储数据。由于内存不是持久性存储设备,所以其带给 Tmpfs Mount 的特征就是 临时性挂载

在⽤ docker run 命令的时候,可以使用 --tmpfs 选项来定义 临时性挂载

docker run -d --name webapp --tmpfs /webapp/cache webapp:latest

# 使用场景

  • 不需要进行持久保存的敏感数据,可以借助 内存的非持久性程序隔离性 进行一定的安全保障。
  • 对读写速度要求较高,并发要求高,但不需要持久保存的数据,可以借助内存的 高读写 速度减少请求的时间。

# 参考

更新时间: 7/30/2020, 8:25:50 PM