Docker——8——Docker数据存储

数据持久化

在这里插入图片描述

一、Storage Driver

数据存储
CentOS7版本的docker,Storage Driver为: Overlay2 backing filesystem: xfs

正常情况下,只有很少量的数据被写入到容器最上层的写入层,并且通过 volume 来写数据,然而也会遇到一些情况需要可以直接写入到容器的写入层,这就需要到了 storage driver 来帮忙啦

Docker 使用一些不同的 storage driver 来管理镜像层和容器层,这些 storage driver 不同于前面说到的 volume

  • 一个镜像是有若干镜像层组成
  • Dockerfile 中的每条指令都会生成一个镜像层,除了最上面的一层之外,其他的都是只读的
  • 最上一层主要是镜像运行时的一些命令
  • 每一层只是与它之前的层有一些不同,层层堆叠在一起
  • 创建容器的时候,只是在底层上添加一个新的可写层。这一层通常称为“容器层”

Docker 支持多种 storage driver,有 AUFS、Device Mapper、Btrfs、OverlayFS、VFS 和 ZFS。它们都能实现分层的架构,同时又有各自的特性。对于 Docker 用户来说,具体选择使用哪个 storage driver 是一个难题,因为:

  1. 没有哪个 driver 能够适应所有的场景。
  2. driver 本身在快速发展和迭代。

不过 Docker 官方给出了一个简单的答案:
优先使用 Linux 发行版默认的 storage driver

Docker 安装时会根据当前系统的配置选择默认的 driver。默认 driver 具有最好的稳定性,因为默认 driver 在发行版上经过了严格的测试。

Ubuntu 用的 AUFS,底层文件系统是 extfs,各层数据存放在 /var/lib/docker/aufs。

Redhat/CentOS 的默认 driver 是 Device Mapper,SUSE 则是 Btrfs。

对于某些容器,直接将数据放在由 storage driver 维护的层中是很好的选择,比如那些无状态的应用。无状态意味着容器没有需要持久化的数据,随时可以从镜像直接创建。

比如 busybox,它是一个工具箱,我们启动 busybox 是为了执行诸如 wget,ping 之类的命令,不需要保存数据供以后使用,使用完直接退出,容器删除时存放在容器层中的工作数据也一起被删除,这没问题,下次再启动新容器即可。

但对于另一类应用这种方式就不合适了,它们有持久化数据的需求,容器启动时需要加载已有的数据,容器销毁时希望保留产生的新数据,也就是说,这类容器是有状态的。

这就要用到 Docker 的另一种存储机制:Data Volume。

二、Data Volume

Data Volume 本质上是 Docker Host 文件系统中的目录或文件,能够直接被 mount 到容器的文件系统中。Data Volume 有以下特点:

  • Data Volume 是目录或文件,而非没有格式化的磁盘(块设备)
  • 容器可以读写 volume 中的数据
  • volume 数据可以被永久的保存,即使使用它的容器已经销毁

现在有数据层(镜像层和容器层)和 volume 都可以用来存放数据,具体使用的时候要怎样选择呢?考虑下面几个场景:
Database 软件 vs Database 数据
Web 应用 vs 应用产生的日志
数据分析软件 vs input/output 数据
Apache Server vs 静态 HTML 文件

相信会做出这样的选择:
前者放在数据层中。因为这部分内容是无状态的,应该作为镜像的一部分。
后者放在 Data Volume 中。这是需要持久化的数据,并且应该与镜像分开存放。
还有个大家可能会关心的问题:如何设置 volume 的容量?
因为 volume 实际上是 docker host 文件系统的一部分,所以 volume 的容量取决于文件系统当前未使用的空间,目前还没有方法设置 volume 的容量。

1、Bind mount

持久化存储:本质上是DockerHost文件系统中的目录或文件,能够直接被Mount到容器的文件系统中。在运行容器时,可以通过-v 实现。

小实验:运行一个nginx服务,做数据持久化

[root@docker01 ~]# mkdir /data/html -p
[root@docker01 ~]# cd /data/html/
[root@docker01 html]# cat >index.html<<EOF
> This is a testfile in dockerhost.
> EOF
[root@docker01 html]# docker run -itd --name t1 -p 80 -v /data/html:/usr/share/nginx/html nginx:latest
[root@docker01 html]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                   NAMES
e7a9c8dcfe94        nginx               "/docker-entrypoint.…"   13 seconds ago      Up 12 seconds       0.0.0.0:32768->80/tcp   t1
[root@docker01 html]# curl 127.0.0.1:32768
This is a testfile in dockerhost.

[root@docker01 html]# docker exec -it t1 bash
root@e7a9c8dcfe94:/# echo update1 > /usr/share/nginx/html/index.html 
root@e7a9c8dcfe94:/# exit
exit
[root@docker01 html]# curl 127.0.0.1:32768
update1

PS:DockerHost上需要被挂载的源文件或目录,必须是已经存在,否则,当做的一个目录挂载到容器中。

host 中的修改确实生效了,bind mount 可以让 host 与容器共享数据。这在管理上是非常方便的。
即使容器没有了,bind mount 也还在。这也合理,bind mount 是 host 文件系统中的数据,只是借给容器用用,哪能随便就删了啊。

默认挂载到容器内的文件,容器是有读写权限。可以在运行容器时 -v 后边加“:ro” 限制容器的写入权限。

限制容器对挂载目录或文件只读权限:

[root@docker01 html]# docker run -itd --name t2 -p 80 -v /data/html:/usr/share/nginx/html:ro nginx:latest 
[root@docker01 html]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                   NAMES
3fb9168d0d31        nginx:latest        "/docker-entrypoint.…"   17 seconds ago      Up 17 seconds       0.0.0.0:32769->80/tcp   t2
e7a9c8dcfe94        nginx               "/docker-entrypoint.…"   6 minutes ago       Up 6 minutes        0.0.0.0:32768->80/tcp   t1
[root@docker01 html]# curl 127.0.0.1:32769
update1

[root@docker01 html]# docker exec -it t2 bash
root@3fb9168d0d31:/# echo update2 > /usr/share/nginx/html/index.html 
bash: /usr/share/nginx/html/index.html: Read-only file system
root@3fb9168d0d31:/# exit
exit
[root@docker01 html]# curl 127.0.0.1:32769
update1

并且还可以挂载单独的文件到容器内部,一般它的使用场景是:如果不想对整个目录进行覆盖,而只希望添加某个文件,就可以使用挂载单个文件。

挂载单个文件

[root@docker01 html]# docker run -itd --name t3 -p 80 -v /data/html/index.html:/usr/share/nginx/html/index.html:ro nginx
[root@docker01 html]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                   NAMES
33e3142458af        nginx               "/docker-entrypoint.…"   14 seconds ago      Up 13 seconds       0.0.0.0:32770->80/tcp   t3
3fb9168d0d31        nginx:latest        "/docker-entrypoint.…"   3 minutes ago       Up 3 minutes        0.0.0.0:32769->80/tcp   t2
e7a9c8dcfe94        nginx               "/docker-entrypoint.…"   9 minutes ago       Up 9 minutes        0.0.0.0:32768->80/tcp   t1
[root@docker01 html]# curl 127.0.0.1:32770
update1
[root@docker01 html]# docker exec -it t3 bash
root@33e3142458af:/# cat /usr/share/nginx/html/index.html 
update1

mount point 有很多应用场景,比如可以将源代码目录 mount 到容器中,在 host 中修改代码就能看到应用的实时效果。再比如将 mysql 容器的数据放在 bind mount 里,这样 host 可以方便地备份和迁移数据。

bind mount 的使用直观高效,易于理解,但它也有不足的地方:bind mount 需要指定 host 文件系统的特定路径,这就限制了容器的可移植性,当需要将容器迁移到其他 host,而该 host 没有要 mount 的数据或者数据不在相同的路径时,操作会失败。

2、Docker Manager Volume

docker managed volume 与 bind mount 在使用上的最大区别是不需要指定 mount 源,指明 mount point 就行了

[root@docker01 html]# docker run -itd --name v1 -P -v /usr/share/nginx/html nginx
[root@docker01 html]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                   NAMES
6bb7c4b8d758        nginx               "/docker-entrypoint.…"   7 seconds ago       Up 6 seconds        0.0.0.0:32771->80/tcp   v1
……
[root@docker01 html]# curl 127.0.0.1:32771
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
……

通过 -v 告诉 docker 需要一个 data volume,并将其 mount 到 /usr/share/nginx/html。那么这个 data volume 具体在哪儿呢?
这个答案可以在容器的配置信息中找到,执行 docker inspect 命令:

[root@docker01 ~]# docker inspect v1
……
        "Mounts": [
            {
                "Type": "volume",
                "Name": "8a127477cd084543baf1e130633e42d186b624d0ca91551b13e80e59997013d9",
                "Source": "/var/lib/docker/volumes/8a127477cd084543baf1e130633e42d186b624d0ca91551b13e80e59997013d9/_data",
                "Destination": "/usr/share/nginx/html",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],
……

每当容器申请 mount docker manged volume 时,docker 都会在/var/lib/docker/volumes 下生成一个目录(例子中是 “/var/lib/docker/volumes/8a127477cd084543baf1e130633e42d186b624d0ca91551b13e80e59997013d9/_data”),这个目录就是 mount 源。

[root@docker01 ~]# cd /var/lib/docker/volumes/8a127477cd084543baf1e130633e42d186b624d0ca91551b13e80e59997013d9/_data/
[root@docker01 _data]# ls
50x.html  index.html
[root@docker01 _data]# cat index.html 
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
……

删除容器的操作,默认不会对dockerHost上的源文件操作,如果想要在删除容器时把源文件也删除,可以在删除容器时添加 -v 选项(一般不推荐使用这种方式,因为文件有可能被其他容器使用)。

除了上述这种方式之外,还可以手动创建volume

手动创建volume

[root@docker01 ~]# docker volume create testweb
testweb
[root@docker01 ~]# docker volume ls
DRIVER              VOLUME NAME
……
local               testweb
[root@docker01 ~]# docker volume inspect testweb 
[
    {
        "CreatedAt": "2020-09-09T05:21:48+08:00",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/testweb/_data",
        "Name": "testweb",
        "Options": {},
        "Scope": "local"
    }
]
[root@docker01 ~]# docker run -itd -P -v testweb:/usr/share/nginx nginx
[root@docker01 ~]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS                   NAMES
caad8f9a76d3        nginx               "/docker-entrypoint.…"   About a minute ago   Up About a minute   0.0.0.0:32772->80/tcp   awesome_wiles
6bb7c4b8d758        nginx               "/docker-entrypoint.…"   9 minutes ago        Up 9 minutes        0.0.0.0:32771->80/tcp   v1
……
[root@docker01 ~]# curl 127.0.0.1:32772
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
……
[root@docker01 ~]# cat /var/lib/docker/volumes/testweb/_data/html/index.html 
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
……

PS: 注意一下,运行容器时使用的手动创建的volume,在书写格式上和bind mount一样,但查看容器的详细信息,其实它仍然使用的是docker manager volume这种方式

总结:

Bind Mount 和 Docker Manager Volume的特点:

bind mountdocker managed volume
volume 位置可任意指定/var/lib/docker/volumes/…
有mount point 影响隐藏并替换为 volume原有数据复制到 volume
是否支持单个文件支持不支持,只能是目录
权限控制可设置为只读,默认为读写权限无控制,均为读写权限
移植性移植性弱,与 host path 绑定移植性强,无需指定 host 目录

容器与容器的数据共享:

volume container:给其他容器提供volume存储卷的容器。并且它可以提供bind mount,也可以提供docker manager volume

创建一个vc_data容器

[root@docker01 ~]# docker create --name vc_data -v ~/html:/usr/share/nginx/html -v /other/useful/tools busybox

使用vc容器

[root@docker01 ~]# docker run -itd --name c1 -P --volumes-from vc_data nginx

容器的跨主机数据共享

docker01:1.128docker02:1.129docker03:1.150
httpdhttpdnfs

要求:docker01和docker02上基于httpd镜像运行2个或多个容器,保证的主目录(默认访问界面内容)是一致的

docker03 的操作
[root@nfs ~]# yum -y install nfs-utils
[root@nfs ~]# mkdir /datashare
[root@nfs ~]# vim /etc/exports
/datashare *(rw,sync,no_root_squash)

[root@nfs ~]# systemctl restart rpcbind
[root@nfs ~]# systemctl enable rpcbind
[root@nfs ~]# systemctl restart nfs-server
[root@nfs ~]# systemctl enable nfs-server
[root@nfs ~]# cat >/datashare/index.html<<EOF
> ghost-webshare
> EOF
docker01 的操作
[root@docker01 ~]# showmount -e 192.168.1.150
Export list for 192.168.1.150:
/datashare *
[root@docker01 ~]# mkdir /htdocs
[root@docker01 ~]# mount -t nfs 192.168.1.150:/datashare /htdocs
[root@docker01 ~]# cat /htdocs/index.html 
ghost-webshare
docker02 的操作
[root@docker02 ~]# showmount -e 192.168.1.150
Export list for 192.168.1.150:
/datashare *
[root@docker02 ~]# mkdir /htdocs
[root@docker02 ~]# mount -t nfs 192.168.1.150:/datashare /htdocs
[root@docker02 ~]# cat /htdocs/index.html 
ghost-webshare

这里先不考虑将代码写入镜像,先以这种方式,分别在docker01和docker02部署httpd服务

docker01

[root@docker01 ~]# docker run -itd --name ghost-web1 -P -v /htdocs:/usr/local/apache2/htdocs httpd:latest
[root@docker01 ~]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                   NAMES
5da15dc977b9        httpd:latest        "httpd-foreground"       5 seconds ago       Up 4 seconds        0.0.0.0:32774->80/tcp   ghost-web1

PS: 查看端口映射0.0.0.0:32774->80/tcp

docker02

[root@docker02 ~]# docker run -itd --name ghost-web2 -P -v /htdocs:/usr/local/apache2/htdocs httpd:latest
[root@docker02 ~]# docker ps
CONTAINER ID        IMAGE               COMMAND              CREATED             STATUS              PORTS                   NAMES
91dbfe3e3ea2        httpd:latest        "httpd-foreground"   5 seconds ago       Up 3 seconds        0.0.0.0:32768->80/tcp   ghost-web2

PS: 查看端口映射0.0.0.0:32768->80/tcp

此时,用浏览器访问,两个WEB服务的主界面是一样。但如果,NFS服务器上的源文件丢失,则两个web服务都会异常

想办法将源数据写入镜像内,在基于镜像做一个vc_data容器。这里因为没接触到docker-compose和docker swarm等docker 编排工具,所以先在docker01和docker02上先手动创建镜像。

dockre01

[root@docker01 htdocs]# vim Dockerfile
FROM busybox
COPY index.html /usr/local/apache2/htdocs/index.html
VOLUME /usr/local/apache2/htdocs

[root@docker01 htdocs]# docker build -t back_data .
Sending build context to Docker daemon  3.072kB
Step 1/3 : FROM busybox
 ---> 6858809bf669
Step 2/3 : COPY index.html /usr/local/apache2/htdocs/index.html
 ---> Using cache
 ---> 3eedc65187bd
Step 3/3 : VOLUME /usr/local/apache2/htdocs
 ---> Using cache
 ---> 273ac7b0bafc
Successfully built 273ac7b0bafc
Successfully tagged back_data:latest

[root@docker01 htdocs]# docker create --name back_container1 back_data:latest

总结:
1)解决容器跨主机数据共享的方案: NFS
2)容器与容器的数据共享: 基于某个容器而来(–volumes-from选项),意味着这些容器和vc容器的数据存储是一样的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值