阿里云
阿里云多端小程序中小企业获客首选
发表主题 回复主题
  • 609阅读
  • 0回复

[环境部署]Docker实用指南:将Python Web应用容器化

级别: 论坛版主
发帖
371
云币
447

前言 u5V<f;  
Web应用随时可能被攻击者利用来夺取整个主机的权限,这是很常见也是很恐怖的一件事。为了更高的安全性,就需要将不同应用之间进行隔离(尤其是在这些应用属于不同的用户的情况下),然而这种隔离的实现一直是个挑战。到目前为止,隔离性的实现方法已经有了很多,然而它们要么太过昂贵(时间的层面以及资源的层面),要么太过复杂(无论对开发者还是对管理员)。 9^v|~f  
^yy\CtG  
7fI[yCh  
本文将讨论如何让“容器化”的Python Web应用跑在安全的沙箱里,严格的坚守在其各的环境中(当然,除非你指定它们与其他应用进行“连接”)。我将一步一步的介绍如何创建一个Docker容器,如何用这个容器来跑我们的Python Web应用,以及如何用一个Dockerfile来描述整个构建过程以实现完整的自动化。 JF: QQ\  
YwoytoXK  
e>nRJH8pK  
目录 Z>o;Yf[  
1. Docker概述 0z#+^  
2. 在Ubuntu上安装Docker -T4?5T_  
3. 基本的Docker命令 JP!$uK{u  
Docker的守护进程与命令行 pSE"] N  
Docker命令 S.t+HwVodO  
4. 创建Docker容器作为Python WSGI应用的沙箱 zclt2?  
在Ubuntu里创建打底Docker容器 fhMtnh:  
安装前的准备工作 8@6:UR.)  
安装Python工具 ;?=] ffa{  
安装我们的Web应用以及其依赖项 Yi7`iC  
配置我们的Python WSGI应用 J:#B,2F+^  
5. 创建Dockerfile以实现镜像创建的自动化 ;]Y.2 J  
Dockerfile概述 S$BwOx3QF  
Dockerfile命令一览 W qci51y>#  
创建Dockerfile P~ffgzP  
定义基本项 J%j#gyTU  
更新默认的应用仓库 VH#]67  
安装基础工具 Nf-IDK  
Python基础套装安装指南 HxU.kcf  
部署应用 t+m$lqm  
引导 hJhdHy=U  
最终的Dockerfile `+1*)bYxU  
使用Dockerfile进行自动化的容器创建 :#d$[:r#  
Docker概述 %9{4g->  
Docker项目提供了一些可以搭配使用的上层工具,这些工具基于Linux内核的一些功能创建。整个项目的目标是帮助开发者和系统管理员门无痛的迁移应用(以及它们所涉及的所有依赖项),让应用在各种系统和机器上都能欢快的跑起来。 :!g|0CF_  
{Swou>X4  
qO&:J\d  
实现这个目标的关键在于一个叫做docker容器的运行环境,这个环境实际上是一个具备安全属性的LXC(Linux Containers)。容器的创建使用了Docker镜像,Docker镜像可以手动敲命令创建,也可以通过Dockerfiles实现自动化创建。 f`/JY!u j{  
9zi/z_G  
cg1<  
注:关于Docker的基础知识(守护进程、CLI、镜像等等),可参阅本系列的第一篇文章Docker Explained: Getting Started。 xx!o]D-}  
*bmk(%g  
p7zHP  
在Ubuntu上安装Docker Mgcq'{[~Y=  
最新版本的Docker(译注:本文撰写时间为2013年12月17日,当时的Docker最新版本为0.7.1)可以在Ubuntu/Debian和CentOS/RHEL等多个Linux发行版上部署(你也可以使用DigitalOcean上现成的Docker镜像,该镜像基于Ubuntu 13.04创建)。 53BXz= k  
#mx;t3ja7  
c)iQ3_&=  
下面快速介绍一下Ubuntu上的安装流程。 | 2p\M?@  
rIW`(IG_  
*7BY$q  
在Ubuntu上安装Docker  ?o9l{4~g  
更新系统: h^x7[qe  
I[tU}ojP  
Fypqf|  
sudo aptitude    update 5!tmG- 'b  
sudo aptitude -y upgrade ,dZ 9=]  
2r PKZ|  
xhg{!w  
检查系统是否支持aufs: G\jr^d\  
-7m;rD4J  
zrnc~I+  
sudo aptitude install linux-image-extra-`uname -r` PJO;[: .I  
,_\h)R_  
D!qtb6<.  
往apt-key添加Docker仓库的密钥(用于软件包的验证): ' ["Y;/>  
,A[HYc|uy  
+1h^9 Y'  
sudo sh -c "wget -qO- https://get.docker.io/gpg | apt-key add -" <C${1FO7If  
K8284A8v  
Nm OQ7T  
往aptitude软件源添加Docker仓库: 0vQkm<  
.Ddl.9p5  
Pvbw>k;  
sudo sh -c "echo deb http://get.docker.io/ubuntu docker main\ g&_0)(a\  
> /etc/apt/sources.list.d/docker.list" K{q(/>:  
f4@Dn >BJ  
Y*w< ~m  
添加之后再更新一次系统: UT[KwM{y  
B: {bmvy  
>A2& Mjo  
sudo aptitude update P d*}0a~  
_ogT(uYyr  
8&dmH&  
最后,下载并安装Docker: #H7(dT  
I>9rfmmTI  
zg8m(=k'  
sudo aptitude install lxc-docker /SN.M6~  
<`6-J `.  
[DjdR_9*I  
Ubuntu默认的防火墙(UFW)的默认设置是拒绝一切转发(forwarding),但是Docker需要转发,所以也需要设置一下UFW。 2Ybz`O!  
jZm1.{[>  
y}#bCRy~.A  
用nano编辑器打开UFW配置文件:   ;h  
C_'Ug  
?+`Zef.g  
sudo nano /etc/default/ufw QKCk. 0Xe  
vcV=9q8P1  
VZ_ 4B *D  
找到DEFAULT_FORWARD_POLICY这一行,将 W&+UF'F2  
eDTEy;^o  
HnOp*FP  
DEFAULT_FORWARD_POLICY="DROP" vQBfT% &Q-  
p1X lni%=  
\Sm.]=b r  
替换为: N'2u`br4KP  
wXNFL9F8  
~rn82an@G  
DEFAULT_FORWARD_POLICY="ACCEPT" b?HW6Kfc  
/!7m@P|&D  
Y\9uR!0  
按 CTRL+X 再按 Y 键,保存退出。 UN,@K9  
B8w 0DJ  
#a}fI  
最后,把UFW重启一下: p|AIz3  
6S`eN\s  
6aMqU?-  
sudo ufw reload O\lt!p3F  
a*%>H(x  
<=jE,6_|  
基本的Docker命令 =b9?r  
在正式开始之前,我们还是先复习一下上次在基础篇中介绍过的一些基本命令。 Cww$ A %}  
{f3T !e{  
P8;|>OLZ)  
Docker的守护进程与命令行 oD!72W_:  
一般来说,Docker守护进程在你安装完成了之后就已经在后台运行,等待接收来自Docker命令行的指令。不过有的时候我们也需要手动启动Docker守护进程: PiAA,  
G}f.fR Y  
-Ux/ Ug@  
sudo docker -d & CS6,mX  
~}wPiu,  
T7!a@  
Docker命令行的基本语法如下: Ybd){Je"z  
6,s@>8n  
]km8M^P  
sudo docker [option] [command] [arguments] rD<@$KpP  
yfuvU2nVH  
oa;[[2c  
注:Docker的运行需要sudo权限。 2eb :(D7Cq  
JUwP<C[  
#2x\d  
Docker命令 cWh Aj>?_Q  
下面列出了目前可用的Docker命令(译注:可以参考InfoQ中文站文章深入浅出Docker(二):Docker命令行探秘): mn 8A%6W  
c67O/ B(  
(AR-8  
attach    附着到一个运行的容器上 Ct"h.rD]  
build     从一个Dockerfile建立镜像 %2/WyD$U  
commit    将一个变更后的容器创建为一个新镜像 > -k$:[l  
cp        在容器和本地文件系统之间复制文件/目录 O^sgUT1O  
create    创建一个新的容器 7jEAhi!Cq(  
diff      检测容器文件系统的变更 OEZXV ;F  
events    从服务获取实时事件 80:na7$)#  
exec      在一个运行中的容器内执行命令 ap_(/W  
export    将一个容器的文件系统输出为tar压缩包 ilzR/DJMa  
history   显示一个镜像的历史 b4,yLVi<T  
images    列出镜像列表 |$6Gp Aq!  
import    从tarball压缩包导入内容以创建一个文件系统镜像 4my8 p Fk  
info      显示系统信息 xa!@$w=U&  
inspect   返回容器或镜像的底层信息 Dr`A4LnqY  
kill      杀死一个运行中的容器 6=|Q>[K  
load      从一个tar压缩包或STDIN加载一个镜像 QN*'MA"M  
login     登入Docker注册表(Docker registry) GAz;4pUZ  
logout    从Docker注册表登出 z2nDD6N  
logs      抓取一个容器的日志 Lqwc:%Y:_  
network   管理Docker网络 ?t?!)#X  
pause     暂停一个容器内的所有进程 {e>}.R  
port      列出该容器的所有端口映射或指定端口映射 V{c n1Af  
ps        列出容器列表 5n?fZ?6(  
pull      从注册表拉取一个镜像或仓库 (QqKttL:  
push      往注册表推送一个镜像或仓库 u~a<Psp&|  
rename    重命名容器 O% 9~1_  
restart   重启容器 l|5ss{llR  
rm        删除一个或多个容器 -C.eXR{s  
rmi       删除一个或多个镜像 Fvi<5v  
run       在一个新的容器中运行一条命令 8G(wYlxi  
save      将镜像保存至tar压缩包 syBYH5  
search    在Docker Hub搜索镜像 . VWH  
start     启动一个或多个容器 ~Dgui/r9J  
stats     显示容器资源使用情况的实时信息流 +F4xCz7f  
stop      停止一个运行中的容器 @I_ A(cr  
tag       向注册表标记一个镜像 ioh_5 5e  
top       显示一个容器下运行的进程 uXtfP?3Vy  
unpause   恢复运行一个容器里所有被暂停的进程 [(UQQa=+  
update    更新容器的资源 GRlA 9Q  
version   显示Docker版本信息 Q:@Y/4=  
volume    管理Docker卷 DF9Br D0{  
wait      阻塞对指定容器的其他调用方法,直到容器停止后退出阻塞。 >(d+E\!A  
QWfwoe&;R:  
S@Jl_`<  
创建Docker容器作为Python WSGI应用的沙箱 _khQ  
我们已经完成了Docker的安装,也熟悉了基本的命令,现在可以为我们的Python WSGI应用创建Docker容器了。 IKAF%0[R|j  
1!BV]&,[  
*4=Fy:R]O  
注:本章介绍的方法主要是练习用,并不适合于生产环境。生产环境下适用的自动化流程将在后续章节中介绍。 N!Kd VDdT|  
kD"dZQx  
NE3G!qxL  
在Ubuntu里创建打底Docker容器 l,sYYU+iY  
Docker的run指令会基于Ubunt镜像创建一个新的容器。接下来,我们要用 -t 标识给这个容器附着(attach)一个终端,并运行一个 bash 进程。 YHCXVu<.b  
${e(#bvGZ  
vT3LhN+1  
我们将暴露这个容器的80端口用于从外部访问。以后在更加复杂的环境中,你可能需要给多个实例做负载均衡,把不同的容器“连接”起来,再用一个反向代理容器去访问它们。 LOe!qt\&  
`4Nc(aUr  
x"U/M ?l  
sudo docker run -i -t -p 80:80 ubuntu /bin/bash :tR%y"  
SLZv`  
rA{h/T"  
注:运行这个命令时,Docker可能需要先下载一个Ubuntu镜像,下载完毕后才创建新容器。 4#4kfGoT  
O(WFjmHx  
UDuKG\_J<y  
注意:你的终端会“附着”(attach)在新创建的容器上。要与容器分离并回到之前的终端访问点,可以按 CTRL+P 接着 CTRL+Q 执行脱离操作。“附着”在一个Docker容器上,基本上相当于从一个VPS内部访问另一个VPS。 f~W.i]  
N9{ivq|fO  
bu@Pxz%_  
从脱离的状态想要回到附着的状态,需要执行如下步骤: nc@ul')  
2Uu,Vv  
TvM{ QGN  
用 sudo docker ps 列出所有运行中的容器 ^JY R^X>_  
找到之前创建的那个容器的ID MS,H12h  
执行 sudo docker attach [id] 完成当前终端到该容器的附着 >+1^XeeS  
注意:我们在容器内部做的一切操作都将仅限于在容器内部执行,对宿主机是完全没有影响的。 T|9Yo=UK%  
=U|J{^ >I  
1Kwl_jf  
安装前的准备工作 .%~ L  
要在容器内部署Python WSGI应用(以及我们所需要的工具),首先我们需要对应的软件仓库。然而在Docker默认的Ubuntu镜像里并没有提供仓库(Docker的设计者认为这样有利于保持事物的简化),因此我们需要给我们这个打底镜像添加Ubuntu的universe软件仓库: mb\vHu*53  
;zz"95X7  
*7!}[ v_  
echo "deb http://archive.ubuntu.com/ubuntu/ $(lsb_release -sc) main universe" >> /etc/apt/sources.list Mzxz-cE  
z[OW%(vrm  
)Tb{O  
更新一下软件列表: rKi)VVkx_  
7"8HlOHA  
UP,0`fh(y  
apt-get update azOp53zR  
kC LeHH|K  
W,wg@2  
再给我们的容器安装一些必要的工具: >@]E1Qfe  
e(!a~{(kq%  
qJISB7F[%O  
apt-get install -y tar \ [C7:Yg7  
                   git \ Z/Mp=273  
                   curl \ &TC  
                   nano \ (v(_ XlMK  
                   wget \ X*FK6,Y|(  
                   dialog \ eZ  ]6 Q  
                   net-tools "q]v2t  
                   build-essential cH2 nG:H  
bM;tQ38*  
D(Xv shQ  
安装Python工具 -AcQ_dS  
本文将用一个简单的Flask应用作为示范。如果你用的是其他框架也没关系,安装部署的方法都是一样的。 @6t3Us~/  
i37W^9 R  
QW~o+N~~  
再提醒一次:以下所有的命令都是在容器内部执行的,不会影响到宿主机。你可以想象成自己在一个全新的VPS上进行操作。 4I,@aj46  
Txp~&a03  
%M'"%Yn@(y  
安装Python和pip: p&ow\A O  
XtQ3$0{*%  
/md`tqI>i<  
# 安装pip依赖:setuptools -,XS2[  
apt-get install -y python python-dev python-distribute python-pip L6Ynid.k  
<u^41  
U ][.ioc  
安装Web应用以及其依赖项 faMUd#o&  
安装我们的应用之前,还是让我们再确认一下所有的依赖都已经就绪。首先是我们的框架——Flask。 [a+?z6qI\}  
QP\vN|r  
z{ymVd0#  
因为我们已经装好了pip,所以就直接用pip来安装Flask: %*jpQOw  
/Ee0S8!Z!1  
BE3~f6 `  
pip install flask M{nz~W80  
75^-93  
.|iUDp6vz  
装好了Flask,创建一个“my_application”文件夹: =U,;/f  
- MBK/  
=}Cb?C[;  
mkdir my_application V8,$<1Fi;-  
)hQ`l d7B  
^[ id8  
cd my_application )<3WVvB  
2;kab^iv'  
_IgG8)k;  
注:如果你想直接部署自己的应用(而不是这里的示范应用),可以参看下面的“小贴士”部分。 ?G8 D6  
@${!C\([1  
F.@yNr"  
我们的示范应用是一个单页面的“Hello World” Flask应用。下面用nano来创建app.py: Wt4!XV  
w$& 10  
[&Qrk8EN  
nano app.py qv,|7yw{  
D/{-  
Gt 2rJ<>  
把下面这些内容复制到新创建的文件里: my04>6j0  
sL!6-[N  
SSH/q/  
from flask import Flask g8vN^nQf[  
app = Flask(__name__) *1elUI2Rg  
NghQ#c  
E^|b3G6T  
@app.route("/") o >W}1_  
def hello(): @c{Z?>dUc#  
    return "Hello World!" ]cA){^.Jz  
KIuYWr7&  
}>p)|Y T"/  
if __name__ == "__main__": }K&K{ 9}  
    app.run() $?'z%a{  
>j$f$*x  
vKI,|UD&-  
按 CTRL+X 再按 Y 键,保存退出。 #b{otc)  
- +>~  
5g/WQo\  
或者,你也可以使用“requirements.txt”来定义应用的依赖项(比如Flask)。我们还是用nano来创建文件: OCR x|  
3[8'pQ!&  
t#<q O6&B  
nano requirements.txt Nzr zLK  
||/noUK  
Pp26UWW  
在文件输入你所有的依赖项(下面只列出两个,如果你需要别的请自行添加): u}u2{pO!  
qOk4qbl[  
,@[Q:fY  
flask ,Q}/#/  
cherrypy 43 |zjE  
P;A"`Il  
SIbQs8h]  
按 CTRL+X 再按 Y 键,保存退出。 Oys.8%+ P  
)iEK7d^-  
Q&n|tQ*4  
注:你可以用pip来生成自定义的依赖项列表。具体的操作方法可以参考这篇Common Python Tools: Using virtualenv, Installing with Pip, and Managing Packages。 U); ,Opr  
d)dIIzv  
I.\u2B/?  
最后,我们这个应用的文件组织结构是这样的: a2x2N_\=/D  
9hLPo  
SHh g&~B  
/my_application b;S~`PL  
    | *]F3pP[  
    |- requirements.txt  # 描述依赖项的文件 8zZR %fZ  
    |- /app              # 应用模块(你的应用应该在这个目录下) vwP516EM  
    |- app.py            # WSGI文件,里面应该包含“app”的实例名称(callable) d eTUfbd'  
    |- server.py         # 可选,用于运行应用服务器(CherryPy) {!E<hQ2<$9  
UpB7hA  
BHU(Hd  
注:关于“server.py”,请参阅下面的章节“配置我们的Python WSGI应用”。 T@k&YJ  
Y B)1dzU  
"jAV7lP  
注意:上面这些应用的文件、目录都是在容器内部创建的。如果你要在宿主机上自动化构建镜像(这个过程将在下面有关Dockerfile的章节中介绍),则你的宿主机上放置Dockerfile的目录下也需要同样的文件结构。 S: /ShT  
@tp/0E?  
]c$%;!ZE  
小贴士:部署自己的应用 ^S, "i V  
在容器内部搞定应用所需的软件仓库和依赖项 @*_#zU#g  
上述步骤描述了在容器内创建应用目录的过程。然而在真实场景下,我们往往需要从软件仓库拉取源代码。 )(?s=<H  
BNaZD<<  
<!=TxV>}A  
要把你的软件仓库复制到容器内部,有几个方法可以实现。下面介绍其中的两个: )w/f 'fq  
@Z.Ne:*J  
i?|K+"=D  
# 方法1 g]jtVQH']  
# 用git下载源代码 R_Eu*Qu j  
# 用法:git clone [源代码所在的URL] vy-{BH  
# 示范: H&%=>hyX  
git clone https://github.com/mitsuhiko/flask/tree/master/examples/flaskr :G}tvFcOAF  
{4:En;  
A7: oq7b  
# 方法2 FeCQGT  
# 下载源代码压缩文件 8N|*n"`}  
# 用法:wget [源代码压缩文件所在的URL] 8t6h^uQ  
# 示范:(用真实的URL替换掉下面这个假的) tkJ/ h<  
wget http://www.github.com/example_usr/application/tarball/v.v.x /G84T,H  
s"a*S\a;b  
/=Xen mmS  
# 解压缩文件 2RM1-j ($  
# 用法:tar vxzf [文件名 .tar (.gz)] %DRDe  
# 示范:(用真实的文件名替换掉下面这个假的) s  fti[  
tar vxzf application.tar.gz 8vUP{f6{  
O4-UVxv}  
([$F5 q1TR  
# 用pip下载安装应用依赖 R>Zn$%j\  
# 下载 requirements.txt (可以用 pip freeze output 生成),再用pip全部安装: -Fl3m  
# 用法:curl [requirements.txt 文件的URL] | pip install -r - 29Gej Lg |  
# 示范:(用真实的URL替换掉下面这个假的) [N$@nA-d  
curl http://www.github.com/example_usr/application/requirements.txt | pip install -r - =`/X Wem  
h]'fX  
W<9G wMU  
配置我们的Python WSGI应用 huN(Q{fj  
要运行这个应用,我们需要一个Web服务器。运行这个WSGI应用的Web服务器需要安装在代码所在的同一台容器中,作为该Docker容器运行的进程。 {0QA+[Yd&!  
}hBv?B2/1  
_abVX#5<  
注:我们在示范中将使用CherryPy自带的HTTP Web服务器,这是一个比较简单而且可以用在生产环境的选择。你也可以用Gunicorn甚至uSWGI(可以让它们跑在Nginx的后面),我们其他的教程中介绍过这种用法。 K1eoZ8=!  
wOa_"  
? th+~dE  
用pip下载安装CherryPy: EIF[e|kZ<  
=jG3wf*  
bF flA  
pip install cherrypy uM4,_)L  
"= %-  
9xu&n%L=  
创建“server.py”,用于服务“app.py”里面的Web应用: %/>_o{"hw  
Riw>cVi~  
'.^JN@  
nano server.py 7unu-P<C  
7,O^c +  
*}2o \h6Q  
把下面的内容复制粘贴到server.py里: bqLv81V  
Bq2}nDP  
Z1OcGRN!  
# 导入应用的语法: 1yE~#KpH  
# from app import application zd;xbH//)b  
# 示范: 9(B)  
}Rf } iG  
KztF#[64W^  
from app import app ?Ts Z_  
yBr{nFOgdY  
:!#-k  
# 导入 CherryPy VLOyUt~O#  
import cherrypy o-(jSaH :;  
z?i82B[Tm  
=|G PSRQ  
if __name__ == '__main__': 4 A  
FEP\5d>  
"iKK &%W  
    # 挂载应用 fh2Pn!h+  
    cherrypy.tree.graft(app, "/") f.8L<<5 c  
=Of#Ps)  
%8$wod6  
    # 从默认服务器上分离 {<|0M%v  
    cherrypy.server.unsubscribe() -'I)2/%g  
0Ifd!  
g|T' oK  
    # 实例化一个新的服务器对象 iI _Fbw8  
    server = cherrypy._cpserver.Server() cq!> B{  
^y h  
c[;I\g  
    # 配置该服务器对象 2[:`w),.  
    server.socket_host = "0.0.0.0" 2h/` RefHJ  
    server.socket_port = 80 sB"]R%`_  
    server.thread_pool = 30 HAU8H'h  
QX8N p{g-  
Oi{jzP  
    # SSL相关配置 F4:ssy^  
    # server.ssl_module            = 'pyopenssl' SWmdU]  
    # server.ssl_certificate       = 'ssl/certificate.crt' w.V8-9{  
    # server.ssl_private_key       = 'ssl/private.key' mM95BUB  
    # server.ssl_certificate_chain = 'ssl/bundle.crt'  a1j 6-p  
*=ALns?y  
a(<nk5  
    # 订阅这个服务器对象 ?Jx8z`(  
    server.subscribe() M9iu#6P  
 %RJW@~!  
kdF# Nm  
    # 启动服务器引擎 &-&6ARb7o  
muY4:F.C(  
|M+ !O93  
    cherrypy.engine.start() ho0T$hB  
    cherrypy.engine.block() Nlo*vu  
iveWau292  
vk$]$6l2  
完成!现在我们就有了一个“Docker化”的Python Web应用,安全的跑在自己专属的沙箱里。只要输入下面的一行命令,它就可以给成千上万个客户端请求提供服务: oinF<-(  
Ok,hm.|  
jd$lu^>I  
python server.py J \G8 g,@  
K.1#cf ^'  
F02NnF  
这是让服务器在前台运行的指令。按下 CTRL+C 终止运行。如果想在后台运行服务器,可输入下面的指令: Z*leEwgz  
N L~}  
6r^(VT  
python server.py & 2"2b\b}my  
dhV =;'   
7Xw #  
后台运行的应用需要用进程管理器(比如htop)来终止运行(kill或stop)。 I*'QD)  
"y62Wo6m)  
sl`\g1<{`  
注:有关CherryPy上跑Python应用的配置,可参阅这篇教程:How to deploy Python WSGI apps Using CherryPy Web Server。 !d^5mati)T  
) |*HkdF`  
v k<By R  
简单的测试一下应用的运行状态(以及端口的分配状态):在浏览器中访问 http://[容器所在的VPS的IP地址] ,应该能够看到“Hello World!”。 rq+E"Uj?  
tEZ@v(D  
s,lrw~17  
创建Dockerfile以实现镜像创建的自动化 e6,/ i  
上面简单的说过,手动创建容器的这个方法并不适合用于生产环境的部署。生产环境里应该用Dockerfile进行构建流程自动化。 _g 3hXsA  
@ VVBl I  
ix6j=5{  
我们已经知道了如何在容器内部进行外部资源的下载和安装,那么Dockerfile其实也是一样的原理。一个Dockerfile定义了Docker要如何生成一个镜像,这个镜像可以直接用来跑我们的Python应用。 bT0CQ_g21  
\_0nH`  
qm] k (/w  
先了解一下Dockerfile的基本功能。 jsH7EhF{'  
L%I@HB9-Q0  
vz/.*u  
Dockerfile概述 ^~( @QfY  
Dockerfile是一种脚本文件,其中包含了一系列顺序执行的命令,Docker通过执行这些命令就可以创建一个新的Docker镜像。这极大的方便了部署。 yB,$4:C  
\za 0?b  
{rf.sN~M  
Dockerfile一般会先用 FROM 命令定义一个打底的镜像,然后执行一系列的动作,动作全部执行完毕之后就形成了最终的镜像,并将完成的镜像提交给宿主机。 evz{@;.R  
eG=Hyc  
4f'1g1@$  
使用: |*K AqTO0  
!S$LRm\ '  
/X; [ 9&  
# 在当前位置用Dockerfile创建一个镜像 {OH "d  
# 将生成的镜像标记为 [name] (比如nginx) j/ow8Jmc*  
# 示范:sudo docker build -t [name] . !^:b?M  
sudo docker build -t nginx_img . LJ\uRfs  
STtjkZ6  
XE6sFU  
注:我们还有一篇专门介绍dockerfile的文章可供查阅:Docker Explained: Using Dockerfiles to Automate Building of Images :Z3Tyj}4  
>kU$bh.(  
Gx,<|v  
Dockerfile命令一览 XOe)tz L  
### Add qkPvE;"  
从宿主机复制文件到容器 S ( e]@  
J!YB_6b  
:2ILN.&  
KP,#x$Bg  
aQzx^%B1  
### CMD 4L)#ku$jW  
设置要执行或者要发给ENTRYPOINT的默认命令 Y=6569U2  
lC|`DG-B  
!8~A`  
.z0NMmz0z  
R2,Z`I  
### ENTRYPOINT &6deds  
设置容器内默认要启动的应用 1u>[0<U~E  
>h)D~U(H  
N)CM^$(T|  
lrQNl^K}=  
.=G ?Zd  
### ENV _?~EWT   
设置环境变量(key = value) [A\DuJx  
Q 8| C>$n  
aN UU' [  
### EXPOSE `s8*n(\h  
暴露一个端口 D`c&Q4$:  
+6~ut^YiM.  
T"IDCT'z  
### FROM -qyhg-k6  
设置打底镜像(base) TF_wT28AU2  
:Eb=jWA  
`@3{}  
### MAINTAINER W79Sz}):  
设置Dockerfile的作者/所有者信息 pG&#xRk  
Nb3uDA5R  
GD-L0kw5  
### RUN {Qba`lOkq  
执行一条命令并提交执行后的(容器)镜像 GAP,$xAaW  
Yj"{aFK#u@  
/$NR@56 \  
### USER |1%eo.  
设置从镜像运行容器的用户名 3D}Pa  
gT_tR_g  
J.(mg D  
### VOLUME A1Es>NK[qW  
从宿主机加载一个目录给容器 Xl,707  
'ws@I?!r  
oL U!x  
### WORKDIR #g#[|c.  
设置CMD运行时所在的目录 l@:&0id4I  
a)S{9q}%  
 5fq4[a  
创建Dockerfile L[voouaqm  
在当前路径下用nano编辑器创建Dockerfile: Hb3..o:  
9 lXnNK |]  
C1==a FD  
sudo nano Dockerfile eGW h]%  
1 8b"vXNB.f  
注:下面的内容需要按顺序添加到Dockerfile中。 14`S9SL{V  
R$x(3eyx  
OIs!,G|  
定义基本项 LJSx~)@  
Dockerfile的基本项包括 FROM 原始镜像(比如Ubuntu)以及维护者姓名 MAINTAINER: Tmk'rOg5  
[(; .D  
 p ~pl|  
############################################################ ,f~)CXNT?  
# 创建Python WSGI应用容器的Dockerfile b63DD(  
# 基于Ubuntu qtjx<`EK>  
############################################################ )}J}d)  
[dy0aR$>d  
19bqz )  
# 设置Ubuntu为打底镜像 79 \SbB  
FROM ubuntu [U,hb1Wi3  
m'KEN<)s  
nTE\EZ+=2  
# 文件作者/维护者 ] u\-_PP  
MAINTAINER Maintaner Name n){u!z)Al  
x|*m ok  
|ADg#oX  
更新默认的应用仓库 # ';b>J  
# 添加软件资源库的URL qgxGq(6K  
RUN echo "deb http://archive.ubuntu.com/ubuntu/ $(lsb_release -sc) main universe" >> /etc/apt/sources.list ] V,#>'  
+FC+nE}O  
>&JS-j Fg  
# 更新资源列表 "d*-k R  
RUN apt-get update Dc3bG@K*G  
lT8^BT  
;m"R.Q9*  
安装基础工具 NkxW*w%}l  
RUN apt-get install -y tar git curl nano wget dialog net-tools build-essential fS4 Ru  
in>?kbaG+  
W . dm1  
注:上述的有些工具可能你用不到,不过为了以防万一还是都装进来先。 } DjbVYH  
} O:l]O`  
U3pMv|b  
Python基础套装安装指南 #VuiY  
一些Python需要的工具(pip)最好还是都先装起来。你的框架(WAF)和Web服务器(WAS)都需要有它们才能安装。 /< \do 1  
2SYV2  
uXdR-@80*  
RUN apt-get install -y python python-dev python-distribute python-pip O/ZyWT  
30QQnMH3  
t7/a5x  
部署应用 b|U3\Fmc  
部署应用可以使用Docker的 ADD 命令直接复制源代码,也可以用 REQUIREMENTS 文件来一步到位。 8'0KHn{#  
GTW5f  
JjG>$z  
注:如果你打算用一个文件描述所有的代码位置,可以参考下面的文件结构。 -l$-\(,M`#  
$B@K  
ys9MV%*  
    文件结构示范 1y.!x~Pi,  
vUg o)C#<  
6}q# c  
    /my_application ] dHV^!  
    | X-5&c$hv  
    |- requirements.txt  # 描述依赖项的文件 PxgLt2dXa  
    |- /app              # 应用模块(你的应用应该在这个目录下) "3VMjF\  
    |- app.py            # WSGI文件,里面应该包含“app”的实例名称(callable) 6n'XRfQp)&  
    |- server.py         # 可选,用于运行应用服务器(CherryPy) u )KtvC!  
>=ng?  
D(Yq<%Q  
这个文件结构的创建过程在前面的章节中已经介绍过,这里不再赘述。总之,以上述文件结构为例,则再给Dockerfile末尾添加如下内容,将源代码复制到容器内: u9)<i]2  
] 73BJ  
.p(~/MnO  
ADD /my_application /my_application .\b# 0w  
f m'Qif q^  
06vxsT@  
如果源代码是公网的git仓库,则可以使用如下内容: `K \(I#z  
|%-:qk4rG  
""^.fh  
RUN git clone [你的源码仓库URL] |DMa2}%  
C6(WnO{6  
m' suAj0  
引导 ;&G8e* bM2  
接下来,再从 requirements.txt 导入所有的依赖项: 9% AL f 9  
Yet!qmZ  
J2adA9R/,  
# 用pip来下载安装 requirements.txt 里面的东西 (@1*-4l  
RUN pip install -r /my_application/requirements.txt y~pJ|E  
OA3* "d*  
^gD%#3>X  
# 暴露端口 b ZEyP W  
EXPOSE 80 GS4 HYF  
DEkFmmw   
{^VvL'n  
# 设置CMD运行的默认路径 53cW`F  
WORKDIR /my_application ?4i:$.A Y  
-O-qEQd  
`* =Tf  
# 要运行的默认命令 9@."Y>1G  
# 该命令在新容器创建时开始执行 px1{=~V/  
# 比如启动CherryPy来运行服务 '9MtIcNb  
CMD python server.py .<C}/Cl  
)m Uc !TP  
BI,j/SRK  
最终的Dockerfile Qpc+1{BQ  
现在,整个Dockerfile看起来应该是这样的: tj_+0J$sw:  
o(I[_oUy\  
HV??B :  
############################################################ tGs=08`  
# Dockerfile to build Python WSGI Application Containers `_6!nk q8  
# Based on Ubuntu 9 1r"-%(r  
############################################################ FN&.PdRT  
TB gD"i-  
rI5)w_E?  
# Set the base image to Ubuntu z6K"}C%  
FROM ubuntu 4 }l,F  
/ W}Za&]  
s|U?{Byb!  
# File Author / Maintainer MEQ :[;1  
MAINTAINER Maintaner Name F(8>"(C  
lbv9 kk[  
a(X?N.w  
# Add the application resources URL BC0T[o(f8  
RUN echo "deb http://archive.ubuntu.com/ubuntu/ $(lsb_release -sc) main universe" >> /etc/apt/sources.list 9IKFrCO9,  
4425,AR  
%p}_4+[;  
# Update the sources list &Z_W*D  
RUN apt-get update d* Y&V$?zl  
zA&lJD $0  
xV'\2n=1T  
# Install basic applications N9D<wAK##)  
RUN apt-get install -y tar git curl nano wget dialog net-tools build-essential |Ebwl]X2  
Memz>uux  
Z@=1-l  
# Install Python and Basic Python Tools *]fBd<(8  
RUN apt-get install -y python python-dev python-distribute python-pip c<j2wKz  
/a\6&Eb  
|~bl%g8xP  
# Copy the application folder inside the container 8 kw`=wSH>  
ADD /my_application /my_application 5tg  
xT I&X9P  
2l#Ogn`k  
# Get pip to download and install requirements: o Q= Q}  
RUN pip install -r /my_application/requirements.txt g*b`V{/Vw  
=yiRB?  
nZa.3/7dJ  
# Expose ports iIq='xwa9  
EXPOSE 80 {Q[{H'Oa  
@^:R1c![s  
o~,dkV  
# Set the default directory where CMD will execute _*1/4^  
WORKDIR /my_application {[PoLOCI  
w1/p wzn  
OS6 l*S('  
# Set the default command to execute     @gQ{*dN  
# when creating a new container AoL4#.r3H  
# i.e. using CherryPy to serve the application q4<3 O"c1  
CMD python server.py v`MCV29!}  
rIg1]q  
 Q0' xn  
按 CTRL+X 再按 Y 键,保存退出。 d6-a\]gF  
jpZ, $  
bR6.Xdt.n  
使用Dockerfile进行自动化的容器创建 Ln0rm9FV-  
在之前的基础教程部分我们提到过,Dockerfile的工作方式利用到了 docker build 命令。 |ul25/B B  
yf+M  
V*rAZ0  
我们通过Dockerfile指示docker从包含源代码的路径复制内容到容器内部,因此在构建之前务必要确认Dockerfile与代码路径的相对位置。 VS<E?JnbFV  
('pNAn!]  
f8kPbpV,  
这样一个Docker镜像可以快速创建起来一个可以运行我们的Python应用的容器,我们需要做的只是输入这样一行指令: r k;k:<c  
 y|U3  
\Q<c Y<  
sudo docker build -t my_application_img . Y;d$x}dh  
7A?~a_Ep  
uyMxBc%6  
我们把这个镜像命名为 my_application_img 。为要从这个镜像启动一个新的容器,只需要输入下面的命令: TaeN?jc5  
m!5P5U x  
eWSA  
sudo docker run -name my_application_instance -p 80:80 -i -t my_application_img Aa-L<wZVPt  
*,X;4?:,  
ya3A^&:  
然后就可以在浏览器里输入你VPS的IP地址,访问应用了。 0Lmq?D  
B}8xA}<  
*ndXZ64  
有关Docker安装的更多教程(包括在其他发行版上安装Docker),可以查阅我们在docker.io上的文档docker installation documentation。 Yo3my>N&g  
wUj[c7Y%  
[ 此帖被寒喵在2018-12-29 19:19重新编辑 ]
本人不是云栖社区工作人员。
无论您在使用中遇到什么问题,不要出言不逊!谢谢合作!
发表主题 回复主题
« 返回列表上一主题下一主题

限100 字节
如果您提交过一次失败了,可以用”恢复数据”来恢复帖子内容
 
验证问题: 阿里云官网域名是什么? 正确答案:www.aliyun.com
上一个 下一个
      ×
      全新阿里云开发者社区, 去探索开发者的新世界吧!
      一站式的体验,更多的精彩!
      通过下面领域大门,一起探索新的技术世界吧~ (点击图标进入)