docker实践入门之六:举个栗子
最后一篇来举个栗子。
样本项目
- 一个基于bottle的python web项目
- 依赖mako
- 暂时只考虑部署一个版本的例子
项目有以下内容:
+-appname.py +-config.json +-requirements.txt +-views +-index.html
requirements.txt的内容为:
bottle mako
virtualenv部署方式
传统的python应用部署方式一般会推荐用virtualenv,它可以实现一个相对隔离的python环境供应用运行,避免因为不同的应用有不同的依赖导致不必要的冲突。
首先你需要建立一个基础环境,包括:
- OS
- 安装apache/nginx等webserver并配置
- 安装相应版本的python
- 安装virtualenv
- 创建应用专用的virtualenv环境
之后通过hg/git更新应用代码,再用pip和requirements.txt更新依赖,再用gunicorn/uwsgi等wsgi server来启动应用(必要时辅以supervisord进行管理)
一个简单的fabric部署脚本类似这样:
with settings(warn_only=True): out = local("ps ax | grep appname.py | awk '{ print $1; }'") if out and len(out) > 0: local("kill {0}".format(out[0])) local("hg pull") local("hg update") local("pip install -r requirements.txt") local("/path_to_virtualenv/appenv/bin/python appname.py")
功能就是检查是否已经运行了应用,如果运行则kill之,更新代码后重新运行。
这里为简单起见,没有使用wsgi server和supervisord。
这个做法存在几个问题:
首先是你需要自己手工准备一个基础环境。其次是不方便部署到多台机器。
接下来看看docker是怎么做的。
docker的部署方式
首先是docker image的准备。基础环境可以直接用前一篇文章里那个python3环境的image例子用的Dockerfile,这里不再重复。
应用环境同样可以用那个例子,区别无非是加上代码更新,而这个更新可以在build之前就在host上运行一次hg pull/hg update更新好(或者直接使用jenkins这种自动化部署工具来更新并部署),之后再build。
所以,需要做的工作只是写一个部署脚本:
def deploy_app(appname, skipbuild=False): if not skip_build: with settings(warn_only=True): local("docker tag {appname} {appname}:old".format(appname=appname)) local("docker rmi {appname}".format(appname=appname)) local("docker build -t {appname} ..".format(appname=appname)) out = local("docker ps -a", capture=True) pat = re.compile("([0-9a-f]{{12}})\s+{0}".format(appname)) m = pat.findall(out) if len(m) > 0: local("docker rm -f {0}".format(m[0])) with settings(warn_only=True): local("docker rmi {0}:old".format(appname)) out = local("ip route | awk '/docker0/ { print $NF }'", capture=True) host_ip = "--add-host=dockerhost:{0}".format(out) local("docker run -d -p 8080:8080 --name={appname} {host_ip} -v /var/www/log:/var/www/log {appname}".format(appname=appname, host_ip=host_ip))
这样只要运行:
fab deploy_app:appname=appname
如果不需要重新构建image,还可以这样直接启动container:
fab deploy_app:appname=appname,skipbuild=True
本文毕竟号称入门实践,到这里基本算是入完门了,剩下就看你自己的。
思考
如果要部署多个版本分别用于开发、测试、生产,每个版本有各自不同的配置文件的情况要如何做?
推送到[go4pro.org]