Django3.0.8生产环境部署nginx、uwsgi、daphne、supervisor、gunicorn、celery

目录

  • 前言
  • 环境配置
  • 技术方法
  • 实现过程
    • 安装Python3.8.3
    • 安装Nginx
    • 安装MySQL
    • 安装Redis
    • 安装虚拟环境 virtualenv
    • 创建虚拟环境
      • python中的virtualenv命令集
    • 在虚拟环境中安装pip包
    • 将项目文件上传到服务器规划的目录
    • 拉取所有需要的static file到同一目录
    • 配置nginx作为反向代理服务器
    • 编辑uwsgi文件
    • 使用supervisor管理daphne和uwsgi进程
    • 使用gunicorn + gevent替代uwsgi, 并发性能会好一点
    • 使用logrotate + crontabs切割日志
    • settings.py 中LOGGING的配置
    • 最后
  • 部署中遇到的错误和解决办法
      • 1. 启动supervisor时报错:
      • 2. Nginx Permission Denie问题:
      • 3. Nginx启动时报错:
      • 4. MySQL报错
        • 原因分析:
        • 解决方案

前言

因为项目中用到了channels做websocket消息推送,所以以前那种nginx+uwsgi的模式行不通了~,channels推荐的是daphne服务器,所以Google了一下,都说是用nginx+uwsgi+daphne+supervisor这一套组合来部署。看了下网上的教程,七零八落的,好不容易才拼凑起来,所以才有自己总结一下的想法。

环境配置

服务器:CentOS8.2 64位
开发平台: Python3.8.3
Web框架: Django3.0.8
数据库: MySQL 8.0
缓存数据库: Redis

技术方法

使用Nginx代理,将http部分请求发送给uwsgi或者gunicorn进行处理,将websocket部分请求发送给daphne进行处理。uwsgi和daphhe均使用supervisord进行控制。
需要注意的是,由于Nginx无法识别http请求和websocket请求,需要通过路由来区分是哪种协议。我使用的方法是规定所有的websocket的路由均以/ws开头(如: ws://www.example/ws/table/table_id/),这样就可以让Nginx将所有以/ws开头的请求全部转发给daphne进行处理。
当然,这一切都是在服务器的一个虚拟环境中。

实现过程

安装Python3.8.3

安装方法在我另一篇博客中: 如何在 CentOS 8 上安装 Python 3.8
如果嫌pip安装太慢,可以参考这篇文章:更换pip源到国内镜像

安装Nginx

  1. 安装 Nginx
yum install nginx -y
  1. 启动软件并设置开机启动
systemctl enable nginx
systemctl start nginx
  1. 查看命令行检测安装完成。
[[email protected] ~]# nginx -V
nginx version: nginx/1.14.1
built by gcc 8.2.1 20180905 (Red Hat 8.2.1-3) (GCC) 
built with OpenSSL 1.1.1 FIPS  11 Sep 2018
TLS SNI support enabled
configure arguments: --prefix=/usr/share/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --http-client-body-temp-path=/var/lib/nginx/tmp/client_body --http-proxy-temp-path=/var/lib/nginx/tmp/proxy --http-fastcgi-temp-path=/var/lib/nginx/tmp/fastcgi --http-uwsgi-temp-path=/var/lib/nginx/tmp/uwsgi --http-scgi-temp-path=/var/lib/nginx/tmp/scgi --pid-path=/run/nginx.pid --lock-path=/run/lock/subsys/nginx --user=nginx --group=nginx --with-file-aio --with-ipv6 --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_addition_module --with-http_xslt_module=dynamic --with-http_image_filter_module=dynamic --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_degradation_module --with-http_slice_module --with-http_stub_status_module --with-http_perl_module=dynamic --with-http_auth_request_module --with-mail=dynamic --with-mail_ssl_module --with-pcre --with-pcre-jit --with-stream=dynamic --with-stream_ssl_module --with-debug --with-cc-opt='-O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector-strong -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -Wl,-E'
  1. 设置防火墙规则
firewall-cmd --permanent --zone=public --add-service=http 
firewall-cmd --permanent --zone=public --add-service=https
firewall-cmd --reload
  1. 访问IP即可

Django3.0.8生产环境部署nginx、uwsgi、daphne、supervisor、gunicorn、celery_第1张图片

配置文件目录

主配置文件:/etc/nginx/nginx.conf

默认文件目录:/usr/share/nginx/html

错误日志:/var/log/nginx/error.log

访问日志:/var/log/nginx/access.log

安装MySQL

参考我的这篇文章: CentOS 8 安装MySQL 8.0

安装Redis

参考我的这篇文章:CentOS 8安装Redis的两种方式

安装虚拟环境 virtualenv

pip install virtualenvwrapper

接下来编辑.bashrc文件,这是重点

[root@ITCMDB ~]# vi ~/.bashrc

添加下面j几行
VIRTUALENVWRAPPER_PYTHON=/usr/bin/python  # 指定virtualenvwrapper执行的python版本
export WORKON_HOME=$HOME/.virtualenvs  # 指定虚拟环境存放目录,.virtualenvs目录名可自拟
VIRTUALENVWRAPPER_VIRTUALENV_ARGS='' # 添加virtualenvwrapper的参数,生成干净隔绝的环境

source /usr/local/bin/virtualenvwrapper.sh  # virtualenvwrapper.sh所在目录

保存后,重载./bashrc文件

source ~/.bashrc

创建虚拟环境

执行命令

mkvirtualenv kzitcmdb(虚拟环境名称)

就创建好了, 当然,还有许多命令选项,我这里就不多说了

python中的virtualenv命令集

irtualenv

# 安装
$ pip install virtualenv
# 激活虚拟环境
$ source my_project/bin/activate
# 退出虚拟环境
$ deactivate
# 输出当前依赖包
$ pip freeze > requirements.txt
# 安装依赖包
$ pip install -r requirements.txt

virtualenvwrapper

# 安装
$ pip install virtualenvwrapper
# 修改~/.zshrc
$ cat ~/.zshrc
if [ -f /usr/local/bin/virtualenvwrapper.sh ]; then
    export WORKON_HOME=$HOME/virtualenvs
    source /usr/local/bin/virtualenvwrapper.sh
fi
# 创建虚拟环境,创建成功后 $WORKON_HOME 下会多出一个文件夹
$ mkvirtualenv my_prj
# 使用虚拟环境
$ workon my_prj
# 退出虚拟环境
$ deactivate
# 删除
$ rmvirtualenv my_prj
# 列出所有环境
$ lsvirtualenv
# 进入到当前虚拟环境中
$ cdvirtualenv
# 进入到当前虚拟环境的 site-packages 中
$ cdsitepackages

.gitignore

*.py[cod]     # 将匹配 .pyc、.pyo 和 .pyd文件
__pycache__/  # 排除整个文件夹

在虚拟环境中安装pip包

将准备的requirements.txt上传到服务器
然后执行下面命令

[root@ITCMDB /]# workon led_dev
(led_dev) [root@ITCMDB /]# pip install -r requirements.txt

等待安装完成即可

将项目文件上传到服务器规划的目录

/root/webapp

同时,将数据库上传到服务器
本例中,用navicat进行数据库的复制。
此时,可以通过python内置的服务器进行测试

(kzitcmdb) [root@ITCMDB wwwroot]# python manage.py runserver 0.0.0.0:9999

启动成功后,在浏览器进行测试。

拉取所有需要的static file到同一目录

在django的setting文件中,添加下面一行内容:

STATIC_URL = '/static/'

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"),
]

并在项目根目录下运行命令:

 python manage.py collectstatic

完成后,重启nginx

配置nginx作为反向代理服务器

我这里是把我的项目的nginx配置文件放在了项目文件夹中,便于管理
在这个目录下新建一个led.conf

[root@ITCMDB nginx]# pwd

/root/webapp/LED/conf/nginx/

编辑led.conf

upstream led {
    server unix:///root/webapp/LED/conf/sock/server.sock fail_timeout=0;
}

upstream websocket {
    server unix:///root/webapp/LED/conf/sock/websocket.sock fail_timeout=0;
}

log_format  access_exp  '$remote_addr - $remote_user [$time_local] "$request" '
                  '$status $body_bytes_sent "$http_referer" '
                  '"$http_user_agent" "$http_x_forwarded_for"';

server {
    listen 8000;
    server_name 192.168.12.156; # substitute your machine's IP address or FQDN
    charset utf-8;
    client_max_body_size 1024M;

    location /media {
        alias /root/webapp/LED/media/;
    }
    location /static {
        alias /root/webapp/LED/static/;
    }

    access_log /root/webapp/LED/log/nginx/access.log access_exp;
    error_log /root/webapp/LED/log/nginx/error.log;
    open_log_file_cache max=10;

    location / {
        # gunicorn 配置
        # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        # proxy_set_header Host $http_host;
        # proxy_redirect off;
        # if (!-f $request_filename) {
            # proxy_pass http://led;
            # break;
        #}

        # uwsgi 配置
        uwsgi_pass led;
        include /root/webapp/LED/conf/uwsgi/uwsgi_params;
    }

    location /ftp {
        alias  /root/webapp/LED/media/file/;   #目录文件服务器根目录
        autoindex on;   #允许nginx在浏览器以文件夹形式访问
        autoindex_exact_size off;  #显示文件大小
        autoindex_localtime on;    #显示文件时间
        charset utf-8; # 避免中文乱码
    }

    location /push {
        proxy_pass http://websocket;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_redirect     off;
        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-Host $server_name;
        proxy_read_timeout  36000s;
        proxy_send_timeout  36000s;
    }
}

然后需要将led.conf软链接到/etc/nginx/conf.d/文件夹下,这样nginx才能加载到你的配置文件

ln -s /root/webapp/LED/conf/nginx/led.conf /etc/nginx/conf.d/

在Nginx和daphne以及uwsgi进行通信时,有http socket和file socket两种通信方式,推荐使用后一种file socket的方式, 也就是我用的这种。

编辑uwsgi文件

我这里是把我的项目uwsgi的ini文件也放在了项目文件夹中,便于管理
在这个目录下新建一个led.ini

[root@ITCMDB uwsgi]# pwd

/root/webapp/LED/conf/uwsgi/

编辑led.ini

[uwsgi]
#使用nginx连接时使用
socket=/root/webapp/LED/conf/sock/server.sock

#直接做web服务器使用 python manage.py runserver ip:port
#http=0.0.0.0:8000

#项目目录
chdir=/root/webapp/LED

#项目中wsgi.py文件的目录,相对于项目目录
wsgi-file=LED/wsgi.py

#指定启动的工作进程数
processes=2

#指定工作进程中的线程数
threads=2

#指定在这些进程里有一个主进程
master=True

#保存启动之后主进程的pid
pidfile=/root/webapp/LED/conf/uwsgi/uwsgi.pid

#设置uwsgi后台运行,uwsgi.log保存日志信息
daemonize=/root/webapp/LED/log/uwsgi.log

#设置日志文件最大字节数
log-maxsize = 100000

#设置每个进程最大请求数
max-requests = 1000

#设置虚拟环境的路径
virtualenv=/root/.virtualenvs/led_dev

重点:nginx的配置文件中有一个uwsgi_params文件,在/etc/nginx/文件夹内,拷贝到这个文件夹

使用supervisor管理daphne和uwsgi进程

安装教程:CentOS安装Supervisor

使用教程: supervised使用教程

我的daphne和uwsgi都是用supervisor管理的

编辑/etc/supervisord.conf 解除注释并修改(文件末尾)

[include]
files = /root/webapp/LED/conf/supervisor/*.conf

在/root/webapp/LED/conf/supervisor/文件夹新建 daphne.conf、uwsgi.conf、celery_worker.conf文件,因为我还用了celery,所以多一个celery的配置。

daphne.conf

[program:led.daphne]
command=/root/.virtualenvs/led_dev/bin/daphne -u /root/webapp/LED/conf/sock/websocket.sock LED.asgi:application
directory=/root/webapp/LED
user=root
autostart=true                ; 随着supervisord的启动而启动
autorestart=true              ; 自动重启。。当然要选上了
startretries=10               ; 启动失败时的最多重试次数
exitcodes=0                   ; 正常退出代码
stopsignal=KILL               ; 用来杀死进程的信号
stopwaitsecs=10               ; 发送SIGKILL前的等待时间
redirect_stderr=true          ; 重定向stderr到stdout
stdout_logfile_maxbytes=20MB
stdout_logfile_backups=20
redirect_stderr=true
stdout_logfile=/root/webapp/LED/log/supervisor/daphne.log
; 给每个进程命名,便于管理
process_name=daphne_led
; 默认为 false,如果设置为 true,当进程收到 stop 信号时,会自动将该信号发给该进$
stopasgroup=true             ; send stop signal to the UNIX process
; 默认为 false,如果设置为 true,当进程收到 kill 信号时,会自动将该信号发给该进$
killasgroup=true             ; SIGKILL the UNIX process group (def false)


uwsgi.conf

;[program:led.uwsgi]
;command=/root/.virtualenvs/led_dev/bin/uwsgi --ini /root/webapp/LED/conf/uwsgi/led.ini
;directory=/root/webapp/LED
;user=root
;autostart=true                ; 随着supervisord的启动而启动
;autorestart=true              ; 自动重启。。当然要选上了
;startretries=10               ; 启动失败时的最多重试次数
;exitcodes=0                   ; 正常退出代码
;stopsignal=KILL               ; 用来杀死进程的信号
;stopwaitsecs=10               ; 发送SIGKILL前的等待时间
;redirect_stderr=true          ; 重定向stderr到stdout
;stdout_logfile_maxbytes=20MB
;stdout_logfile_backups=20
;stdout_logfile=/root/webapp/LED/log/supervisor/uwsgi.log
; 默认为 false,如果设置为 true,当进程收到 stop 信号时,会自动将该信号发给该进$
;stopasgroup=true             ; send stop signal to the UNIX process
; 默认为 false,如果设置为 true,当进程收到 kill 信号时,会自动将该信号发给该进$
;killasgroup=true             ; SIGKILL the UNIX process group (def false)

celery_worker.conf

[program:led.celery.worker]
command=/root/.virtualenvs/led_dev/bin/celery -A LED worker -B -l info  ; 被监控的进程路径
directory=/root/webapp/LED
user=root
numprocs=1
autostart=true                ; 随着supervisord的启动而启动
autorestart=true              ; 自动重启。。当然要选上了
startretries=3               ; 启动失败时的最多重试次数
exitcodes=0                   ; 正常退出代码
stopsignal=KILL               ; 用来杀死进程的信号
stopwaitsecs=10               ; 发送SIGKILL前的等待时间
redirect_stderr=true          ; 重定向stderr到stdout
stdout_logfile_maxbytes=20MB
stdout_logfile_backups=20
stdout_logfile=/root/webapp/LED/log/supervisor/celery.log
; 给每个进程命名,便于管理
process_name=celery_led
; 默认为 false,如果设置为 true,当进程收到 stop 信号时,会自动将该信号发给该进$
stopasgroup=true             ; send stop signal to the UNIX process
; 默认为 false,如果设置为 true,当进程收到 kill 信号时,会自动将该信号发给该进$
killasgroup=true             ; SIGKILL the UNIX process group (def false)

启动supervisor

supervisord -c /etc/supervisord.conf

使用gunicorn + gevent替代uwsgi, 并发性能会好一点

gunicorn使用教程:
python之gunicorn的配置
gunicorn 使用教程
gunicorn + nginx:通过套接字或代理服务器

  1. 安装gunicorn
pip install gunicorn
pip install gevent   # 因为我使用的是gevent方式启动gunicorn
  1. 配置gunicorn
    在/root/webapp/LED/conf/gunicorn/ 文件夹内新建gunicorn.conf.py
import multiprocessing
import os
import sys

BASE_DIR = '/root/webapp/LED/'

sys.path.append(BASE_DIR)

LOG_DIR = os.path.join(BASE_DIR, 'log/gunicorn/')
if not os.path.exists(LOG_DIR):
    os.makedirs(LOG_DIR)

# 监听地址和端口
# bind = "127.0.0.1:8001"

# 使用套接字
bind = "unix:/root/webapp/LED/conf/sock/server.sock"

# 以守护进程的形式后台运行
daemon = True

# 服务器中在pending状态的最大连接数,即client处于waiting的数目。超过这个数目, client连接会得到一个error。
# 建议值64-2048。
backlog = 512

# 超时
timeout = 30

# 调试状态
debug = False

# gunicorn要切换到的目的工作目录
chdir = BASE_DIR

# 工作进程类型(默认的是 sync 模式,还包括 eventlet, gevent, or tornado, gthread, gaiohttp)
worker_class = 'gevent'

# 工作进程数
workers = (multiprocessing.cpu_count()) + 1

# 客户端最大同时连接数。只适用于eventlet, gevent工作方式。
worker_connections = ((multiprocessing.cpu_count()) + 1) * 1000

# 指定每个工作进程开启的线程数
# threads = multiprocessing.cpu_count() * 2

# 日志
# 日志级别,这个日志级别指的是错误日志的级别(debug、info、warning、error、critical),而访问日志的级别无法设置
loglevel = 'info'

# 其每个选项的含义如下:
'''
h          remote address
l          '-'
u          currently '-', may be user name in future releases
t          date of the request
r          status line (e.g. ``GET / HTTP/1.1``)
s          status
b          response length or '-'
f          referer
a          user agent
T          request time in seconds
D          request time in microseconds
L          request time in decimal seconds
p          process ID
'''

# 访问日志文件
accesslog = os.path.join(LOG_DIR, 'gunicorn.access.log')

# 错误日志文件
errorlog = os.path.join(LOG_DIR, 'gunicorn.error.log')

# pid 文件
pidfile = os.path.join(BASE_DIR, 'conf/gunicorn/led_gunicorn.pid')

# 进程名
proc_name = 'led.pid'

# 指定worker进程的运行用户名。
user = 'root'

# 更多配置请执行:gunicorn -h 进行查看

在/root/webapp/LED/conf/supervisor/文件夹新建 gunicorn.conf,注意: gunicorn.conf 和 uwsgi.conf部署的时候二选一就行了。到时候可以把另一个注释掉。
gunicorn.conf

[program:led.gunicorn]
command=/root/.virtualenvs/led_dev/bin/gunicorn LED.wsgi:application -c /root/webapp/LED/conf/gunicorn/gunicorn.conf.py  --log-level=info
directory=/root/webapp/LED
user=root
autostart=true                ; 随着supervisord的启动而启动
autorestart=true              ; 自动重启。。当然要选上了
startretries=10               ; 启动失败时的最多重试次数
exitcodes=0                   ; 正常退出代码
stopsignal=KILL               ; 用来杀死进程的信号
stopwaitsecs=10               ; 发送SIGKILL前的等待时间
redirect_stderr=true          ; 重定向stderr到stdout
stdout_logfile_maxbytes=20MB
stdout_logfile_backups=20
stdout_logfile=/root/webapp/LED/log/supervisor/gunicorn.log
; 给每个进程命名,便于管理
process_name=gunicorn_led
; 默认为 false,如果设置为 true,当进程收到 stop 信号时,会自动将该信号发给该进$
stopasgroup=true             ; send stop signal to the UNIX process
; 默认为 false,如果设置为 true,当进程收到 kill 信号时,会自动将该信号发给该进$
killasgroup=true             ; SIGKILL the UNIX process group (def false)

使用gunicorn的nginx配置:
led.conf

upstream led {
    server unix:///root/webapp/LED/conf/sock/server.sock fail_timeout=0;
}

upstream websocket {
    server unix:///root/webapp/LED/conf/sock/websocket.sock fail_timeout=0;
}

log_format  access_exp  '$remote_addr - $remote_user [$time_local] "$request" '
                  '$status $body_bytes_sent "$http_referer" '
                  '"$http_user_agent" "$http_x_forwarded_for"';

server {
    listen 8000;
    server_name 192.168.12.156; # substitute your machine's IP address or FQDN
    charset utf-8;
    client_max_body_size 1024M;

    location /media {
        alias /root/webapp/LED/media/;
    }
    location /static {
        alias /root/webapp/LED/static/;
    }

    access_log /root/webapp/LED/log/nginx/access.log access_exp;
    error_log /root/webapp/LED/log/nginx/error.log;
    open_log_file_cache max=10;

    location / {
        # gunicorn 配置
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        if (!-f $request_filename) {
            proxy_pass http://led;
            break;
        }

        # uwsgi 配置
        # uwsgi_pass led;
        # include /root/webapp/LED/conf/uwsgi/uwsgi_params;
    }

    location /ftp {
        alias  /root/webapp/LED/media/file/;   #目录文件服务器根目录
        autoindex on;   #允许nginx在浏览器以文件夹形式访问
        autoindex_exact_size off;  #显示文件大小
        autoindex_localtime on;    #显示文件时间
        charset utf-8; # 避免中文乱码
    }

    location /push {
        proxy_pass http://websocket;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_redirect     off;
        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-Host $server_name;
        proxy_read_timeout  36000s;
        proxy_send_timeout  36000s;
    }
}

启动supervisor

supervisord -c /etc/supervisord.conf

启动nginx

systemctl start nginx.service

没报错的话就是可以了~

使用logrotate + crontabs切割日志

安装:

yum -y install logrotate crontabs

配置:

  1. 先配置logrotate
    gunicorn-log-rotate和nginx-log-rotate文件:
/root/webapp/LED/log/gunicorn/*.log {
  su root root
  nocompress
  daily
  copytruncate
  create
  notifempty
  rotate 7
  olddir /root/webapp/LED/log/gunicorn
  missingok
  dateext
  postrotate
    [ -e /root/webapp/LED/conf/gunicorn/led_gunicorn.pid ] && kill -USR1 $(cat /root/webapp/LED/conf/gunicorn/led_gunicorn.pid)
  endscript
}
/root/webapp/LED/log/nginx/*.log {
  su root root
  nocompress
  daily
  copytruncate
  create
  notifempty
  rotate 7
  olddir /root/webapp/LED/log/nginx
  missingok
  dateext
  postrotate
    [ -e /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
  endscript
}
  1. 配置crontabs, 每天0点定时切割日志
    运行:
sudo crontab  -e

然后编辑:

# rotate nginx log erery day

0 0 * * * /usr/sbin/logrotate -f /root/webapp/LED/conf/logrotate/nginx-log-rotate

0 0 * * * /usr/sbin/logrotate -f /root/webapp/LED/conf/logrotate/gunicorn-log-rotate

settings.py 中LOGGING的配置

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'filters': {
        'require_debug_false': {
            '()': 'django.utils.log.RequireDebugFalse'
        },
        'skip_static_requests': {
            '()': 'django.utils.log.CallbackFilter',
            'callback': skip_static_requests
        },
    },
    'formatters': {
        'verbose': {
            'format': '%(levelname)s %(asctime)s %(process)d %(thread)d %(name)s:%(lineno)s %(funcName)s() %(message)s'
        },
        'verbose_sql': {
            'format': '%(levelname)s %(asctime)s %(process)d %(thread)d %(name)s:%(lineno)s %(funcName)s() %(sql)s\n%(params)s\n%(duration)ss'
        },
    },
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'filters': ['skip_static_requests'],
            'class': 'logging.StreamHandler',
            'formatter': 'verbose',
        },
        'console_error': {
            'level': 'ERROR',
            'filters': ['skip_static_requests'],
            'class': 'logging.StreamHandler',
            'formatter': 'verbose',
        },
        'console_sql': {
            'level': 'WARNING',
            'filters': ['skip_static_requests'],
            'class': 'logging.StreamHandler',
            'formatter': 'verbose_sql',
        },
    },
    'loggers': {
        'django': {
            'handlers': ['console'],
            'level': 'INFO',
            'propagate': True,
        },
        'django.request': {
            'handlers': ['console_error'],
            'level': 'ERROR',
            'propagate': False,
        },
        'django.db.backends': {
            'handlers': ['console_sql'],
            'level': 'WARNING',
            'propagate': False,
        },
        'django.security': {
            'handlers': ['console_error'],
            'level': 'ERROR',
            'propagate': False,
        },
        'daphne': {
            'handlers': [
                'console',
            ],
            'level': 'INFO'
        },

    },
}
def skip_static_requests(record):
    if isinstance(record.args, dict):
        if record.args.get('path', None):
            if record.args.get('path').startswith('/static/'):
                return False
            if record.args.get('path').startswith('/media/'):
                return False
            if record.args.get('path').startswith('/captcha/image/'):
                return False
    return True

最后

按上面步骤一步步来的话,基本就可以部署成功了,记得部署之前要把项目的settings配置文件配置好。

部署中遇到的错误和解决办法

1. 启动supervisor时报错:

 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. 

解决办法:
运行命令

unlink /tmp/supervisor.sock

2. Nginx Permission Denie问题:

open() "/etc/nginx/conf.d/led.conf" failed (13: Permission denied) in /etc/nginx/nginx.conf

或者是nginx静态资源文件无法访问,403 forbidden错误:

解决方法:

  1. CentOS8 下 解决Nginx Permission Denied问题(推荐)
  2. 解决Nginx出现403 forbidden (13: Permission denied)报错的四种方法

3. Nginx启动时报错:

nginx: [error] invalid PID number "" in "/run/nginx.pid"

解决办法
需要先执行

nginx -c /etc/nginx/nginx.conf

nginx.conf文件的路径可以从nginx -t的返回中找到。

nginx -t
nginx -s reload

4. MySQL报错

ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2)

原因分析:

我们在连接mysql的时候,如果host是localhost, 就会使用Unix Domain Socket来连接, MySql默认的sock文件路径是/tmp/mysql.sock, 一些mysql 安装方法 将 mysql .sock放在/var/lib/mysql .sock或者其他的什么地方,你可以通过修改/etc/my.cnf文件来修正它(8.0的配置文件好像是在/etc/my.cnf.d/mysql-server.cnf),打开文件,可以看到如下的东东:

[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
log-error=/var/log/mysql/mysqld.log
pid-file=/run/mysqld/mysqld.pid

解决方案

1. 修改HOST
打开settings.py文件,找到DATABASES的HOST字段。
有两种修改方案:

  • 把localhost改成127.0.0.1, 使用内网连接
  • 把localhost改成/var/lib/mysql/mysql.sock
DATABASES = {
     'default': {                                                                                                               
          'ENGINE': 'django.db.backends.mysql',
          'NAME': 'xxx',
          'USER': 'root',
          'PASSWORD': 'passwd',
          'HOST':'127.0.0.1', # 或者 `/tmp/mysql.sock`
          'PORT':'3306',
      }
  }

2. 修改mysql的sock文件路径

vi etc/my.cnf  # 或者 /etc/my.cnf.d/mysql-server.cnf

将sock路径变更为/var/lib/mysql/mysql.sock

socket = /tmp/mysql.sock

重启MySql服务。

Centos机器上

systemctl restart mysqld.service

在这里我用的是软链接的方式,因为我不想修改这些默认的配置,毕竟小白一个…

mysql.sock 一般不是在 /tmp/mysql.sock 就是在 /var/lib/mysql/mysql.sock 这里,没有的话就用

ln -s /var/lib/mysql/mysql.sock /tmp/mysql.sock

或者是

ln -s /tmp/mysql.sock /var/lib/mysql/mysql.sock

3. 数据库连接指定sock路径

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'xxx',
        'USER': 'root',
        'PASSWORD': 'passwd',
        'OPTIONS': {
            "unix_socket": "/tmp/mysql.sock",
        },
    }
}

你可能感兴趣的:(python,Linux,Django,python)