• docker默认有2种方式用于持久化数据,volumes和bind mounts,也可以使用tmpfs,其中使用volume是持久化数据的最好方式,volume由docker控制管理,使用docker volume create创建一个volume时,其目录会生成到/var/lib/docker/volumes目录下。volumes和bind mounts用于将数据持久化到硬盘中,tmpfs的数据只存在于内存中,主要用于存储容器运行过程中的临时数据。容器运行中如果会产生大量无需持久化的数据,建议将数据存放在tempfs中,这样会提高性能
  • volumes和bind mounts类似,但volume不依赖host机器的系统文件,因此使用上兼容性更大,权限隔离性更高。volume由docker管理,限制了文件的访问范围(用户和组),增强安全性。如果容器需要与host使用同一个文件目录(为了使用host的配置文件,如/etc/resolv.conf,或不同docker之间共享编译件等),可以考虑使用bind mount。当mount到一个容器中的非空目录时,volume会保留该目录的内容,而bind mount会覆盖该目录。
  • volumes和bind mounts默认使用rprivate类型的bind propagation,将命名空间中所有的mount point变为private propagation类型。

使用docker目录创建一个volume,并将该volume挂载到容器的/my_Cvol目录下

# docker volume create my_vo
# docker run -itd --rm --mount source=my_vol,target=/my_Cvol busybox:latest /bin/sh

查看该volume,其源目录实际在/var/lib/docker/volumes/my_vol/_data下

# docker volume inspect my_vol
[
{
"CreatedAt": "2018-12-24T22:42:18+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/my_vol/_data",
"Name": "my_vol",
"Options": null,
"Scope": "local"
}
]

查看使用docker inspect容器相关信息,可以看到volume的挂载信息,挂载到容器中的目录是可读写的。这样在容器的/my_Cvol目录下的操作也会同步到host的/var/lib/docker/volumes/my_vol/_data目录中。

"Mounts": [
{
"Type": "volume",
"Name": "my_vol",
"Source": "/var/lib/docker/volumes/my_vol/_data",
"Destination": "/my_Cvol",
"Driver": "local",
"Mode": "z",
"RW": true,
"Propagation": ""
}

查看host上/var/lib/docker/volumes/my_vol/_data的MAC属性可以看到它们变为了容器的MAC属性,这样也防止了容器操作不属于其权限范围的文件

# ls -Z
-rw-------. root root system_u:object_r:container_var_lib_t:s0 metadata.db
drwxr-xr-x. root root system_u:object_r:container_var_lib_t:s0 my_vol

使用tmpfs主要用于存储临时数据,由于tmpfs使用的是共享内存方式,所以其效率比较高。使用tmpfs时有如下2个选项用于指定tmpfs的大小和访问权限:

tmpfs-size:指定tmpfs的大小

tmpfs-mode:指定mount的目录权限

  • docker有很多插件可以实现不同的需求,如实现NFS卷共享,使用云提供商的存储介质等(更多查看Volume plugins)。下面使用ssh在不同node节点间共享卷

首先安装docker插件

# docker plugin install --grant-all-permissions vieux/sshfs

在node1节点上创建位于node2节点的卷,登陆的ssh密码为root,对端ip为192.168.80.161

# docker volume create --driver vieux/sshfs -o sshcmd=root@192.168.80.161:/home/sshvolume -o password=root sshvolume

在host上查看容器进程的挂载信息,可以看到其实际使用了fuse.sshfs的方式挂载了来自的root@192.168.80.161:/home/sshvolume目录

# cat /proc//mountinfo |grep
: / /sshvolume rw,nosuid,nodev,relatime master: - fuse.sshfs root@192.168.80.161:/home/sshvolume rw,user_id=,group_id=

node1上查看docker卷信息,可以看到新增了drive为vieux/sshfs:latest,名字为sshvolume的卷

# docker volume ls
DRIVER VOLUME NAME
vieux/sshfs:latest sshvolume

在node1上创建一个容器,并将上一步的卷挂载到容器,在容器内部创建2个文件夹,登陆到node2的/home/sshvolume,可以看到该目录下有node1的容器创建的文件夹

docker run -itd --mount source=sshvolume,target=/sshvolume busybox:latest /bin/sh

docker storage driver

storage driver负责不同layer之间的交互,它允许在容器的读写层创建数据,读写层数据不会被持久化,且读写效率较低。如下图所示,容器镜像的layer是只读的,当创建一个容器时,会新增一个读写层,称为”container layer“,对容器的所有修改都在该layer上进行。当容器删除后,该读写层也会被删除。不同的storage driver实现不同,但所有的storage driver都使用了如下栈式镜像结构以及CoW(copy-on-write)策略。这是对CoW的描述

而CoW技术可以让所有的容器共享image的文件系统,所有数据都从image中读取,只有当要对文件进行写操作时,才从image里把要写的文件复制到自己的文件系统进行修改。所以无论有多少个容器共享同一个image,所做的写操作都是对从image中复制到自己的文件系统中的复本上进行,并不会修改image的源文件,且多个容器操作同一个文件,会在每个容器的文件系统里生成一个复本,每个容器修改的都是自己的复本,相互隔离,相互不影响。使用CoW可以有效的提高磁盘的利用率。

使用docker ps -s可以查看镜像大小,可以看到"SIZE"有2个值,如container id为5b22377a773d的容器中,38B表示容器读写层的数据总和;virtual表示只读的镜像层加上读写层的大小,不同的容器可能会共用部分或全部的镜像层,因此计算容器占用空间大小不能简单地对virtual进行叠加

# docker ps -s
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES SIZE
5b22377a773d echo:v1 "/bin/sh" hours ago Up hours practical_darwin 38B (virtual .2MB)
803ee1eb5acf echo:v1 "sh -c /home/echo.sh" hours ago Up hours hungry_hertz 66B (virtual .2MB)

当在5b22377a773d中手动创建一个非空文件之后,可以看到size变为了95B

# docker ps -s
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES SIZE
5b22377a773d echo:v1 "/bin/sh" hours ago Up hours practical_darwin 95B (virtual .2MB)
803ee1eb5acf 6d495122f721 "sh -c /home/echo.sh" hours ago Up hours hungry_hertz 66B (virtual .2MB)

当使用docker pull拉取一个容器镜像时,会在/var/lib/docker/<storage-driver>/layers/下面保存各个layer。

容器的读写层只保存修改过的变动,而未修改的文件或目录等则不会被保存在读写层。当修改容器中已经存在的文件时,会执行CoW操作,此时在镜像层中逐层搜索该文件,当找到该文件时,会将文件拷贝到容器的读写层(容器的镜像只读层可共享,但读写层不可以共享,CoW技术可以最大化减小容器占用的磁盘,提高磁盘利用率)。当CoW的读写效率比较低,可能会影响IO效率,需要注意以下2点:

  • 如果程序运行时需要大量修改存在于镜像只读层的文件,可以考虑将这些需要大量修改的文件单独放在独立于容器运行的volume中,可以提高IO
  • 如果镜像的layer比较多或需要修改的文件的目录比较深也会影响CoW的效率

ocker的storage driver使用插件方式提供功能。插件的选择取决于docker的版本以及使用的系统等,官方对storage driver的选择有如下建议,可以看出目前主要推荐overlay2。overlay和devicemapper已经在docker 18.09版本中被废除

Linux distribution Recommended storage drivers Alternative drivers
Docker Engine - Community on Ubuntu overlay2 or aufs (for Ubuntu 14.04 running on kernel 3.13) overlaydevicemapperzfsvfs
Docker Engine - Community on Debian overlay2 (Debian Stretch), aufs or devicemapper (older versions) overlayvfs
Docker Engine - Community on CentOS overlay2 overlaydevicemapperzfsvfs
Docker Engine - Community on Fedora overlay2 overlaydevicemapperzfsvfs

不同storage driver所需要的文件系统如下:

Storage driver Supported backing filesystems
overlay2overlay xfs with ftype=1, ext4
aufs xfsext4
devicemapper direct-lvm
btrfs btrfs
zfs zfs
vfs any filesystem

不同的storage driver各有优缺点:

  • overlay2,aufs和overlay工作在文件级别(非块级别),该模式下内存使用会更有效,但在对容器的读写层进行大量文件的修改时会导致读写层变大;
  • devicemapper,btrfs和zfs工作在块级别,它们在进行大量文件的修改时则比上述更好,btrfs和zfs会消耗更多内存;
  • 在对小而多的文件进行修改或文件系统层级比较深的情况下,overlay比overlay2更有效,但会消耗更多inode资源;
  • zfs在高密集操作时更适用;
  • overlay2,aufs,overlay和devicemapper的可靠性更高

在对storage driver修改时需要注意

Important: When you change the storage driver, any existing images and containers become inaccessible. This is because their layers cannot be used by the new storage driver. If you revert your changes, you can access the old images and containers again, but any that you pulled or created using the new driver are then inaccessible.

下面讲解下overlay2的文件结构和特点,首先下载一个centos镜像,查看改镜像有如下3个layer

# docker history centos:latest
IMAGE CREATED CREATED BY SIZE COMMENT
1e1148e4cc2c months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> months ago /bin/sh -c #(nop) LABEL org.label-schema.sc… 0B
<missing> months ago /bin/sh -c #(nop) ADD file:6f877549795f4798a… 202MB

使用docker inspect查看该镜像可以看到其文件系统

"GraphDriver": {
"Data": {
"MergedDir": "/var/lib/docker/overlay2/0bb8525e901534d5c3884a9e47b91d27f887278d0433506126c45081a2a482b8/merged",
"UpperDir": "/var/lib/docker/overlay2/0bb8525e901534d5c3884a9e47b91d27f887278d0433506126c45081a2a482b8/diff",
"WorkDir": "/var/lib/docker/overlay2/0bb8525e901534d5c3884a9e47b91d27f887278d0433506126c45081a2a482b8/work"
},
"Name": "overlay2"
},

使用docker run -itd centos:latest /bin/sh启动一个centos的容器,此时会自动创建overlay需要的lowerdir,upperdir,merged和workdir,使用docker inspect命令,可以看到该容器的overlay2使用情况

 "GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/7aa485418eedcd1443f76018b94b76870de074d732f6e0d5b3e4305a6d896f0d-init/diff:/var/lib/docker/overlay2/0bb8525e901534d5c3884a9e47b91d27f887278d0433506126c45081a2a482b8/diff",
"MergedDir": "/var/lib/docker/overlay2/7aa485418eedcd1443f76018b94b76870de074d732f6e0d5b3e4305a6d896f0d/merged",
"UpperDir": "/var/lib/docker/overlay2/7aa485418eedcd1443f76018b94b76870de074d732f6e0d5b3e4305a6d896f0d/diff",
"WorkDir": "/var/lib/docker/overlay2/7aa485418eedcd1443f76018b94b76870de074d732f6e0d5b3e4305a6d896f0d/work"
},
"Name": "overlay2"
},

merged,upperdir和lowerdir的定义如下,upperdir为容器的读写层,lowerdir为容器的镜像只读层,merged为二者的合集

在容器创建后在/var/lib/docker/overlay2下面会生成2个新的目录,其中7aa485418eedcd1443f76018b94b76870de074d732f6e0d5b3e4305a6d896f0d-init用于设置容器的初始环境

drwx------.  root root      Feb  : 7aa485418eedcd1443f76018b94b76870de074d732f6e0d5b3e4305a6d896f0d
drwx------. root root Feb : 7aa485418eedcd1443f76018b94b76870de074d732f6e0d5b3e4305a6d896f0d-init

在7aa485418eedcd1443f76018b94b76870de074d732f6e0d5b3e4305a6d896f0d目录下可以看到如下文件和目录:diff为该容器的UpperDir,对于容器的读写层,在容器中创建的文件或目录都会体现在该目录中(如下图,在容器的/home下创建一个名为newfile的文件和一个名为newfoler的目录,在diff/home下面也会同步体现该变化)。

# tree -L 1
  .
  ├── diff
  ├── link
  ├── lower
  ├── merged
  └── work

# pwd
  /var/lib/docker/overlay2/7aa485418eedcd1443f76018b94b76870de074d732f6e0d5b3e4305a6d896f0d/diff/home
# ll
  total 0
  -rw-r--r--. 1 root root 0 Feb 13 23:52 newfile
  drwxr-xr-x. 2 root root 6 Feb 13 23:54 newfolder

# cat link
  SKDGVP5O54VJTAXE7CQNUMIVLQ

link中包含了一个指向本目录diff文件夹的索引SKDGVP5O54VJTAXE7CQNUMIVLQ,可以在/var/lib/docker/l目录下找到其定义,其实是个系统链接(l目录存在的意义是防止挂载时符号超出页大小限制--默认4k)。

# ll ../l |grep SKDGVP5O54VJTAXE7CQNUMIVLQ
lrwxrwxrwx. root root Feb : SKDGVP5O54VJTAXE7CQNUMIVLQ -> ../7aa485418eedcd1443f76018b94b76870de074d732f6e0d5b3e4305a6d896f0d/diff

lower的内容如下,其实就是上述的GraphDriver.Data.LowerDir,对应容器镜像的只读层

# cat lower
l/G6URSFRXVFKZI5ESH2BGXG7LFS:l/T6OXBIDQ2GU523P5CXINFO3VH5

merged为lower和link的合集,work为OverlayFS内部使用的文件夹。

overlayFS读文件时使用时有如下特性:

  • 文件不在容器层:此时会从镜像查找并读取该文件,该操作会影响一部分性能
  • 文件存在于容器层:直接读取即可
  • 同时存在于镜像层和容器层:读取容器层文件,忽略镜像层

overlayFS写文件或目录时有如下特性:

  • 当修改的文件不在容器层时,会执行copy_up操作,将该文件从镜像层(lowerdir)拷贝到容器层(upperdir),需要注意的是,如果该文件很大时会影响性能

    • 对同一个文件的copy_up只会进行一次,后续对该文件的操作都会基于该文件的副本

由于overlayFS的CoW特性,在容器中需要注意以下2点(详情参见Use the OverlayFS storage driver):

  • 如使用fd1=open("foo", O_RDONLY) 后调用 fd2=open("foo", O_RDWR).打开镜像层的文件时,原意是打开并返回同一个文件的2个描述符,但实际上由于此时触发了copy_up机制,fd1指向的是镜像层的文件,而fd2指向容器层的文件,一种解决办法是在调用open之前触发copy_up,如使用touch命令
  • overlayFS不完全支持rename系统调用

TIPS:

  • overlayFS使用了2层结构(lower和upper),相比aufs提高了执行效率
  • 可以通过在/var/lib/docker/overlay2下面直接查看容器的镜像只读层和容器读写层的信息,但容器异常退出后,容器读写层会被删除,只能查看镜像只读层的文件信息
  • overlay在部署多个容器时会出现inode资源占用过大问题,建议使用overlay2

参考:

https://docs.docker.com/storage/storagedriver/

https://arkingc.github.io/2017/05/05/2017-05-05-docker-filesystem-overlay/

https://docs.docker.com/storage/storagedriver/overlayfs-driver/

docker storage driver的更多相关文章

  1. Docker storage driver(十四)

    目录 一.storage driver 作用 1.Images and layers 2.Container and layers Copy-on-Write 3.Data volumes and t ...

  2. Linux kernel support docker storage driver aufs

    How to make docker use aufs in CentOS 7? - Server Faulthttps://serverfault.com/questions/650208/how- ...

  3. docker with devicemapper storage driver

    storage driver的选择依据很多的条件,比如发行版版本,团队技术积累,稳定性等. device mapper是redhat/centos中最适合的, 稳定性也可以,内核原生支持,基于块设备, ...

  4. docker 报错Failed to start Docker Storage Setup. 的处理基本都是容器满了

    :: localhost docker-storage-setup: Volume group extents): required. Apr :: localhost systemd: docker ...

  5. Openshift 错误解决 "修改docker cgroup driver"

    一.Openshift 错误解决 "修改docker cgroup driver" 一.错误如下 failed to run Kubelet: failed to create k ...

  6. Docker storage drivers

    因为Docker的镜像是分层的,包含只读层和可读写层,因此选择正确的Storage Driver对于容器的性能是非常重要的. 支持的Linux发行版本 目前推荐使用性能最好的Overlay2.RHEL ...

  7. TP框架中/ThinkPHP/Library/Think/Storage/Driver/File.class.php 错误

        /ThinkPHP/Library/Think/Storage/Driver/File.class.php LINE: 48错误 这是一个将windows下运行正常的ThinkPHP框架移到L ...

  8. Warning:detected "cgroupfs" as the Docker cgroup driver. The recommended driver is "systemd".

    执行kubeadm init集群初始化时遇到: [WARNING IsDockerSystemdCheck]: detected "cgroupfs" as the Docker ...

  9. 【kubeadm初始化报错】failed to run Kubelet: misconfiguration: kubelet cgroup driver: "cgroupfs" is different from docker cgroup driver: "systemd"

    复现场景 环境 系统:Centos7 kubernetes:1.18.5 docker:19.03.9 复现步骤 1.通过 yum 或 rpm 安装 kubelet kubectl kubeadm,并 ...

随机推荐

  1. Tomcat9.0.13 Bug引发的java.io.IOException:(打开的文件过多 Too many open files)导致服务假死

    问题背景: 笔者所在的项目组最近把生产环境Tomcat迁移到Linux,算是顺利运行了一段时间,最近一个低概率密度的(too many open files)问题导致服务假死并停止响应客户端客户端请求 ...

  2. C++学习札记(1)

    指针 按别名传递 下面是个例子: #include <iostream> using namespace std; void swap(int &a,int &b) { i ...

  3. ef core中使用code first

    这个配置还是挺坑,照这个一步步做倒是可以 https://www.cnblogs.com/chenzhaoyu/p/7831980.html

  4. 深入理解JVM(六)类文件结构

    6.1 关于类文件 1.class文件的一次编译,到处运行的跨平台性: 2.JVM不止有跨平台性,还有跨语言性,不管是JRuby还是Groovy写出来的程序,只要编译出符合JVM规范的class文件就 ...

  5. 叙述 activemq 与spring 主题实现 小功能实现

    在上一篇文章里 我说到了 maven的配置  我现在直接说 xml配置 首先我先描述 生产者的信息 <?xml version="1.0" encoding="UT ...

  6. Django用openLDAP做认证

    前言 之前有需求要做一个django+ldap用户管理的简单接口,研究了好几个模块,最后终于能实现django用ldap做用户认证了.也是自己的水平有限吧,做了好长时间,现在就和大家分享一下这个过程吧 ...

  7. InternalResourceViewResolver视图解析器(转)

    转载地址:https://www.cnblogs.com/liruiloveparents/p/5054605.html springmvc在处理器方法中通常返回的是逻辑视图,如何定位到真正的页面,就 ...

  8. FZU.Software Engineering1816 ·The Second Assignment of the Team

    1.Team Leader Link:        柯奇豪:点我 2.NABCD Model: Need(需求)——客户需求是什么? *. 希望能够有一款集成日常办公所需功能的软件(如:想法搜集.投 ...

  9. Floyd多源最短路

    可以对每一个顶点使用Dijkstra算法求多源最短路. 这里我们来介绍另一种解法:Floyd Floyd算法的主要思想是迭代.每次迭代会朝着答案更近一步. 首先定义一个二维数组Dk[i][j](k初始 ...

  10. CPU性能分析

    CPU性能分析工具 lscpu:查看CPU硬件信息 lscpu Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Litt ...