在了解Docker数据持久化之前,需要对Docker的存储类型有一个简单的了解,执行以下命令即可看出:
[root@docker01 ~]# docker info //查看Docker的详细信息
Containers: 1 //一共有几个容器
Running: 1 //正在运行的有几个容器
Paused: 0 //挂起、暂停的有几个容器
Stopped: 0 //停止的有几个容器
Images: 2 //有几个镜像
Server Version: 18.09.0 //docker的版本信息
Storage Driver: overlay2 //存储驱动类型为overlay2
Backing Filesystem: xfs //支持的文件系统:xfs
Supports d_type: true
Native Overlay Diff: false
Logging Driver: json-file
Cgroup Driver: cgroupfs
Plugins:
Volume: local //本地存储
Network: bridge host macvlan null overlay //支持的网络类型
Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
Swarm: inactive
Runtimes: runc
Default Runtime: runc
Init Binary: docker-init
containerd version: 7ad184331fa3e55e52b890ea95e65ba581ae3429
runc version: dc9208a3303feef5b3839f4323d9beb36df0a9dd
init version: fec3683
Security Options:
seccomp
Profile: default
Kernel Version: 3.10.0-514.el7.x86_64 //内核信息
Operating System: CentOS Linux 7 (Core) //操作系统
OSType: linux //操作系统类型
Architecture: x86_64
CPUs: 4 //CPU个数
Total Memory: 3.686GiB //内存容量
Name: docker01
ID: X6ON:W73P:YWXD:NV7U:3C2J:RXH3:GNPO:SYIP:FF53:CNFN:HSN3:EZG2
Docker Root Dir: /var/lib/docker
Debug Mode (client): false
Debug Mode (server): false
Registry: https://index.docker.io/v1/
Labels:
Experimental: false
Insecure Registries:
127.0.0.0/8
Registry Mirrors: //采用的加速器信息
https://weimtsj8.mirror.aliyuncs.com/
Live Restore Enabled: false
Product License: Community Engine
在docker中实现数据持久化有两种方式:Bind mount和Docker Manager Volume。
Bind mount和Docker Manager Volume的区别:
Bind mount数据持久化的方式,如果是挂载本地的一个目录,则容器内对应的目录下的内容会被本地的目录覆盖掉,而Docker Manager Volume这种方式则不会,不管哪种方式的持久化,在容器被销毁后,本地的数据都不会丢失。
使用“-v”选项挂载时,Bind mount明确指定了要挂载docker host本地的某个目录到容器中,而Docker Manager Volume则只指定了要对容器内的某个目录进行挂载,而挂载的是docker host本地的哪个目录,则是由docker来管理的。
1、Bind mount——数据卷容器:–volumes-from方式实现数据持久化
以下数据卷容器挂载的方式就是Bind mount实现方式。
实现的大概思路如下:
1.运行一个容器作为数据卷容器,挂载本地目录到容器内的本地目录,无需所挂载的源目录或目标挂载点是否存在,docker会自动创建相应的目录的,也无需考虑使用哪个镜像来运行这个容器,任意镜像都可以;
2.之后无论运行多少容器,都可以使用–volumes-from选项来指定第一个运行的容器进行数据持久化;
3.实现的效果为:挂载数据卷容器实现数据持久化的容器,会自动将数据卷容器挂载的本地目录挂载到该容器本身(本身的挂载点与数据卷容器的挂载点自动保持一致),也仅仅只会挂载数据卷容器实现了数据持久化的目录到自己本身,而不是数据卷容器的全部目录。
上面实现的效果可能我表达的不够好,举个例子 :
有A、B、C这三个容器,其中A作为数据卷容器,挂载了本地的/data/web01和/data/web02这两个目录到容器内的/usr/share/nginx/html/和/data这两个目录。
容器B和容器C在运行之初,通过–volumes-from选项来指定容器A的名称或ID,那么最终实现的效果就是,A、B、C这三个容器内都会存在/usr/share/nginx/html及/data这两个目录,并且是实现了数据持久化的,对应的本地目录都是/data/web01和/data/web02。
持久化存储:本质上是DockerHost文件系统中的目录或文件,能够直接被Mount到容器的文件系统中。在运行容器时,可以通过-v 实现。
Bind mount的特点:
Data Volume是目录或文件,不能是没有格式化的磁盘(块设备);
容器可以读写volume中的数据;
随源文件变化而变化;
volume数据可以永久保存,即使使用它的容器已经被销毁;
示例:
[root@localhost ~]# cd /tmp/
[root@localhost tmp]# mkdir /html
[root@localhost tmp]# echo "huan ying ni zjz" >> /html/index.html //创建测试页面
[root@localhost tmp]# docker run -itd --name testweb -p 80:80 -v /html:/usr/share/nginx/html
nginx:latest
//运行容器并使用“-v”选项指定挂载目录,前面为docker host的目录,“:”后面为容器中的目录
[root@localhost tmp]# curl 127.0.0.1 //可以看到挂载已经生效
huan ying ni zjz
[root@localhost tmp]# echo "hello" >> /html/index.html
[root@localhost tmp]# curl 127.0.0.1
//这种方式可以看出当源文件发生变化时,目标文件也会随之发生变化
huan ying ni zjz
hello
[root@localhost tmp]# docker inspect testweb //查看容器的详细信息
注意:
DockerHost上需要被挂着的源文件或目录,必须是已经存在,否则,当做的一个目录挂着到容器中;
默认挂载到容器内的文件,容器是有读写权限。可以在运行容器“-v”选项后边加“:ro” 选项来限制容器的写入权限;
可以挂载单独的文件到容器内部,使用场景:如果不想对整个目录进行覆盖,而只希望添加某个文件,就可以使用挂载单个文件;
2、Docker Manager Volume实现数据持久化
示例:
[root@docker ~]# docker run -itd --name test1 -v /usr/share/nginx/html nginx:latest
//这种方式“-v”选项后,只需添加容器中的目录即可
[root@docker ~]# docker inspect test1
[root@docker~]#ls /var/lib/docker/volumes/46d00301afb2b9c8d5279475d50d9242470af37969fa52338e916de1e35a6e93/_data/
50x.html index.html
//可以看出宿主机上的目录就是容器中挂载的目录
这种方式特点:
会随着源文件的变化而变化,跟Bind mount效果是一样的!
删除容器的操作,默认不会对dockerhost主机上的原文件进行删除,如果想要在删除容器是将原文件删除,可以在删除容器时添加“-v”选项,(一般情况下不建议使用,因为文件有可能被其他容器就使用);
3.Volume container(容器与容器的数据共享)
Volume container:给其他容器提供volume存储卷的容器。并且它可以提供bind mount,也可以提供docker manager volume。
[root@docker ~]# docker create --name vc_data -v /html:/usr/share/nginx/html busybox:latest
//创建一个容器(无须运行)
[root@docker ~]# docker run -itd --name test3 -P --volumes-from vc_data nginx:latest
//使用“--volumes-from”来挂载vc_data容器中的数据间到新的容器test3
[root@docker ~]# docker ps //查看其映射的端口
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0c0429f3e8c0 nginx:latest "/docker-entrypoint.…" About a minute ago Up About a minute 0.0.0.0:32768->80/tcp test3
[root@docker ~]# curl 127.0.0.1:32768 //测试效果
huan ying ni zjz
hello
注意:以上方式在源目录删除后,容器中的数据也会发生丢失现象!
可以采用编写dockerfile文件的方式,将目录或文件写到镜像中,然后根据镜像生成容器,才可保证原数据丢失,容器中的数据不会发生变化!
由于这种方式随机性、灵活性太差,这里就不多做介绍了这样就可以通过数据卷容器实现容器之间的数据共享。
通过以上机制,即使容器在运行过程中出现故障,用户也不用担心数据发生丢失。如果发生意外,只需快速重新创建容器即可!
注意:生产环境中最注重的就是存储的可靠性,以及存储的可动态扩展性,一定要在做数据卷时考虑到这一点,在这方面比较出色的还要数GFS文件系统了,我上面只是做了简单的配置,若在生产环境中,一定要好好考虑,就比如上面做的镜像卷容器,就可以在宿主机本地挂载GFS文件系统,然后创建镜像卷容器时,将挂载GFS的目录映射到容器中的镜像卷,这样才是一个合格的镜像卷容器。
Bind Mount 和 Docker Manager Volume的特点;
4.容器的跨主机数据共享
docket1 | docker2 | docker3 |
---|---|---|
httpd | httpd | nfs |
要求:docker01和docker02的主目录,是一样的。
docker3的操作
[root@nfs ~]# yum -y install nfs-utils #安装nfs
[root@nfs ~]# mkdir /datashare #创建共享目录
[root@nfs ~]# vim /etc/exports #设置权限
/datashare *(rw,sync,no_root_squash)
[root@nfs ~]# systemctl start rpcbind
[root@nfs ~]# systemctl enable rpcbind
[root@nfs ~]# systemctl start nfs-server.service #启动
[root@nfs ~]# systemctl enable nfs-server.service #开机自启
[root@nfs ~]# showmount -e #验证
Export list for nfs:
/datashare *
[root@nfs ~]# cd /datashare/
[root@nfs datashare]# touch 123.txt #创建测试文件
[root@nfs datashare]# echo "12312321312" > 123.txt
[root@nfs datashare]# cat 123.txt
12312321312
[root@nfs datashare]# echo "hell world" > index.html
docker1上的操作
#也需安装nfs
[root@docker1 ~]# showmount -e 192.168.10.54 #验证
Export list for 192.168.10.54:
/datashare *
[root@docker1 ~]# mkdir /htdocs #创建目录
[root@docker1 htdocs]# mount -t nfs 192.168.10.54:/datashare /htdocs #将/htdocs目录挂载到192.168.10.54:/datashare 下
[root@docker1 ~]# cd /htdocs/
[root@docker1 htdocs]# pwd
/htdocs
[root@docker1 htdocs]# ls
123.txt index.html
[root@docker1 htdocs]# cat 123.txt
12312321312
[root@docker1 ~]# docker run -itd --name web1 -P -v /htdocs:/usr/local/apache2/htdocs httpd:latest
[root@docker1 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ed9710a468a2 httpd:latest "httpd-foreground" 43 seconds ago Up 41 seconds 0.0.0.0:32768->80/tcp web1
docker2上的操作和docker1操作一样
#也需安装nfs
[root@docker2 ~]# showmount -e 192.168.10.54 #验证
Export list for 192.168.10.54:
/datashare *
[root@docker2 ~]# mkdir /htdocs
[root@docker2 htdocs]# mount -t nfs 192.168.10.54:/datashare /htdocs
[root@docker2 ~]# cd /htdocs/
[root@docker2 htdocs]# pwd
/htdocs
[root@docker2 htdocs]# ls
123.txt index.html
[root@docker2 htdocs]# cat 123.txt
12312321312
[root@docker2 htdocs]# docker run -itd --name web2 -P -v /htdocs:/usr/local/apache2/htdocs httpd:latest
[root@docker2 htdocs]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2a5ddae0894d httpd:latest "httpd-foreground" 7 seconds ago Up 5 seconds 0.0.0.0:32769->80/tcp web2
此时,用浏览器访问,两个WEB服务的主界面是一样。但如果,NFS服务 器上的源文件丢失,则两个web服务都会异常
想办法将源数据写入镜像内,在基于镜像做一个vc_data容器,所以我们 在docker01和docker02上先手动创建镜像
docker1上操作
[root@docker1 htdocs]# vim Dockerfile
FROM busybox
ADD index.html /usr/local/apache2/htdocs/index.html
VOLUME /usr/local/apache2/htdocs
[root@docker1 htdocs]# docker build -t back_data .
[root@docker1 htdocs]# docker create --name back_container1 back_data:latest
[root@docker1 htdocs]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
back_data latest bfc2314995b5 24 seconds ago 1.22MB
[root@docker1 htdocs]# docker run -itd --name web3 -P --volumes-from back_container1 httpd:latest
[root@docker1 htdocs]# docker inspect web3
**总结: 1)解决容器跨主机数据共享的方案: NFS 2)容器与容器的数据共享: 基于某个容器而来(--volumes-from选 项),意味着这些容器和vc容器的数据存储是一样的。**