鉴于是公司的项目,项目源码不好公开,就专门讲踩坑的经历好了,我以前没有接触过docker,一直以为它是个轻量化的虚拟机,但在看了《docker - 从入门到实践》前言与介绍后,我意识到我的认识是不全面的,准确来说,docker是一个基于操作系统内核的虚拟化技术,能够低消耗地封装你想要的进程。以往的虚拟机都是在系统上又构建了一个完整的系统,自然耗费巨大,而docker则提供了一个可以让你随心所欲地称王的果壳,让每个进程在这个果壳中都能享有一个自己的宇宙,而这个果壳又立足于真正的宇宙--操作系统内核上。
docker有三个重要概念:镜像,容器,仓库。按我的理解:
镜像就是剧本,这个剧本描述了所有的场景布置(系统环境),演员结构(软件依赖),剧情设计(运行程序)等等,是果壳里这场戏的根本,没有剧本,就没有戏。
容器就是实际演出,是剧本的实现,你作为导演,如果看的不爽,可以闯入进去,进行各种调整与改变,当你调整改变完了,你想沿用下来,那就commit作为新剧本。(但官方推荐使用Dockerfile,因为可以完整地再现一个剧本的创作,相当于给剧本作说明,要不然剧本就成为了黑盒子)
仓库就是剧本存放箱子,各种各样的剧本都在这里,有的是同一场戏的,但是分新旧版本,你需要给剧本打上标签,标明它是哪个版本的,最近版本就叫latest,仓库的公开服务可以让你向全天下分享你的剧本,别人也可以拿过来就演,也可以改完再演,也可以改完变成新剧本,说到这里,感觉docker跟github的思想非常相近。
不得不说linux下的docker安装真是轻松+写意,而windows下的docker安装简直就是灾难!虽然官方出了toolbox等一系列工具,做了很多兼容工作,但还是会偶尔出现一些问题(尤其是各种管理工具不能用),安装完docker后,我推荐使用docker管理工具DockerFly,作者helyho是个很不错的人哦。(实际上我是到都快整完了,才发现的。。。唉!早点发现能省多少事!)
我的项目是使用Django框架开发,现阶段暂时是Django + uwsgi + nginx + mysql,根据一些好的建议,我使用了多容器部署,这样可以使服务与数据分离,有极大的好处,但相对的,对于我这样的小白,就意味着要有更多的坑要踩。
我按照这个教程来进行,但一开始就遇到麻烦,我的开发环境是使用conda管理的,其中一些conda找不到的包使用pip安装的,不得已就得自己在镜像中安装miniconda,但是在Dockerfile中安装miniconda真是坑多多,因为miniconda必须要通过sh脚本安装,它途中会各种乱七八糟地问你是否许可这个那个的,导致Docker build不能自动地运行下去,当时没有想到好的解决办法,只好去找miniconda的专门镜像,找到了continuumio/miniconda,但现在我知道,当时可以在运行容器中装好,然后重新生成新镜像。
有了教程的帮助,我省了不少的力气,于是我在Dockerfile中乱写一气,把本该用bash脚本写的用来“临场指挥”的一些命令也全都用 RUN 写到了Dockerfile中,使得Dockerfile成了一锅杂烩,直到后来,我才知道,Dockerfile中有下面的东西,让你“临场指挥”用,例如启动nginx。
ENTRYPOINT [ "/usr/src/run.sh"]
CMD [ "/bin/bash" ]
我的web容器要与mysql容器连通,才能让web操作数据库的数据,但即便在教程的帮助下,我依然无法将这两个牵到一起,像这样的代码
docker run --name mysql \
-p 3309:3306 \
-d daocloud.io/mysql:5.6.30 \
-p是将mysql的端口映射到本地,我以为让web程序访问3309的端口,就能连接到数据库,但事实是死活不通,我变换了各种接口,发现172.17.0.×是通的,原来那是docker容器的ip,顿时感觉找到希望和光明,但随即又想到,这是根据容器顺序配给的,mysql容器如果停止重启了,ip改变,难道还要去改程序不成?经过一连串的百度与论坛咨询,(开源中国回答我的技术问题的人实在不算多,也许是太low了),我才知道,我想要的,在docker中叫做容器互联,在我的代码中
docker run --name django \
--link mysql:mysql \
-p 2000:8000 \
-d mine/django-app \
--link就是干这个用的,host就是mysql,而-p映射的端口是给你在本地连接查看用的,不能用作容器互联。
web容器终于运行起来了,但奇怪的是,容器运行不过几秒就自动退出,这令我倍感惊讶,百度后发现,docker的机制是这样的,docker容器是进程的容器,一个容器里可以运行多个进程,但最后一个运行的进程不能退出,否则进程退出后,容器也就退出了。在容器启动运行run.sh,进程的缺省设置是后台运行,这样导致run.sh运行结束,容器跟着run.sh退出而退出。因此,在run.sh中,最后的进程应强制采用前台运行,例如crond -f。这样run.sh就不会退出, docker run -d 运行时就可以保持容器后台运行。还有同志建议写个死循环
$ sudo docker run -d ubuntu:14.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
不过,我觉得不太好,我采用了tail -f ×××.log。
最后应该将web落到nginx上了,但nginx要定位static静态文件,但我又不愿意将static目录从web中抽出,放到nginx中(这样是不是好的?如果读者感觉不好,请在评论中指出),怎么好呢,docker的数据卷及时救援了我,docker的数据卷可以有效地解决多容器共享目录的问题,并且数据卷的更新,不会影响到容器,使用-v标记创建数据卷挂载到容器里,也可以挂载一个本地目录到容器,于是我
docker run --name django \
-v /usr/src/backend \
-v /usr/src/backend/static \
-d mine/django-app \
而nginx则使用 --volumes-from django 从而可以访问到静态文件与web目录。
经过这一周的踩坑,我对docker有了初步的了解,有不少心得体会,也还有好多不足,要学习一项新的技术,果然要走不少弯路,docker虚拟化技术的前途,可见其光明。