以往我们想安装某个服务器应用,需要提前安装各种依赖包,然后安装目标应用,然后是各种设置。。。幸运的话,也许1,2个小时后,就能看到期待的画面,但事实经常是半天过去了,还在排查各种ERROR,你感到挫折,也许你很坚强,不达目的不罢休,也许有人就放弃了,比如本人就经常这样,于是丢失潜在宝贵用户。。。
容器技术在我看来是一种革命性的应用分发,运行服务器应用,你只需1步
docker run app-img-name
比如 docker run -p 80:80 nginx
然后你就可以http://localhost了。
这个命令做了什么事情呢?
- 查看本地是否存在nginx镜像,若没有就下载→PULL
- 创建Contianer for nginx,然后载入内存并运行(本例还把主机端口80同容器端口80做了映射)→Deploy
因为容器技术,重新构建并运行应用变得容易且快速,容器化应用基本是无需进一步配置的(已经打包好所有依赖并且已配置),只需PULL,然后Deploy(也许还需要一份docker-compose.yml)就好。我们无需太担心硬件,OS,库以及应用本身的潜在Bug导致应用挂掉,挂掉就挂掉吧,销毁然后重新运行一个新的就好,而且马上就可Ready。需要重点关心的是如何保存,备份,恢复应用产生的数据,本文记录了容器管理数据的一些调查。
数据存放的位置
大体可分2类
- 容器层(Container layer)
- 数据卷(data volume)
容器层(Container layer)
这是一个薄的可读写层,何为薄?因为对应用可见的文件系统,基本都存在于底层镜像,只在需要修改时,才会CoW(copy-on-write)到这一层,另外还保存一些运行时生成的配置(例如Dockers Engine Daemon分配的IP和DNS设置等)和log。在没有定义Volume时,容器内部运行时生成的数据会写到这一层,因为Container layer是容器的一部分,伴随容器的生命周期死亡,销毁,所以关键数据不能放在这里,不过可以用作缓存,另外如果stop&start或者restart容器,数据不会丢失,因为容器没有销毁。在容器外获取这一层数据的方式似乎只有
docker cp
命令,并且为了安全copy还可以被禁止。
数据卷(data volume)
Data volumes are designed to persist data, independent of the container’s lifecycle.
数据卷是独立的存储,与容器层不同,不依赖于容器的生命周期。
从数据卷的定义时机看有2种,一个是在Dockfile也就是生成镜像的时候,一个是在运行容器的时候。
Dockfile定义Volume
VOLUME ["/data"]
实际上这是定义一个挂载点,当用docker run命令运行容器时,如果镜像中存在挂载点相同路径的文件,会copy到数据卷以初始化。(这里说的只适用于匿名卷和命名卷,在绑定主机目录时,copy行为不会发生,原有的镜像中同名路径的文件系统会被覆盖(Overlay),对容器不可见)
运行时定义Volume
2种方式
- 命令行
docker run --rm -v /foo -v awesome:/bar -v /hoge:/hoge busybox top
这里定义了匿名/foo,命名/bar(名字awesome),以及主机/hoge和容器/hoge的绑定。 - docker-compose.yml
volumes:
# Just specify a path and let the Engine create a volume
- /var/lib/mysql
# Specify an absolute path mapping
- /opt/data:/var/lib/mysql
# Named volume
- datavolume:/var/lib/mysql
数据卷的类型
数据卷有2中类型:volume和bind,其中volume又包含匿名数据卷(anonymous data volume)和命名数据卷(named data volume)。bind就是主机绑定。另外还有一种叫做数据卷容器(data volume container)的机制,本质上就像定义了数据卷模板,其他容器复制这个模板,参照相同的位置,这样就可达到多个容器共享数据卷的目的,并不是一种新的数据卷类型。下面分别讲述。
匿名数据卷和命名数据卷
可以用下面命令查看数据卷
simon@home:~/devops/busybox$ sudo docker volume ls
DRIVER VOLUME NAME
local 8cb8d18e377aae5f4a85f0fb040aa326c8ba0cad9640807d73af693355754d32 #匿名卷
local awesome #命名卷
simon@home:~/devops/busybox$
可以发现,匿名卷的名字是一长串字符。另外,前面提到,数据卷独立于容器的生命周期,容器删除时,数据卷依然存在,但是对于匿名卷,在容器运行时如果添加--rm
,那么匿名卷会随同容器一同删除。
这2种卷在主机的位置是/var/lib/docker/volumes
,容器删除后,如果不想保留卷可用docker volume prune
清除。
主机绑定数据卷
主机绑定的卷,用docker volume ls
是无法查看的,完全脱离了docker的管理,所以与容器删除与否无关,docker volume prune
也无法清除,只能用系统的rm
命令清除。另外,主机绑定的目录会完全覆盖镜像的同名目录,原来存在于基础镜像中的同名目录内容在容器运行时完全不可见,而匿名卷和命名卷在初始化时则会copy基础镜像中的文件,以供后续修改。
数据卷容器
创建
sudo docker create -v /dbdata --name dbstore training/postgres /bin/true
引用
sudo docker run -d --volumes-from dbstore --name db1 training/postgres
sudo docker run -d --volumes-from dbstore --name db2 training/postgres
用sudo docker inspect db1
和sudo docker inspect db2
查看,可以发现MOUNT段完全一样,这样在db1中写入/dbdata的内容,db2也可看到,反之同理。另外,dbstore甚至可以删除,因为数据卷配置已经copy过来,不在依赖dbstore了。
SWARM模式下的数据管理
上面讨论,都是基于同一Docker主机为前提的,但是在实际生产环境SWARM集群模式会经常用到,由于Docker的设计特性,就是动态分配Task在不同的worker node上,而数据卷却不会随着Task迁移,另外同一Task可能有多份replica,数据一致性也是问题。因此部署到SWARM中的服务最好是无状态的,但是应用总会参照或生成数据,怎么办?思路是把保存应用状态的服务独立出来,不要放在SWARM中,但是要加入SWARM所在的网络,参照的地方用名称访问(微服务的理念,尚未验证)。
总结
Docker真是一个好工具,让应用分发,运行一键搞定,Docker Swarm之类的容器集群管理工具又让分布式部署变得轻松,运维人员只要盯好数据,其他的事情就交给Docker。