gunicorn部署django项目

Gunicorn是纯Python编写的轻量级、高性能的WSGI HTTP Server,下面基于典型的Django项目来介绍配置。

Gunicorn配置

创建配置文件gunicorn.conf.py

1
2
3
4
5
6
7
8
9
10
11
12
13
bind = '127.0.0.1:9015'   # 监听ip和端口
wsgi_app = 'public.wsgi:application' # 指定wsgi app
workers = 2 # 启动的工作进程
threads = 8 # 每个进程可用于处理请求的线程数(可同时处理的最大请求量为 workers * threads)
timeout = 30 # 请求处理超时时间
max_requests = 2000 # 每个工作进程最大处理请求量(达到后自动重启工作进程,可防止内存泄露)
max_requests_jitter = 200 # 重启工作进程的最大抖动(防止工作进程在达到max requests时同时重启导致服务中断)
accesslog = '-' # 访问日志(可指定文件,- 意味着输出到标准输出)
errorlog = 'access.log' # 错误日志
capture_output = True # 捕获标准输出和标准错误输出至errorlog(结合accesslog='-'意味所有输出都会被记录到errolog中)
access_log_format = '%({x-forwarded-for}i)s %(t)s %(r)s %(s)s generated %(b)s bytes in %(M)s ms' # 访问日志格式
daemon = True # 守护进程模式
reload = True # 自动重载(开发测试环境可用,检测到代码变化自动重载)

上述配置可以把所有程序运行中产生的输出都记录到errorlog配置的文件中。

注意,代码中使用print()打印的内容因为Python的缓冲输出,会在进程结束或缓冲区满时才写入到文件,所以如果你想要实时写入日志文件就需要结合设置环境变量PYTHONUNBUFFERED来实现(参考下述的控制脚本配置)。

Nginx配置

基础的nginx配置,可根据实际情况调整

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server {
listen 80;
server_name example.com;

client_max_body_size 10M;

location / {
proxy_pass http://127.0.0.1:9015;
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_set_header X-Forwarded-Proto $scheme;
}
}

控制脚本

再附加个控制脚本control.sh来控制服务的启动、重启、重载、停止等功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#!/bin/bash
#

WORK_DIR="$(dirname $0)/../" # 设置项目跟目录,需要根据情况调整
BIN=venv/bin/gunicorn # 指定gunicorn 路径
PIDFILE=tools/run.pid # pid文件存放路径
CONF=tools/gunicorn.conf.py # 配置文件存放路径
export PYTHONUNBUFFERED=1 # 关闭Python输出缓冲

PID=$(cat ${WORK_DIR}/${PIDFILE} 2> /dev/null)
cd ${WORK_DIR}

result_echo() {
if [ $1 == 0 ]; then
echo -e "\033[32mSuccess\033[0m"
else
echo -e "\033[31mFailed\033[0m"
fi
}

start_server() {
if [ "$PID" != "" ] && ps -c ${PID} &> /dev/null; then
echo "Server already running (pid: $PID) ..."
else
echo -n "Starting server ... "
${BIN} -c ${CONF} -p ${PIDFILE} > /dev/null
result_echo $?
fi
}

stop_server() {
if [ "$PID" = "" ] || ! ps -c ${PID} &> /dev/null; then
echo "Server already stopped"
else
echo -n "Stopping server ... "
kill -TERM ${PID} &> /dev/null
result_echo $?
fi
}

reload_server() {
if [ "$PID" = "" ] || ! ps -c $PID &> /dev/null; then
start_server
else
echo -n "Reloading server ... "
kill -HUP ${PID} &> /dev/null
result_echo $?
fi
}


case "$1" in
start)
start_server
;;
stop)
stop_server
;;
restart)
stop_server
sleep 2
start_server
;;
reload)
reload_server
;;
*)
echo "Usage: $0 start|stop|restart|reload"
;;
esac

使用控制脚本示例

  • 启动服务

    1
    bash control.sh start
  • 重载服务(不会中断服务)

    1
    bash control.sh reload
  • 重启服务

    1
    bash control.sh restart
  • 停止服务

    1
    bash control.sh stop