阿里云
阿里云大学认证0元起
发表主题 回复主题
  • 99阅读
  • 0回复

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

级别: 论坛版主
发帖
370
云币
634

前言 TY}?>t+  
Web应用随时可能被攻击者利用来夺取整个主机的权限,这是很常见也是很恐怖的一件事。为了更高的安全性,就需要将不同应用之间进行隔离(尤其是在这些应用属于不同的用户的情况下),然而这种隔离的实现一直是个挑战。到目前为止,隔离性的实现方法已经有了很多,然而它们要么太过昂贵(时间的层面以及资源的层面),要么太过复杂(无论对开发者还是对管理员)。 G})mw  
oj,  
uq_SF.a'v  
本文将讨论如何让“容器化”的Python Web应用跑在安全的沙箱里,严格的坚守在其各的环境中(当然,除非你指定它们与其他应用进行“连接”)。我将一步一步的介绍如何创建一个Docker容器,如何用这个容器来跑我们的Python Web应用,以及如何用一个Dockerfile来描述整个构建过程以实现完整的自动化。 'tj4;+xf^  
dEn hNPeRl  
. j },  
目录 t<=L&:<N  
1. Docker概述 H#:Yw|t  
2. 在Ubuntu上安装Docker *9PQJeyR  
3. 基本的Docker命令 TnrMR1Zx  
Docker的守护进程与命令行 ' =kX   
Docker命令 !~#31kL&  
4. 创建Docker容器作为Python WSGI应用的沙箱 >=(e}~5y  
在Ubuntu里创建打底Docker容器 &+sN= J.x  
安装前的准备工作 ? "gy`oCv  
安装Python工具 8~bPoWP  
安装我们的Web应用以及其依赖项 @QofsWC  
配置我们的Python WSGI应用 ,o`qB81  
5. 创建Dockerfile以实现镜像创建的自动化 &nss[w$%C  
Dockerfile概述 5VN4A<))  
Dockerfile命令一览 @ NF8?>!  
创建Dockerfile b]~M$y60q  
定义基本项 ME(!xI//JZ  
更新默认的应用仓库 K:0RP?L  
安装基础工具 A, os rv  
Python基础套装安装指南 5D#*lMSP"'  
部署应用 DmYm~hzJ  
引导 0-FbV,:;  
最终的Dockerfile 6\bbP>ql  
使用Dockerfile进行自动化的容器创建 Ldu!uihx  
Docker概述 }5gQZ'ys'  
Docker项目提供了一些可以搭配使用的上层工具,这些工具基于Linux内核的一些功能创建。整个项目的目标是帮助开发者和系统管理员门无痛的迁移应用(以及它们所涉及的所有依赖项),让应用在各种系统和机器上都能欢快的跑起来。 _MR2,mC  
=F`h2A;a  
P_;oSN|>  
实现这个目标的关键在于一个叫做docker容器的运行环境,这个环境实际上是一个具备安全属性的LXC(Linux Containers)。容器的创建使用了Docker镜像,Docker镜像可以手动敲命令创建,也可以通过Dockerfiles实现自动化创建。 C'Ymz`iQ  
0Ec -/   
`>V.}K^4  
注:关于Docker的基础知识(守护进程、CLI、镜像等等),可参阅本系列的第一篇文章Docker Explained: Getting Started。 `yhL11 ]~  
E,;nx^`!l  
/xmd]XM=_  
在Ubuntu上安装Docker =\3*;59\  
最新版本的Docker(译注:本文撰写时间为2013年12月17日,当时的Docker最新版本为0.7.1)可以在Ubuntu/Debian和CentOS/RHEL等多个Linux发行版上部署(你也可以使用DigitalOcean上现成的Docker镜像,该镜像基于Ubuntu 13.04创建)。 BdceINI  
Vq*p?cF .  
pyNPdEy  
下面快速介绍一下Ubuntu上的安装流程。 6x{B  
E?;W@MJi  
Qcw/>LaL:  
在Ubuntu上安装Docker }>j$Wr_h  
更新系统: T57S!CJ^$5  
5W'T7asOh  
d+bTRnL  
sudo aptitude    update wiutUb Y  
sudo aptitude -y upgrade p%?R;W`u2  
-Ju!2by  
bZ389dSn  
检查系统是否支持aufs: 4-TM3Cw`d&  
F8pLA@7[  
^Ab|\ 5^3  
sudo aptitude install linux-image-extra-`uname -r` $ }&6p6|  
Z6Mh`:7  
?e BN_a,r6  
往apt-key添加Docker仓库的密钥(用于软件包的验证): ThHK1{87X}  
m1(rAr1  
L.8-nTg"y  
sudo sh -c "wget -qO- https://get.docker.io/gpg | apt-key add -" <@?bYp  
DukCXyB*l  
lwK Au!l  
往aptitude软件源添加Docker仓库: $,R|$0B7  
Ly^r8I  
Kj'uTEM  
sudo sh -c "echo deb http://get.docker.io/ubuntu docker main\ $FD0MrB_+  
> /etc/apt/sources.list.d/docker.list" /{Nx%PqL  
1X=}  
wL}=$DN  
添加之后再更新一次系统: 7VR+EV  
N5 g!,3  
(pAGS{{  
sudo aptitude update &u.t5m7(  
kMUjSa~\  
JD,/oL.KA  
最后,下载并安装Docker: `,xKK+~YG-  
@c5TSHSL.  
TJ+yBMd*%  
sudo aptitude install lxc-docker hH%@8'1v  
@q|I$'K]x  
d?'q(6&H  
Ubuntu默认的防火墙(UFW)的默认设置是拒绝一切转发(forwarding),但是Docker需要转发,所以也需要设置一下UFW。 $+-2/=>Xk  
q*52|?  
dZ_Hj X7  
用nano编辑器打开UFW配置文件: ]M#_o]  
KYMz  
3s"x{mtH  
sudo nano /etc/default/ufw U }xRvNz  
|s=)*DZv  
Xji<oih  
找到DEFAULT_FORWARD_POLICY这一行,将 jo/-'Lf{?  
'f]\@&Np  
u 6;SgPw  
DEFAULT_FORWARD_POLICY="DROP" 3y<;fdS7  
Wu?4oF  
$SA8$!:  
替换为: ,~>A>J  
n}}$-xl  
!-)Hog5\  
DEFAULT_FORWARD_POLICY="ACCEPT" [ lW~v:W  
E:!?A@Fy  
M|6 l  
按 CTRL+X 再按 Y 键,保存退出。 *\L\Bzm  
[AA'Ko  
?%(:  
最后,把UFW重启一下: ^U)xQD"  
x(ue |UG  
kzcl   
sudo ufw reload 2= S;<J  
S&^i*R4]  
iK%%  
基本的Docker命令 xJlf}LEyF  
在正式开始之前,我们还是先复习一下上次在基础篇中介绍过的一些基本命令。 )! +~q!A  
.?|pv}V  
W"MwpV  
Docker的守护进程与命令行 bVO{,P2 o  
一般来说,Docker守护进程在你安装完成了之后就已经在后台运行,等待接收来自Docker命令行的指令。不过有的时候我们也需要手动启动Docker守护进程: ^}8qPBz  
lXso@TNrZ0  
"&lQ5]N.%  
sudo docker -d & }M*yE]LL;Z  
-!4Mmp"2@u  
l/ufu[x!a  
Docker命令行的基本语法如下: s_:7dD  
}JPLhr|d^  
F/.nr  
sudo docker [option] [command] [arguments] 'ONCz  
{B|)!_M#  
5\S s`#g  
注:Docker的运行需要sudo权限。 xw2dNJL  
; D'6sd"  
jTHgh>n  
Docker命令 b5!D('w>]  
下面列出了目前可用的Docker命令(译注:可以参考InfoQ中文站文章深入浅出Docker(二):Docker命令行探秘): xNAX)v3Z  
:yFUlO:  
oyC5M+shP9  
attach    附着到一个运行的容器上 /hF@Xh%hY  
build     从一个Dockerfile建立镜像 rD_\NgVAs  
commit    将一个变更后的容器创建为一个新镜像 |[./jg"  
cp        在容器和本地文件系统之间复制文件/目录 05SK$ Y<<  
create    创建一个新的容器 g$S|CqRG  
diff      检测容器文件系统的变更 )wqG^yv  
events    从服务获取实时事件 8@rddk  
exec      在一个运行中的容器内执行命令 4 x|yzUx  
export    将一个容器的文件系统输出为tar压缩包 kD*r@s]=  
history   显示一个镜像的历史 ag02=}Q'r  
images    列出镜像列表 RwyX,|  
import    从tarball压缩包导入内容以创建一个文件系统镜像 i0q<,VSl$_  
info      显示系统信息 l~(A(1  
inspect   返回容器或镜像的底层信息 0#y i5U  
kill      杀死一个运行中的容器 x u<oQBt  
load      从一个tar压缩包或STDIN加载一个镜像 ,azBk`$iQr  
login     登入Docker注册表(Docker registry) ?Z(xu~^/  
logout    从Docker注册表登出 a'!p^/6?  
logs      抓取一个容器的日志 jCp^CNbA  
network   管理Docker网络 W @ ?*~  
pause     暂停一个容器内的所有进程 eXA@J[- M:  
port      列出该容器的所有端口映射或指定端口映射 IoKN.#;^  
ps        列出容器列表 2H,n"-9+  
pull      从注册表拉取一个镜像或仓库 2I$-&c]  
push      往注册表推送一个镜像或仓库 328gTP1  
rename    重命名容器 Wj I NY  
restart   重启容器 &"6%D|Z0  
rm        删除一个或多个容器 uz;zmK  
rmi       删除一个或多个镜像 HRg< f= oz  
run       在一个新的容器中运行一条命令 XMlcY;W  
save      将镜像保存至tar压缩包 Vw";< <0HZ  
search    在Docker Hub搜索镜像 +/ U6p!  
start     启动一个或多个容器 ;&9wG`  
stats     显示容器资源使用情况的实时信息流 0zc~!r~  
stop      停止一个运行中的容器 b wqd` C  
tag       向注册表标记一个镜像 \AY*x=PF  
top       显示一个容器下运行的进程 9s2 N!bx  
unpause   恢复运行一个容器里所有被暂停的进程 y^}00Z+l  
update    更新容器的资源 |R3A$r#-  
version   显示Docker版本信息 _>u0vGF-  
volume    管理Docker卷 !SAR/sdXf  
wait      阻塞对指定容器的其他调用方法,直到容器停止后退出阻塞。 C ^ 1;r9  
rUDMQxLruV  
5pz(6gA  
创建Docker容器作为Python WSGI应用的沙箱 krecUpo  
我们已经完成了Docker的安装,也熟悉了基本的命令,现在可以为我们的Python WSGI应用创建Docker容器了。 :O9i:Xq[QW  
- p*j9 z  
_ lE d8Cb  
注:本章介绍的方法主要是练习用,并不适合于生产环境。生产环境下适用的自动化流程将在后续章节中介绍。 wvz_)b N~A  
C^fUhLVSZ^  
ZIp"X  
在Ubuntu里创建打底Docker容器 ;@s'JSPt  
Docker的run指令会基于Ubunt镜像创建一个新的容器。接下来,我们要用 -t 标识给这个容器附着(attach)一个终端,并运行一个 bash 进程。 eUEO~M2&U{  
^"{txd?6  
HwxME%w  
我们将暴露这个容器的80端口用于从外部访问。以后在更加复杂的环境中,你可能需要给多个实例做负载均衡,把不同的容器“连接”起来,再用一个反向代理容器去访问它们。 Bs;.oK5!n@  
l~'NqmXe  
JG*Lc@Q  
sudo docker run -i -t -p 80:80 ubuntu /bin/bash ;S}_/'  
~@ML>z 7  
i1ss}JJp*  
注:运行这个命令时,Docker可能需要先下载一个Ubuntu镜像,下载完毕后才创建新容器。 't#E-+o  
Q|Go7MQZ@k  
+^<-;/FZue  
注意:你的终端会“附着”(attach)在新创建的容器上。要与容器分离并回到之前的终端访问点,可以按 CTRL+P 接着 CTRL+Q 执行脱离操作。“附着”在一个Docker容器上,基本上相当于从一个VPS内部访问另一个VPS。 XHYVcwmDz-  
 IA{I|g<  
DcX,o*ec!  
从脱离的状态想要回到附着的状态,需要执行如下步骤: 8j jq)d4#  
%Kc2n9W  
.?LP$O=  
用 sudo docker ps 列出所有运行中的容器 }1? 2  
找到之前创建的那个容器的ID I7} o>{  
执行 sudo docker attach [id] 完成当前终端到该容器的附着 @DniYt/  
注意:我们在容器内部做的一切操作都将仅限于在容器内部执行,对宿主机是完全没有影响的。 Y4,LXuQ  
99u9L)  
~spfQV~  
安装前的准备工作 b5Vn_;V*  
要在容器内部署Python WSGI应用(以及我们所需要的工具),首先我们需要对应的软件仓库。然而在Docker默认的Ubuntu镜像里并没有提供仓库(Docker的设计者认为这样有利于保持事物的简化),因此我们需要给我们这个打底镜像添加Ubuntu的universe软件仓库: HWxwG'EEY,  
rh$q]  
m\;@~o'k  
echo "deb http://archive.ubuntu.com/ubuntu/ $(lsb_release -sc) main universe" >> /etc/apt/sources.list Yxd&hr  
:}3;z'2]l  
Gb_y"rx?0  
更新一下软件列表: 4@1C$|k  
L_QJS2  
1jkMje  
apt-get update n Wb0S  
yp\s Jc`  
;=F]{w]$+  
再给我们的容器安装一些必要的工具: #P5tTCM  
Nlk'  
7^*[ XH  
apt-get install -y tar \ }RN&w ]<  
                   git \ 1k?k{Ri  
                   curl \ C">w3#M%  
                   nano \ Z0Df~ @  
                   wget \ iR6w)  
                   dialog \ t182&gpd`  
                   net-tools a^QyYX}\qR  
                   build-essential  k.("<)  
_5JwJcQ  
y;1l].L  
安装Python工具 g}Esj"7  
本文将用一个简单的Flask应用作为示范。如果你用的是其他框架也没关系,安装部署的方法都是一样的。 RT$.r5l_@  
OjRJyhzS*  
M qG`P  
再提醒一次:以下所有的命令都是在容器内部执行的,不会影响到宿主机。你可以想象成自己在一个全新的VPS上进行操作。 TX7]$Wj  
j& ~`wGM  
ZQ'bB5I  
安装Python和pip: 41Htsj  
]v,>!~8r  
E0-<-w3'  
# 安装pip依赖:setuptools k7\ ,N o}  
apt-get install -y python python-dev python-distribute python-pip /2#1Oi)o  
^;=L|{Xl  
oGXndfd"  
安装Web应用以及其依赖项 ~L7@,d:  
安装我们的应用之前,还是让我们再确认一下所有的依赖都已经就绪。首先是我们的框架——Flask。 `R+I(Cb  
kjVJ!R\  
vk] vtjf&%  
因为我们已经装好了pip,所以就直接用pip来安装Flask: PGaYYc3X  
!Ey=  
3sy|pa  
pip install flask r}]%(D](v  
r#;GVJR6  
n13#}i {tm  
装好了Flask,创建一个“my_application”文件夹: ;%_s4  
CW9vC  
~qP_1() ?  
mkdir my_application QaYUcma~n  
JX!@j3  
xG|T_|?  
cd my_application okv`+VeA  
E)}& p\{E  
Z2cumx(  
注:如果你想直接部署自己的应用(而不是这里的示范应用),可以参看下面的“小贴士”部分。 swGp{wJ  
6+s10?  
*~>} *  
我们的示范应用是一个单页面的“Hello World” Flask应用。下面用nano来创建app.py: dr~6}S#  
!i{9wI  
8M,AFZ>F  
nano app.py &ap&dM0@%a  
m:  
yur5" $n  
把下面这些内容复制到新创建的文件里: 2b i:Q9  
6J""gyK.  
>L_nu.x  
from flask import Flask ?Xq"Q^o4#e  
app = Flask(__name__) f}%paE"  
(<ZpT%2  
7XU$O$C  
@app.route("/") 3<%ci&B  
def hello(): }8e_  
    return "Hello World!" n;+`%;6  
&h<\jqN/  
r\."=l  
if __name__ == "__main__": ]o<&Q52|  
    app.run() `:>N.9'o  
:I !}ZD+Z  
dWK"Tkf\  
按 CTRL+X 再按 Y 键,保存退出。 Krw'|<  
$3'xb/3|  
Qt+i0xd  
或者,你也可以使用“requirements.txt”来定义应用的依赖项(比如Flask)。我们还是用nano来创建文件: ,%X"Caz  
h; "pAE  
/a7N:Z_Bz  
nano requirements.txt vXLGdv::  
s$D"  
?l>Ra0  
在文件输入你所有的依赖项(下面只列出两个,如果你需要别的请自行添加): /:.p{y  
l&Cy K#B:\  
F6Ne?[b  
flask Tf{lH9ca$  
cherrypy \TS.9 >\  
Memb`3  
m8,jVR  
按 CTRL+X 再按 Y 键,保存退出。 ~o X`Gih  
*ORa@ x  
| <bZ*7G  
注:你可以用pip来生成自定义的依赖项列表。具体的操作方法可以参考这篇Common Python Tools: Using virtualenv, Installing with Pip, and Managing Packages。 Q79WGW  
E0*62OI~O  
Sah!|9  
最后,我们这个应用的文件组织结构是这样的: -_^#7]  
qE*hUzA  
rKkFflOVO  
/my_application j*_>/gi  
    | 4Dw| I${O  
    |- requirements.txt  # 描述依赖项的文件 >>T,M@s-:  
    |- /app              # 应用模块(你的应用应该在这个目录下) =6d'/D#J  
    |- app.py            # WSGI文件,里面应该包含“app”的实例名称(callable) MZ:Ty,pw:O  
    |- server.py         # 可选,用于运行应用服务器(CherryPy) z Ek/#&  
30{+gYA  
xA>3]<O  
注:关于“server.py”,请参阅下面的章节“配置我们的Python WSGI应用”。 c?}{>ig/)  
H8A=]Gq  
+HF*X~},i  
注意:上面这些应用的文件、目录都是在容器内部创建的。如果你要在宿主机上自动化构建镜像(这个过程将在下面有关Dockerfile的章节中介绍),则你的宿主机上放置Dockerfile的目录下也需要同样的文件结构。 ,T8fo\a4  
Z|BOuB^   
asL!@YE  
小贴士:部署自己的应用 0Ci:w|J  
在容器内部搞定应用所需的软件仓库和依赖项 yHs'E4V`$  
上述步骤描述了在容器内创建应用目录的过程。然而在真实场景下,我们往往需要从软件仓库拉取源代码。 |k<5yj4?  
v%)=!T ,  
/rnP/X)T  
要把你的软件仓库复制到容器内部,有几个方法可以实现。下面介绍其中的两个: .#_g.0<  
fl71{jJ_  
N6h.zl&04  
# 方法1 ksV ^Y=]  
# 用git下载源代码 &t@|/~%[  
# 用法:git clone [源代码所在的URL] 66'AaA;0^i  
# 示范: kYu"`_n}  
git clone https://github.com/mitsuhiko/flask/tree/master/examples/flaskr TL@{yJ;s  
e$4l[&kH_  
ee*E:Ltz\  
# 方法2 yG:Pg MrB  
# 下载源代码压缩文件 [p96H)8YU  
# 用法:wget [源代码压缩文件所在的URL] 2rqYm6  
# 示范:(用真实的URL替换掉下面这个假的) p2j=73$  
wget http://www.github.com/example_usr/application/tarball/v.v.x 'cf8VD  
;mXw4_{  
p} i5z_tS  
# 解压缩文件 0[!38  
# 用法:tar vxzf [文件名 .tar (.gz)] QO3QR/Ww  
# 示范:(用真实的文件名替换掉下面这个假的) gg QI  
tar vxzf application.tar.gz K Z Q `  
^vr`t9EE  
Z$ 6yB  
# 用pip下载安装应用依赖  `AxhA.&V  
# 下载 requirements.txt (可以用 pip freeze output 生成),再用pip全部安装: c{cJ>d 0  
# 用法:curl [requirements.txt 文件的URL] | pip install -r - z=a{;1A  
# 示范:(用真实的URL替换掉下面这个假的) -\~D6OA  
curl http://www.github.com/example_usr/application/requirements.txt | pip install -r - Cs8e("w  
M UqV$#4@I  
#AUa'qB t  
配置我们的Python WSGI应用 ,XmyC7y<  
要运行这个应用,我们需要一个Web服务器。运行这个WSGI应用的Web服务器需要安装在代码所在的同一台容器中,作为该Docker容器运行的进程。 }~$96|J  
Auf2JH~  
^n8r mh_%  
注:我们在示范中将使用CherryPy自带的HTTP Web服务器,这是一个比较简单而且可以用在生产环境的选择。你也可以用Gunicorn甚至uSWGI(可以让它们跑在Nginx的后面),我们其他的教程中介绍过这种用法。 a%)-iL X8&  
Dv[ 35[Yh  
;c;PNihg  
用pip下载安装CherryPy: mdPEF)-  
jwZBWt )5  
1U;p+k5c  
pip install cherrypy xbhU:,o  
cQldBc  
rg{|/ ;imT  
创建“server.py”,用于服务“app.py”里面的Web应用: >6"u{Qmr  
0s8w)%4$  
1VC:o]$  
nano server.py "=9kX`(1y  
$E]W U?U  
o$_,2$>mn  
把下面的内容复制粘贴到server.py里: uGMmS9v$ J  
)I`Ma6bX  
^z\*; f  
# 导入应用的语法: 1C' _I  
# from app import application }Pn]j7u!  
# 示范: +>wBGVvS  
CDTM<0`%  
dCkk5&2n  
from app import app D!d1%hac  
a@&P\"k  
o0'!u  
# 导入 CherryPy K6l{wyMb|  
import cherrypy v8!Ts"  
b qNM  
=1'vXPv`  
if __name__ == '__main__': <|MF\D'  
HM(S}>  
O>H'o k  
    # 挂载应用 fyE#8h_>4  
    cherrypy.tree.graft(app, "/") W dNOE;R  
l@OY8z-_  
|kXx9vGq@  
    # 从默认服务器上分离 $+$S}i=  
    cherrypy.server.unsubscribe() !ENDQ?1  
9Oe~e  
#{BHH;J+  
    # 实例化一个新的服务器对象 ^Voi 4;  
    server = cherrypy._cpserver.Server() uVn"'p-  
)Z0bMO<  
iir]M`A.-  
    # 配置该服务器对象 2aR<xcSg  
    server.socket_host = "0.0.0.0" NB|yLkoDyI  
    server.socket_port = 80 ;0c -+,  
    server.thread_pool = 30 L" GQ Q  
1\aJ[t  
zG. \xmp  
    # SSL相关配置 3\B 28m  
    # server.ssl_module            = 'pyopenssl' 5u89?-UD  
    # server.ssl_certificate       = 'ssl/certificate.crt' [ u.r]\[J  
    # server.ssl_private_key       = 'ssl/private.key' ~>"m`Q&[  
    # server.ssl_certificate_chain = 'ssl/bundle.crt' 1R+/T  
nde_%d$  
j LS<S_`  
    # 订阅这个服务器对象 P\lEfsuR  
    server.subscribe() iX|K4.Pz{  
<1%(%KdN[  
 nLD1j  
    # 启动服务器引擎 G=ly .  
Nm7YH@x*o  
?-Zl(uX  
    cherrypy.engine.start() Rl/5eE8  
    cherrypy.engine.block() o^ zrF  
?bH&F  
*t%Z'IA  
完成!现在我们就有了一个“Docker化”的Python Web应用,安全的跑在自己专属的沙箱里。只要输入下面的一行命令,它就可以给成千上万个客户端请求提供服务: W2Ik!wEe&  
4Y5lP00!}  
YEPQ/Pc  
python server.py I?&/J4o:  
-k8<LR3  
mfqnRPZ  
这是让服务器在前台运行的指令。按下 CTRL+C 终止运行。如果想在后台运行服务器,可输入下面的指令: 'EHt A9M  
XU y[l  
MO>9A,&f  
python server.py & OAauD$Hh  
4=~+B z  
fg)VO6Wo&  
后台运行的应用需要用进程管理器(比如htop)来终止运行(kill或stop)。 FMi:2.E  
8g0VTY4$jP  
Gl"|t't(  
注:有关CherryPy上跑Python应用的配置,可参阅这篇教程:How to deploy Python WSGI apps Using CherryPy Web Server。 QdrZi.qKH  
g&*,j+$ }  
![i)_XO  
简单的测试一下应用的运行状态(以及端口的分配状态):在浏览器中访问 http://[容器所在的VPS的IP地址] ,应该能够看到“Hello World!”。 |z7V1xF  
~ \]?5 nj  
?KtF!:_C  
创建Dockerfile以实现镜像创建的自动化 X- ZZLl#  
上面简单的说过,手动创建容器的这个方法并不适合用于生产环境的部署。生产环境里应该用Dockerfile进行构建流程自动化。 * '_(.Z:  
@C|nc&E2s  
GXp`yK9c  
我们已经知道了如何在容器内部进行外部资源的下载和安装,那么Dockerfile其实也是一样的原理。一个Dockerfile定义了Docker要如何生成一个镜像,这个镜像可以直接用来跑我们的Python应用。 ZZn$N-  
vSu dT  
/go|r '  
先了解一下Dockerfile的基本功能。 Vw|P;LLl`  
BsU}HuQZQ  
]rG/?1'^i  
Dockerfile概述 oY4^CGk=  
Dockerfile是一种脚本文件,其中包含了一系列顺序执行的命令,Docker通过执行这些命令就可以创建一个新的Docker镜像。这极大的方便了部署。  l*?_@  
0|Xz-Y  
&|<f|B MX  
Dockerfile一般会先用 FROM 命令定义一个打底的镜像,然后执行一系列的动作,动作全部执行完毕之后就形成了最终的镜像,并将完成的镜像提交给宿主机。 gYCr,-_i  
=EYWiK77a  
c: r25  
使用: j4<K0-?  
kO5lLqE  
%q}[ZD/HD  
# 在当前位置用Dockerfile创建一个镜像 PY;tu#W!%  
# 将生成的镜像标记为 [name] (比如nginx) ua)jGif  
# 示范:sudo docker build -t [name] . $AT@r"  
sudo docker build -t nginx_img . hrm<!uKn  
+fvD1xHI  
'4nJ*Xa  
注:我们还有一篇专门介绍dockerfile的文章可供查阅:Docker Explained: Using Dockerfiles to Automate Building of Images ~nTj't2R  
N+ak{3  
?G5,}%  
Dockerfile命令一览 ^?$,sS ;Q  
### Add 2E?!Q I\O  
从宿主机复制文件到容器 mU]VFPr5  
qi B~  
)S 7+y6f&*  
1X[^^p~^  
u4_QLf@I  
### CMD z)S6f79`Q  
设置要执行或者要发给ENTRYPOINT的默认命令 \"1>NJn&k)  
~_-]> SI  
J*[@M*R;&  
D8<C7  
K Ax=C}9  
### ENTRYPOINT b,^Gj]7  
设置容器内默认要启动的应用 mUbm3JIjJ  
00.x*v  
a3>/B$pE  
PR Mg6  
@uH!n~QV  
### ENV "bIb?e2h9G  
设置环境变量(key = value) N(Ru/9!y"  
X3\PVsH$K  
F?|Efpzow?  
### EXPOSE gLB(A\yG  
暴露一个端口 V2ih/mh   
{_Wrs.a'8  
Td'Mc-/  
### FROM -BH/)$-$  
设置打底镜像(base) 3l~+VBR_  
g#=<;X2  
393c |8M  
### MAINTAINER b3^:Bh9  
设置Dockerfile的作者/所有者信息 lN#j%0MaUo  
n.&7lg^X  
n@8Y6+7i  
### RUN T82 `-bZ  
执行一条命令并提交执行后的(容器)镜像 orFwy!  
KClkPL!jP  
1ysfpX{=  
### USER  u]Ku96!  
设置从镜像运行容器的用户名 +6#$6hG  
C< c6Ub  
Q>,&@  
### VOLUME Z)s !p  
从宿主机加载一个目录给容器 2y - QH  
ryL1<u ~  
6G2~'zqPc~  
### WORKDIR A|\A|8=b  
设置CMD运行时所在的目录 "V' r}>  
8ttJ\m  
Uj7YTB  
创建Dockerfile nQ\`]_C  
在当前路径下用nano编辑器创建Dockerfile: @Chl>s  
g8Ex$,\,  
4Y8=  
sudo nano Dockerfile LP"g(D2'n  
1 _ LHbP=B  
注:下面的内容需要按顺序添加到Dockerfile中。 ,D;8~l lM  
klUV&O+=%  
` IVQ  
定义基本项 gy}3ZA*F  
Dockerfile的基本项包括 FROM 原始镜像(比如Ubuntu)以及维护者姓名 MAINTAINER: s9ix&m  
] `$6=) _X  
U4"^NLAq  
############################################################ C ~h#pAh  
# 创建Python WSGI应用容器的Dockerfile v WXo#  
# 基于Ubuntu |HycBTN#E  
############################################################ f SkC>mWv  
*:CTIV5N0  
D`p2aeI  
# 设置Ubuntu为打底镜像 |KhpF1/(  
FROM ubuntu Yh1</C  
CH4Nz'X2  
jWUrw  
# 文件作者/维护者 ]Ozz"4Z  
MAINTAINER Maintaner Name c,r6+oX  
jwk+&S  
>Lcu  
更新默认的应用仓库 (d &" @  
# 添加软件资源库的URL &MX&5@ Vu  
RUN echo "deb http://archive.ubuntu.com/ubuntu/ $(lsb_release -sc) main universe" >> /etc/apt/sources.list 1|p\rHGd  
2?ue.1C  
I%4eX0QY=z  
# 更新资源列表 (Iv@SiZf(  
RUN apt-get update %`$bQU  
[\ku,yd%0  
z"< S$sDh  
安装基础工具 cUug}/!I  
RUN apt-get install -y tar git curl nano wget dialog net-tools build-essential vVBWhY]  
l3u[  
D!* SA  
注:上述的有些工具可能你用不到,不过为了以防万一还是都装进来先。 \ ERBb.  
8;qOsV)UDT  
3SY1>}(Y  
Python基础套装安装指南 w8+ phN(-M  
一些Python需要的工具(pip)最好还是都先装起来。你的框架(WAF)和Web服务器(WAS)都需要有它们才能安装。 ):Fg {7b]n  
HHx5 VI  
3x(Y+ ymP  
RUN apt-get install -y python python-dev python-distribute python-pip 67/@J)z0%  
<1vogUDW  
l%V+] skS  
部署应用 qi@Nz=t#HJ  
部署应用可以使用Docker的 ADD 命令直接复制源代码,也可以用 REQUIREMENTS 文件来一步到位。 Mprn7=I{Tg  
}G(#jOYk  
V~Guw[RA  
注:如果你打算用一个文件描述所有的代码位置,可以参考下面的文件结构。 ^E, #}cW  
qW3XA$g|j'  
.5HD i-  
    文件结构示范 s|WcJV  
MNh:NFCRA  
Z2;~{$&M+  
    /my_application L]YJ#5  
    | W<#Kam:8e  
    |- requirements.txt  # 描述依赖项的文件 b 2\J<Nw  
    |- /app              # 应用模块(你的应用应该在这个目录下) =m|<~t  
    |- app.py            # WSGI文件,里面应该包含“app”的实例名称(callable) ;qzn_W  
    |- server.py         # 可选,用于运行应用服务器(CherryPy) <&+0  
v%#@.D!)  
;Zf7|i`R3  
这个文件结构的创建过程在前面的章节中已经介绍过,这里不再赘述。总之,以上述文件结构为例,则再给Dockerfile末尾添加如下内容,将源代码复制到容器内: HII@Ed f?  
2(Xu?W 7d  
fG?a"6~  
ADD /my_application /my_application a'q&[08  
Nn0j}ZI)1  
Ut%{pc 7^F  
如果源代码是公网的git仓库,则可以使用如下内容: i1{)\/f3  
\"I418T K  
vqF=kB"P  
RUN git clone [你的源码仓库URL] jJ RaY3  
pgUjje>#  
[pt U}  
引导 A(XX2f!i  
接下来,再从 requirements.txt 导入所有的依赖项: PTQN.[bBh  
Wa!C2nB  
ZKS]BbMZa  
# 用pip来下载安装 requirements.txt 里面的东西 tI0D{Xrc  
RUN pip install -r /my_application/requirements.txt xgsEe3|  
{p$X*2ReB  
B90fUK2g  
# 暴露端口 SJ,];mC0  
EXPOSE 80 ;+3@S`2r  
-23sm~`  
%xX b5aY  
# 设置CMD运行的默认路径 fG@]G9Z  
WORKDIR /my_application H-% B<7  
'~{kR=+  
X4k|k>  
# 要运行的默认命令 b$/7rVH!  
# 该命令在新容器创建时开始执行 R2Q1Rk#  
# 比如启动CherryPy来运行服务 gd0a,_`M  
CMD python server.py z wn#E  
5sB~.z@  
x45F-w{  
最终的Dockerfile ACEVd! q  
现在,整个Dockerfile看起来应该是这样的: a 4? c~bs  
F:Yp1Wrb<  
bhKe"#m|S  
############################################################ 0Y!~xyg/  
# Dockerfile to build Python WSGI Application Containers 1_mqPMm  
# Based on Ubuntu OL.{lKJ3DV  
############################################################ %YG[?"P'  
$b )k  
[KEw5-=i@  
# Set the base image to Ubuntu cU "uKR  
FROM ubuntu 'qoaMJxN`  
=|V#~p*  
Jn\>S z(96  
# File Author / Maintainer MZ^(BOe_  
MAINTAINER Maintaner Name ,C^u8Z|T  
dFk$rr>q  
F>k/;@d  
# Add the application resources URL Hr(%y&0  
RUN echo "deb http://archive.ubuntu.com/ubuntu/ $(lsb_release -sc) main universe" >> /etc/apt/sources.list (Wn^~-`=+  
5"L.C32  
5QG?*Z~?7  
# Update the sources list #a 4X*X.8c  
RUN apt-get update &3 x [0DV  
eB<R"Yvi  
!dwa. lZ&X  
# Install basic applications u"U7aYGkY  
RUN apt-get install -y tar git curl nano wget dialog net-tools build-essential V+$fh2t  
S0lt _~  
kq> I?wg  
# Install Python and Basic Python Tools nx5I  
RUN apt-get install -y python python-dev python-distribute python-pip "BC;zH:  
( $,qxPOn  
Ime"}*9  
# Copy the application folder inside the container }k @S mO8  
ADD /my_application /my_application 8M7Bw[Q1  
Bh65qHQO  
j;EH[3  
# Get pip to download and install requirements: rb]?"lizi  
RUN pip install -r /my_application/requirements.txt Ed1y%mR>  
Oh,]"(+  
 =tc!"{  
# Expose ports 5p~hUP]tT  
EXPOSE 80 ^MvBW6#1  
rw[Ioyr-  
yVyh\u\  
# Set the default directory where CMD will execute /sdZf|Zl  
WORKDIR /my_application CsN^u H  
pL2{zW`FDh  
L fZF  
# Set the default command to execute     wotw nE  
# when creating a new container )D&xyC}  
# i.e. using CherryPy to serve the application k\-h-0[|  
CMD python server.py =G`g-E2  
%RzCJxT  
+9B .}t#  
按 CTRL+X 再按 Y 键,保存退出。 m"wP]OQH*+  
k&17 (Tv$  
Qw<&N$  
使用Dockerfile进行自动化的容器创建 \q^:$iY~  
在之前的基础教程部分我们提到过,Dockerfile的工作方式利用到了 docker build 命令。 U+&Eps&NI  
8k;il54#  
>+):eB L  
我们通过Dockerfile指示docker从包含源代码的路径复制内容到容器内部,因此在构建之前务必要确认Dockerfile与代码路径的相对位置。 i ;^Ya  
$CZ'[`+  
 BO.Db``  
这样一个Docker镜像可以快速创建起来一个可以运行我们的Python应用的容器,我们需要做的只是输入这样一行指令: <jBRUa[j_  
w8a49Fv  
pj7v{H+  
sudo docker build -t my_application_img . .AZwVP<  
/O~Np|~v  
R[@}Lg7+v  
我们把这个镜像命名为 my_application_img 。为要从这个镜像启动一个新的容器,只需要输入下面的命令: .h(iyCxP  
Zn[ppsz|  
YLuf2ja}X  
sudo docker run -name my_application_instance -p 80:80 -i -t my_application_img gU1Pb]]  
rR 86D  
#/{3qPN?@  
然后就可以在浏览器里输入你VPS的IP地址,访问应用了。 l]OzE-*$b  
-gUp/ #l1  
'.k'*=cq0  
有关Docker安装的更多教程(包括在其他发行版上安装Docker),可以查阅我们在docker.io上的文档docker installation documentation。 KC)}M zt6_  
s;$f6X  
[ 此帖被寒喵在2018-12-29 19:19重新编辑 ]
本人不是云栖社区工作人员。
无论您在使用中遇到什么问题,不要出言不逊!谢谢合作!
发表主题 回复主题
« 返回列表上一主题下一主题

限100 字节
如果您在写长篇帖子又不马上发表,建议存为草稿
 
验证问题: 64 - 13 = ?
上一个 下一个