组件介绍
Nginx: 高性能
Web
服务器,负责反向代理;gunicorn: (Green Unicorn,绿色独角兽)高性能 uWSGI 服务器;
gevent: 将
Python
同步代码转换为异步的协议库;supervisor: 监控服务流程的工具;
版本信息
gunicorn --version |
输出gunicorn (version 19.9.0)
安装 gunicorn 和 gevent
pip install gunicorn |
pip 安装的 gunicorn 无法找到路径,可以使用sudo apt install gunicorn3
(Ubuntu)命令安装,或者手动编写/root/venv/bin/gunicorn
(其中 venv 为你的 python 虚拟环境名称)#!/root/venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from gunicorn.app.wsgiapp import run
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(run())
之后执行chmod +x /root/venv/bin/gunicorn
,然后运行gunicorn --version
可以查看其版本信息
配置 gunicorn
# gun.py |
工作模式是通过 work_class 参数配置的值:sync
gevent
eventlet
tornado
gaiohttp
gthread
其中默认值为sync
Sync Workers (sync)
最简单的同步工作模式
Async Workers (gevent, eventlet)
gevent 和 eventlet 都是基于 Greenlet 库,利用 python 协程实现的
Tornado Workers (tornado)
利用 python Tornado 框架实现
AsyncIO Workers (gthread, gaiohttp)
gaiohttp 利用 aiohttp 库实现异步 I/O,支持 web socket
gthread
采用的是线程工作模式,利用线程池管理连接
启动服务
gunicorn -k gevent -c gun.py runserver:app |
此时正常访问http://10.10.15.111:5000/
应该可以看到首页信息提示表示连接服务正常。
注意:此处 ip 和端口均由自己设置,所以访问时需要做出相应调整。
如果无法正常访问,则需要验证防火墙是否正常:
- CentOS 7 以下版本
# 查询
iptables -nL|grep 5000
# 开放指定端口
iptables -I INPUT -p tcp --dport 5000 -j ACCEPT
# 再次查询
iptables -nL|grep 5000
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:5000 - CentOS 7+
使用firewall-cmd
管理防火墙端口firewall-cmd --query-port=5000/tcp # no
firewall-cmd --add-port=5000/tcp --permanent
firewall-cmd --reload
firewall-cmd --query-port=5000/tcp # yes
systemctl status firewall
systemctl status firewalld
systemctl restart firewalld安装 nginx 和 supervisor
yum -y install nginx supervisor |
如果提示找不到,可以使用yum install epel-release
更新源之后再尝试。
配置 nginx
默认安装的 Nginx 配置文件/etc/nginx/nginx.conf
内有如下配置:
include /etc/nginx/conf.d/*.conf; |
即从外部目录/etc/nginx/conf.d/
文件夹下还引入了其他配置文件。
这样,我们不修改默认配置,只在/etc/nginx/conf.d/
目录下增加一个***.conf
,来在外面增加新配置。
在/etc/nginx/conf.d/
增加app.conf
,内容如下:
server { |
启动服务
systemctl start nginx |
- 报错
nginx: [emerg] getpwnam("nginx") failed in /etc/nginx/nginx.conf:5
查看配置该行内容为user nginx;
,添加用户useradd nginx
;如果是手动编译安装 nginx,可能无法使用
systemctl
直接管理进程,可以手动创建配置文件/usr/lib/systemd/system/nginx.service
写入:[Unit]
# 描述服务
Description=The nginx HTTP and reverse proxy server
# 描述服务类别
After=network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target
[Service]
# 指定为后台运行的形式
Type=forking
# 进程号写入文件
PIDFile=/run/nginx.pid
# Nginx will fail to start if /run/nginx.pid already exists but has the wrong
# SELinux context. This might happen when running `nginx -t` from the cmdline.
# https://bugzilla.redhat.com/show_bug.cgi?id=1268621
ExecStartPre=/usr/bin/rm -f /run/nginx.pid
# 实际路径根据编译安装的路径可能有所不同
ExecStartPre=/usr/local/nginx/sbin/nginx -t
ExecStart=/usr/local/nginx/sbin/nginx
ExecReload=/usr/local/nginx/sbin/nginx -s reload
ExecStop=/usr/local/nginx/sbin/nginx -s quit
KillSignal=SIGQUIT
TimeoutStopSec=5
KillMode=process
# 表示给服务分配独立的临时空间
PrivateTmp=true
[Install]
WantedBy=multi-user.target
参阅:NGINX systemd service file | NGINX
报错
ERR_CONTENT_LENGTH_MISMATCH
查看 nginx 错误日志tailf /var/log/nginx/error.log
2019/08/09 03:04:21 [crit] 24616#0: *204 open() "/var/lib/nginx/tmp/proxy/2/03/0000000032" failed (13: Permission denied) while reading upstream, client: 10.10.15.199, server: localhost, request: "GET /static/js/app.1b53c809113e333c2727.js.map HTTP/1.1", upstream: "http://0.0.0.0:5000/static/js/app.1b53c809113e333c2727.js.map", host: "10.10.15.111:82"
进入首页出现
403 Forbidden
- nginx 启动用户和配置中的工作用户不一致(注意:如果你的
nginx
服务是root
用户运行,则配置中user
项配置为root
); - 配置文件中缺少 index index.html index.htm index.php 行;
- nginx 用户没有相应工作目录的操作权限(
chown -R nginx:nginx WORK_DIR_PATH
); - 防火墙设置。
参考这里:解决 Nginx 出现 403 forbidden (13: Permission denied)报错的四种方法 - selinux 处于开启状态
- 临时
setenforce 0
- 永久禁用
vi /etc/sysconfig/selinux
配置
SELINUX=disable
- 临时
- nginx 启动用户和配置中的工作用户不一致(注意:如果你的
- Address already in use所以在配置文件
Sep 07 10:26:07 iZ9bq05sg6feeodta154guZ nginx[17582]: nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
````
提示端口占用,可以使用`lsof -i:80`或者`netstat -tulpn`查看被占用端口的进程,比较诡异的是我当时看到的就是 nginx 的服务,但是`systemctl status nginx`看到的状态就是 faild,后面注意到报错信息:
```plain
Sep 07 10:14:48 iZ9bq05sg6feeodta154guZ systemd[1]: PID file /run/nginx.pid not readable (yet?) after start. # 此行
Sep 07 10:16:18 iZ9bq05sg6feeodta154guZ systemd[1]: nginx.service start operation timed out. Terminating.
Sep 07 10:16:18 iZ9bq05sg6feeodta154guZ systemd[1]: Failed to start The nginx HTTP and reverse proxy server.
Sep 07 10:16:18 iZ9bq05sg6feeodta154guZ systemd[1]: Unit nginx.service entered failed state.
Sep 07 10:16:18 iZ9bq05sg6feeodta154guZ systemd[1]: nginx.service failed./usr/local/nginx/conf/nginx.conf
中找到 pid 文件指定行并修改为pid /run/nginx.pid;
,之后kill -9 xxx
杀死进程重新启动之后即可。理论上讲你也可以放开原配置注释,然后修改nginx.service
相应配置。参阅:NGINX systemd: PID file /run/nginx.pid not readable (yet?) - Computer How To 。
配置 Supervisor
echo_supervisord_conf
用来生成默认的配置文件,一般生成默认文件为 supervisor.conf
首先检查是否存在配置文件,一般配置文件的路径是/etc/supervisord.conf
,如果配置文件不存在,我们可以通过命令来生成:echo_supervisord_conf > /etc/supervisord.conf
配置文件涉及的内容很多,项目配置可以参照官网文档
打开配置文件,可以看到最后一行:;[include]
;files = /etc/supervisord/*.conf
默认一般是注释掉的,我们直接取消注释即可:[include]
files = /etc/supervisord/*.conf
这行配置的作用也很浅显,就是导入设置的路径下的所有conf
文件,这使得我们如果有多个项目可以不用都写在同一个配置文件里,可以一个项目一个配置文件,更适合管理。这里的路径也是可以按照实际需求随意更改。
注意
supervisor 配置兼容ini
和conf
格式,例如本人安装时版本为ini
格式,所以我们自写的配置也调整为相应的.ini
格式即可。[include]
files = supervisord.d/*.ini
Supervisord
可以理解成 supervisor 的服务端,运行 supervisor 时会启动一个进程 supervisord,它负责启动所管理的进程,并将所管理的进程作为自己的子进程来启动,而且可以在所管理的进程出现崩溃时自动重启
- 手动启动 Supervisord在设置的路径下新建一个配置文件,命令请根据上一步设置的扩展名。
# 配置文件路径根据自己实际的路径决定
supervisord -c /etc/supervisord.conf[program:project_name]
;具体路径根据自己安装或者虚拟环境中的位置进行配置
command=/usr/bin/gunicorn -c gun.py runserver:app
;项目绝对路径
directory=/project_path/
startsecs=0
stopwaitsecs=0
autostart=true
autorestart=trueproject_name
按照你的实际需求修改,作为你这个服务的唯一标识,用于启动停止服务时使用。command
修改为测试gunicorn
时使用的命令,建议使用绝对路径。directory
指定了工作路径,通常设置为项目根目录,我们填写的 gun.py 和 app 都是基于这个路径的。
supervisorctl
可以理解成 supervisor 的客户端,管理Supervisor
的项目是使用supervisorctl
命令,我们可以启动项目试试看:
supervisorctl start {{PROJECT_NAME}} |
如果没有报错,应该可以和上一步测试gunicorn
一样可以正常访问项目了。
使用supervisorctl
工具验证应用状态:supervisorctl
app RUNNING pid 24639, uptime 0:20:55
设置开机自启
- 加入 systemctl 管理服务
创建/etc/systemd/system/supervisord.service
文件,写入:[Unit]
Description=Supervisor daemon
Documentation=http://supervisord.org
After=network.target
[Service]
根据实际配置和位置编写,使用which 命令查找路径
ExecStart=/usr/local/bin/supervisord -n -c /etc/supervisor/supervisord.conf
ExecStop=/usr/local/bin/supervisorctl $OPTIONS shutdown
ExecReload=/usr/local/bin/supervisorctl $OPTIONS reload
KillMode=process
Restart=on-failure
RestartSec=42s[Unit]
Description=Supervisor daemon
Documentation=http://supervisord.org
After=network.target
[Install]
WantedBy=multi-user.target
执行命令使能:systemctl enable supervisord.service
报错记录
注意
ini 格式文件不支持行内注释!请确保你的注释为独立注释,且以;
开头,而不是#
(Python 中的注释符)
- Error: Another program is already listening on a port that one of our HTTP servers is configured to use. Shut this program down first before starting supervisord. For help, use /usr/bin/supervisord -h 找到路径
# 可以查看supervisor.sock并删除
find / -name supervisor.sock删除之:/run/supervisor/supervisor.sock
再次重新启动unlink /run/supervisor/supervisor.sock
supervisord -c /etc/supervisord.conf
程序启动失败
执行裸命令/usr/bin/gunicorn -c gun.py runserver:app
提示:Traceback (most recent call last):
File "/root/venv/bin/gunicorn", line 5, in <module>
from gunicorn.app.wsgiapp import run
File "/root/venv/lib/python3.6/site-packages/gunicorn/app/wsgiapp.py", line 9, in <module>
from gunicorn.app.base import Application
File "/root/venv/lib/python3.6/site-packages/gunicorn/app/base.py", line 13, in <module>
from gunicorn.config import Config, get_default_config_file
File "/root/venv/lib/python3.6/site-packages/gunicorn/config.py", line 16, in <module>
import ssl
File "/usr/local/lib/python3.6/ssl.py", line 101, in <module>
import _ssl # if we can't import it, let the error propagate
ModuleNotFoundError: No module named '_ssl'如果项目使用 Python3.6,可以尝试下面的方法:
这时可能需要重新安装 Python3,首先需要安装 ssl 依赖openssl-devel
和openssl
:yum install openssl-devel -y
如果是 Python3.7,建议使用下面的方法:2. 在类 Unix 环境下使用 Python — Python 3.10.1 文档
Python3.7 installation (solve ssl problem) - Programmer All- 去Tags · openssl/openssl 下载较高版本 openssl;
- 解压并编译安装 openssl
cd openssl-1.1.1-pre8
./config --prefix=/usr/local/openssl # 新版openssl将安装在/usr/local/openssl目录下
make && make install - 备份原版 openssl
mv /usr/bin/openssl /usr/bin/openssl.bak # backup
mv /usr/include/openssl/ /usr/include/openssl.bak - 备份好之后为新版 openssl 配置软连接
# 将安装好的openssl的openssl命令软连到/usr/bin/openssl
ln -s /usr/local/openssl/include/openssl /usr/include/openssl
# 软链到升级后的libssl.so
ln -s /usr/local/openssl/lib/libssl.so.1.1 /usr/local/lib64/libssl.so
# 将安装好的openssl命令软连到/usr/bin/openssl
ln -s /usr/local/openssl/bin/openssl /usr/bin/openssl - 最后再修改下系统配置即可
# 写入openssl库文件的搜索路径
echo "/usr/local/openssl/lib" >> /etc/ld.so.conf # 需要root权限
# 使修改后的/etc/ld.so.conf生效
ldconfig -v - 验证 openssl 版本之后重新编译安装 Python3。
openssl version
OpenSSL 1.1.1m 14 Dec 2021./configure --prefix=/usr/local/python3 --enable-shared CFLAGS=-fPIC
make && make install
直接退出
使用一下命令查看报错信息supervisorctl tail {{PROJECT_NAME}} stderr # PROJECT_NAME为项目名称
- 修改配置之后重启应用程序
supervisorctl reread
supervisorctl update
supervisorctl restart {{PROJECT_NAME}} - xxx: ERROR (no such process)
提示找不到进程,需要重新读取配置(reread->update->start) - xxx:ERROR (spawn error)
可能是配置写得有问题,注意对齐,如果有 app 的日志,则必须保证目录和文件存在。
Docker 容器化
以下部分供参考,部分未完成。
安装 docker
yum install docker -y |
- 编写 Dockerfile
从仓库拉取 带有 python 3.6 的 Linux 环境
FROM python:3.6
设置 python 环境变量
ENV PYTHONUNBUFFERED 1
设置测试环境变量
ENV ENV test
创建 code 文件夹并将其设置为工作目录
RUN mkdir /code
WORKDIR /code
国内换源
RUN pip config set global.index-url https://mirrors.aliyun.com/pypi/simple
RUN pip config set install.trusted-host mirrors.aliyun.com
更新 pip
RUN pip install pip -U
yum 换源
首先下载源 wget -O ./CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
ADD CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo
安装依赖
RUN yum install zlib-devel -y
RUN yum install python-devel -y
RUN yum install openssl-devel -y
将 requirements.txt 复制到容器的 code 目录
ADD requirements.txt /code/
安装库
RUN pip install -r requirements.txt
将当前目录复制到容器的 code 目录
ADD . /code/安装 docker-compose
如果提示没有添加到环境变量,则需要编辑pip install docker-compose
~/.bashrc
并写入export PATH=/usr/local/python3/bin:$PATH
。当然,其中的路径应为 python 的真实路径。使用上述版本安装的可能运行报错,怀疑是版本不对(1.29.2),如果遇到同样错误,使用下述命令安装:
sudo curl -L https://get.daocloud.io/docker/compose/releases/download/1.22.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
此版本不会报错。 - 编写 docker-compose.yml
测试环境,我们直接使用 Django 的 web 服务器就好,所以 command 比较简单。docker-compose.yml
version: "3"
services:
app:
restart: always
# 指定一个包含 Dockerfile 的路径
build: . # '点'代表当前目录
# 容器运行时需要执行的命令
command: "python manage.py runserver 0.0.0.0:8000"
volumes:
- .:/code
ports:
# 定义了宿主机和容器的端口映射。容器的隔离不止环境,甚至连端口都隔离起来了。但 web 应用不通过端口跟外界通信当然不行,因此这里定义将宿主机的 8000 端口映射到容器的 8000 端口
- "8100:8000"启动
- 启动容器服务
docker-compose up
可以看到 Docker 按照配置文件的要求,成功构建了镜像及容器,并启动了容器。docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
96347684e5ce sz_mobile_app "python manage.py ru…" 2 hours ago Up 5 seconds 0.0.0.0:8100->8000/tcp sz_mobile_app_1
……
打开浏览器,输入本地 IP 端口 192.168.1.88:8100
,访问请求,其中 ip 和端口需要根据个人实际情况决定。注意端口为宿主机器的实际端口,并且防火墙必须开放该端口。
按 Ctrl + C
即可停止开发服务器运行,停止服务器后实际上容器还存在,只是停止运行了而已。
此外还有一些指令我们可能需要使用:
- 删除容器
docker-compose down
- 后台运行容器
docker-compose up -d
- 重新构建镜像如果修改了 Dockerfile,则需要
docker-compose build
docker-compose up --build
重新构建 - 启动和停止已有的容器通常来说,我们在测试环境中,只需要让 Django 服务简单启动起来即可,而在生产环境,则需要 nginx 来处理静态文件和大流量请求,所以需要隔离测试环境和生产环境。
docker-compose start
docker-compose stop
生产配置通常与开发配置略有不同。 比如使用 Gunicorn 为 Django 应用程序提供服务,使用 NGINX 作为反向代理和静态/媒体文件服务器。
这种设置虽然有利于生产,但在开发中并不是很方便,因为开发的目标通常是快速响应。 再比如,为了稳定性我决定使用托管数据库服务,因此不需要容器化的数据库服务。
云端部署
所以我们编写生产环境中的docker-compose.prod.yml
文件。version: "3"
services:
web:
build: .
restart: on-failure
env_file:
- ./.env
command: gunicorn --bind 0.0.0.0:8080 meta.wsgi
ports:
- "8100:8080"
depends_on:
- nginx
nginx:
image: "nginx"
restart: always
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
- ./staticfiles:/static
- ./mediafiles:/media
ports:
- "80:80"
网络相关:docker network 基础 - wadeson - 博客园
参考来源
常态化部署
- Huawei Cloud Centos7 Flask+Gunicorn+Gevent+Supervisor+Nginx Multi-site Production Environment Deployment
- Flask + Nginx + Gunicorn + Gevent 部署
- gunicorn+gevent+nginx 部署 flask 应用
- CentOS 上 Flask + uWSGI + Nginx 部署
- Nginx、Gunicorn 在服务器中分别起什么作用
- 用 gunicorn 实现 Django 高并发的解决方案_旷古的寂寞的博客-CSDN 博客
- Django 部署多个网站_Li-boss-CSDN 博客
- gunicorn 和 nginx 端口映射_qq_40472064 的博客-CSDN 博客_gunicorn 端口
- Gunicorn-Django 部署 - 朝朝哥 - 博客园
Docker 部署
- Django-Docker 容器化部署:Django-Docker-MySQL-Nginx-Gunicorn 云端部署 - 杜赛的博客
- Docker 部署 Django+MySQL+Redis+Gunicorn+Nginx | Python 技术论坛
- 隔离测试与生产环境:Dockerizing Django in development and production