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

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

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

前言 "&o@%){]  
Web应用随时可能被攻击者利用来夺取整个主机的权限,这是很常见也是很恐怖的一件事。为了更高的安全性,就需要将不同应用之间进行隔离(尤其是在这些应用属于不同的用户的情况下),然而这种隔离的实现一直是个挑战。到目前为止,隔离性的实现方法已经有了很多,然而它们要么太过昂贵(时间的层面以及资源的层面),要么太过复杂(无论对开发者还是对管理员)。 r|BKp,u9  
H9m2Whq  
w)SxwlW}  
本文将讨论如何让“容器化”的Python Web应用跑在安全的沙箱里,严格的坚守在其各的环境中(当然,除非你指定它们与其他应用进行“连接”)。我将一步一步的介绍如何创建一个Docker容器,如何用这个容器来跑我们的Python Web应用,以及如何用一个Dockerfile来描述整个构建过程以实现完整的自动化。 \D k^\-  
~(P\F&A(&  
zw:b7B]  
目录 6 FxndR;  
1. Docker概述 1ucUnNkcV  
2. 在Ubuntu上安装Docker m64\@ [  
3. 基本的Docker命令 @?AE75E{  
Docker的守护进程与命令行 R,Uy3N  
Docker命令 <.XoC?j  
4. 创建Docker容器作为Python WSGI应用的沙箱 AQGE(%X  
在Ubuntu里创建打底Docker容器 yAkN2  
安装前的准备工作  .5r0%  
安装Python工具 Z+Zh;Ms  
安装我们的Web应用以及其依赖项 9 f/tNQ7W  
配置我们的Python WSGI应用 6j![m+vo%  
5. 创建Dockerfile以实现镜像创建的自动化 bvvx(?!  
Dockerfile概述 D)$k{v#~  
Dockerfile命令一览 'B:De"_(N  
创建Dockerfile &,|uTIs  
定义基本项 ".Z+bi2l  
更新默认的应用仓库 NT:>.~ah@&  
安装基础工具 \zDV|n~{w  
Python基础套装安装指南 45JLx?rN_  
部署应用 0tyU%z{RV  
引导 7-B'G/PS/  
最终的Dockerfile -<.NEV  
使用Dockerfile进行自动化的容器创建 t $%}*@x7  
Docker概述 K?eo)|4)DB  
Docker项目提供了一些可以搭配使用的上层工具,这些工具基于Linux内核的一些功能创建。整个项目的目标是帮助开发者和系统管理员门无痛的迁移应用(以及它们所涉及的所有依赖项),让应用在各种系统和机器上都能欢快的跑起来。 N=x,96CF  
]c+'SJQ  
sTYl' Ieg  
实现这个目标的关键在于一个叫做docker容器的运行环境,这个环境实际上是一个具备安全属性的LXC(Linux Containers)。容器的创建使用了Docker镜像,Docker镜像可以手动敲命令创建,也可以通过Dockerfiles实现自动化创建。 2Vr'AEIQ  
-^N '18:  
0+h?Bk  
注:关于Docker的基础知识(守护进程、CLI、镜像等等),可参阅本系列的第一篇文章Docker Explained: Getting Started。 _CT|5wQF<  
(P_+m#  
2AE|N_v8W  
在Ubuntu上安装Docker _Y~?.hs^  
最新版本的Docker(译注:本文撰写时间为2013年12月17日,当时的Docker最新版本为0.7.1)可以在Ubuntu/Debian和CentOS/RHEL等多个Linux发行版上部署(你也可以使用DigitalOcean上现成的Docker镜像,该镜像基于Ubuntu 13.04创建)。 Etk<`GRfA  
bsVOO9.4-  
8b $7#  
下面快速介绍一下Ubuntu上的安装流程。 1yf&ck1R  
u^9,u/gj  
Q5ao2-\   
在Ubuntu上安装Docker }])f^  
更新系统: v]sGdZ(6-  
{QJ`.6Kt  
fA5# 2P{  
sudo aptitude    update eIl&=gZ6>  
sudo aptitude -y upgrade !b_IH0]U  
{^iV<>J  
\:S8mDI^s  
检查系统是否支持aufs: v4zARE9#  
lnUy ? 0(  
g'@+#NMw  
sudo aptitude install linux-image-extra-`uname -r` !Ai;S  
P nDZi  
zyE yZc?  
往apt-key添加Docker仓库的密钥(用于软件包的验证): c{0?gt.  
k2a^gCBC  
yDrJn* r^  
sudo sh -c "wget -qO- https://get.docker.io/gpg | apt-key add -" 9:WKG'E8a  
7;V5hul  
2J5dZYW  
往aptitude软件源添加Docker仓库: %|~ UNP$  
LGkKR{ep(  
)sr]}S0  
sudo sh -c "echo deb http://get.docker.io/ubuntu docker main\ s Y,3  
> /etc/apt/sources.list.d/docker.list" g}7B0 yo  
'_%Jw:4k  
62q-7nV  
添加之后再更新一次系统: m[XN,IE#u  
b~p <   
t&0p@xLQ  
sudo aptitude update n >^?BU  
qi$8GX=~r  
h=aHZ6v  
最后,下载并安装Docker: $n) w4p_  
?bEYvHAzg  
{>qCZ#E5WO  
sudo aptitude install lxc-docker bP[/  
^/,s$dj  
&*}S 0  
Ubuntu默认的防火墙(UFW)的默认设置是拒绝一切转发(forwarding),但是Docker需要转发,所以也需要设置一下UFW。 ,DjZDw  
VmW_,  
"T'!cy  
用nano编辑器打开UFW配置文件: A, os rv  
%2{E'^#)p-  
(0S"ZT  
sudo nano /etc/default/ufw >3JOQ;:d8  
pGC`HTo|  
+RM3EvglDQ  
找到DEFAULT_FORWARD_POLICY这一行,将 s}.nh>Q  
KNn E5f  
O =fT;&%.  
DEFAULT_FORWARD_POLICY="DROP" cIX59y#7  
FR&RIFy  
$KiA~l  
替换为: +$9w[ARN+  
h ( Z7a%_  
.C1^QY-wL  
DEFAULT_FORWARD_POLICY="ACCEPT" |^=`ln!  
dZm{?\^_  
(z[cf|he  
按 CTRL+X 再按 Y 键,保存退出。 $6_J` 7  
Ai/#C$MY$  
?vhW`LXNB  
最后,把UFW重启一下: dO=<3W  
mr*zl*  
8E" .y$AW  
sudo ufw reload Nb$)YMbA  
3 3V/<v  
.{Xi&[jw  
基本的Docker命令 OTRTa{TB  
在正式开始之前,我们还是先复习一下上次在基础篇中介绍过的一些基本命令。 'HC4Q{b`  
.8,lhcpY  
29E^]IL?  
Docker的守护进程与命令行 ]t,ppFC#  
一般来说,Docker守护进程在你安装完成了之后就已经在后台运行,等待接收来自Docker命令行的指令。不过有的时候我们也需要手动启动Docker守护进程: q3;HfZ  
:!R+/5a  
#):FXB$a  
sudo docker -d & ''V:+@Toh  
7~IAgjo,@  
~h1'_0t   
Docker命令行的基本语法如下: D3_,2  
(BJs6":BFe  
4Fnr8 r8W  
sudo docker [option] [command] [arguments] S]<Hx_[}  
{RzlmDStV  
)37|rB E  
注:Docker的运行需要sudo权限。 * @oAM,@  
Oh|Hy/&6W  
?&N JN/+%  
Docker命令 IQR?n}ce  
下面列出了目前可用的Docker命令(译注:可以参考InfoQ中文站文章深入浅出Docker(二):Docker命令行探秘): lc*<UZR  
%=GnGgu  
D@!#79:)  
attach    附着到一个运行的容器上 s:Memvf  
build     从一个Dockerfile建立镜像 VG)kPKoi  
commit    将一个变更后的容器创建为一个新镜像 %POoyH@D}  
cp        在容器和本地文件系统之间复制文件/目录 R7vO,kZ6Q  
create    创建一个新的容器 _PJd1P.k  
diff      检测容器文件系统的变更 mio\}S A  
events    从服务获取实时事件 >R :Bkf-  
exec      在一个运行中的容器内执行命令 Uc6P@O*,  
export    将一个容器的文件系统输出为tar压缩包 "/wZtc  
history   显示一个镜像的历史 @dw0oRF  
images    列出镜像列表 ZXp=QH+f  
import    从tarball压缩包导入内容以创建一个文件系统镜像 |o6B:NH,rg  
info      显示系统信息 /-1[}h%U'  
inspect   返回容器或镜像的底层信息 b1_HDC(  
kill      杀死一个运行中的容器  w;)@2}  
load      从一个tar压缩包或STDIN加载一个镜像 %gmf  
login     登入Docker注册表(Docker registry) S)p1[&" M  
logout    从Docker注册表登出 E7ixl~  
logs      抓取一个容器的日志 tK0?9M.)  
network   管理Docker网络 ~Sh8. ++}  
pause     暂停一个容器内的所有进程 8eQ 4[wJY  
port      列出该容器的所有端口映射或指定端口映射 %i$]S`A}  
ps        列出容器列表 3 l QGU  
pull      从注册表拉取一个镜像或仓库 XJ.bK  
push      往注册表推送一个镜像或仓库 94\k++kc  
rename    重命名容器 SBZqO'}7  
restart   重启容器 Xb.WI\Eh  
rm        删除一个或多个容器 "'~55bG  
rmi       删除一个或多个镜像 1UT&kD!si  
run       在一个新的容器中运行一条命令 gWL'Fl}H  
save      将镜像保存至tar压缩包 { LZ` _1D  
search    在Docker Hub搜索镜像 `2( )Vf  
start     启动一个或多个容器 5Z@OgR  
stats     显示容器资源使用情况的实时信息流 *;5P65:u$>  
stop      停止一个运行中的容器 @FU9!  
tag       向注册表标记一个镜像 EPkmBru ^  
top       显示一个容器下运行的进程 B=8],_  
unpause   恢复运行一个容器里所有被暂停的进程 R,>LUa*u  
update    更新容器的资源 tY'fFz^Ho  
version   显示Docker版本信息 C5"=%v[gQv  
volume    管理Docker卷 H$^IT#  
wait      阻塞对指定容器的其他调用方法,直到容器停止后退出阻塞。 -C1,$mkj  
$qO%lJ:  
/?C}PM  
创建Docker容器作为Python WSGI应用的沙箱 xy;u"JY*  
我们已经完成了Docker的安装,也熟悉了基本的命令,现在可以为我们的Python WSGI应用创建Docker容器了。 mO%F {'  
#6* j+SX^  
RzgA;ZC'  
注:本章介绍的方法主要是练习用,并不适合于生产环境。生产环境下适用的自动化流程将在后续章节中介绍。 mhpaPin*JS  
 <m7m  
|soDt <y+L  
在Ubuntu里创建打底Docker容器 :rR)rj'  
Docker的run指令会基于Ubunt镜像创建一个新的容器。接下来,我们要用 -t 标识给这个容器附着(attach)一个终端,并运行一个 bash 进程。 J'4Pp<  
OpWTw&B"+  
&![3{G"+>l  
我们将暴露这个容器的80端口用于从外部访问。以后在更加复杂的环境中,你可能需要给多个实例做负载均衡,把不同的容器“连接”起来,再用一个反向代理容器去访问它们。 kMd1)6%6A  
]l/ PyX  
>JVdL\3  
sudo docker run -i -t -p 80:80 ubuntu /bin/bash ;@/^hk{A  
Q &~|P}  
0K T^V R  
注:运行这个命令时,Docker可能需要先下载一个Ubuntu镜像,下载完毕后才创建新容器。 Z8 eB5!$  
xfegi$  
VO u/9]a  
注意:你的终端会“附着”(attach)在新创建的容器上。要与容器分离并回到之前的终端访问点,可以按 CTRL+P 接着 CTRL+Q 执行脱离操作。“附着”在一个Docker容器上,基本上相当于从一个VPS内部访问另一个VPS。 v_G1YC7TU  
Tew?e&eO  
w&F.LiX^  
从脱离的状态想要回到附着的状态,需要执行如下步骤: J> Z.2  
UmEc")3  
\k 9EimT}  
用 sudo docker ps 列出所有运行中的容器 %7}ibz4iF  
找到之前创建的那个容器的ID {#U 3A_y  
执行 sudo docker attach [id] 完成当前终端到该容器的附着 P z< \q;  
注意:我们在容器内部做的一切操作都将仅限于在容器内部执行,对宿主机是完全没有影响的。 CqFk(Td9-D  
H"5=z7w  
wa1Qt  
安装前的准备工作 H6/n  
要在容器内部署Python WSGI应用(以及我们所需要的工具),首先我们需要对应的软件仓库。然而在Docker默认的Ubuntu镜像里并没有提供仓库(Docker的设计者认为这样有利于保持事物的简化),因此我们需要给我们这个打底镜像添加Ubuntu的universe软件仓库: " i!Xiy~  
&) qs0  
\0fS;Q^{j  
echo "deb http://archive.ubuntu.com/ubuntu/ $(lsb_release -sc) main universe" >> /etc/apt/sources.list v{r,Wy3  
fug F k  
T"_f9?  
更新一下软件列表: ;M<R e  
Fswr @du  
4ux^K:z  
apt-get update _jWGwO  
]iezwz`'  
vX;~m7+  
再给我们的容器安装一些必要的工具: G0h/]%I  
Q@(tyW+8U@  
Um%$TGw5  
apt-get install -y tar \ }'u0Q6Obj  
                   git \ 9M;k(B!  
                   curl \ CNM pyr  
                   nano \ wX+KW0|>  
                   wget \ PblO?@~O  
                   dialog \ ~5:-;ZbZ  
                   net-tools ]@A31P4t|  
                   build-essential )(V!& w6  
}.t8C y9G  
\5DOp-2  
安装Python工具 ItxC}qT  
本文将用一个简单的Flask应用作为示范。如果你用的是其他框架也没关系,安装部署的方法都是一样的。 ]I: h4hgw  
M0K+Vz=  
~y" ^t@!E  
再提醒一次:以下所有的命令都是在容器内部执行的,不会影响到宿主机。你可以想象成自己在一个全新的VPS上进行操作。 G[lNgVbU@  
Jty/gjK+  
Z(c2F]  
安装Python和pip: Oi4y~C_Xd  
oi\e[qE  
3:MAdh[w  
# 安装pip依赖:setuptools ~JX+4~qT  
apt-get install -y python python-dev python-distribute python-pip ' }T6dS  
_ipY;  
v%8S:3  
安装Web应用以及其依赖项 XK,l9 {*  
安装我们的应用之前,还是让我们再确认一下所有的依赖都已经就绪。首先是我们的框架——Flask。 NsF8`r g  
C@OY)!x!  
(( {4)5}  
因为我们已经装好了pip,所以就直接用pip来安装Flask: E-i <^&E  
~L?q.*q  
Af XlV-v  
pip install flask !~QmY,R  
CAtdx!  
<~iA{sY)O  
装好了Flask,创建一个“my_application”文件夹: cwBf((~  
+&qj`hA-b  
2 `nOYK  
mkdir my_application B`/p[U5  
3-U@==:T  
=N<Z@'c  
cd my_application 1%k$9[!l%  
? yek\X  
'J(B{B7|  
注:如果你想直接部署自己的应用(而不是这里的示范应用),可以参看下面的“小贴士”部分。 HN~  
K [M[0D  
5/C#*%EH'  
我们的示范应用是一个单页面的“Hello World” Flask应用。下面用nano来创建app.py: Fpckb18}(O  
/><+[\q4LM  
ylPDM7Ka  
nano app.py P,ud"F=r  
jyFXAs2  
\K%A}gnHe  
把下面这些内容复制到新创建的文件里: >'e(|P4  
.F@0`*#rE~  
e sDd>W  
from flask import Flask ;=F]{w]$+  
app = Flask(__name__) U]W+ers  
`&!J6)OJ  
_NkbB"+L  
@app.route("/") 2#t35fU  
def hello(): 1k?k{Ri  
    return "Hello World!" ^Uq"hT(41  
,/6 aA7(  
=#K$b *#  
if __name__ == "__main__": \SQwIM   
    app.run() |Y|gT*v  
 k.("<)  
_5JwJcQ  
按 CTRL+X 再按 Y 键,保存退出。 y;1l].L  
,+hH|$  
l4smAT  
或者,你也可以使用“requirements.txt”来定义应用的依赖项(比如Flask)。我们还是用nano来创建文件: iQJ[?l`  
w|0w<K  
Ql.abU  
nano requirements.txt ;!H|0sv  
>YuiCf?c7  
R0_O/o+{  
在文件输入你所有的依赖项(下面只列出两个,如果你需要别的请自行添加): KOHYeiry~A  
e SlZAdK  
E"[h20`\/  
flask bUBQ  
cherrypy *D6X&Hg&5  
Ln C5"  
OPP^n-iPr  
按 CTRL+X 再按 Y 键,保存退出。 ** !  
4A@77#:J5  
=%+O.  
注:你可以用pip来生成自定义的依赖项列表。具体的操作方法可以参考这篇Common Python Tools: Using virtualenv, Installing with Pip, and Managing Packages。 z-X_O32  
::eYd23  
#FQkwX'g  
最后,我们这个应用的文件组织结构是这样的: jt?.g'  
"0edk"hk  
Obb"#W@3  
/my_application "x P2GZ  
    | %pk'YA{M)q  
    |- requirements.txt  # 描述依赖项的文件 m_pqU(sP  
    |- /app              # 应用模块(你的应用应该在这个目录下) DLP G  
    |- app.py            # WSGI文件,里面应该包含“app”的实例名称(callable) j68_3zpl  
    |- server.py         # 可选,用于运行应用服务器(CherryPy) MR{JMo=r  
J jp)%c#_  
<yq kJ  
注:关于“server.py”,请参阅下面的章节“配置我们的Python WSGI应用”。 0/@ ^He8l  
iF#|Z$g-(  
~?#B(t  
注意:上面这些应用的文件、目录都是在容器内部创建的。如果你要在宿主机上自动化构建镜像(这个过程将在下面有关Dockerfile的章节中介绍),则你的宿主机上放置Dockerfile的目录下也需要同样的文件结构。 ! O~:  
|0Y: /uL#)  
*`g'*R  
小贴士:部署自己的应用 RL|d-A+;  
在容器内部搞定应用所需的软件仓库和依赖项 V ~%C me  
上述步骤描述了在容器内创建应用目录的过程。然而在真实场景下,我们往往需要从软件仓库拉取源代码。 & uMx*TTY  
yJRqX]MLA  
tzn+ M0'  
要把你的软件仓库复制到容器内部,有几个方法可以实现。下面介绍其中的两个: RcG 1J7#i  
(O@fgBM  
NQpC]#n  
# 方法1 T>cO{I  
# 用git下载源代码 %&[=%zc  
# 用法:git clone [源代码所在的URL] Nm.G,6<J  
# 示范: mN&B|KWU  
git clone https://github.com/mitsuhiko/flask/tree/master/examples/flaskr bMp[:dw`y  
BGOajYD  
t|j p]Vp  
# 方法2 FO S5?%J  
# 下载源代码压缩文件 S8Ec.]T   
# 用法:wget [源代码压缩文件所在的URL] z9qF<m  
# 示范:(用真实的URL替换掉下面这个假的) $c0<I59&|  
wget http://www.github.com/example_usr/application/tarball/v.v.x ]H$Trf:L  
"pInb5F  
aT_%G&.  
# 解压缩文件 AdgZau[Y6  
# 用法:tar vxzf [文件名 .tar (.gz)] RE%25t|  
# 示范:(用真实的文件名替换掉下面这个假的) j' }4ZwEh  
tar vxzf application.tar.gz `37%|e3bQ  
[fZhfZ)<  
ZTg[}+0e  
# 用pip下载安装应用依赖 _fM=J+  
# 下载 requirements.txt (可以用 pip freeze output 生成),再用pip全部安装: o5;|14O  
# 用法:curl [requirements.txt 文件的URL] | pip install -r - Q u_=K_W  
# 示范:(用真实的URL替换掉下面这个假的) eZdFfmYW^R  
curl http://www.github.com/example_usr/application/requirements.txt | pip install -r - 7I=vgT1F  
m 88(f2Ch  
%$6?em_  
配置我们的Python WSGI应用 yW]>v>l:Eg  
要运行这个应用,我们需要一个Web服务器。运行这个WSGI应用的Web服务器需要安装在代码所在的同一台容器中,作为该Docker容器运行的进程。 W1 \dGskV  
044Q>Qz,  
E|"QYsi.Ck  
注:我们在示范中将使用CherryPy自带的HTTP Web服务器,这是一个比较简单而且可以用在生产环境的选择。你也可以用Gunicorn甚至uSWGI(可以让它们跑在Nginx的后面),我们其他的教程中介绍过这种用法。 #B6$ r/%  
A)80qx:  
|<2<`3  
用pip下载安装CherryPy: SNrX(V::z  
otmyI;v 7<  
d04fj/B  
pip install cherrypy #:E^($v  
*]>~lO1  
Zjn![  
创建“server.py”,用于服务“app.py”里面的Web应用: S76x EL  
*(o^w'5  
p>1Klh:8.'  
nano server.py }*|aVBvU  
0~Iq9}{*P  
YDaGr6y4i  
把下面的内容复制粘贴到server.py里: 4Ix~Feuph  
|]'gd)%S\  
asL!@YE  
# 导入应用的语法: 0Ci:w|J  
# from app import application yHs'E4V`$  
# 示范: b8h6fB:2  
v%)=!T ,  
/rnP/X)T  
from app import app LDbo=w  
h{CMPJjD  
1Ba.'~:  
# 导入 CherryPy keS%w]87  
import cherrypy jTN!\RH9NF  
r1zuc:W 1  
 V/t-  
if __name__ == '__main__': Mo&Po9  
R QCKH]&!  
WO+_ |*&  
    # 挂载应用 r(Y@;  
    cherrypy.tree.graft(app, "/") ca"20NQ)  
LLJsBHi-  
o%l|16DR  
    # 从默认服务器上分离 fHacVj J  
    cherrypy.server.unsubscribe() $jN,] N~  
O^4K o}  
cp 5  
    # 实例化一个新的服务器对象 S*a_  
    server = cherrypy._cpserver.Server() #6za  
|^t8ct?x~  
_.BX#BIF  
    # 配置该服务器对象 r&D&xsbQ  
    server.socket_host = "0.0.0.0" Ks.kn7<l  
    server.socket_port = 80 <'<{|$Pw  
    server.thread_pool = 30 :a y-2  
>TwL&la  
['_G1_p  
    # SSL相关配置 aDE)Nf}  
    # server.ssl_module            = 'pyopenssl' P[a\Q`}L  
    # server.ssl_certificate       = 'ssl/certificate.crt' 5Y(f7,JX  
    # server.ssl_private_key       = 'ssl/private.key' roE*8:Y  
    # server.ssl_certificate_chain = 'ssl/bundle.crt' &HYs^|ydrr  
Avi8&@ya  
3NC-)S  
    # 订阅这个服务器对象 y1+~IjY  
    server.subscribe() P]||Xbbp  
 XV*uu "F  
9|RR;k[  
    # 启动服务器引擎 e$y VV#  
nK5FPFz8  
tO]` I-  
    cherrypy.engine.start() 7cMSJM(]G  
    cherrypy.engine.block() KsBi<wY  
s7}46\/U  
Vpsv@\@J>  
完成!现在我们就有了一个“Docker化”的Python Web应用,安全的跑在自己专属的沙箱里。只要输入下面的一行命令,它就可以给成千上万个客户端请求提供服务: G!3d!$t  
0N19R5NN8  
yZ]u{LJS  
python server.py TEi~X 2u  
BV01&.<|  
M;K%=l$NG  
这是让服务器在前台运行的指令。按下 CTRL+C 终止运行。如果想在后台运行服务器,可输入下面的指令: =g+Rk+jn  
#DFfySH)A  
O5eTkKUc  
python server.py & Yx{qVU  
%\2w 1  
*eonXJYD  
后台运行的应用需要用进程管理器(比如htop)来终止运行(kill或stop)。 hbg:}R=B<  
H~Fb=.h]U  
eAD uk!Iq  
注:有关CherryPy上跑Python应用的配置,可参阅这篇教程:How to deploy Python WSGI apps Using CherryPy Web Server。  N\9 Wxz$  
=xq+r]g6  
Gn8'h TM  
简单的测试一下应用的运行状态(以及端口的分配状态):在浏览器中访问 http://[容器所在的VPS的IP地址] ,应该能够看到“Hello World!”。 CFU'- #b  
wjeuZNYf  
swh8-_[c/  
创建Dockerfile以实现镜像创建的自动化 wfXm(RYM  
上面简单的说过,手动创建容器的这个方法并不适合用于生产环境的部署。生产环境里应该用Dockerfile进行构建流程自动化。 c/Ykk7T9--  
(7Q Fy  
nPU=n[t8O  
我们已经知道了如何在容器内部进行外部资源的下载和安装,那么Dockerfile其实也是一样的原理。一个Dockerfile定义了Docker要如何生成一个镜像,这个镜像可以直接用来跑我们的Python应用。 ?'$Yj>R6  
QwSYjR:K  
~d072qUos  
先了解一下Dockerfile的基本功能。 OmR) W'  
q&RezHK l  
<_N<L\  
Dockerfile概述 ,'f^K!iA   
Dockerfile是一种脚本文件,其中包含了一系列顺序执行的命令,Docker通过执行这些命令就可以创建一个新的Docker镜像。这极大的方便了部署。 Oe/\@f0bLT  
[, )G\  
=W_Pph  
Dockerfile一般会先用 FROM 命令定义一个打底的镜像,然后执行一系列的动作,动作全部执行完毕之后就形成了最终的镜像,并将完成的镜像提交给宿主机。 XDdF7i}  
il5Qo  
vk&6L%_~a  
使用: 8$TSQ~  
tvlrUp  
._i|+[  
# 在当前位置用Dockerfile创建一个镜像 7B)m/%>3s  
# 将生成的镜像标记为 [name] (比如nginx) CVy\']  
# 示范:sudo docker build -t [name] . #@3& 1 }J/  
sudo docker build -t nginx_img . :+q d>;yf#  
2]}4)_&d<e  
Bgvv6(i  
注:我们还有一篇专门介绍dockerfile的文章可供查阅:Docker Explained: Using Dockerfiles to Automate Building of Images o<8('j   
<1%(%KdN[  
};Oyv7D+b  
Dockerfile命令一览 cM hBOm*  
### Add k:QeZn(  
从宿主机复制文件到容器 9>6DA^  
Rl/5eE8  
h%%ryQQ&<  
X;&Iu{&=  
*t%Z'IA  
### CMD <%iRa$i5  
设置要执行或者要发给ENTRYPOINT的默认命令 2if7|o$=  
/Bw <?:  
.<Rw16O  
YT`,f*t  
B%^W$7 q  
### ENTRYPOINT hl]q6ZK!6  
设置容器内默认要启动的应用 Cvp!(<<gK  
TT@ U_^o  
]?6Pt:N2  
bQ3txuha  
7,lnfCm H  
### ENV gHo sPY[  
设置环境变量(key = value) ~NMx:PP  
2O 2HmL  
Uc;~q-??#  
### EXPOSE w7@TM%nS  
暴露一个端口 @PT([1C  
jNvDE}'  
_70Z1_ ;  
### FROM Qhq' %LR  
设置打底镜像(base) v YJ9G"E  
kV+%(Gl8  
Mbp7%^E"A  
### MAINTAINER Q+oV? S3{  
设置Dockerfile的作者/所有者信息 rQxiG[0  
f{.4# C'  
;1HzY\d%<  
### RUN RR+{uSO,t  
执行一条命令并提交执行后的(容器)镜像 yeI> b 1>Q  
%tMx48'N  
N=PSr4  
### USER iF9d?9TWl  
设置从镜像运行容器的用户名 ?<`oKBn  
z2>LjM) #  
%.m+6 zaF  
### VOLUME _u+ 7>  
从宿主机加载一个目录给容器 "[eH|z/  
Mf2F LrAh  
1uO2I&B  
### WORKDIR Sbl=U  
设置CMD运行时所在的目录 o] Xt2E  
au04F]-|j8  
qJag>OY  
创建Dockerfile &JXb) W  
在当前路径下用nano编辑器创建Dockerfile: Y hQ)M5  
0-uw3U<  
(6.uNLr  
sudo nano Dockerfile z&WtPSyGj  
1 j.ucv  
注:下面的内容需要按顺序添加到Dockerfile中。 )S 7+y6f&*  
fd4C8>*7G  
{vGJ}q?Sd"  
定义基本项 a%e`  
Dockerfile的基本项包括 FROM 原始镜像(比如Ubuntu)以及维护者姓名 MAINTAINER: tEL9hZzI  
Lo~ ;pvv  
K8aqC{  
############################################################ G[\TbPh  
# 创建Python WSGI应用容器的Dockerfile J09jBQ] R  
# 基于Ubuntu `0N7Gc  
############################################################ Jup)A`64  
$'%GB $.  
 v&|65[<  
# 设置Ubuntu为打底镜像 !uQT4< g  
FROM ubuntu {Q3OT  
>Jmla~A  
&yvvea]  
# 文件作者/维护者 54CJ6"q  
MAINTAINER Maintaner Name X58U>4a  
MzD1sWmK  
}! EVf  
更新默认的应用仓库 \y5lYb,*c_  
# 添加软件资源库的URL Z-Uu/GjB  
RUN echo "deb http://archive.ubuntu.com/ubuntu/ $(lsb_release -sc) main universe" >> /etc/apt/sources.list g#=<;X2  
c1"wS*u  
u<Kowt<ci  
# 更新资源列表 4AS%^&ah  
RUN apt-get update b3^:Bh9  
lN#j%0MaUo  
l S)^8  
安装基础工具 .;]WcC<3  
RUN apt-get install -y tar git curl nano wget dialog net-tools build-essential =I`S7oF  
orFwy!  
Hw o _;fV  
注:上述的有些工具可能你用不到,不过为了以防万一还是都装进来先。 \ZZ6r^99  
 u]Ku96!  
+6#$6hG  
Python基础套装安装指南 C< c6Ub  
一些Python需要的工具(pip)最好还是都先装起来。你的框架(WAF)和Web服务器(WAS)都需要有它们才能安装。 Q>,&@  
]SCHni_  
2y - QH  
RUN apt-get install -y python python-dev python-distribute python-pip ryL1<u ~  
:C*}Yg  
A|\A|8=b  
部署应用 x95s%29RS  
部署应用可以使用Docker的 ADD 命令直接复制源代码,也可以用 REQUIREMENTS 文件来一步到位。 8ttJ\m  
$@L;j  
;k!.ey $S  
注:如果你打算用一个文件描述所有的代码位置,可以参考下面的文件结构。 8"j$=T6;W  
KnkmGy  
tnJ7m8JmC  
    文件结构示范 g{V(WyT@  
<=NnrZOF  
yv4x.cfI2W  
    /my_application ESCN/ocV  
    | TRi'l#m4  
    |- requirements.txt  # 描述依赖项的文件 uppa`addK  
    |- /app              # 应用模块(你的应用应该在这个目录下) m^QoB  
    |- app.py            # WSGI文件,里面应该包含“app”的实例名称(callable) L<!h3n  
    |- server.py         # 可选,用于运行应用服务器(CherryPy) \6wltTW]#  
G3_7e A#;  
%unn{92)  
这个文件结构的创建过程在前面的章节中已经介绍过,这里不再赘述。总之,以上述文件结构为例,则再给Dockerfile末尾添加如下内容,将源代码复制到容器内: Fqeqn[,  
@&m [w'tn  
zIF1A*UH  
ADD /my_application /my_application hoFgs9  
L ci?  
jWUrw  
如果源代码是公网的git仓库,则可以使用如下内容: Z;uKnJh  
X"TL'"?fo  
jwk+&S  
RUN git clone [你的源码仓库URL] >Lcu  
e8`d<U  
"q?(rx;  
引导 K}cZK  
接下来,再从 requirements.txt 导入所有的依赖项: 1|p\rHGd  
%g7j7$c  
Q,.dIPla  
# 用pip来下载安装 requirements.txt 里面的东西 (Iv@SiZf(  
RUN pip install -r /my_application/requirements.txt %`$bQU  
{Vu=qNx  
a0ms9%Y;Q[  
# 暴露端口 f4S}Nga(  
EXPOSE 80 iX?j"=!  
gX!K%qJBg  
&5HI   
# 设置CMD运行的默认路径 gkK(7=r%  
WORKDIR /my_application A@3'I  ;  
NkUY_rKPb  
|~76dxU  
# 要运行的默认命令 ` &=%p|  
# 该命令在新容器创建时开始执行 t]sk[  
# 比如启动CherryPy来运行服务 eF;Jj>\R+i  
CMD python server.py -n@,r%`UK  
6U).vg<  
g#P]72TQ  
最终的Dockerfile 6Lq8#{/]u  
现在,整个Dockerfile看起来应该是这样的: "X<V>q$0~c  
#F/W_G7v  
fm#7}Y  
############################################################ :tENn r.9v  
# Dockerfile to build Python WSGI Application Containers 1&h\\&ic  
# Based on Ubuntu |s#,^SJ0  
############################################################ .D7Gog3^<  
&|IO+'_  
Eb,M+c?  
# Set the base image to Ubuntu W G3 _(mM  
FROM ubuntu -R9{Ak  
W^W.* ?e`  
k(`>(w  
# File Author / Maintainer s&%r?  
MAINTAINER Maintaner Name p>U= Jg  
auB+g'l  
r{* Qsaw  
# Add the application resources URL ;Gp9 ?0  
RUN echo "deb http://archive.ubuntu.com/ubuntu/ $(lsb_release -sc) main universe" >> /etc/apt/sources.list 0{!-h  
t%y i3  
-{oZK{a1  
# Update the sources list 0#5&*  
RUN apt-get update aEEb1Y  
vqF=kB"P  
CTqhXk[  
# Install basic applications A'uubFRL2[  
RUN apt-get install -y tar git curl nano wget dialog net-tools build-essential ?`:+SncI"b  
6 {`J I  
5HB*  
# Install Python and Basic Python Tools {iQ4jJ`n  
RUN apt-get install -y python python-dev python-distribute python-pip iYW<qgz  
Tv,ZS   
)*}?EI4.  
# Copy the application folder inside the container xgsEe3|  
ADD /my_application /my_application {p$X*2ReB  
oB<!U%BN  
SJ,];mC0  
# Get pip to download and install requirements: ;+3@S`2r  
RUN pip install -r /my_application/requirements.txt z2A,*|I  
Qdy/KL1]  
6 ZXRb  
# Expose ports ##" Hui  
EXPOSE 80 %4wHiCOg  
MHCwjo"  
i_y%HG  
# Set the default directory where CMD will execute !@h)3f]`1G  
WORKDIR /my_application dA#'HMh@  
Co/04F.  
JGYJ;j{E]  
# Set the default command to execute     Gfep m$*%  
# when creating a new container Xag#ZT  
# i.e. using CherryPy to serve the application 0,cU^HMA  
CMD python server.py 5^{2 g^jH6  
94lz?-j  
y+' ,jM  
按 CTRL+X 再按 Y 键,保存退出。 c&g*nDuDj  
h.xtkD)Y~  
Gl4f:`  
使用Dockerfile进行自动化的容器创建 ] `;Fc8$  
在之前的基础教程部分我们提到过,Dockerfile的工作方式利用到了 docker build 命令。 V|2[>\Cv  
TOx@Y$_9Q8  
<Ug1g0.  
我们通过Dockerfile指示docker从包含源代码的路径复制内容到容器内部,因此在构建之前务必要确认Dockerfile与代码路径的相对位置。 ddlF4L_  
"!#KQ''R  
)|&FBz;  
这样一个Docker镜像可以快速创建起来一个可以运行我们的Python应用的容器,我们需要做的只是输入这样一行指令: @f#6Nu  
DH])Q5  
nKch:g  
sudo docker build -t my_application_img . lgefTT GX)  
: TP\pH7E  
F{eI[A  
我们把这个镜像命名为 my_application_img 。为要从这个镜像启动一个新的容器,只需要输入下面的命令: e'3y^Vg  
(I#3![q  
3g3Znb  
sudo docker run -name my_application_instance -p 80:80 -i -t my_application_img \ Ju7.3.  
1 l-Y)   
/b."d\  
然后就可以在浏览器里输入你VPS的IP地址,访问应用了。 1(WBvAPS  
fy>~ GFk(  
WS\Ir-B  
有关Docker安装的更多教程(包括在其他发行版上安装Docker),可以查阅我们在docker.io上的文档docker installation documentation。 H6M G5f_  
@h%Nn)QBq  
[ 此帖被寒喵在2018-12-29 19:19重新编辑 ]
本人不是云栖社区工作人员。
无论您在使用中遇到什么问题,不要出言不逊!谢谢合作!
发表主题 回复主题
« 返回列表上一主题下一主题

限100 字节
批量上传需要先选择文件,再选择上传
 
验证问题: 阿里云官网域名是什么? 正确答案:www.aliyun.com
上一个 下一个