如果正在运行中的容器生成了新的数据,或者修改了现有的一个已经存在的文件内容,那么新产生的数据将会被复制到读写层进行持久化保存,这个读写层也就是容器的工作目录,此即 “写时复制(COW)copy on write” 机制。
Docker 的镜像是分层设计的,镜像层是只读的,通过镜像启动的容器添加了一层可读写的文件系统,用户写入的数据都保存在这一层当中,如果要将写入到容器的数据永久保存,则需要将容器中的数据保存到宿主机的指定目录。
如果 Dokcer 没有挂载 Volume 存储卷,则 Docker 容器生成的数据都存放在容器的可写层,这个可写层随容器的删除而删除。
查看容器的目录挂载:
root@ubuntu:~# docker inspect 6960834428ff
在容器中生成的数据存放在宿主机的什么地方?
在容器中生成一个 testfile 文件:
root@ubuntu:~# docker exec -it 6960834428ff bash
root@6960834428ff:/# dd if=/dev/zero of=testfile bs=1M count=100
100+0 records in
100+0 records out
104857600 bytes (105 MB, 100 MiB) copied, 0.177367 s, 591 MB/s
root@6960834428ff:/# md5sum testfile
2f282b84e7e608d5852449ed940bfc51 testfile
宿主机查找验证:
root@ubuntu:~# find / -name testfile
/var/lib/docker/overlay2/ffa61dc6259aae6c3ac677f13f2c0201c6cfe1cb531a06065d36afc07738859e/merged/testfile
/var/lib/docker/overlay2/ffa61dc6259aae6c3ac677f13f2c0201c6cfe1cb531a06065d36afc07738859e/diff/testfile
root@ubuntu:~# md5sum /var/lib/docker/overlay2/ffa61dc6259aae6c3ac677f13f2c0201c6cfe1cb531a06065d36afc07738859e/merged/testfile
2f282b84e7e608d5852449ed940bfc51 /var/lib/docker/overlay2/ffa61dc6259aae6c3ac677f13f2c0201c6cfe1cb531a06065d36afc07738859e/merged/testfile
root@ubuntu:~# md5sum /var/lib/docker/overlay2/ffa61dc6259aae6c3ac677f13f2c0201c6cfe1cb531a06065d36afc07738859e/diff/testfile
2f282b84e7e608d5852449ed940bfc51 /var/lib/docker/overlay2/ffa61dc6259aae6c3ac677f13f2c0201c6cfe1cb531a06065d36afc07738859e/diff/testfile
当容器被删除后,数据也将被删除:
root@ubuntu:~# docker rm -fv `docker ps -q`
6960834428ff
root@ubuntu:~# md5sum /var/lib/docker/overlay2/ffa61dc6259aae6c3ac677f13f2c0201c6cfe1cb531a06065d36afc07738859e/diff/testfile
md5sum: /var/lib/docker/overlay2/ffa61dc6259aae6c3ac677f13f2c0201c6cfe1cb531a06065d36afc07738859e/diff/testfile: No such file or directory
root@ubuntu:~# md5sum /var/lib/docker/overlay2/ffa61dc6259aae6c3ac677f13f2c0201c6cfe1cb531a06065d36afc07738859e/merged/testfile
md5sum: /var/lib/docker/overlay2/ffa61dc6259aae6c3ac677f13f2c0201c6cfe1cb531a06065d36afc07738859e/merged/testfile: No such file or directory
如果要想让 Docker 容器的数据持久化,可以将 Docker 容器数据写入到 Volume,这就相当于将容器的数据写入到宿主机,从而在容器被删除后,容器数据依然存在。
目前 Docker Volume 的挂载方式分为两种:
数据卷实际上就是宿主机上的目录或者是文件,可以被直接 mount 到容器当中使用。
实际生产环境中,需要针对不同类型的服务、不同类型的数据存储要求做相应的规划,最终保证服务的可扩展性、稳定性以及数据的安全性。
docker run --volume / -v :Bind mount a volume.
数据卷使用场景:
将应用程序以 Volume 的方式挂载到容器,而不是直接打入镜像容器(适用于小型站点);
使用 Tomcat 官方的 sample.war 作为测试 webapp:
root@ubuntu:~# mkdir /data/sampleapp -pv
mkdir: created directory '/data'
mkdir: created directory '/data/sampleapp'
root@ubuntu:~# cp sample.war /data/sampleapp/
root@ubuntu:~# cd /data/sampleapp/
root@ubuntu:/data/sampleapp# jar xf sample.war
拉取镜像:
root@ubuntu:~# docker pull tomcat
先启动一个容器并进入,查看 Tomcat 容器的 webapp 根目录:
root@ubuntu:~# docker run -it tomcat bash
root@377e6d44989f:/usr/local/tomcat# grep "appBase" conf/server.xml
启动两个容器,分别映射宿主机的 8080 和 8081 端口,挂载 webapp 所在目录,并设为只读:
root@ubuntu:~# docker run -itd -p 8080:8080 --name tomcat8080 -v /data/sampleapp/:/usr/local/tomcat/webapps/sampleapp:ro tomcat
root@ubuntu:~# docker run -itd -p 8081:8080 --name tomcat8081 -v /data/sampleapp/:/usr/local/tomcat/webapps/sampleapp:ro tomcat
http://192.168.1.111:8080/sampleapp/
http://192.168.1.111:8081/sampleapp/
在宿主机将 sampleapp 的主页内容进行修改:
root@ubuntu:~# echo "sampleapp" > /data/sampleapp/index.html
http://192.168.1.111:8080/sampleapp/
http://192.168.1.111:8081/sampleapp/
删除容器后,宿[p0-主机的数据不受影响:
root@ubuntu:~# docker rm -fv `docker ps -aq`
42752ca08095
58273680f803
root@ubuntu:~# ll /data/sampleapp/
total 36
drwxr-xr-x 5 root root 4096 Jan 25 11:46 ./
drwxr-xr-x 3 root root 4096 Jan 25 11:45 ../
-rw-r--r-- 1 root root 376 Jan 25 11:46 hello.jsp
drwxr-xr-x 2 root root 4096 Jan 25 11:46 images/
-rw-r--r-- 1 root root 10 Jan 25 12:02 index.html
drwxr-xr-x 2 root root 4096 Jan 25 11:46 META-INF/
-rw-r--r-- 1 root root 4606 Jan 25 11:46 sample.war
drwxr-xr-x 4 root root 4096 Jan 25 11:46 WEB-INF/
root@ubuntu:~# cat /data/sampleapp/index.html
sampleapp
挂载文件到容器用于很少修改文件内容的场景,比如挂载配置文件(Nginx、Tomcat 等);
启动一个 tomcat 容器,得到 catalina.sh:
root@ubuntu:~# docker run -itd tomcat
d6847848c0a979a261c4d082cb6d3a92c47fe103ec65232ab6b8c45d6b16900f
root@ubuntu:~# find / -name "catalina.sh"
/var/lib/docker/overlay2/8eb7b75d01f746bcc3da255c4dd09fa4a9e3f1cbbb083ec88b8aa4c7e3449346/diff/apps/apache-tomcat-8.5.60/bin/catalina.sh
root@ubuntu:~# mkdir /data/scripts
root@ubuntu:~# cp /var/lib/docker/overlay2/8eb7b75d01f746bcc3da255c4dd09fa4a9e3f1cbbb083ec88b8aa4c7e3449346/diff/apps/apache-tomcat-8.5.60/bin/catalina.sh /data/scripts
添加自定义 JAVA 选项参数:
JAVA_OPTS="-server -Xms4g -Xmx4g -Xss512k -Xmn1g -XX:CMSInitiatingOccupancyFraction=65 -XX:+UseFastAccessorMethods -XX:+AggressiveOpts -XX:+UseBiasedLocking -XX:+DisableExplicitGC -XX:MaxTenuringThreshold=10 -XX:NewSize=2048M -XX:MaxNewSize=2048M -XX:NewRatio=2 -XX:PermSize=128m -XX:MaxPermSize=512m -XX:CMSFullGCsBeforeCompaction=5 -XX:+ExplicitGCInvokesConcurrent -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods"
将宿主机的 /data/scripts/catalina.sh 以只读方式挂载到容器的 /usr/local/tomcat/bin/catalina.sh:
root@ubuntu:~# docker run -itd -p 8080:8080 --name tomcat1 -v /data/scripts/catalina.sh:/usr/local/tomcat/bin/catalina.sh:ro tomcat
数据卷容器最大的功能是可以让数据在多个 docker 容器之间共享,即可以让容器 B 访问容器 A 的内容,而容器 C 也可以访问容器 A 的内容,相当于先创建一个后台运行的容器作为 Server,用于卷提供,这个卷可以为其他容器提供数据存储服务,其他使用此卷的容器作为client 端;
可以在生产中启动一个数据卷容器,挂载宿主机的文件或目录;然后其它容器分别基于此数据卷容器挂载宿主机的文件或目录,这样可以保证容器之间的数据一致性。
将 /data/sampleapp 挂载到数据卷容器(volume-server)的 /usr/local/tomcat/webapps/sampleapp:
root@ubuntu:~# docker run -d --name volume-server -v /data/sampleapp:/usr/local/tomcat/webapps/sampleapp tomcat
启动 tomcat8080 和 tomcat8081 两个容器,都从数据卷容器(volume-server)获取数据卷的挂载:
root@ubuntu:~# docker run -d --name tomcat8080 -p 8080:8080 --volumes-from volume-server tomcat
root@ubuntu:~# docker run -d --name tomcat8081 -p 8081:8080 --volumes-from volume-server tomcat
http://192.168.1.111:8080/sampleapp/
http://192.168.1.111:8081/sampleapp/
停止数据卷容器:
root@ubuntu:~# docker stop volume-server
volume-server
基于数据卷容器启动新的容器:
root@ubuntu:~# docker run -d --name tomcat8082 -p 8082:8080 --volumes-from volume-server tomcat
http://192.168.1.111:8082/sampleapp/
删除数据卷容器:
root@ubuntu:~# docker rm -fv volume-server
volume-server
之前的容器访问都正常,不受影响:
http://192.168.1.111:8080/sampleapp/
http://192.168.1.111:8081/sampleapp/
http://192.168.1.111:8082/sampleapp/