Docker 探索-管理和存储数据

Docker 里,容器运行的文件系统处于沙盒环境中,与外界其实是隔离的,那么我们又要如何在 Docker 中合理的通过文件与外界进行数据交换呢?
我也不知道。
数据管理实现方式
Docker 容器中的文件系统的弊端:
  • 沙盒文件系统是跟随容器生命周期所创建和移除的,数据无法直接被持久化存储。即进程结束就没了。
  • 由于容器隔离,我们很难从容器外部获得或操作容器内部文件中的数据。
Docker 是如何解决这些问题的呢?这要归功于Docker的容器文件系统基于UnionFS。
由于UnionFS 支持挂载不同类型的文件系统到统一的目录结构中,所以我们只需要将宿主操作系统中,文件系统里的文件或者目录挂载到容器中,便能够让容器内外共享这个文件。
由于通过这种方式可以互通容器内外的文件,那么文件数据持久化和操作容器内文件的问题就自然而然的解决了。
一 挂载方式
基于底层存储实现,Docker 提供了三种适用于不同场景的文件系统挂载方式:Bind MountVolume 和 Tmpfs Mount

Bind Mount 能够直接将宿主操作系统中的目录和文件挂载到容器内的文件系统中,通过指定容器外的路径和容器内的路径,就可以形成挂载映射关系,在容器内外对文件的读写,都是相互可见的。
Tmpfs Mount 支持挂载系统内存中的一部分到容器的文件系统里,不过由于内存和容器的特征,它的存储并不是持久的,其中的内容会随着容器的停止而消失。
Volume 也是从宿主操作系统中挂载目录到容器内,只不过这个挂载的目录由 Docker 进行管理,我们只需要指定容器内的目录,不需要关心具体挂载到了宿主操作系统中的哪里
挂载文件到容器
要将宿主操作系统中的目录挂载到容器之后,我们可以在容器创建的时候通过传递 -v 或 --volume选项来指定内外挂载的对应目录或文件。
root@ubuntu:/home/jack# sudo docker run -d --name nginx_v -v /webapp/html:/usr/share/nginx/html nginx
fde11f1f3e48eea9bb9e9bd256f56ed875c24605b00f01bfe9bd294d3dd5518d
-v <host-path>:<container-path>
host-path: 代表宿主操作系统中的目录和容器中的目录。使用的是绝对路径,不能使用相对路径
container-path: 容器中的目录。
宿主中的文件会出现在容器中的目录里面
docker exec nginx_v ls /usr/share/nginx/html 会有一个index.html。前提是 你的/webapp/html 里面本来就有index.html
docker inspect 的结果里面,我们可以看到有关容器数据挂载相关的信息。
docker inspect nginx_v
"Mounts": [
{
"Type": "bind",
"Source": "/webapp/html",
"Destination": "/usr/share/nginx/html",
"Mode": "",
"RW": true, 表示挂载目录或文件的读写性(Read and Write)
"Propagation": "rprivate"
}
],
Docker 还支持以只读的方式挂载,通过只读方式挂载的目录和文件,只能被容器中的程序读取,但不接受容器中程序修改它们的请求。在挂载选项 -v 后再接上 :ro 就可以只读挂载了。
sudo docker run -d --name nginx_ro -v /webapp/html:/usr/share/nginx/html:ro nginx
303127ee41886e27180bd63767540dd68688fc8baa8219f8cd2a59e2b1953a74
执行 docker inspect nginx_ro
"Mounts": [
{
"Type": "bind", #类型是绑定
"Source": "/webapp/html",
"Destination": "/usr/share/nginx/html",
"Mode": "ro",# readonly 只读
"RW": false,
"Propagation": "rprivate"
}
由于宿主操作系统文件挂载在权限允许的情况下能够挂载任何目录或文件,这给系统的安全性造成了一定的隐患,所以我们在使用 Bind Mount 的时候,一定要特别注意挂载的外部目录选择。当然,在保证安全性的前提下,有几种常见场景非常适合使用这种挂载方式。
当我们需要从宿主操作系统共享配置的时候。对于一些配置项,我们可以直接从容器外部挂载到容器中,这利于保证容器中的配置为我们所确认的值,也方便我们对配置进行监控。例如,遇到容器中时区不正确的时候,我们可以直接将操作系统的时区配置,也就是 /etc/timezone 这个文件挂载并覆盖容器中的时区配置。
当我们需要借助 Docker 进行开发的时候。虽然在 Docker 中,推崇直接将代码和配置打包进镜像,以便快速部署和快速重建。但这在开发过程中显然非常不方便,因为每次构建镜像需要耗费一定的时间,这些时间积少成多,就是对开发工作效率的严重浪费了。如果我们直接把代码挂载进入容器,那么我们每次对代码的修改都可以直接在容器外部进行。
挂载临时文件目录
Tmpfs Mount 是一种特殊的挂载方式,它主要利用内存来存储数据。由于内存不是持久性存储设备,所以其带给 Tmpfs Mount 的特征就是临时性挂载。
与挂载宿主操作系统目录或文件不同,挂载临时文件目录要通过 --tmpfs 这个选项来完成。由于内存的具体位置不需要我们来指定,这个选项里我们只需要传递挂载到容器内的目录即可。
$ sudo docker run -d --name webapp --tmpfs /webapp/cache webapp:latest
容器已挂载的临时文件目录我们也可以通过 docker inspect 命令查看。
$ sudo docker inspect webapp [ { ## ...... "Tmpfs": { "/webapp/cache": "" }, ## ...... } ]
挂载临时文件首先要注意它不是持久存储这一特性,在此基础上,它有几种常见的适应场景。
  • 应用中使用到,但不需要进行持久保存的敏感数据,可以借助内存的非持久性和程序隔离性进行一定的安全保障。
  • 读写速度要求较高,数据变化量大,但不需要持久保存的数据,可以借助内存的高读写速度减少操作的时间。
数据卷
Docker 还创造了数据卷 ( Volume ) 这个概念。数据卷的本质其实依然是宿主操作系统上的一个目录,只不过这个目录存放在 Docker 内部,接受 Docker 的管理。
在使用数据卷进行挂载时,我们不需要知道数据具体存储在了宿主操作系统的何处,只需要给定容器中的哪个目录会被挂载即可
sudo docker run -d --name nginx_volume -v /usr/share/nginx/html nginx
"Mounts": [
{
"Type": "volume", # 注意这里是 数据卷
"Name": "a067a477051b4f40b5f56c0cf1a6a423f40fa472c72b97cc6da5d22d0c207ff1",
"Source": "/var/lib/docker/volumes/a067a477051b4f40b5f56c0cf1a6a423f40fa472c72b97cc6da5d22d0c207ff1/_data",
"Destination": "/usr/share/nginx/html",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
其中 Source 是 Docker 为我们分配用于挂载的宿主机目录,其位于 Docker 的资源区域 ( 这里是默认的 /var/lib/docker ) 内。当然,我们并不需要关心这个目录,一切对它的管理都已经在 Docker 内实现了。
为了方便识别数据卷,我们可以像命名容器一样为数据卷命名,这里的 Name 就是数据卷的命名。在我们未给出数据卷命名的时候,Docker 会采用数据卷的 ID 命名数据卷。我们也可以通过 -v <name>:<container-path> 这种形式来命名数据卷。
$ sudo docker run -d --name webapp -v appdata:/webapp/storage webapp:latest
由于 -v 选项既承载了 Bind Mount 的定义,又参与了 Volume 的定义,所以其传参方式需要特别留意。前面提到了,-v 在定义绑定挂载时必须使用绝对路径,其目的主要是为了避免与数据卷挂载中命名这种形式的冲突。
虽然与绑定挂载的原理差别不大,但数据卷在许多实际场景下你会发现它很有用。
  • 当希望将数据在多个容器间共享时,利用数据卷可以在保证数据持久性和完整性的前提下,完成更多自动化操作。
  • 当我们希望对容器中挂载的内容进行管理时,可以直接利用数据卷自身的管理方法实现。
  • 当使用远程服务器或云服务作为存储介质的时候,数据卷能够隐藏更多的细节,让整个过程变得更加简单。
共用数据卷
数据卷的另一大作用是实现容器间的目录共享,也就是通过挂载相同的数据卷,让容器之间能够同时看到并操作数据卷中的内容。
由于数据卷的命名在 Docker 中是唯一的,所以我们很容易通过数据卷的名称确定数据卷。
$ sudo docker run -d --name webapp -v html:/home/jack webapp:latest $ sudo docker run -d --name nginx -v html:/usr/share/nginx/html:ro nginx:1.12
用insepect去看上面两个容器的话 会发现 source 是一样的 都是 /var/lib/docker/volumes/html/_data
"Mounts": [
{
"Type": "volume",
"Name": "html",
"Source": "/var/lib/docker/volumes/html/_data",
"Destination": "/home/jack",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
不依赖容器创建数据卷
sudo docker volume create appdata
列出当前已创建的数据卷
sudo docker volume ls
虽然数据卷的目的是用来持久化存储数据的,但有时候我们也难免有删除它们以释放空间的需求
docker volume rm appdata
在删除数据卷之前,我们必须保证数据卷没有被任何容器所使用 ( 也就是之前引用过这个数据卷的容器都已经删除 ),否则 Docker 不会允许我们删除这个数据卷。
docker rm 删除容器的命令中,我们可以通过增加-v 选项来删除容器关联的数据卷。
sudo docker rm -v webapp
如果我们没有随容器删除这些数据卷,Docker 在创建新的容器时也不会启用它们,即使它们与新创建容器所定义的数据卷有完全一致的特征。也就是说,此时它们已经变成了孤魂野鬼,纯粹的占用着硬盘空间而又不受管理。
此时我们可以通过 docker volume rm 来删除它们,但前提时你能在一堆乱码般的数据卷 ID 中找出哪个是没有被容器引用的数据卷。
为此,Docker 向我们提供了 docker volume prune 这个命令,它可以删除那些没有被容器引用的数据卷。
sudo docker volume prune -f
数据卷容器
所谓数据卷容器,就是一个没有具体指定的应用,甚至不需要运行的容器,我们使用它的目的,是为了定义一个或多个数据卷并持有它们的引用。

创建命令:
docker create --name appdata -v /webapp/storage ubuntu
在使用数据卷容器时,我们不建议再定义数据卷的名称,因为我们可以通过对数据卷容器的引用来完成数据卷的引用。而不设置数据卷的名称,也避免了在同一 Docker 中数据卷重名的尴尬
之前我们提到,Docker 的 Network 是容器间的网络桥梁,如果做类比,数据卷容器就可以算是容器间的文件系统桥梁。我们可以像加入网络一样引用数据卷容器,只需要在创建新容器时使用专门的 --volumes-from 选项即可。
sudo docker run -d --name webapp --volumes-from appdata webapp:latest
跟webapp的一样
"Mounts": [
{
"Type": "volume",
"Name": "html",
"Source": "/var/lib/docker/volumes/html/_data",
"Destination": "/home/jack",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
引用数据卷容器时,不需要再定义数据卷挂载到容器中的位置,Docker 会以数据卷容器中的挂载定义将数据卷挂载到引用的容器中。
备份和迁移数据卷
创建一个数据卷
docker create --name appdata -v /home/jack nginx
备份
docker run --rm --volumes-from appdata -v /home/jack/backup:/home/jack/backup nginx tar -cvPf /home/jack/backup/back2.tar /home/jack/backup
-v 后面宿主机和虚拟机地址 都写的一样
宿主机地址 /home/jack/backup/back2.tar
宿主机地址 /home/jack/backup(保存back2.tar的地方)
解压
docker run --rm --volumes-from appdata -v /home/jack/backup:/home/jack/backup nginx tar xvPf /home/jack/backzup1.tar -C /home/jack/backup
/home/jack/backup/
/home/jack/backup/xixi/
/home/jack/backup/xixi/2.txt
这俩破命令敲了我半天。

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注