使用Flask+nginx+uwsgi+Docker部署Python应用
1.Flask、nginx和uwsgi的概念及关系nginx就是一个web服务器Flask就是一个web框架,常用的其他web框架还有Djangouwsgi与WSGI一样,是一种通信协议。首先要说明,uWSGI是一个web服务器,它实现了WSGI、uwsgi、http等协议,其作用就是把HTTP协议转化成语言支持的网络协议,用于处理客户端请求,并向客户端返回响应。这里的uwsgi则是...
1. Flask、nginx和uwsgi的概念及关系
- nginx是一个web服务器
- Flask也是一个web框架,常用的其他web框架还有Django
- uwsgi与WSGI一样,是一种通信协议。首先要说明,uWSGI是一个web服务器,它实现了WSGI、uwsgi、http等协议,其作用就是把HTTP协议转化成语言支持的网络协议,用于处理客户端请求,并向客户端返回响应。这里的uwsgi则是uWSGI服务器的独占协议(只适用于uWSGI服务器?),与WSGI是两种不同的协议
通过上面的描述中可以得出,uWSGI+Flask就可以实现一个完整的web服务,似乎不需要Nginx服务器。
当一个服务访问量过大时,我们可能会考虑多部署几台web服务器,这几台web服务器都可以处理客户端请求,但问题是如何将客户端请求分发到各个web服务器上?这就是Nginx的作用->反向代理服务器。它们的关系如下图所示:
废话少说,我们接下来直接实战。
2. 创建一个Flask轻量级的Web应用
项目的目录结构如下:
- cert 证书文件夹:包含两个文件,注册好域名后就可以下载到这两文件,本人是在在阿里上注册的。
- .env : 设置环境变量的文件
- Dockerfile: 用于build image的
- main.py: Flask Web应用程序的入口
- nginx.conf: nginx配置文件
- uwsgi.ini: uwsgi 配置文件
- requirements.txt: python依赖包
Flask 应用的比较轻量简单,主要看main.py,具体解释直接看代码就好。
import os
import sys
# sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
import json
from flask import request, Flask, jsonify
from dotenv import load_dotenv
# 创建一个Flask应用
app = Flask(__name__)
app.config['JSON_AS_ASCII'] = False
@app.route('/')
def show_hello():
myname = os.getenv('myname')
return myname
if __name__ == '__main__':
# 从.env文件中加载环境变量
load_dotenv()
# 本地运行的话,可以直接访问localhost:8086
app.run(port=8086)
3. 配置uwsgi
这是uwsgi.ini里面的内容,可以参考里面的注释, 注意这里采用的是socket通信,端口是8086,如果只想用uwsgi作web服务器就使用http-socket,如果和nginx一起使用,就用socket, 和上面的命令是二选一的关系。更过关于nWSGI相关的内容可参考这里
[uwsgi]
chdir = /app
wsgi-file = main.py
venv = venv
socket = :8086
callable = app
chmod-socket = 666
plugins = python3
buffer-size = 65535
# 使用uwsgi前一定要安装此插件 sudo apt-get install uwsgi-plugin-python3
# [uwsgi]
# chdir = /home/cong/Desktop/docker-flask-nginx-demo/my_flask
# wsgi-file = run.py # 指定要加载的WSGI文件,也即包含Flask实例(app)的文件名称
# callable = app # 指出uWSGI加载的模块中哪个变量将被调用, 也即Flask实例名称
# socket = :8086 # 指定socket文件,也可以指定为127.0.0.1:9000,用于配置监听特定端口的套接字
# http-socket = :8086 # 如果只想用uwsgi作web服务器就使用http-socket,如果和nginx一起使用,就用socket, 和上面的命令是二选一的关系。
# venv = venv
# chmod-socket = 666
# processes = 4 # 指定开启的工作进程数量(这里是开启4个进程)
# threads = 2 # 设置每个工作进程的线程数
# master = true # 启动主进程,来管理其他进程,其它的uwsgi进程都是这个master进程的子进程
# vacuum = true # 当服务器退出的时候自动删除unix socket文件和pid文件
# die-on-term = true # ?
# buffer-size = 65535 # 设置用于uwsgi包解析的内部缓存区大小为128k。默认是4k
# limit-post = 104857600 # 限制http请求体大小
4. 配置nginx
这是nginx.conf里面的内容,8086是uwsgi里定义的端口,我们主要使用这个, 80 端会自己重定向到443,以便用户直接使用https来访问我们的应用,8085和81是用作测试的,可以忽略。
user www-data;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
#include /etc/nginx/conf.d/*.conf;
server {
listen 80;
server_name xxxxxter.cn;
return 301 https://$server_name$request_uri; # 重定向
}
server {
listen 81;
charset utf-8;
client_max_body_size 75M;
location ^~/exam {
proxy_pass http://127.0.0.1:8085; #使用代码
}
}
server {
listen 82;
charset utf-8;
client_max_body_size 75M;
location ^~/exam {
include /etc/nginx/uwsgi_params;
uwsgi_pass 127.0.0.1:8086;
uwsgi_send_timeout 300;
uwsgi_connect_timeout 300;
uwsgi_read_timeout 300;
}
}
server {
listen 443 ssl;
server_name xxxxxter.cn;
ssl_certificate /etc/nginx/cert/3421147_bingter.cn.pem;
ssl_certificate_key /etc/nginx/cert/3421147_bingter.cn.key;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_session_timeout 5m;
charset utf-8;
client_max_body_size 75M;
location ^~/exam {
include /etc/nginx/uwsgi_params;
uwsgi_pass 127.0.0.1:8086;
uwsgi_send_timeout 300;
uwsgi_connect_timeout 300;
uwsgi_read_timeout 300;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_redirect off;
}
location ^~/exam/static {
proxy_pass http://127.0.0.1:8085/static;
}
# location ^~/api/{
# proxy_pass http://demo;
# proxy_http_version 1.1;
# proxy_set_header Host $host;
# proxy_set_header Upgrade $http_upgrade;
# proxy_set_header Connection $connection_upgrade;
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_redirect off;
# proxy_connect_timeout 1;
# proxy_buffer_size 64k;
# proxy_buffers 4 64k;
# proxy_busy_buffers_size 128k;
# }
# location / {
# root /root/react-demo/;
# index index.html;
# }
}
server {
listen 8085;
charset utf-8;
client_max_body_size 75M;
location / {
include /etc/nginx/uwsgi_params;
uwsgi_pass 127.0.0.1:8086;
uwsgi_send_timeout 300;
uwsgi_connect_timeout 300;
uwsgi_read_timeout 300;
}
}
}
5. Dockerfile
使用它来用于生成自定义的镜像。
# 使用最新的ubuntu基础镜像
FROM ubuntu
RUN mkdir /app
COPY . /app
WORKDIR /app
RUN apt-get update -yqq
# uwsgi-plugin-python3 这是在uwsgi中运行python程序的一个插件
RUN apt-get install python3 python3-pip python3-venv nginx-core nginx uwsgi uwsgi-plugin-python3 -y
# 将nginx的配置文件拷贝到/etc/nginx下
COPY nginx.conf /etc/nginx/nginx.conf
# 添加证书文件
COPY cert /etc/nginx/cert
# nginx运行是需要有用户名的,默认安装好nginx后就有用户名www-data,如果想用其他用户可以使用如下代码增加组和用户
# RUN groupadd www-data && useradd -G www-data www-data
# Python一般是在虚拟环境下运行了,下面三步是用于创建虚拟环境:
# 第一步:定义虚拟环境的名称
ENV VIRTUAL_ENV=venv
# 第二步:生成虚拟环境
RUN python3 -m venv venv
# 第三步:最关键,它会激活虚拟环境,这是英文说明:The most important part is setting PATH: PATH is a list of directories which are searched for commands to run. activate simply adds the virtualenv’s bin/ directory to the start of the list.
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
# 因为docker image是分层的,每一行的Run命令就是一层,同时也是不同的进程,所以像如下设置虚拟环境,是没用的。
# RUN source venv/bin/activate
# ubuntu 默认使用得是/bin/sh, 而不是/bin/bash, bash用得是source, sh用得是点
# RUN . ./venv/bin/activate
# 对pip进行升级
RUN python3 -m pip install --upgrade pip
# 安装运行python所需要的包
RUN pip3 install -r requirements.txt
# 启动nginx和uwsgi,这里一定要加上-g,主要目的就是设置pid=1。因为docker 容器默认会把容器内部第一个进程,也就是pid=1的程序作为docker容器是否正在运行的依据,如果pid=1的程序挂了,那容器就挂了。
ENTRYPOINT nginx -g "daemon on;" && uwsgi --ini uwsgi.ini
6. Build镜像与运行容器
# 打包镜像
cd /home/cong/ xxxxx_api
docker build -t wucong60/xxxxx_api .
docker login -u wucong60
docker push wucong60/xxxxx_api
# 在容器中运行
docker pull wucong60/xxxxx_api
docker run -itd -p 80:80 -p 443:443 --name=xxxxx_api wucong60/xxxxx_api
7. 运行效果
可以看到容器正常运行,进入容器内部,nginx作为pid=1 也正常的运行。
浏览器访问API,返回成功,注意这里是https的哦
加油!
参考链接
https://pythonspeed.com/articles/activate-virtualenv-dockerfile/
更多推荐
所有评论(0)