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

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

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

前言 k p8kp`S7  
Web应用随时可能被攻击者利用来夺取整个主机的权限,这是很常见也是很恐怖的一件事。为了更高的安全性,就需要将不同应用之间进行隔离(尤其是在这些应用属于不同的用户的情况下),然而这种隔离的实现一直是个挑战。到目前为止,隔离性的实现方法已经有了很多,然而它们要么太过昂贵(时间的层面以及资源的层面),要么太过复杂(无论对开发者还是对管理员)。 hEfFMi=a`  
HC RmW'  
T-.Bof(?w  
本文将讨论如何让“容器化”的Python Web应用跑在安全的沙箱里,严格的坚守在其各的环境中(当然,除非你指定它们与其他应用进行“连接”)。我将一步一步的介绍如何创建一个Docker容器,如何用这个容器来跑我们的Python Web应用,以及如何用一个Dockerfile来描述整个构建过程以实现完整的自动化。 O/lu0acI  
S,GM!YZg  
wbbr8WiU  
目录 'ExTnv ~  
1. Docker概述 Ya ~lPc  
2. 在Ubuntu上安装Docker WbHI>tt  
3. 基本的Docker命令 Z]k+dJ[-  
Docker的守护进程与命令行 86ml.VOR  
Docker命令 Vv.q{fRvYB  
4. 创建Docker容器作为Python WSGI应用的沙箱 K~jN"ev  
在Ubuntu里创建打底Docker容器 csms8J  
安装前的准备工作 1l+j^Dt'[  
安装Python工具 7=@Mn F`  
安装我们的Web应用以及其依赖项 V6tUijz  
配置我们的Python WSGI应用 DTM xfQdk  
5. 创建Dockerfile以实现镜像创建的自动化 5dEek7wnf  
Dockerfile概述 Kz9h{ Tu4  
Dockerfile命令一览 \j~LxV  
创建Dockerfile d<>jhp5el  
定义基本项 W=?s-*F[~  
更新默认的应用仓库 ZsL-vlv  
安装基础工具 L`0}wR?+  
Python基础套装安装指南 XFcIBWS  
部署应用 nISfRXU;  
引导 Bt1 &C?_$T  
最终的Dockerfile 4e9'yi  
使用Dockerfile进行自动化的容器创建 m; m4/z3U  
Docker概述 ?G$X 4KY6`  
Docker项目提供了一些可以搭配使用的上层工具,这些工具基于Linux内核的一些功能创建。整个项目的目标是帮助开发者和系统管理员门无痛的迁移应用(以及它们所涉及的所有依赖项),让应用在各种系统和机器上都能欢快的跑起来。 a3(q;^v  
tC'@yX  
YLe$Vv735  
实现这个目标的关键在于一个叫做docker容器的运行环境,这个环境实际上是一个具备安全属性的LXC(Linux Containers)。容器的创建使用了Docker镜像,Docker镜像可以手动敲命令创建,也可以通过Dockerfiles实现自动化创建。 6)Dp2  
O-YB +~"3Z  
t?cO>4*|  
注:关于Docker的基础知识(守护进程、CLI、镜像等等),可参阅本系列的第一篇文章Docker Explained: Getting Started。 q NE( @at  
* 57y.](w  
x2 m A  
在Ubuntu上安装Docker bhc .UmH  
最新版本的Docker(译注:本文撰写时间为2013年12月17日,当时的Docker最新版本为0.7.1)可以在Ubuntu/Debian和CentOS/RHEL等多个Linux发行版上部署(你也可以使用DigitalOcean上现成的Docker镜像,该镜像基于Ubuntu 13.04创建)。 x $=-lB  
parc\]M  
bF{14F$  
下面快速介绍一下Ubuntu上的安装流程。 <aEY=IF4  
} l4d/I  
/Jw 65 e  
在Ubuntu上安装Docker aBx8wl*Vm  
更新系统: ziBg'  
7K}Sk  
C`>|D [  
sudo aptitude    update vW:XM0  
sudo aptitude -y upgrade ~R\Z&oQ  
ILq"/S.  
< &~KYu\r  
检查系统是否支持aufs: jM  DG  
,6FmU$ Kn  
-jOCzp  
sudo aptitude install linux-image-extra-`uname -r` *1fZcw'C.  
Qg;?C  
@x z?^20N  
往apt-key添加Docker仓库的密钥(用于软件包的验证): %knPeo&  
K,\Bj/V(  
8C!D=Vhh  
sudo sh -c "wget -qO- https://get.docker.io/gpg | apt-key add -" Sx%vJYH0  
LV X01ox$  
zIjUfgO/M  
往aptitude软件源添加Docker仓库: _N-JRM m<  
".R5K ?  
8!uqR!M<C  
sudo sh -c "echo deb http://get.docker.io/ubuntu docker main\ C/L+:b&x~  
> /etc/apt/sources.list.d/docker.list" t!"XQ$g'  
umD[4aP~;  
R0!qweGi@  
添加之后再更新一次系统: ^7l^ /GSO  
Ni4*V3VB  
,wvzY7%  
sudo aptitude update '/"xMpN4  
 EL[N%M3  
^,.G<2Kx&  
最后,下载并安装Docker: }4n?k'_s?  
)=)=]|3  
>v DD.  
sudo aptitude install lxc-docker 5c~OG6COx  
8v)PDO~D}A  
@9!,]n  
Ubuntu默认的防火墙(UFW)的默认设置是拒绝一切转发(forwarding),但是Docker需要转发,所以也需要设置一下UFW。 *8*E\nZx!  
xgeKz^,  
%hu] =  
用nano编辑器打开UFW配置文件: 8VwByk8  
2-3|0<`  
A S#D9o  
sudo nano /etc/default/ufw /}Z0\ ,  
3 #zw Y  
'e(`2  
找到DEFAULT_FORWARD_POLICY这一行,将 ;-koMD!2F  
!%x=o&  
v<`1z?dch  
DEFAULT_FORWARD_POLICY="DROP"  /_r g*y*  
Z-!W#   
nFn@Z'T$N  
替换为: {bUd"Tu  
btC.EmX  
2_x~y|<9  
DEFAULT_FORWARD_POLICY="ACCEPT" *1L;%u| [  
B(GcPDj(K  
Us ]Uy|j  
按 CTRL+X 再按 Y 键,保存退出。 # Z*nc0C  
7x%0 ^~/n  
q1nGj  
最后,把UFW重启一下: o 6$Q>g`]  
6c3+q+#J2  
`NC{+A  
sudo ufw reload %EuXL% B  
?^F#}>C  
h<wF;g,  
基本的Docker命令 T#1>pED  
在正式开始之前,我们还是先复习一下上次在基础篇中介绍过的一些基本命令。 9^m&  [Z  
9's/~T  
nD\ X3g `V  
Docker的守护进程与命令行 1u\fLAXn  
一般来说,Docker守护进程在你安装完成了之后就已经在后台运行,等待接收来自Docker命令行的指令。不过有的时候我们也需要手动启动Docker守护进程: ")U`Wgx  
89B1\ff  
k!]Tg"]JAh  
sudo docker -d & Aa5IccR  
Zcg=a_  
#![i {7  
Docker命令行的基本语法如下: ^Slwg|t*~P  
,/\%-u? 1x  
c7jft|4S  
sudo docker [option] [command] [arguments] ,=tVa])  
OC'cP[$ _  
9-+6Ed^2  
注:Docker的运行需要sudo权限。 1anV!&a<K(  
.cA[b  
Q1Z;vzQfg  
Docker命令 K \}xb2s  
下面列出了目前可用的Docker命令(译注:可以参考InfoQ中文站文章深入浅出Docker(二):Docker命令行探秘): 5Mb1==/R  
x3L3K/qMg  
L4aT=of-  
attach    附着到一个运行的容器上 #Dx$KPD  
build     从一个Dockerfile建立镜像 J=/|iW  
commit    将一个变更后的容器创建为一个新镜像 T7YzO,b/   
cp        在容器和本地文件系统之间复制文件/目录 6^zuRY;  
create    创建一个新的容器 ru)%0Cyx  
diff      检测容器文件系统的变更 FXFQ@q*}v  
events    从服务获取实时事件 M`umfw T  
exec      在一个运行中的容器内执行命令 ^HKxaW9W  
export    将一个容器的文件系统输出为tar压缩包 kX {c+qHM  
history   显示一个镜像的历史 ||7r'Q  
images    列出镜像列表 T7n;Bf  
import    从tarball压缩包导入内容以创建一个文件系统镜像 }rVnuRq  
info      显示系统信息 8 k+Ctk  
inspect   返回容器或镜像的底层信息 '<m[  
kill      杀死一个运行中的容器 SZc6=^$  
load      从一个tar压缩包或STDIN加载一个镜像 J?5O 2n  
login     登入Docker注册表(Docker registry) 1K,bmb xRt  
logout    从Docker注册表登出 2o/}GIKj  
logs      抓取一个容器的日志 lN1T\  
network   管理Docker网络 z@&_3 Gl  
pause     暂停一个容器内的所有进程 0;e>kz3o  
port      列出该容器的所有端口映射或指定端口映射 XLFJ?$)Tro  
ps        列出容器列表 SR~~rD|V  
pull      从注册表拉取一个镜像或仓库 lbg!B4,  
push      往注册表推送一个镜像或仓库 GVY_u@6   
rename    重命名容器 6ssZg@}nf{  
restart   重启容器 LP_ !g  
rm        删除一个或多个容器 RKIqg4>E  
rmi       删除一个或多个镜像 a&ZH  
run       在一个新的容器中运行一条命令 ,[+gE\z{{u  
save      将镜像保存至tar压缩包 w0moC9#$?  
search    在Docker Hub搜索镜像 Z/hSH 0(~  
start     启动一个或多个容器 =g/K>B  
stats     显示容器资源使用情况的实时信息流 [ OMcSd|nf  
stop      停止一个运行中的容器 50rq} -  
tag       向注册表标记一个镜像 V# w$|B\  
top       显示一个容器下运行的进程 H@__%KBw  
unpause   恢复运行一个容器里所有被暂停的进程 if:2sS9r  
update    更新容器的资源 ngEjbCV+  
version   显示Docker版本信息 x1+8f2[  
volume    管理Docker卷 HnioB=fc  
wait      阻塞对指定容器的其他调用方法,直到容器停止后退出阻塞。 5Z6$90!k  
z7{b>oub('  
:(A]Bm3  
创建Docker容器作为Python WSGI应用的沙箱 `6 lc]r  
我们已经完成了Docker的安装,也熟悉了基本的命令,现在可以为我们的Python WSGI应用创建Docker容器了。 QS_" fsyN:  
T -C2V$1  
wvJm)Mj+  
注:本章介绍的方法主要是练习用,并不适合于生产环境。生产环境下适用的自动化流程将在后续章节中介绍。 (EuHQ &<^9  
$+S'Boo   
im%'S6_X4  
在Ubuntu里创建打底Docker容器 )"A+T&  
Docker的run指令会基于Ubunt镜像创建一个新的容器。接下来,我们要用 -t 标识给这个容器附着(attach)一个终端,并运行一个 bash 进程。 6/s#'#jh  
![U|2x   
gjL>FOe8u  
我们将暴露这个容器的80端口用于从外部访问。以后在更加复杂的环境中,你可能需要给多个实例做负载均衡,把不同的容器“连接”起来,再用一个反向代理容器去访问它们。 7qgHH p  
a,M7Bb x  
RjSVa.x  
sudo docker run -i -t -p 80:80 ubuntu /bin/bash yWF DGk  
v~ZdMQvwt  
?8b?{`@V  
注:运行这个命令时,Docker可能需要先下载一个Ubuntu镜像,下载完毕后才创建新容器。 U $X"W'  
N!/^s":  
Z!~~6Sq  
注意:你的终端会“附着”(attach)在新创建的容器上。要与容器分离并回到之前的终端访问点,可以按 CTRL+P 接着 CTRL+Q 执行脱离操作。“附着”在一个Docker容器上,基本上相当于从一个VPS内部访问另一个VPS。 %@kmuz??  
5RI"g f  
>F!2ib8  
从脱离的状态想要回到附着的状态,需要执行如下步骤: k1_f7_m  
5Ee%!Pk  
C{-e(G`Yd  
用 sudo docker ps 列出所有运行中的容器 6*GY%~JbD  
找到之前创建的那个容器的ID ,~JxYh  
执行 sudo docker attach [id] 完成当前终端到该容器的附着 pDCQ?VW  
注意:我们在容器内部做的一切操作都将仅限于在容器内部执行,对宿主机是完全没有影响的。 DE^{8YX,  
(Pbdwzao  
m:)v>vu  
安装前的准备工作 "^Tb8!  
要在容器内部署Python WSGI应用(以及我们所需要的工具),首先我们需要对应的软件仓库。然而在Docker默认的Ubuntu镜像里并没有提供仓库(Docker的设计者认为这样有利于保持事物的简化),因此我们需要给我们这个打底镜像添加Ubuntu的universe软件仓库: ^6Hfq^ejt  
z6ArSLlZ  
NK$k9,  
echo "deb http://archive.ubuntu.com/ubuntu/ $(lsb_release -sc) main universe" >> /etc/apt/sources.list #fRhG^QKp  
2j Oh~-LU  
I|n<B"Q6^  
更新一下软件列表: |t!kD(~r  
}tua0{N:z  
5|9,S  
apt-get update ]06LNE  
M0S}-eXc5  
0.~QA+BD:S  
再给我们的容器安装一些必要的工具: tTLD6#  
_K_!(]t  
,<d[5;7x  
apt-get install -y tar \ |K,9EM3  
                   git \ Zq}w}v  
                   curl \ :0Rd )*k,v  
                   nano \ 0 j:8 Ve  
                   wget \ %kxq"=3  
                   dialog \ 0 gL]^_+7  
                   net-tools l|/h4BJ'  
                   build-essential ^}8(o  
m$ NBGw  
|ITp$  _S  
安装Python工具 \|F4@  
本文将用一个简单的Flask应用作为示范。如果你用的是其他框架也没关系,安装部署的方法都是一样的。 1j`-lD  
1y2D]h/'  
E5~HH($b  
再提醒一次:以下所有的命令都是在容器内部执行的,不会影响到宿主机。你可以想象成自己在一个全新的VPS上进行操作。 JN .\{ Y  
2%m H  
u}\F9~W-{  
安装Python和pip: d(3F:dbk  
r`qMif'  
c*-8h{}  
# 安装pip依赖:setuptools h3Nwxj~E  
apt-get install -y python python-dev python-distribute python-pip *`mPPts}  
:2pd2S  
&=Gz[1 L  
安装Web应用以及其依赖项 #v0"hFOH,  
安装我们的应用之前,还是让我们再确认一下所有的依赖都已经就绪。首先是我们的框架——Flask。 CC0@RU  
5Q#;4  
=Mzg={)v  
因为我们已经装好了pip,所以就直接用pip来安装Flask: y>Zvose  
I= G%r/3  
vIF=kKl9,  
pip install flask 4v_?i @,L  
5+vCuVZ  
6vbWe@#U/  
装好了Flask,创建一个“my_application”文件夹: kTb.I;S  
|5 _bFB+&  
bY|%ois4  
mkdir my_application 3~z4#8=  
es]\ xw  
g0v},n  
cd my_application }`8g0DPuD9  
"nPmQ  
\(Dq=UzQI  
注:如果你想直接部署自己的应用(而不是这里的示范应用),可以参看下面的“小贴士”部分。 =2] .G Gg  
7}OzTup  
J~eY,n.6]  
我们的示范应用是一个单页面的“Hello World” Flask应用。下面用nano来创建app.py: {_0Efc=7  
)z&0 g2Am  
a% |[m,FvP  
nano app.py A%> Ir`I  
/=w9bUj5v  
fu?5gzT+b  
把下面这些内容复制到新创建的文件里: ,Dfq%~:grT  
`au(' xi<  
hLPg=8nJ_  
from flask import Flask @[u!  
app = Flask(__name__) jIv%?8+%  
rUEoz|e4a  
xT70Rp(2po  
@app.route("/") TC[_Ip&  
def hello(): ,B%M P<Rz1  
    return "Hello World!" cN0|! nm*  
ZKzXSI4  
j!q5Bc?  
if __name__ == "__main__": h>-JXuN  
    app.run() gvvl3`S{  
aDFu!PLB{)  
nv1'iSEeOl  
按 CTRL+X 再按 Y 键,保存退出。 &f'\9lO  
j2# nCU54Z  
Qna ^Ry?6)  
或者,你也可以使用“requirements.txt”来定义应用的依赖项(比如Flask)。我们还是用nano来创建文件: sUN>uroi !  
6kuN)  
b/Y9fQ n  
nano requirements.txt mE(EyB<  
N(>a-a  
XIh2Y\33ys  
在文件输入你所有的依赖项(下面只列出两个,如果你需要别的请自行添加): :VP4|H#SP  
Z.Lm[$/edn  
vA@Kb3 ,  
flask a]'sby  
cherrypy JIvVbI  
0Ge*\Q  
5QB] 2c^  
按 CTRL+X 再按 Y 键,保存退出。 *6^|i}  
jIJVl \i]  
{Md xIp[  
注:你可以用pip来生成自定义的依赖项列表。具体的操作方法可以参考这篇Common Python Tools: Using virtualenv, Installing with Pip, and Managing Packages。 kCfSF%W&  
Ou</{l/  
'$pT:4EuGq  
最后,我们这个应用的文件组织结构是这样的: UMwB.*  
1MHP#X;|  
0#4_vg .  
/my_application ^6[KzE#*  
    | ]'V8{l  
    |- requirements.txt  # 描述依赖项的文件 pg~vteq5  
    |- /app              # 应用模块(你的应用应该在这个目录下) au7%K5  
    |- app.py            # WSGI文件,里面应该包含“app”的实例名称(callable) (-*NRY3*  
    |- server.py         # 可选,用于运行应用服务器(CherryPy) z_R^n#A~r  
<HM\ZDo@P  
rHPda?&H  
注:关于“server.py”,请参阅下面的章节“配置我们的Python WSGI应用”。 =BQM(mal  
rB;` &)-  
B 3|zR  
注意:上面这些应用的文件、目录都是在容器内部创建的。如果你要在宿主机上自动化构建镜像(这个过程将在下面有关Dockerfile的章节中介绍),则你的宿主机上放置Dockerfile的目录下也需要同样的文件结构。 txQyHQ)@  
c_c]0Tm  
HmKvu"3  
小贴士:部署自己的应用 v{`Z  
在容器内部搞定应用所需的软件仓库和依赖项 j*f%<`2`j  
上述步骤描述了在容器内创建应用目录的过程。然而在真实场景下,我们往往需要从软件仓库拉取源代码。 44FK%TmtF  
UfjLNe}wA  
9|WBJ6  
要把你的软件仓库复制到容器内部,有几个方法可以实现。下面介绍其中的两个: -/|O*oZ  
;KJJK#j  
Onb*nm  
# 方法1 u\=gps/Z  
# 用git下载源代码 /tRzb8`  
# 用法:git clone [源代码所在的URL] B%gk[!d}8  
# 示范: KJWYG^zI  
git clone https://github.com/mitsuhiko/flask/tree/master/examples/flaskr =o\ :@I[  
uXq?Z@af|f  
k1Zu&4C\  
# 方法2 !P/ ]o  
# 下载源代码压缩文件 -v?,{?$0  
# 用法:wget [源代码压缩文件所在的URL] MV6 %~T  
# 示范:(用真实的URL替换掉下面这个假的) ;e;lPM{+  
wget http://www.github.com/example_usr/application/tarball/v.v.x S!.aBAW  
3S1V^C-eBx  
S_LY>k?  
# 解压缩文件 $_ub.g|  
# 用法:tar vxzf [文件名 .tar (.gz)] oc' #sE  
# 示范:(用真实的文件名替换掉下面这个假的) `%;n HQ"  
tar vxzf application.tar.gz nP?=uGqCBq  
u_.V]Rjc  
Gir_.yc/  
# 用pip下载安装应用依赖 :|`' \%zW-  
# 下载 requirements.txt (可以用 pip freeze output 生成),再用pip全部安装: Ug^C}".&  
# 用法:curl [requirements.txt 文件的URL] | pip install -r - 5~k-c Ua  
# 示范:(用真实的URL替换掉下面这个假的) 9(lIz{  
curl http://www.github.com/example_usr/application/requirements.txt | pip install -r - Gp9 >R~$  
heoOOP(#  
3~6F`G  
配置我们的Python WSGI应用 l|O^yNS  
要运行这个应用,我们需要一个Web服务器。运行这个WSGI应用的Web服务器需要安装在代码所在的同一台容器中,作为该Docker容器运行的进程。 D *W+0  
T~8` {^  
_Ptf^+  
注:我们在示范中将使用CherryPy自带的HTTP Web服务器,这是一个比较简单而且可以用在生产环境的选择。你也可以用Gunicorn甚至uSWGI(可以让它们跑在Nginx的后面),我们其他的教程中介绍过这种用法。 ju0]~,  
5Zs"CDU  
^!k^=ST1J  
用pip下载安装CherryPy: (ii 5pnq  
rMxst  
X +;Q=  
pip install cherrypy  s=#IoNh  
$m:}{:LDCf  
j{V xB  
创建“server.py”,用于服务“app.py”里面的Web应用: acB,u&  
i_g="^  
1pXAPTV  
nano server.py ^q"p 8   
W6'+#Fp  
!Y=s_)X  
把下面的内容复制粘贴到server.py里: 9UOx~Ty  
vq$%Ug/B  
1mOZ\L!m*  
# 导入应用的语法: 5{ #9b^  
# from app import application kr8NKZ/  
# 示范: GK/a^[f+'l  
g-mK(kY4p  
M7yJ2u<Ty  
from app import app @SPmb o  
X X{:$f+  
n$y1kD  
# 导入 CherryPy `@ qSDW!b  
import cherrypy <y*#[:i  
/.'1i4Xa1P  
8v1asFxs.  
if __name__ == '__main__': @l"GfDf L9  
{ZbeF#*"  
h1fJ`WT6,  
    # 挂载应用 fouy??  
    cherrypy.tree.graft(app, "/") aEXV^5;,pJ  
N<Bi.\XC  
a&L8W4  
    # 从默认服务器上分离 BxZ}YS:  
    cherrypy.server.unsubscribe() wVE"nN#  
/qI80KVnN  
\'E_  
    # 实例化一个新的服务器对象 j])iyn~-Ke  
    server = cherrypy._cpserver.Server() rC_K L  
./#K@V1  
`<HY$PAe  
    # 配置该服务器对象 W7(OrA!  
    server.socket_host = "0.0.0.0" }E>2U/wpXY  
    server.socket_port = 80 5 F^,7A4I0  
    server.thread_pool = 30 5 1 x^gX|  
=AgY8cF!sl  
b@1QE  
    # SSL相关配置 DX$`\PA  
    # server.ssl_module            = 'pyopenssl' 2"<}9A<Xs  
    # server.ssl_certificate       = 'ssl/certificate.crt' OFRzzG@  
    # server.ssl_private_key       = 'ssl/private.key' 2d.I3z:[  
    # server.ssl_certificate_chain = 'ssl/bundle.crt' (Egykh>  
H:[z#f|t  
P]y2W#Rs  
    # 订阅这个服务器对象 u8o7J(aQsR  
    server.subscribe() NPB':r-8  
/cI]Z^&  
iZSj T"l^  
    # 启动服务器引擎  }O1F.5I1  
gmLw.|-  
[&{"1Z  
    cherrypy.engine.start() gLpWfT29V  
    cherrypy.engine.block() $S,Uoh  
,{oP`4\Lm  
)@K|Co  
完成!现在我们就有了一个“Docker化”的Python Web应用,安全的跑在自己专属的沙箱里。只要输入下面的一行命令,它就可以给成千上万个客户端请求提供服务: r<|\4zIo/  
3ZZJYf=  
yaX,s 4p  
python server.py qtgK}*9ptv  
jNIM1_JjD  
xE6y9"}!h  
这是让服务器在前台运行的指令。按下 CTRL+C 终止运行。如果想在后台运行服务器,可输入下面的指令: (|6q N  
P$pl  
3ZB;-F5v  
python server.py & yS3x))  
} KMdfA  
a,j!B hu  
后台运行的应用需要用进程管理器(比如htop)来终止运行(kill或stop)。 +=}% 7o  
6B+ @76wH  
fptW#_V2  
注:有关CherryPy上跑Python应用的配置,可参阅这篇教程:How to deploy Python WSGI apps Using CherryPy Web Server。 pt0H*quwI  
V_ ]4UE  
[9LYR3 p  
简单的测试一下应用的运行状态(以及端口的分配状态):在浏览器中访问 http://[容器所在的VPS的IP地址] ,应该能够看到“Hello World!”。 9CN / v  
C*gSx3OG  
F~DG:x~  
创建Dockerfile以实现镜像创建的自动化 9J%>2AA  
上面简单的说过,手动创建容器的这个方法并不适合用于生产环境的部署。生产环境里应该用Dockerfile进行构建流程自动化。 ol YSr .Q`  
$ o t"Du  
\7C >4  
我们已经知道了如何在容器内部进行外部资源的下载和安装,那么Dockerfile其实也是一样的原理。一个Dockerfile定义了Docker要如何生成一个镜像,这个镜像可以直接用来跑我们的Python应用。 #g|j;{P  
$PTedJ}*Y  
/=3g-$o{`  
先了解一下Dockerfile的基本功能。 {T^'&W>8G8  
_c(=>  
<wSmfg,yF  
Dockerfile概述 +f7?L]wzic  
Dockerfile是一种脚本文件,其中包含了一系列顺序执行的命令,Docker通过执行这些命令就可以创建一个新的Docker镜像。这极大的方便了部署。 ,F *e^#>  
.i. |wY  
7}lZa~/  
Dockerfile一般会先用 FROM 命令定义一个打底的镜像,然后执行一系列的动作,动作全部执行完毕之后就形成了最终的镜像,并将完成的镜像提交给宿主机。 e6_.ID'3  
oVhw2pKpM  
m9q%l_  
使用: q%y_<Fw#E  
ag+ML1#)  
VAthQ<  
# 在当前位置用Dockerfile创建一个镜像 '|q :h  
# 将生成的镜像标记为 [name] (比如nginx) ke4q$pD  
# 示范:sudo docker build -t [name] . PIrUls0}  
sudo docker build -t nginx_img . T,IV)aq  
P"]+6sm&es  
xUw\Y(!  
注:我们还有一篇专门介绍dockerfile的文章可供查阅:Docker Explained: Using Dockerfiles to Automate Building of Images 0 /kbxpih  
Eyn3Vv?v  
.*f;v4!  
Dockerfile命令一览 $yc,D=*Isi  
### Add C= m Y  
从宿主机复制文件到容器 '^J/aV  
K;97/"  
,,Db:4qfjD  
FU[,,a0<<  
njX:[_&  
### CMD \=_8G:1  
设置要执行或者要发给ENTRYPOINT的默认命令 yd%\3}-  
1Efl|lV  
$%E9^F  
bOFLI#p&  
s7 KKH w  
### ENTRYPOINT Pz)QOrrG~  
设置容器内默认要启动的应用 m@Nx`aS?  
7E5Dz7  
xWwPrd  
F@*lR(4C  
6^aYW#O<Ua  
### ENV MU e 'xK  
设置环境变量(key = value) 1h#k&r#*3  
9j2I6lGQ  
I>3]4mI*a  
### EXPOSE dbuOiZ  
暴露一个端口 #-Rz`Y<&  
0;H6b=  
i]-gO  
### FROM uy9B8&Sr  
设置打底镜像(base) 5 muW*7  
,%'0e /  
OT& E)eR  
### MAINTAINER /2cI{]B  
设置Dockerfile的作者/所有者信息 sKuTG93sr@  
Uaj=}p\+.p  
5Hm!5:ZB  
### RUN D+)=bPMe  
执行一条命令并提交执行后的(容器)镜像 |Hm'.-   
_tReZ(Vw  
>h[!gXL^  
### USER sBb.Y k  
设置从镜像运行容器的用户名 xs I/DW  
QP[a^5;Tt  
h3lDDyu  
### VOLUME WDGGT .hG  
从宿主机加载一个目录给容器 ?Bzi#Z  
3N"&P@/0x  
JZ% F  
### WORKDIR 6}T%m?/}  
设置CMD运行时所在的目录 k2uiu  
KY`96~z  
cQ.;dtT0  
创建Dockerfile hcgc =$^  
在当前路径下用nano编辑器创建Dockerfile: D'`"_  
=]QH78\3  
kN j3!u$  
sudo nano Dockerfile 0c61q Q6  
1 lZM3Q58?\  
注:下面的内容需要按顺序添加到Dockerfile中。 !NYM(6!(  
v7(|K  
, imvA5  
定义基本项 +w.Kv ;  
Dockerfile的基本项包括 FROM 原始镜像(比如Ubuntu)以及维护者姓名 MAINTAINER: +q j*P9  
9I\3T6&tr  
ZGpTw[5ql  
############################################################ R5eB,FN  
# 创建Python WSGI应用容器的Dockerfile kLVn(dC "  
# 基于Ubuntu 3EVC8ue  
############################################################ NceB'YG|  
OJsd[l3xR  
s!'A\nVV1$  
# 设置Ubuntu为打底镜像 N+M&d3H`  
FROM ubuntu cN WcNMm  
`MsYgd  
LBkcs4+  
# 文件作者/维护者 As+;qNO  
MAINTAINER Maintaner Name e?| URW  
uR ;-eK  
~$4.Mf,u  
更新默认的应用仓库 `8(h,aj;  
# 添加软件资源库的URL w2d]96*kQe  
RUN echo "deb http://archive.ubuntu.com/ubuntu/ $(lsb_release -sc) main universe" >> /etc/apt/sources.list 6"i{P  
pf8O`e,Awf  
,xYsH+ybA  
# 更新资源列表 4`6c28K0?  
RUN apt-get update rocB"0  
'q92E(  
{@V3?pG?p  
安装基础工具 Aw4?y[{H  
RUN apt-get install -y tar git curl nano wget dialog net-tools build-essential 3kiE3*H  
H=&/Q  
vK6ibl0  
注:上述的有些工具可能你用不到,不过为了以防万一还是都装进来先。 ySQ-!fQnP  
V3mjb H>F  
)Ig+uDGk  
Python基础套装安装指南 @0z0m;8  
一些Python需要的工具(pip)最好还是都先装起来。你的框架(WAF)和Web服务器(WAS)都需要有它们才能安装。 nuSN)}b<Q  
A~SL5h  
(/U)> %n  
RUN apt-get install -y python python-dev python-distribute python-pip G#w^:UL  
*:\:5*SY  
VW9>xVd4  
部署应用 tL~,ZCQz  
部署应用可以使用Docker的 ADD 命令直接复制源代码,也可以用 REQUIREMENTS 文件来一步到位。 {N!E5*$Tr  
]$~Fzs  
;%u_ ;,((  
注:如果你打算用一个文件描述所有的代码位置,可以参考下面的文件结构。 #zS1Z f^KP  
R CBf;$O  
VYamskK[G:  
    文件结构示范 U{uPt*GUd/  
v /R[?H)  
Z5G]p4  
    /my_application 1BQ0M{&  
    | XM6".eF)M  
    |- requirements.txt  # 描述依赖项的文件 ME$2P!o  
    |- /app              # 应用模块(你的应用应该在这个目录下) d4Co^A&  
    |- app.py            # WSGI文件,里面应该包含“app”的实例名称(callable) EBoGJ_l  
    |- server.py         # 可选,用于运行应用服务器(CherryPy) wMb)6YZs  
1, "I=  
L*g. 6+2  
这个文件结构的创建过程在前面的章节中已经介绍过,这里不再赘述。总之,以上述文件结构为例,则再给Dockerfile末尾添加如下内容,将源代码复制到容器内: #l*a~^dhqC  
*jk3 \KaoV  
 hv+|s(  
ADD /my_application /my_application j G-  
gi6g"~%@q1  
d_Q*$Iz)3  
如果源代码是公网的git仓库,则可以使用如下内容: pheE^jUr  
b4>``n  
0A;" V'i  
RUN git clone [你的源码仓库URL] I=K!)X$  
eV"!/A2:N5  
S4cpQq.  
引导 G' Blp  
接下来,再从 requirements.txt 导入所有的依赖项: CRH{E}>  
{?E<](+0  
^SZw`]  
# 用pip来下载安装 requirements.txt 里面的东西 (` 5FZgN  
RUN pip install -r /my_application/requirements.txt B:4Ka]{YO  
3&'2aW   
YT>KJ  
# 暴露端口 #/Ruz'H1>  
EXPOSE 80 qv[[Q[RK-5  
M)&Io6>  
0n Y6A~  
# 设置CMD运行的默认路径 2 {bhA5L  
WORKDIR /my_application 1y J5l,q  
Nln`fE/Ht  
/<);=&[  
# 要运行的默认命令 zuSq+px L@  
# 该命令在新容器创建时开始执行 v??TJ^1  
# 比如启动CherryPy来运行服务 K]c4"JJ  
CMD python server.py f-Jbs`(+  
:\XI0E  
>ys[I0bo  
最终的Dockerfile $B;_Jo\|  
现在,整个Dockerfile看起来应该是这样的: r9# \13-  
 Tc6:UF  
dBSbu=^$)  
############################################################ )p<WDiX1!e  
# Dockerfile to build Python WSGI Application Containers B z^|SkEit  
# Based on Ubuntu Q_]d5pl  
############################################################ M|q~6oM  
}pVTTs`  
NQfYxB1Yr:  
# Set the base image to Ubuntu `lO(s%HC  
FROM ubuntu  ~*M$O&  
?+~cA^-3T  
5;\gJf  
# File Author / Maintainer <A >)[u  
MAINTAINER Maintaner Name ^Dg <Ki  
K_~h*Yc  
UDy(dn>J:J  
# Add the application resources URL "9IYB)Js  
RUN echo "deb http://archive.ubuntu.com/ubuntu/ $(lsb_release -sc) main universe" >> /etc/apt/sources.list '$G"[ljr  
6sJw@Oa J  
Df4+^B,1  
# Update the sources list 'OkGReKt  
RUN apt-get update :u6JjW[a)  
ST5V!jz  
%ft &Q  
# Install basic applications X T[zj <&_  
RUN apt-get install -y tar git curl nano wget dialog net-tools build-essential %c|UmKKi  
 x}TS  
=-KMb`xT  
# Install Python and Basic Python Tools p =(@3%k  
RUN apt-get install -y python python-dev python-distribute python-pip E<E3&;qD  
SJ?6{2^  
%zBCq"y  
# Copy the application folder inside the container <IJu7t>  
ADD /my_application /my_application ^03j8Pc-c  
^Hrn  ]  
4dawg8K`9  
# Get pip to download and install requirements: 9Fo fr  
RUN pip install -r /my_application/requirements.txt :#5xA?=* S  
q%(EYM5Y  
69m ;XdkKz  
# Expose ports 7ZxaPkIu&%  
EXPOSE 80 H 3@Z.D  
Wy .IcWK  
gF?[rqz{  
# Set the default directory where CMD will execute t5B7I59  
WORKDIR /my_application =(v^5  
@"-</x3o  
#[{3} %b  
# Set the default command to execute     5xCT~y/a  
# when creating a new container S! Rc|6y%  
# i.e. using CherryPy to serve the application $ _j[2EU  
CMD python server.py cD*}..-/4  
x5}'7,A  
%`MQmXgM  
按 CTRL+X 再按 Y 键,保存退出。 >Fio;cn?  
IW=cym7  
kOO Gw:/  
使用Dockerfile进行自动化的容器创建 o?L'Pg  
在之前的基础教程部分我们提到过,Dockerfile的工作方式利用到了 docker build 命令。 ;Uc0o!1  
6!x&LoM  
)J]9 lW&y  
我们通过Dockerfile指示docker从包含源代码的路径复制内容到容器内部,因此在构建之前务必要确认Dockerfile与代码路径的相对位置。 `Z: R Ce^  
T>TWU:  
[x=jH>Y  
这样一个Docker镜像可以快速创建起来一个可以运行我们的Python应用的容器,我们需要做的只是输入这样一行指令: o[oM8o<  
%^sTU4D5  
w(ln5q  
sudo docker build -t my_application_img . *IgE)N >  
**9x?s  
ZkL8e  
我们把这个镜像命名为 my_application_img 。为要从这个镜像启动一个新的容器,只需要输入下面的命令: $mf u:tbP  
Q!- 0xlx  
*eI)Z=8  
sudo docker run -name my_application_instance -p 80:80 -i -t my_application_img Lbe\@S   
rX_@Ihv'  
T134ZXqqz  
然后就可以在浏览器里输入你VPS的IP地址,访问应用了。 XL#[ %X9  
sn7AR88M;  
%CWPbk^  
有关Docker安装的更多教程(包括在其他发行版上安装Docker),可以查阅我们在docker.io上的文档docker installation documentation。 zp\8_U @  
^ ,m< 9  
[ 此帖被寒喵在2018-12-29 19:19重新编辑 ]
本人不是云栖社区工作人员。
无论您在使用中遇到什么问题,不要出言不逊!谢谢合作!
发表主题 回复主题
« 返回列表上一主题下一主题

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