说完了网络,现在来说存储。
对于redis这样的应用来说,我们通常并不需要它的持久化存储,只是拿它做缓存而已,所以存储问题并不重要,但是对于mysql这样的数据库来说,存储就非常重要了。
前面说到了container和image的区别,但并不完全,它们还有一个重要区别就在于存储。docker使用了一套自己的文件存储机制,基于aufs、devicemapper、btrfs、vfs等文件系统,它的特点是分层的:
image 自带的存储内容都是只读的,当它运行成一个container时,docker就会在上面添加一层可写层,container中运行的程序对文件系统的写 操作都是保存在这个可写层里,而且当这个程序运行完成,这个可写层自动固化为一个新的只读层叠加在原来image的只读文件系统上,运行下一个程序的时候 又添加一个新的可写层。当进行commit操作时,就是把这些运行过程序的各个层合并生成一个新的image。
同样的,如果不进行commit操作,那么当一个container运行完被删除后,所有未保存的内容都会丢失,这对于数据库应用这样需要持久化的情况,是肯定不能接受的。
所以,实际上对于这类应用,你需要通过文件系统映射,把主机上的文件目录映射到container里,这样container里的程序保存到这个映射目录下的内容实际上都会存在于主机的文件系统,这样即使container被销毁,保存的内容还在。
映 射的方法有两种,一个是构建image时在Dockerfile里用VOLUME命令指定,但这样不够灵活,因为会映射到host机上的一个特定目录,不 好找。但有些情况并不需要关注它存在哪里,只要是存在host机上即可,比如数据库,这时用这种方式比较简单。如果需要查找volume在host机的位 置,可以用如下命令:
docker inspect -f {{.volumes}} container
另一个更好的方法是在运行container时指定:
docker run -d -v /host/path:/container/path --name mysql-server mysql_image
其中的-v参数就是指定文件夹映射。
另一种需要映射文件目录的应用场景类似这样:
令狐装了一个arch linux,自带的gcc版本比较高,然而他需要编译的代码必须用gcc4.9编译,不能用高版本。因为gcc这种东西依赖太可怕了,删除高版本重装低版本是不现实的,所以我建议他用docker来解决,方法就是:
docker run -it -v /host/path_to_source_code:/container/path gcclhc /bin/bash
然后在这个container环境里用gcc编译/container/path下的代码即可,最后回到host的/host/path_to_source_code取得编译结果。
是不是比装个虚拟机要方便很多。
最后说一下volume container,顾名思义就是一个只做volume用的container。因为它只做volume用,所以不需要以-d方式运行起来即可使用,很节约资源。
那 么用什么样的image来运行呢?虽然用一些小的image来跑好像可以节约存储空间,但实际上这并不是最节约的。因为docker的container 是在image上加一层读写层,所以同一个image跑两个container时会共用下面的只读层,不会增加任何存储消耗,所以如果要把volume container用来做数据库存储的话,用数据库image来跑才是最节约的,因为没有任何额外的存储消耗。
例如:
docker run --name dbvol postgres echo "DB volume." docker run -d --name pgserver --volumes-from dbvol postgres
注意,第一句那个volume container并没有用-d参数,所以执行完echo就退出了(但是只要不rm的话,container还在,所以volume也在)。第二个container就使用了第一个的volume来存储数据。
还有一点需要补充说明的是,不用的volume记得删除,默认对container执行rm操作时并不会删除volume,需要加上-v参数,或者在run的时候加上--rm参数。