当前位置: 首页 > 科技观察

说说Python应用容器化部署流程

时间:2023-03-19 18:36:44 科技观察

1。简介Docker是一种被主流IT公司广泛接受和使用的工具,用于构建、管理和保护他们的应用程序。Docker等容器允许开发人员在单个操作系统上隔离和运行多个应用程序,而不是为服务器上的每个应用程序专用虚拟机。容器更轻量级,从而降低成本、更好地利用资源和提高性能。本文将使用Flask开发一个简单的PythonWeb应用程序,并为“容器化”做准备。然后创建一个Docker镜像并将其部署到测试和生产环境。注意:请确保机器上安装了Docker,如果没有,请参考Docker官方安装教程。2.Docker简介Docker是一种工具,使开发人员能够交付他们的应用程序(和库或其他依赖项),确保他们能够以正确的配置运行,而不管部署环境如何。这是通过将应用程序隔离在单独的容器中来实现的,这些容器虽然被容器分隔开,但可以共享操作系统和其他资源。Docker由两部分组成:DockerEngine—用于打包应用程序的应用程序打包工具。DockerHub—用于在云上管理容器化应用程序的工具。3、为什么选择容器了解容器的重要性和实用性是非常重要的。虽然和直接将应用部署到服务器上没有太大区别,但是当涉及到更复杂和资源密集型的应用时,尤其是当多个应用部署在同一台服务器上,或者同一个应用部署到多台服务器上时。容器可能非常有用。在容器出现之前,这可以通过VMWare和Hypervisor等虚拟机解决,但事实证明,它们在效率、速度和可移植性方面都不是最佳选择。Docker容器是虚拟机的轻量级替代品——与VM不同,我们不需要为它预先分配RAM、CPU或其他资源,也不需要为每个应用程序启动一个VM,只需一个操作系统。能。使用容器,开发者不需要为不同的环境制作特殊的版本,因此他们可以专注于应用程序的核心业务逻辑。4.创建Python应用程序Flask是Python的轻量级Web应用程序框架。它易于使用,可以非常快速地创建Web应用程序。我们用它来创建这个演示应用程序。如果你还没有安装Flask模块,可以使用如下命令安装:$pipinstallflask安装成功后,新建一个应用目录,命名为FlaskDemo。并在该目录下创建应用代码文件app.py。在app.py中,首先引入Flask模块,然后创建一个web应用:fromflaskimportFlaskapp=Flask(__name__)然后定义route/及其对应的requesthandler:@app.route("/")defindex():return"""

PythonFlaskinDocker!

Asampleweb-appforrunningFlaskinsideDocker.

"""最后添加并运行主程序,启动脚本:if__name__=="__main__":app.run(debug=True,host='0.0.0.0')$pythonapp.py然后在浏览器中访问http://localhost:5000/,可以看到一个类似DockerzingPythonappusingFlask的页面。5.Docker打包应用程序要在Docker上运行应用程序,必须首先构建一个容器,并且必须包含所有使用的依赖项——在我们的例子中只有Flask。因此,创建一个包含所有依赖项的新requirements.txt文件,然后创建一个描述构建映像过程的Dockerfile。另外,启动容器时需要释放应用的HTTP端口。准备工作requirements.txt文件很简单,只需要填写项目的依赖包及其对应的版本:Flask==1.0.2接下来需要将应用运行所需的所有Python文件放入顶级文件夹中,例如,名为app的目录。同时,建议将主入口程序命名为app.py,将脚本中创建的Flask对象命名为app是比较常见的做法,这样也可以简化部署。FlaskApp├──requirements.txt├──Dockerfile└──app└──app.py└──创建DockerfileDockerfile本质上是一个文本文件,明确定义了如何为我们的项目构建Docker镜像.接下来,创建一个基于Ubuntu16.04和Python3.X的Docker镜像:FROMubuntu:16.04MAINTAINERjhao104"j_hao104@163.com"RUNapt-getupdate-y&&\apt-getinstall-ypython3-pippython3-devCOPY./requirements.txt/requirements。txtWORKDIR/RUNpip3install-rrequirements.txtCOPY./ENTRYPOINT["python3"]CMD["app/app.py"]Dockerfile的基本指令有13条,上面用到了其中的一些;FROM-所有Dockerfile的第一条指令必须是ItisFROM,用于指定构建镜像的基本源镜像。如果它在本地不可用,它将从公共图书馆中提取。如果没有指定图像标签,将使用默认的最新标签。如果需要在一个Dockerfile中构建多个镜像,可以多次使用。MAINTAINER-描述图像的创建者、姓名和电子邮件。RUN-RUN命令是一个常用的命令,执行后会成为一个新的镜像,通常用于运行安装任务,为镜像添加额外的内容。在这里,我们需要更新包,安装python3和pip。在第二个RUN命令中使用pip安装requirements.txt文件中的所有包。COPY-复制本地文件或目录并将其添加到指定的容器目录,在本例中将requirements.txt复制到镜像中。WORKDIR-为RUN、CMD、ENTRYPOINT命令配置工作目录。可以使用多个WORKDIR指令,如果后面的参数是相对路径,则以前面命令指定的路径为准。ENTRYPOINT-在启动容器时提供默认命令入口。RUN-在应用程序目录中运行app.py。Docker镜像构建原理Docker镜像是使用Dockerbuild命令构建的。构建镜像时,Docker会创建所谓的“层”。每一层记录Dockerfile中命令引起的变化,以及运行命令后镜像的状态。Docker在内部缓存这些层,以便在重建映像时只需要重新创建更改的层。例如,这里使用ubuntu:16.04的基础镜像,同一容器的所有后续构建都可以重用它,因为它不会改变。但是因为工程修改,下次rebuild的时候app目录的内容可能会不一样,所以只重建这一层。请注意,每当重建一个层时,Dockerfile中的所有后续层也需要重建。例如,我们首先复制requirements.txt文件,然后再复制应用程序的其余部分。这样,只要之前安装的依赖没有新的依赖,即使应用中的其他文件发生变化,也不需要重新构建这一层。创建Dockerfile时必须注意这一点。因此,可以通过将pip安装与应用程序其余部分的部署分离来优化容器构建过程。构建Docker镜像现在Dockerfile已准备就绪并且我们了解了Docker构建过程,让我们为我们的应用程序创建一个Docker镜像:dockerbuild-tdocker-flask:0.1。以调试模式运行根据上述容器化的优势,开发的应用程序通过容器进行部署,保证应用程序构建的环境从一开始就是干净的,从而消除交付过程中的意外。然而,在开发应用程序的过程中,更重要的是快速重建和测试以检查验证过程中的每个中间步骤。为此,Web应用的开发者需要依赖Flask等框架提供的自动重启功能(在Debug模式下,代码修改后自动重启)。而且这个特性也可以用在容器中。要启用自动重启,在启动Docker容器时将宿主机中的development目录映射到容器中的app目录。通过这种方式,Flask可以监听主机中的文件更改(通过映射)以发现代码更改并在检测到更改时自动重启应用程序。此外,还需要完成应用程序从容器到主机的端口转发。这是为了使主机上的浏览器能够访问该应用程序。因此,在启动Docker容器时需要使用volume-mapping和port-forwarding选项:dockerrun--nameflask_app-v$PWD/app:/app-p5000:5000docker-flask:0.1该命令将执行以下操作:基于之前构建的docker-flask镜像启动一个容器;此容器的名称设置为flask_app。如果没有--name选项,Docker将为容器生成一个名称。显式指定名称可以帮助我们定位容器(用于停止等);-v选项将主机的app目录挂载到容器;-p选项将容器的端口映射到主机。该应用程序现在可以通过http://localhost:5000或http://0.0.0.0:5000/访问:如果我们在容器运行时修改应用程序代码,Flask将检测到更改并重新启动应用程序。要停止容器,请使用Ctrl+C,然后运行??dockerrmflask_app以删除容器。在生产模式下运行虽然直接使用Flask裸运行应用程序足以用于开发,但我们需要在生产中使用更健壮的部署方法。目前主流的部署方案是nginx+uwsgi。下面我们将介绍如何为生产环境部署Web应用。Nginx是一个开源Web服务器,而uWSGI是一个快速、自我修复、开发人员和系统管理员友好的服务器。首先,我们创建一个入口脚本来控制是否以开发模式或生产模式启动我们的应用程序。两者的区别在于选择直接运行python还是nginx模式。然后写一个简单的shell启动脚本entry-point.sh:#!/bin/bashif[!-f/debug0];thentouch/debug0whilegetopts'hd:'flag;docase"${flag}"inh)echo"options:"echo"-hshowbriefhelp"echo"-ddebugmode,nonginxoruwsgi,directstartwith'python3app/app.py'"exit0;;d)touch/debug1;;*)break;;esacdonefiif[-e/debug1];thenecho"Runningappindebugmode!"python3app/app.pyelseecho"Runningappinproductionmode!"nginx&&uwsgi--ini/app.inifi然后创建uWSGI配置文件app.ini:[uwsgi]plugins=/usr/lib/uwsgi/plugins/python3chdir=/appmodule=app:appuid=nginxgid=nginxsocket=/run/uwsgiApp.sockpidfile=/run/.pidprocesses=4threads=2和nginx配置文件nginx.conf:usernginx;worker_processes4;pid/run/nginx.pid;events{worker_connections20000;}http{includemime.types;sendfileon;keepalive_timeout65;gzipoff;server{listen80;access_logoff;error_logoff;location/{try_files$uri@flaskApp;}location@flaskApp{includeuwsgi_params;uwsgi_passunix:/run/uwsgiApp.sock;}}}最后修改Dockerfile为ngi安装nx和uWSGI到镜像,复制配置文件到镜像,设置运行nginx所需的用户权限:FROMubuntu:16.04MAINTAINERjhao104"j_hao104@163.com"RUNapt-getupdate-y&&\apt-getinstall-ypython3-pippython3-dev&&\apt-getinstall-ynginxuwsgiuwsgi-plugin-python3COPY./requirements.txt/requirements.txtCOPY./nginx.conf/etc/nginx/nginx.confWORKDIR/RUNpip3install-requirements.txtCOPY./RUNadduser--disabled-password--gecos''nginx\&&chown-Rnginx:nginx/app\&&chmod777/run/-R\&&chmod777/root/-RENTRYPOINT["/bin/bash","/entry-point.sh"]然后重新打包镜像:dockerbuild-tdocker-烧瓶:0.1。然后使用nginx启动应用:dockerrun-d--nameflaskapp--restart=always-p8091:80docker-flask:0.1这个镜像包含了python、ngix、uwsgi的完整环境,部署的时候只需要指定端口映射即可然后你可以自动部署应用程序要停止和删除这个容器,请运行以下命令:dockerstopflaskapp&&dockerrmflaskapp此外,如果我们仍然需要上述调试功能或修改一些代码,我们也可以像上面那样以调试模式运行容器:dockerrun-it--nameflaskapp-p5000:5000-v$PWD/app:/appdocker-flask:0.1-ddebug6.管理外部依赖性将应用程序作为容器交付时要记住的一件关键事情是开发人员管理依赖性的责任增加.除了识别和指定正确的依赖项和版本之外,您还负责在容器环境中安装和设置这些依赖项。在Python项目中管理安装依赖项更容易。可以使用requirements.txt指定依赖和对应的版本,然后通过pip安装。重申一下,只要修改了requirements.txt文件,就需要重新构建Docker镜像。在启动时安装依赖项可能需要在版本更新期间安装额外的依赖项。例如,在开发过程中使用了一个新包。如果你不想每次都重建你的Docker镜像,或者你想从最新的可用版本开始。这可以通过修改启动器以在应用程序启动时运行安装程序来实现。同样,我们也可以安装额外的系统级包依赖。修改entry-point.sh:#!/bin/bashif[!-fdebug0];thentouchdebug0if[-erequirements_os.txt];thenapt-getinstall-y$(catrequirements_os.txt)fiif[-erequirements.txt];thenpip3install-rrequirements。txtfiwhilegetopts'hd:'flag;docase"${flag}"inh)echo"options:"echo"-hshowbriefhelp"echo"-ddebugmode,nonginxoruwsgi,directstartwith'python3app/app.py'"exit0;;d)touchdebug1;;*)break;;esacdonefiif[-edebug1];thenecho"Runningappindebugmode!"python3app/app.pyelseecho"Runningappinproductionmode!"nginx&&uwsgi--ini/app.inifi这样我们就可以在requirements_os中指定要安装的系统包名称。txt,这些包名在同一行上用空格分隔。它们将在应用程序启动之前安装,就像requirements.txt中的Python依赖项一样。虽然这在应用程序的迭代开发过程中提供了便利,但在启动时安装依赖项并不是一个好的做法,原因如下:它违背了容器化的目标之一,即修复和测试不会更改的依赖项;给应用启动增加额外的开销,会增加容器的启动时间;每次启动应用都需要安装依赖,需要网络资源。