(转载误入)python上手实践 -- docker+nginx+gunicorn+flask项目部署

最近在研究这个,时间不够,没办法只能转载了,仅做自己学习,误入

flask属于轻量级python的web框架,其流行程度可以与django媲美。因为是轻量型,所以对于开发一些中小型项目就非常方便。不过flask自带的server速度较慢,测试环境还可以,真正实际使用起来还是很多问题。同时在部署时会移植到linux系统中,稳定性更好。

1.使用虚拟环境创建flask项目

在使用flask来开发项目时,为了保证项目移植的顺平性(如在windows中开发的项目移植到linux中),通常会采用env虚拟环境方式,将pip安装的一系列第三方库放在虚拟环境env目录下。移动整个项目工程也就会将虚拟环境迁移走。

(1)建立虚拟环境env,并激活使用

首先新建一个flask工程目录,并使用python -m venv env命令创建虚拟环境目录:

mkdir Flask_Proj
cd Flask_Proj
python -m venv env   #创建虚拟环境目录env

上述命令执行完成后,就会在Flask_Proj目录下新建一个env目录,并有如下内容:

[hadoop@big01 env]$ ll
total 4
drwxrwxr-x. 2 hadoop hadoop 202 May 17 21:03 bin
drwxrwxr-x. 2 hadoop hadoop   6 May 17 20:53 include
drwxrwxr-x. 3 hadoop hadoop  23 May 17 20:53 lib
lrwxrwxrwx. 1 hadoop hadoop   3 May 17 20:53 lib64 -> lib
-rw-rw-r--. 1 hadoop hadoop  69 May 17 20:53 pyvenv.cfg

然后使用source命令激活bin目录下的activate,就可以激活虚拟环境使用了:

source env/bin/activate

反过来如果想退出虚拟环境,使用deactivate即可。

(2)有了这个env虚拟环境后,在当前工程目录下pip install flask,开启安装flask库。如果默认pypi官方链接速度较慢,可以使用:

pip install -i https://mirrors.aliyun.com/pypi/simple flask

到底是国内镜像,速度不是一般的快。

安装完成后,可以去看一下这个库不是放在python默认安装目录里,而是放在刚创建的虚拟环境目录env里的lib文件夹下,路径为:env/lib/python3.7/site-packages。

flask安装成功后,可以在工程目录下新建一个main.py文件,在其中输入如下内容:

#main.py
from flask import Flask
app = Flask(__name__)

@app.route('/')
def index():
    return 'jianhua_helloworld2020'

if __name__ == '__main__':
    app.run(port=2021,host='0.0.0.0')  #host设置为0.0.0.0,可以允许外部远程访问

然后使用python直接运行这个文件,就可以开启一个测试的web服务:

[hadoop@big01 asmarket]$ python main.py
 * Serving Flask app "main" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://0.0.0.0:2021/ (Press CTRL+C to quit)

此时可以在外部浏览器上访问这个地址,端口号为2021:

2. gunicorn部署flask项目

上述在flask工程项目中创建env虚拟环境,是为了保证许多依赖的第三方库版本的一致。如上在启动了flask自带的server后,可以实现外部访问。但这种方式仅适用于测试,无法用于实际部署,因此一般推荐使用gunicorn来搭建flask服务器。

Gunicorn (独角兽)是一个高效的Python WSGI Server,通常用它来运行 wsgi application(由我们自己编写遵循WSGI application的编写规范) 或者 wsgi framework(如Django,Paster),地位相当于Java中的Tomcat。

(1)安装gunicorn

gunicorn是一个第三方库,可以直接使用pip来安装:

pip install -i https://mirrors.aliyun.com/pypi/simple gunicorn

(2)使用gunicorn命令

基本使用方式:

gunicorn --workers=3 main:app 

--workers=3表示三个进程,main:app,其中main为之前flask工程中的main.py,意味这将main.py对象实例化为app。

允许上述命令后,就会出现如下提示:

(env) [hadoop@big01 asmarket]$ gunicorn --workers=4 te:app
[2020-05-17 22:21:04 +0800] [9123] [INFO] Starting gunicorn 20.0.4
[2020-05-17 22:21:04 +0800] [9123] [INFO] Listening at: http://127.0.0.1:8000 (9123)
[2020-05-17 22:21:04 +0800] [9123] [INFO] Using worker: sync
[2020-05-17 22:21:04 +0800] [9126] [INFO] Booting worker with pid: 9126
[2020-05-17 22:21:04 +0800] [9127] [INFO] Booting worker with pid: 9127
[2020-05-17 22:21:05 +0800] [9128] [INFO] Booting worker with pid: 9128
[2020-05-17 22:21:05 +0800] [9129] [INFO] Booting worker with pid: 9129

可以看到上述监听地址为:127.0.0.0,端口为8000。工作模式为sync,即同步工程模式。这两种参数都可以进行修改,其中监听地址和端口号可以在上述命令后添加 -b ip:port方式实现:

gunicorn --workers=3 main:app -b 0.0.0.0:2021

在shell命令窗口:

(env) [hadoop@big01 asmarket]$ gunicorn --workers=4 te:app -b 0.0.0.0:2021
[2020-05-17 22:26:29 +0800] [9162] [INFO] Starting gunicorn 20.0.4
[2020-05-17 22:26:29 +0800] [9162] [INFO] Listening at: http://0.0.0.0:2021 (9162)
[2020-05-17 22:26:29 +0800] [9162] [INFO] Using worker: sync
[2020-05-17 22:26:29 +0800] [9165] [INFO] Booting worker with pid: 9165
[2020-05-17 22:26:29 +0800] [9166] [INFO] Booting worker with pid: 9166
[2020-05-17 22:26:29 +0800] [9167] [INFO] Booting worker with pid: 9167
[2020-05-17 22:26:29 +0800] [9168] [INFO] Booting worker with pid: 9168

此时同样可以在外部浏览器中访问,获得的效果与直接使用flask来搭建server服务一致。

对于工作模式,默认是sync,即同步模式。这种模式就是说在调用的时候,必须等待调用返回结果后,决定后续的行为。而异步则是在调用这个job的时候,不用等待其执行结果,还可以执行其他job。

举个例子:

打电话问酒店晚上有没有房间,如果是同步通信机制,酒店前台会礼貌的说:“请您稍等,我查一下",等她查到结果了就告诉你结果,这个过程中你的电话是一直通着的,在等她的结果,决定住她们家还是去别的酒店。如果是异步通信机制,酒店前台会礼貌的说:”我先查一下,一会给您回过去。“然后她把电话挂了。你马上就可以拿手机看看周边是否有便利的餐馆。等她查到了,她会主动给你打电话。而此时餐馆也查好了,可以开启美好的旅行了。

如果要更换为异步模式,可以使用gevent。此时还需要pip来安装gevent。

gunicorn --workers=3 main:app -b 0.0.0.0:2021 -k 'gevent'

(3)使用参数配置文件设定

使用上述脚本命令还是不方便的,gunicorn可以使用-c参数,就是使用配置文件。将一些参数设定放在该配置文件里:

import os
bind='0.0.0.0:5001'   #绑定监听ip和端口号
workers=3               #同时执行的进程数,推荐为当前CPU个数*2+1
backlog=2048            #等待服务客户的数量,最大为2048,即最大挂起的连接数
worker_class="gevent" #sync, gevent,meinheld   #工作模式选择,默认为sync,这里设定为gevent异步
max_requests=1000       #默认的最大客户端并发数量
daemon=True           # 是否后台运行
reload=True            # 当代码有修改时,自动重启workers。适用于开发环境。
pidfile='./gunicore.pid'    #设置pid文件的文件名
loglevel='debug'     # debug error warning error critical
accesslog='log/gunicorn.log'  #设置访问日志
errorlog='log/gunicorn.err.log' #设置问题记录日志

将上述内容存放在config.py文件中,然后在命令行输入:

gunicorn -c config.py main:app

如下为本次实践时配置的config.py参数:

import  os
from  gevent import monkey
monkey.patch_all()
import multiprocessing
debug = False
bind = "0.0.0.0:5001"
pidfile = "gunicorn.pid"
accesslog="/home/hadoop/asmarket/logs/gunicorn.log"
workers = multiprocessing.cpu_count()*2 + 1
worker_class = "gevent"
daemon=True

然后开启运行,此时gunicorn设置为后台看守进程,先直接从外部浏览器访问,然后查看log文件,内容如下:

[hadoop@big01 logs]$ more gunicorn.log 
192.168.58.1 - - [17/May/2020:23:30:44 +0800] "GET / HTTP/1.1" 200 23 "-" "Mozi
lla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
68.0.3440.106 Safari/537.36"
192.168.58.1 - - [17/May/2020:23:30:46 +0800] "GET / HTTP/1.1" 200 23 "-" "Mozi
lla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
68.0.3440.106 Safari/537.36"
192.168.58.1 - - [17/May/2020:23:30:47 +0800] "GET / HTTP/1.1" 200 23 "-" "Mozi
lla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
68.0.3440.106 Safari/537.36"

至此基本的gunicorn+flask异步服务部署就实现了。

3. gunicorn+nginx配置

有了gunicorn和gevent后,gunicorn可以实现多进程http服务,不过其性能还是相对nginx这种专业的web服务要差一些,主要体现在对高并发的处理、安全问题、静态资源文件的处理等。因此一般情况会在gunicorn之上再配置一层nginx服务。其基本架构示意如下(图来源于百度):

(1)docker部署nginx

由于nginx采用安装方式还相对比较麻烦,可以直接使用docker来部署。不过当然首先在root账户下安装docker服务:

#yum安装docker
yum install docker
#启动docker进程服务
systemctl start docker
systemctl enable docker  

有了docker后,使用docker的search和pull服务就可以将nginx拉取到本机上:

[root@big01 ~]# docker pull nginx
Using default tag: latest
Trying to pull repository docker.io/library/nginx ... 
latest: Pulling from docker.io/library/nginx
afb6ec6fdc1c: Pull complete 
b90c53a0b692: Pull complete 
11fa52a0fdc0: Pull complete 
Digest: sha256:30dfa439718a17baafefadf16c5e7c9d0a1cde97b4fd84f63b69e13513be7097
Status: Downloaded newer image for docker.io/nginx:latest

然后使用docker 查看镜像:

[root@big01 ~]# docker images
REPOSITORY              TAG                 IMAGE ID            CREATED             SIZE
docker.io/nginx         latest              9beeba249f3e        2 days ago          127 MB
docker.io/hello-world   latest              bf756fb1ae65        4 months ago        13.3 kB

接下来可以运行nginx容器:

[root@big01 ~]# docker run --name mynginx -p 8080:80 -d nginx
79b2f668784f866869f41ab08468784cf5f694fb451486250f37eaaa11808411

此时可以从外部浏览器访问获得默认的nginx响应页面:

下面对nginx访问页面做一个映射,因为如果要进入docker内部访问的话还是很不方便的,因此一般情况将docker镜像作为一个服务,而将实际的资源进行一个映射。在本地机器上放置资源,映射到容器内部,nginx访问内部文件路径时就映射到访问外部本地资源上了,这样便于资源分配以及web文件的管理。这里就是增加一个docker的-v参数,格式为本地资源:容器资源。

先进入docker内部,查看nginx的配置文件:

[root@big01 nginx]# docker exec -it 16528ae739c4 /bin/bash
root@16528ae739c4:/# cd /etc/nginx/conf.d/
root@16528ae739c4:/etc/nginx/conf.d# more default.conf 
server {
    listen       80;
    server_name  localhost;

    #charset koi8-r;
    #access_log  /var/log/nginx/host.access.log  main;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;

默认访问的路径为/usr/share/nginx/html下的html文件,这个可以后续修改。我们可以先测试一下,将这个路径映射到本地机器上。这样需要重新run一个镜像:

[root@big01 nginx]# docker run --name mynginxt -v /usr/share/nginx/:/usr/share/nginx/html -p 8021:80 -d nginx
16528ae739c40d28a68be67b10aac42343b97b5fa468565127eaf051ea25886c

上述命令中:-v /usr/share/nginx/:/usr/share/nginx/html,就是将本地的/usr/share/nginx目录映射到容器内部的/usr/share/nginx/html目录中,如果我们在本地的nginx目录下新建一个index.html网页,那访问的时候就是访问这个新建的index.html网页。如下新建一个简单网页并保存为index.html。

<html>
<head>
  <meta charset="utf-8">
</head>
<body>
<h1>我的第一个标题</h1>
<p>我的第一个段落。</p>
</body>
</html>

接下来就可以在外部浏览器访问,注意端口现在为8021。

(2)nginx+gunicorn部署

上述gunicorn部署时,ip为0.0.0.0,端口号为5001。在使用nginx代理这个服务时,修改nginx相应的配置文件即可。不过因为是docker部署,因此还需使用docker来操作。此时也可以将配置文件映射到外部宿主机上。

首先启动gunicorn+flask项目服务:

[hadoop@big01 asmarket]$ gunicorn -c config.py main:app

然后在/usr/share/nginx目录下新建一个nginx.conf文件,在其中输入如下内容:

server {
    listen 80;
    server_name asmarket.com; # 这是HOST机器的外部域名,用地址也行

    location / {
        proxy_pass http://0.0.0.0:5001; # 这里是指向 gunicorn host 的服务地址
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
  }

将其映射到nginx容器里的default.conf配置文件:

[root@big01 nginx]# docker run --name mynginx -v /usr/share/nginx/nginx.conf:/etc/nginx/conf.d/default.conf -d nginx
39026ba7d80eac3d59d00ead25d7275e5f2b125dde3a9e3379dffb5c10ef9662

这样在启动nginx时直接使用的就是刚才新建立的nginx.conf配置文件。

此时nginx已经启动了:

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                   PORTS                  NAMES
39026ba7d80e        nginx               "nginx -g 'daemon ..."   7 minutes ago       Up 7 minutes             80/tcp                 mynginx

然后就可以运行flask项目了。

如何验证确实是代理了gunicornweb服务,可以直接使用nginx原有的默认80端口访问,如果出现错误,说明nginx已经代理了web服务,否则就是没成功。

(3)supervisor进程守护

nginx一般不会莫名其妙被关闭,但gunicorn是一个进程,完成有有可能因为一些原因被关闭或者阻塞,为了保证gunicorn进程,需要使用看护进程插件。这里使用supervisor来解决这个问题。

supervisor专门用户linux端进程管理,首先使用pip安装一下这个插件:

[root@big01 ~]# pip install supervisor

安装成功后,可以创建一个配置文件:

# 设置默认配置
$ echo_supervisord_conf > /etc/supervisord.conf
$ vi /etc/supervisord.conf

这个配置文件放在/etc/目录下,名为supervisor.conf。接下来就可以修改其配置了:

[program:myapp]
command=/usr/local/bin/gunicorn -c config.py main:app               
directory=/home/hadoop/asmarket
autostart=true                ; start at supervisord start (default: true)
startsecs=1                   ; # of secs prog must stay up to be running (def. 1)
startretries=3                ; max # of serial start failures when starting (default 3)
exitcodes=0                   ; 'expected' exit codes used with autorestart (default 0)
stdout_logfile=/home/hadoop/asmarket/logs/main.logs
stdout_logfile_maxbytes=50MB   ; max # logfile bytes b4 rotation (default 50MB)
user=root

修改完成后,直接使用supervisord来执行:

supervisord -c supervisor.conf

这样myapp的进程就启动了。可以使用supervisorctl status命令来查看当前进程状态:

supervisorctl status

猜你喜欢

转载自blog.csdn.net/lijinshanba/article/details/106542545