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

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

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

前言 Yf1&"WW4  
Web应用随时可能被攻击者利用来夺取整个主机的权限,这是很常见也是很恐怖的一件事。为了更高的安全性,就需要将不同应用之间进行隔离(尤其是在这些应用属于不同的用户的情况下),然而这种隔离的实现一直是个挑战。到目前为止,隔离性的实现方法已经有了很多,然而它们要么太过昂贵(时间的层面以及资源的层面),要么太过复杂(无论对开发者还是对管理员)。 U'4j+vUc  
?X $#J'U;  
.M(')$\U  
本文将讨论如何让“容器化”的Python Web应用跑在安全的沙箱里,严格的坚守在其各的环境中(当然,除非你指定它们与其他应用进行“连接”)。我将一步一步的介绍如何创建一个Docker容器,如何用这个容器来跑我们的Python Web应用,以及如何用一个Dockerfile来描述整个构建过程以实现完整的自动化。 Oly"ll*K  
v9*ugu[K9  
`G/%U~  
目录 v@,n]"  
1. Docker概述 ~l {*XM  
2. 在Ubuntu上安装Docker xA7>";sla[  
3. 基本的Docker命令 #3VOC#.  
Docker的守护进程与命令行 '%Fg+cZN\  
Docker命令 4Fu:ov ]M  
4. 创建Docker容器作为Python WSGI应用的沙箱 6=pE5UfT  
在Ubuntu里创建打底Docker容器 J[|4`GT  
安装前的准备工作 8-#_xsZ^;  
安装Python工具 2uSXC*Phz  
安装我们的Web应用以及其依赖项 E5 ;6ks)  
配置我们的Python WSGI应用 /$[9-G?  
5. 创建Dockerfile以实现镜像创建的自动化 y<9' 3\  
Dockerfile概述 Ga<Uvr%+  
Dockerfile命令一览 IL0e:-@!0  
创建Dockerfile UY5wef2sF  
定义基本项 bwR$9 10b  
更新默认的应用仓库 jm ORKX+)  
安装基础工具 $Wy(Wtrx|  
Python基础套装安装指南 1j_gQ,'20  
部署应用 yf4I<v$y  
引导 !l(D0 C  
最终的Dockerfile F>F2Yql&W  
使用Dockerfile进行自动化的容器创建 {<}9r6k;f  
Docker概述 60\`TsFobT  
Docker项目提供了一些可以搭配使用的上层工具,这些工具基于Linux内核的一些功能创建。整个项目的目标是帮助开发者和系统管理员门无痛的迁移应用(以及它们所涉及的所有依赖项),让应用在各种系统和机器上都能欢快的跑起来。 ayGcc`  
A4~- {.w=  
zH5pe  
实现这个目标的关键在于一个叫做docker容器的运行环境,这个环境实际上是一个具备安全属性的LXC(Linux Containers)。容器的创建使用了Docker镜像,Docker镜像可以手动敲命令创建,也可以通过Dockerfiles实现自动化创建。 J#'8]p3E  
ApD`i+Y@  
.IU\wN  
注:关于Docker的基础知识(守护进程、CLI、镜像等等),可参阅本系列的第一篇文章Docker Explained: Getting Started。 L*6R5i>  
V|{\8&  2  
=?3b3PZn  
在Ubuntu上安装Docker 8P?p  
最新版本的Docker(译注:本文撰写时间为2013年12月17日,当时的Docker最新版本为0.7.1)可以在Ubuntu/Debian和CentOS/RHEL等多个Linux发行版上部署(你也可以使用DigitalOcean上现成的Docker镜像,该镜像基于Ubuntu 13.04创建)。 ROhhd.  
9 _oAs"w  
JW},7Ox  
下面快速介绍一下Ubuntu上的安装流程。 tE8aL{<R  
{Mpx33  
0SV<Pl^  
在Ubuntu上安装Docker gbrn'NT  
更新系统: y9Q #%a8V  
"Cz0r"N  
V/&JArW  
sudo aptitude    update 5La' I7q  
sudo aptitude -y upgrade 5O%?J-Hp  
V8hmfV~=]P  
>Jk]=_%  
检查系统是否支持aufs: /:;"rnvq  
$L4h'(s  
ge4QaK  
sudo aptitude install linux-image-extra-`uname -r` /jl/SV+  
8B`w!@hf  
F{G.dXZZ<  
往apt-key添加Docker仓库的密钥(用于软件包的验证): +;ylld  
_Rb>py  
t-i\gq^  
sudo sh -c "wget -qO- https://get.docker.io/gpg | apt-key add -" b OolBKV  
fm0]nT   
@~QI3)=s  
往aptitude软件源添加Docker仓库: *.6m,QqJ(  
r3NdE~OAi  
+QEP:#qZw  
sudo sh -c "echo deb http://get.docker.io/ubuntu docker main\ 4;2  
> /etc/apt/sources.list.d/docker.list" - i#Kpf  
1)#dgsa  
?J@P0(M#  
添加之后再更新一次系统: .(7m[-iF!  
[Y4Wm?  
gW-mXb  
sudo aptitude update JRR,ooN*i  
mpNS}n6  
\4KV9wm  
最后,下载并安装Docker:  u]OYu  
||))gI`3a  
v,kvLjqt  
sudo aptitude install lxc-docker R2A#2{+H  
\30rF]F`l  
d2?#&d'aq  
Ubuntu默认的防火墙(UFW)的默认设置是拒绝一切转发(forwarding),但是Docker需要转发,所以也需要设置一下UFW。 s o7.$]aV  
e^}@X[*'#  
^ X-6j[".  
用nano编辑器打开UFW配置文件: ^ib =fLu  
CK#i 6!~r  
Q*hXFayx  
sudo nano /etc/default/ufw ,h9?o  
;Z9(ll:<$  
`n RF"T_  
找到DEFAULT_FORWARD_POLICY这一行,将 )nGH$Mu  
[$c"}=g[+  
[1mEdtqf*  
DEFAULT_FORWARD_POLICY="DROP" Tol"D2cyf  
7&dK_x,a  
CQPq5/@Y4  
替换为: pL . 0_  
p=m:^9/  
0g;)je2_2?  
DEFAULT_FORWARD_POLICY="ACCEPT" T!I3.  
pekNBq Wm  
[C\B2iU7_M  
按 CTRL+X 再按 Y 键,保存退出。 {:|b,ep T  
8Wo!NG:V5  
dvM%" k  
最后,把UFW重启一下: K $WMrp  
(I#mo2  
\XO'7bNu-  
sudo ufw reload }>EWF E`  
6 FN#Xg  
^]D+H9Tl  
基本的Docker命令 PuhvJHT  
在正式开始之前,我们还是先复习一下上次在基础篇中介绍过的一些基本命令。 :5F(,Z_  
X:lStO#5  
!zsrORF{  
Docker的守护进程与命令行 /=T:W*C  
一般来说,Docker守护进程在你安装完成了之后就已经在后台运行,等待接收来自Docker命令行的指令。不过有的时候我们也需要手动启动Docker守护进程: UmArl)R/  
e,r7UtjoxR  
0 6M?ecN  
sudo docker -d & #hP>IU  
1rS8+!9C  
D&@Iuo  
Docker命令行的基本语法如下: tLxeq?Oo]  
VkO*+"cGv  
'tDUPm38  
sudo docker [option] [command] [arguments]  fZ&' _  
/2 hk9XM  
d"n"A?nXh  
注:Docker的运行需要sudo权限。 ef)zf+o  
CB X}_]9X  
eco&!R[G  
Docker命令 &r,)4q+  
下面列出了目前可用的Docker命令(译注:可以参考InfoQ中文站文章深入浅出Docker(二):Docker命令行探秘): !.-u'6e  
|fIyq}{7  
c-v-U O%  
attach    附着到一个运行的容器上 yn"4qC#Z  
build     从一个Dockerfile建立镜像 T--%UZD]W  
commit    将一个变更后的容器创建为一个新镜像 G')zDx  
cp        在容器和本地文件系统之间复制文件/目录 bdY:-8!3  
create    创建一个新的容器 ?D6|~k i  
diff      检测容器文件系统的变更 [ [w |  
events    从服务获取实时事件 !A1~{G2VL_  
exec      在一个运行中的容器内执行命令 FE]UqB  
export    将一个容器的文件系统输出为tar压缩包 2wDDVUwyB  
history   显示一个镜像的历史 Mi{ns $B%  
images    列出镜像列表 q3#+G:nh  
import    从tarball压缩包导入内容以创建一个文件系统镜像 ( Zd(?">i  
info      显示系统信息 u1Slu%^e  
inspect   返回容器或镜像的底层信息 %q~q,=H$]  
kill      杀死一个运行中的容器 _Gf-s51s  
load      从一个tar压缩包或STDIN加载一个镜像 G~v:@  
login     登入Docker注册表(Docker registry) cs ?WE9N  
logout    从Docker注册表登出 Goc?HR  
logs      抓取一个容器的日志 tXH;4K@  
network   管理Docker网络 wwywiFj  
pause     暂停一个容器内的所有进程 ]@ke_' "  
port      列出该容器的所有端口映射或指定端口映射 EFljUT?&  
ps        列出容器列表 _ilitwRN3  
pull      从注册表拉取一个镜像或仓库 (ie%zrhS  
push      往注册表推送一个镜像或仓库 1YJ?Y  
rename    重命名容器 @4D{lb"{  
restart   重启容器 u '/)l}  
rm        删除一个或多个容器 mJ=V <_  
rmi       删除一个或多个镜像 hc+B+-,  
run       在一个新的容器中运行一条命令 CoNaGb  
save      将镜像保存至tar压缩包 -egnMc67  
search    在Docker Hub搜索镜像 _^NaP  
start     启动一个或多个容器 U1X"UN)  
stats     显示容器资源使用情况的实时信息流 abm 3q!a-  
stop      停止一个运行中的容器 N0Y4m_dm*  
tag       向注册表标记一个镜像 E:ci/09wD  
top       显示一个容器下运行的进程 ~/9RSdv7  
unpause   恢复运行一个容器里所有被暂停的进程 vdAd@Z~\  
update    更新容器的资源 .=TXi<8Brw  
version   显示Docker版本信息 lFnYQab  
volume    管理Docker卷 GT)7VFrL  
wait      阻塞对指定容器的其他调用方法,直到容器停止后退出阻塞。 o2U5irU  
!cYID \}S,  
Y`(Ri-U4  
创建Docker容器作为Python WSGI应用的沙箱 1=C12  
我们已经完成了Docker的安装,也熟悉了基本的命令,现在可以为我们的Python WSGI应用创建Docker容器了。 bTmhz  
h !K" ;qw  
G^#>HE|  
注:本章介绍的方法主要是练习用,并不适合于生产环境。生产环境下适用的自动化流程将在后续章节中介绍。 GRbbU#/=G  
S-88m/"]s  
`dG;SM$T,  
在Ubuntu里创建打底Docker容器 $zp|()_  
Docker的run指令会基于Ubunt镜像创建一个新的容器。接下来,我们要用 -t 标识给这个容器附着(attach)一个终端,并运行一个 bash 进程。 +cqUp6x.  
VGD~) z57  
W/xPVmnV  
我们将暴露这个容器的80端口用于从外部访问。以后在更加复杂的环境中,你可能需要给多个实例做负载均衡,把不同的容器“连接”起来,再用一个反向代理容器去访问它们。 1h?ve,$  
U364'O8_  
&Wcz~Gx3Q  
sudo docker run -i -t -p 80:80 ubuntu /bin/bash @4T   
y zp#  
~Yi4?B<  
注:运行这个命令时,Docker可能需要先下载一个Ubuntu镜像,下载完毕后才创建新容器。 ~Jr'4%   
T?m@`"L,  
,^8':X"A{!  
注意:你的终端会“附着”(attach)在新创建的容器上。要与容器分离并回到之前的终端访问点,可以按 CTRL+P 接着 CTRL+Q 执行脱离操作。“附着”在一个Docker容器上,基本上相当于从一个VPS内部访问另一个VPS。  P>iZ gv  
<I34@;R c  
W1X3ArP]m8  
从脱离的状态想要回到附着的状态,需要执行如下步骤: $j\>T@  
||QK)$"  
x0J W  
用 sudo docker ps 列出所有运行中的容器 UWC4PWL,>C  
找到之前创建的那个容器的ID UTKyPCfj  
执行 sudo docker attach [id] 完成当前终端到该容器的附着 V<S6 a  
注意:我们在容器内部做的一切操作都将仅限于在容器内部执行,对宿主机是完全没有影响的。 uK$ Xqo%L  
!>> A@3  
3= sBe HL  
安装前的准备工作 \ opM}qZ  
要在容器内部署Python WSGI应用(以及我们所需要的工具),首先我们需要对应的软件仓库。然而在Docker默认的Ubuntu镜像里并没有提供仓库(Docker的设计者认为这样有利于保持事物的简化),因此我们需要给我们这个打底镜像添加Ubuntu的universe软件仓库: xw}yl4WT{  
@ewaj!  
wrgB =o  
echo "deb http://archive.ubuntu.com/ubuntu/ $(lsb_release -sc) main universe" >> /etc/apt/sources.list o/o6|[=3  
E#:!&{O  
8Lx/ZGy  
更新一下软件列表: ?[zw5fUDS  
uq s   
}i"[5:  
apt-get update 1gkpK`u(B  
,zM@)Q ;9  
9gw;MFP)D  
再给我们的容器安装一些必要的工具: ~:7y!=8#  
{24>&<p  
s=(q#Z  
apt-get install -y tar \ dV38-IfGkl  
                   git \ A/2$~4,  
                   curl \ s2SV   
                   nano \ 8bdO-LJ9  
                   wget \ ,H+Y1N4W(  
                   dialog \ Ue 9Y+'-x  
                   net-tools Eej Lso#\  
                   build-essential SXqB<j$.;  
7yyX8p>  
9&f+I@K  
安装Python工具 a)pc+w#  
本文将用一个简单的Flask应用作为示范。如果你用的是其他框架也没关系,安装部署的方法都是一样的。 PY7H0\S)  
bl a`B=r  
@,.D]43  
再提醒一次:以下所有的命令都是在容器内部执行的,不会影响到宿主机。你可以想象成自己在一个全新的VPS上进行操作。 mA$y$73=T  
"NA<^2W@J  
G7"(,L` 5  
安装Python和pip: 4RVqfD  
mTtaqo_Bh  
P'tXG  
# 安装pip依赖:setuptools ,M{G X  
apt-get install -y python python-dev python-distribute python-pip uOzoE_i  
xA7~"q&u  
PF/K&&9}  
安装Web应用以及其依赖项 o|h=M/  
安装我们的应用之前,还是让我们再确认一下所有的依赖都已经就绪。首先是我们的框架——Flask。 7 nnF!9JOv  
<<?32r~  
# tu>h  
因为我们已经装好了pip,所以就直接用pip来安装Flask: L#K`F8Wi=  
W/BPf{U  
SgxrU&::  
pip install flask d"Zu10  
v%T'!(0j/  
<!>\ n\A  
装好了Flask,创建一个“my_application”文件夹: EB!ne)X  
37kFbR@x  
i \NV<I  
mkdir my_application Ny7*MZ-  
./XX  
TSewq4`K  
cd my_application 7d%x7!E   
k/{WlLN  
7\%JJw6h  
注:如果你想直接部署自己的应用(而不是这里的示范应用),可以参看下面的“小贴士”部分。 IXtG 36O  
,V!"4 T,Z  
G-)e(u   
我们的示范应用是一个单页面的“Hello World” Flask应用。下面用nano来创建app.py: Lq>lj`>  
i!}k5k*Z  
G!$~'o%/  
nano app.py dV B#Np  
].xSX0YQ%  
k)7i^ 1U  
把下面这些内容复制到新创建的文件里: =QVkY7  
kE<CuO  
FhY#3-jH  
from flask import Flask oH6zlmqG"  
app = Flask(__name__) 7mYcO3{5{  
"dIWHfQB  
P _fCb  
@app.route("/") %Ve@DF8G  
def hello(): o%~fJx:]y  
    return "Hello World!" N2s"$Ttq  
mM&P&mz/D  
J' uaZI>'  
if __name__ == "__main__": L>IP!.J]?  
    app.run() 1+$F= M~  
X <8|uP4  
QD!NV*  
按 CTRL+X 再按 Y 键,保存退出。 M}[Q2v\  
r]cq|Nv8:  
{rMf/RAE  
或者,你也可以使用“requirements.txt”来定义应用的依赖项(比如Flask)。我们还是用nano来创建文件: $,by!w'e:l  
~_JfI7={Jn  
7<]&pSt=  
nano requirements.txt *B!Ox}CI.L  
 >I4BysR  
%HpPTjAW  
在文件输入你所有的依赖项(下面只列出两个,如果你需要别的请自行添加): 1 jB0gNe  
05ClPT\BCr  
[?7QmZK  
flask Kt*b) <  
cherrypy ?1\I/ 'E9  
3Ed  
z8a{M$-Q  
按 CTRL+X 再按 Y 键,保存退出。 3G4WKg.^  
wp1O*)/q  
"W6cQsi  
注:你可以用pip来生成自定义的依赖项列表。具体的操作方法可以参考这篇Common Python Tools: Using virtualenv, Installing with Pip, and Managing Packages。 :a)RMp+^0  
S\N l|U[  
J&\Q3_vro9  
最后,我们这个应用的文件组织结构是这样的: ku m@cA  
^}$t(t  
."dT6uE  
/my_application Wtu-g**KN  
    | Td|,3 n  
    |- requirements.txt  # 描述依赖项的文件 &M@ .d$<C  
    |- /app              # 应用模块(你的应用应该在这个目录下) Awfd0L;9  
    |- app.py            # WSGI文件,里面应该包含“app”的实例名称(callable) C0w_pu  
    |- server.py         # 可选,用于运行应用服务器(CherryPy) ;/+<N  
bX38=.up  
-'miM ~kG[  
注:关于“server.py”,请参阅下面的章节“配置我们的Python WSGI应用”。 QmSj6pB>  
r&R~a9+)  
[7.Num_L  
注意:上面这些应用的文件、目录都是在容器内部创建的。如果你要在宿主机上自动化构建镜像(这个过程将在下面有关Dockerfile的章节中介绍),则你的宿主机上放置Dockerfile的目录下也需要同样的文件结构。 ASi2;Q_{_  
A7Y CSjB  
Ynf "g#(  
小贴士:部署自己的应用 .a2R2~35  
在容器内部搞定应用所需的软件仓库和依赖项 "Fmq$.$%  
上述步骤描述了在容器内创建应用目录的过程。然而在真实场景下,我们往往需要从软件仓库拉取源代码。 AoGpM,W]5  
}*!L~B!  
hb8oq3*x  
要把你的软件仓库复制到容器内部,有几个方法可以实现。下面介绍其中的两个: g~21|Sa$[  
>yn?@ve@  
Vr1r2G2  
# 方法1 1 M!4hM Q  
# 用git下载源代码 4IZAJqw(*  
# 用法:git clone [源代码所在的URL] X n0HJ^"_  
# 示范: ]v:,<=S  
git clone https://github.com/mitsuhiko/flask/tree/master/examples/flaskr c[M4l  
0aR.ct%  
tlO=>  
# 方法2 FP{=b/  
# 下载源代码压缩文件 !Usmm8!K  
# 用法:wget [源代码压缩文件所在的URL] (&*Bl\YoX  
# 示范:(用真实的URL替换掉下面这个假的) [YlRz  
wget http://www.github.com/example_usr/application/tarball/v.v.x `Qb!W45  
tC1'IE-h  
2va[= >_  
# 解压缩文件 B_Gcz5  
# 用法:tar vxzf [文件名 .tar (.gz)] L=#B>Eu  
# 示范:(用真实的文件名替换掉下面这个假的) i88 5T '  
tar vxzf application.tar.gz V 7~9z\lW  
cyd_xB5K  
Ye|gW=FUR  
# 用pip下载安装应用依赖 G $*=9`  
# 下载 requirements.txt (可以用 pip freeze output 生成),再用pip全部安装: M3KK^YRN  
# 用法:curl [requirements.txt 文件的URL] | pip install -r - Da! fwth  
# 示范:(用真实的URL替换掉下面这个假的) 2@_3V_  
curl http://www.github.com/example_usr/application/requirements.txt | pip install -r - (@T{ [\  
s0_HMP x  
#bGYHN  
配置我们的Python WSGI应用 ZiKO|U@/  
要运行这个应用,我们需要一个Web服务器。运行这个WSGI应用的Web服务器需要安装在代码所在的同一台容器中,作为该Docker容器运行的进程。 B;Z _'.i,d  
 !N\_D  
[Ti ' X#  
注:我们在示范中将使用CherryPy自带的HTTP Web服务器,这是一个比较简单而且可以用在生产环境的选择。你也可以用Gunicorn甚至uSWGI(可以让它们跑在Nginx的后面),我们其他的教程中介绍过这种用法。 ~<IQe-Q 5  
Z{ YuX  
Z 5 .cfI[  
用pip下载安装CherryPy: , =*^XlO=c  
kN<;*jHV  
,lCFe0>k!=  
pip install cherrypy b-`P-  
a]V#mF |{  
o^uh3,.  
创建“server.py”,用于服务“app.py”里面的Web应用: k<fR)o  
^|U5@u_  
es69P)  
nano server.py !eb{#9S*  
~tNk\Kkv  
Eos;7$u[  
把下面的内容复制粘贴到server.py里: vcy(!r  
FbdC3G|oA  
M?gZKdj  
# 导入应用的语法: ga1b%5]v.  
# from app import application jtpk5 fJB  
# 示范: qncZpXw^  
}]=A:*jD  
[;toumv  
from app import app K;`*n7=IA  
Cx) N;x  
y </i1qM  
# 导入 CherryPy sSiZG  
import cherrypy ](ztb)  
=AHV{V~  
f4X}F|!h  
if __name__ == '__main__': )5<dmK@  
M-!#-l  
RzB64  
    # 挂载应用 _ -ec(w~/  
    cherrypy.tree.graft(app, "/") )B @&q.2B=  
ZkMHy1  
OWN|W,  
    # 从默认服务器上分离 -42 U  
    cherrypy.server.unsubscribe() zb9vUxN [  
1Ji"z>H*  
M 8mNeh  
    # 实例化一个新的服务器对象 q<fj1t1w  
    server = cherrypy._cpserver.Server() a  ?wg~|g  
.L]2g$W\p  
zzKU s"u  
    # 配置该服务器对象 _B]Bd@<w  
    server.socket_host = "0.0.0.0" Oo\~' I  
    server.socket_port = 80 !\&4,l(  
    server.thread_pool = 30 @[rlwwG,  
yg4ILL  
n;dWb$:  
    # SSL相关配置 R?*-ZI[>w  
    # server.ssl_module            = 'pyopenssl' 8wS9%+  
    # server.ssl_certificate       = 'ssl/certificate.crt' zWdz9;=_  
    # server.ssl_private_key       = 'ssl/private.key' URTJA<r8D  
    # server.ssl_certificate_chain = 'ssl/bundle.crt' \ItAc2,Fl  
cja-MljD  
o{ | |Ig  
    # 订阅这个服务器对象 0ENqK2  
    server.subscribe() ;Y &2G'  
C4C!-12  
KEr?&e  
    # 启动服务器引擎 l %xeM !}  
.aIFm5N3?  
yD ur9Qd6  
    cherrypy.engine.start() ui^v.YCMI  
    cherrypy.engine.block() w?c~be$  
=u&NdMy  
|1 LKdP  
完成!现在我们就有了一个“Docker化”的Python Web应用,安全的跑在自己专属的沙箱里。只要输入下面的一行命令,它就可以给成千上万个客户端请求提供服务: y|D-W>0cX3  
D-5~CK4`  
l_=kW!l  
python server.py xm<sH!,j  
,PyPRPk  
O$peCv   
这是让服务器在前台运行的指令。按下 CTRL+C 终止运行。如果想在后台运行服务器,可输入下面的指令: pvd9wKz  
q/YO5>s15  
Jb z>j\  
python server.py & }pPt- k  
3nkO+ qQ  
7otqGE\2  
后台运行的应用需要用进程管理器(比如htop)来终止运行(kill或stop)。 |.U- yyz  
SVyJUd_  
fm,:8%  
注:有关CherryPy上跑Python应用的配置,可参阅这篇教程:How to deploy Python WSGI apps Using CherryPy Web Server。 qS2]|7q?Tc  
1^=[k  
X 7"hTD  
简单的测试一下应用的运行状态(以及端口的分配状态):在浏览器中访问 http://[容器所在的VPS的IP地址] ,应该能够看到“Hello World!”。 6I\mhw!pQ  
D61e  
vro5G')  
创建Dockerfile以实现镜像创建的自动化 s=|&NlO$  
上面简单的说过,手动创建容器的这个方法并不适合用于生产环境的部署。生产环境里应该用Dockerfile进行构建流程自动化。 SxCzI$SGu  
'Xzi$}E D  
 wZUR  
我们已经知道了如何在容器内部进行外部资源的下载和安装,那么Dockerfile其实也是一样的原理。一个Dockerfile定义了Docker要如何生成一个镜像,这个镜像可以直接用来跑我们的Python应用。 O0<GFL$)&  
d "vd_}P~  
qxW 2q8QHo  
先了解一下Dockerfile的基本功能。 ( G6N@>V(`  
lf9_!`DGV  
^l/$ 13=  
Dockerfile概述 ) N"gW*  
Dockerfile是一种脚本文件,其中包含了一系列顺序执行的命令,Docker通过执行这些命令就可以创建一个新的Docker镜像。这极大的方便了部署。 0H{0aQQ  
4) nQBFX  
9^c"HyR  
Dockerfile一般会先用 FROM 命令定义一个打底的镜像,然后执行一系列的动作,动作全部执行完毕之后就形成了最终的镜像,并将完成的镜像提交给宿主机。 ;w}5:3+  
Mky8qVQ2  
S\UM0G}v  
使用: $pGk%8l%  
&hYjQ&n  
p^``hP:J  
# 在当前位置用Dockerfile创建一个镜像 6Gs,-Kb:  
# 将生成的镜像标记为 [name] (比如nginx) FR@ dBcJUU  
# 示范:sudo docker build -t [name] . cBA2;5E  
sudo docker build -t nginx_img . uy;3s=03^  
"]`QQT-{0  
MPd#C*c  
注:我们还有一篇专门介绍dockerfile的文章可供查阅:Docker Explained: Using Dockerfiles to Automate Building of Images gB@Wv9 1  
a[8_ O-   
I1H:h  
Dockerfile命令一览 <!|2Ru  
### Add :PaFC{O)*  
从宿主机复制文件到容器 7iKbd  
t(MlZ>H  
*lG$B@;rc|  
F'#e]/V1  
3^us;aOr  
### CMD } q ? iJ?P  
设置要执行或者要发给ENTRYPOINT的默认命令 (Qq$ql27  
a~~"2LE`  
q& Vt*  
U%45qCU  
es(vWf'  
### ENTRYPOINT EX&y !  
设置容器内默认要启动的应用 MJX m7<(  
9H3#8T] ;  
I/)dXk~  
^HL#)fK2I  
uYMW5k_,>  
### ENV N]yk<55  
设置环境变量(key = value) 1^Zx-p3J  
M=N`&m\  
Z?hBn`.  
### EXPOSE R4JO)<'K&  
暴露一个端口 D0k7)\puQ  
*G~c6B Z  
ccm <rZ7  
### FROM xHr  
设置打底镜像(base) Y!*,G]7  
v @N8v  
<qRw! 'S^  
### MAINTAINER {@ , L  
设置Dockerfile的作者/所有者信息 .(1$Q6yG  
>v<}$v6D~  
H[Pb Wy:  
### RUN G(0 bulq  
执行一条命令并提交执行后的(容器)镜像 ln=zGX.e  
v*BA\&  
Q#g s)2  
### USER 0`ib_&yI  
设置从镜像运行容器的用户名 6 3u'-Z"4  
FUcs=7c  
H*\ }W  
### VOLUME P|(J]/  
从宿主机加载一个目录给容器 #uXOyiE  
*<A;jP  
<WbD4Q<3?  
### WORKDIR UZAWh R  
设置CMD运行时所在的目录 7R2)Klt  
eO9nn9lql  
X)&Z{ V>  
创建Dockerfile g<dCUIbcQ  
在当前路径下用nano编辑器创建Dockerfile: G#*!)#M <  
pM7BdMp   
B]qh22Yib  
sudo nano Dockerfile n `Xz<Q!  
1 */+s^{W7  
注:下面的内容需要按顺序添加到Dockerfile中。 vaxg^n|v9  
%!$-N!e  
RYhdf  
定义基本项 6aK--k  
Dockerfile的基本项包括 FROM 原始镜像(比如Ubuntu)以及维护者姓名 MAINTAINER: CM#EA"9  
%5KR}NXX6  
( gFA? aD<  
############################################################ GB+d0 S4  
# 创建Python WSGI应用容器的Dockerfile `x#~ -  
# 基于Ubuntu Yptsq@s  
############################################################ w0I /  
%7@H7^s}9  
J-f0  
# 设置Ubuntu为打底镜像 dB,#`tc=,  
FROM ubuntu G7202(w <  
W|Tew-H{h_  
ii2Z }qe  
# 文件作者/维护者 %N  
MAINTAINER Maintaner Name } 2.}fHb2  
Zll^tF#  
>V$#Um?AXj  
更新默认的应用仓库 WuQYEbap  
# 添加软件资源库的URL &zd7t6  
RUN echo "deb http://archive.ubuntu.com/ubuntu/ $(lsb_release -sc) main universe" >> /etc/apt/sources.list @iceMD.  
-U $pW(~  
dtBV0$  
# 更新资源列表 :Ir:OD# o  
RUN apt-get update / IS WC   
Ny'v/+nQ  
DnFl*T>  
安装基础工具 _Xv/S_yW  
RUN apt-get install -y tar git curl nano wget dialog net-tools build-essential 614/wI8(  
{4 d$]o0V  
8'[wa  
注:上述的有些工具可能你用不到,不过为了以防万一还是都装进来先。 1c"m$)a4  
bx^EaXj(r  
Kzu9Qm-+z^  
Python基础套装安装指南 PPO<{  
一些Python需要的工具(pip)最好还是都先装起来。你的框架(WAF)和Web服务器(WAS)都需要有它们才能安装。 >[: 2  
TjK5UML  
_fCHj$I*]  
RUN apt-get install -y python python-dev python-distribute python-pip +"cyOC  
FxX3Pq8h  
')-(N um  
部署应用 ` W>B8  
部署应用可以使用Docker的 ADD 命令直接复制源代码,也可以用 REQUIREMENTS 文件来一步到位。 4Z<l>!  
O*{H;7Pv  
F s\P/YX  
注:如果你打算用一个文件描述所有的代码位置,可以参考下面的文件结构。 o#) !b:/  
* _l o;  
I{I [N &N  
    文件结构示范 y;Ln ao7i  
2H+DT-hK  
P$>kBW53  
    /my_application BQ:Kx_   
    | r(T/^<  
    |- requirements.txt  # 描述依赖项的文件 Q3q.*(#  
    |- /app              # 应用模块(你的应用应该在这个目录下) 5)5bt q)[  
    |- app.py            # WSGI文件,里面应该包含“app”的实例名称(callable) m=,c,*>  
    |- server.py         # 可选,用于运行应用服务器(CherryPy) Pq>[q?>?  
k1U8wdoT  
o=# [^Zv  
这个文件结构的创建过程在前面的章节中已经介绍过,这里不再赘述。总之,以上述文件结构为例,则再给Dockerfile末尾添加如下内容,将源代码复制到容器内: dKQV4dc>  
67\Ojl~(1  
"HfU,$[  
ADD /my_application /my_application _xg VuJ   
X7d.Ie  
}lTZq|;A  
如果源代码是公网的git仓库,则可以使用如下内容: xyjV dD\  
)B$P#dP)i  
`N.$LY;8  
RUN git clone [你的源码仓库URL] rL sK-qQ  
<ii1nz  
:+Q"MIU  
引导 =&VXn{e  
接下来,再从 requirements.txt 导入所有的依赖项: q[OTaSQ~u^  
G+ X [R^RD  
%"[dGB$S  
# 用pip来下载安装 requirements.txt 里面的东西 %SO%{.}Z f  
RUN pip install -r /my_application/requirements.txt B4:l*P'  
Wejwj/EU%  
Y0B1xL@  
# 暴露端口 km\ld&d]$  
EXPOSE 80 .`& /QiD  
RjGB#AK  
7 Jx-W|  
# 设置CMD运行的默认路径 JD{MdhhV  
WORKDIR /my_application 0 {JK4]C  
)ozcr^  
I>%S4Z+o  
# 要运行的默认命令 LJ(n?/z%  
# 该命令在新容器创建时开始执行 yLz,V}  
# 比如启动CherryPy来运行服务 2>}\XKF).  
CMD python server.py 4iv]N 4  
`<}V !Lo  
Dq zA U7  
最终的Dockerfile 0)oN[  
现在,整个Dockerfile看起来应该是这样的: lke~>0;  
J,1osG<6x  
_MdZDhtm  
############################################################ 3IK(f .  
# Dockerfile to build Python WSGI Application Containers ?ng14e  
# Based on Ubuntu |#&V:GZp  
############################################################ M[K0t>ih  
]`@]<6  
Sc/`=h]T  
# Set the base image to Ubuntu Aa`MK$29F  
FROM ubuntu TsX+. i'  
>Qm<-g  
#A3v]'7B  
# File Author / Maintainer e$Bf[F#;-  
MAINTAINER Maintaner Name i$bzdc#s  
%K^gUd>,R  
`.>k)=F&  
# Add the application resources URL ~#x :z ^U  
RUN echo "deb http://archive.ubuntu.com/ubuntu/ $(lsb_release -sc) main universe" >> /etc/apt/sources.list |0L=8~M(j  
}. xrJ52Tz  
W]O@DS zR  
# Update the sources list _V& !4Zd9:  
RUN apt-get update $7UoL,N>  
&"[)s[m+t  
RVkU+7  
# Install basic applications 5 o#<`_=J  
RUN apt-get install -y tar git curl nano wget dialog net-tools build-essential `V&1]C8x  
|.(CIu~b  
PUP"ky^q"  
# Install Python and Basic Python Tools cq,v1Y<  
RUN apt-get install -y python python-dev python-distribute python-pip fVDDYo2\  
'%SR.JL  
1yBt/U2  
# Copy the application folder inside the container ;8ET!&k*>E  
ADD /my_application /my_application 2mqK3-c  
~+N76BX  
iuC7Y|  
# Get pip to download and install requirements: FxCZRo&  
RUN pip install -r /my_application/requirements.txt q^ a|wTC  
c~}={4M]  
'qL5$zG  
# Expose ports 7G5y)Qb  
EXPOSE 80 y?pD(u  
ZfPd0 p  
9u,8q:I.?  
# Set the default directory where CMD will execute O' +"d%2'  
WORKDIR /my_application u8YB)kG  
bMN@H\Ek  
e~{^oM  
# Set the default command to execute     ]~-*hOcQ4  
# when creating a new container FR:d^mL  
# i.e. using CherryPy to serve the application :qKF58W  
CMD python server.py J["H[T*  
DY9fF4[9a  
g(aZT#ii=  
按 CTRL+X 再按 Y 键,保存退出。 &E bI Op  
/+V Iw`E  
?Jt$a;  
使用Dockerfile进行自动化的容器创建 4% HGMr  
在之前的基础教程部分我们提到过,Dockerfile的工作方式利用到了 docker build 命令。 #s"851e  
zfhTc=(/  
d%0+i/p  
我们通过Dockerfile指示docker从包含源代码的路径复制内容到容器内部,因此在构建之前务必要确认Dockerfile与代码路径的相对位置。 *E:x E/M!2  
25:Z;J>  
kG/X"6pZ  
这样一个Docker镜像可以快速创建起来一个可以运行我们的Python应用的容器,我们需要做的只是输入这样一行指令: b'i'GJBQ+$  
s]U4B<q  
'\`6ot8  
sudo docker build -t my_application_img . C+w__gO&r  
mjb { ~  
9UwLF`XM  
我们把这个镜像命名为 my_application_img 。为要从这个镜像启动一个新的容器,只需要输入下面的命令: No) m/17y  
F|9+ +)  
OifvUTl9b  
sudo docker run -name my_application_instance -p 80:80 -i -t my_application_img jM~Bu.7 i6  
y&"!m }  
g\U/&.}DN  
然后就可以在浏览器里输入你VPS的IP地址,访问应用了。 c-kA^z{f  
$;2)s} ci  
!@G)$g=<  
有关Docker安装的更多教程(包括在其他发行版上安装Docker),可以查阅我们在docker.io上的文档docker installation documentation。 7J UbVa%  
P z ?m>>#  
[ 此帖被寒喵在2018-12-29 19:19重新编辑 ]
本人不是云栖社区工作人员。
无论您在使用中遇到什么问题,不要出言不逊!谢谢合作!
发表主题 回复主题
« 返回列表上一主题下一主题

限100 字节
批量上传需要先选择文件,再选择上传
 
验证问题: 16 + 56 = ?
上一个 下一个
      ×
      全新阿里云开发者社区, 去探索开发者的新世界吧!
      一站式的体验,更多的精彩!
      通过下面领域大门,一起探索新的技术世界吧~ (点击图标进入)