django系列(N)之django项目部署
一、概述
django部署有多种可用的web服务器,如下所述:同步项目:
gunicorn+nginx, gunicorn 纯python编写,配置简单,易上手,适合中小型项目,不支持高并发,与docker,k8s等非常适配
uwsgi+nginx, uwsgi 是c语言编写,配置较复杂,适合中大型高并发项目
异步项目:
daphne+nginx, daphne 支持异步,是asgi协议
本文介绍uwsgi+nginx的方式
二、部署
2.1、环境准备
# 1、准备一台linux服务器,本文部署使用的是redhat7.9
# 2、在linux中部署python ,本文使用的是python3.9.13,
python安装请见我另一篇博客:https://www.cnblogs.com/sunjiwei/articles/18066608
# 3、创建项目用户并配置sudo
useradd www
visudo # 打开配置sudo的配置文件,然后在配置文件最后加入下面这行
www ALL=(ALL) NOPASSWD: ALL
# 4、在linux中部署nginx,yum install -y nginx
或者也可以下载二进制包安装:
1、下载地址:https://nginx.org/download/
2、上传到服务器并解压
tar -xvf nginx-1.24.0.tar.gz
3、安装依赖
yum -y install gcc pcre pcre-devel zlib zlib-devel openssl openssl-devel
4、编译安装
cd nginx-1.24.0/
./configure --prefix=/usr/local/nginx --with-http_ssl_module
make && make install
# 授权
chown -R www:www /usr/local/nginx
# 5、创建项目目录并授权
mkdir -p /data/www
chown -R www:www /data/www
chmod -R 755 /data/www
# 6、进入项目目录
cd /data/www/wms_project
# 7、clone代码或上传代码
# 如果是clone,需要自行学习git的安装使用
git clone 你的代码仓库地址
# 重新授权
chown -R www:www /data/www
chmod -R 755 /data/www
# 8、创建虚拟环境(在/data/www/wms_project这个目录下创建虚拟环境)
python3 -m venv wms_venv # wms_venv 是虚拟环境名称
source /data/www/wms_project/wms_venv/bin/activate # 激活虚拟环境
# 9、安装依赖
/data/www/wms_project/wms_venv/bin/python3 -m pip install --upgrade pip
pip3 install -r /data/www/wms_project/warehouse_management/requirements.txt -i https://mirrors.aliyun.com/pypi/simple
# 10、安装 uWSGI
pip3 install uwsgi -i https://mirrors.aliyun.com/pypi/simple
2.2、配置django
1. 修改配置文件以适配生产环境(注意,我这里偷懒了,正常应该创建新的生产配置文件)
# 以下是需要修改的项或者需要添加的项,其他配置项正常用就可以
PARENT_BASE_DIR = Path(__file__).resolve().parent.parent.parent
# 一定要改为False
DEBUG = False
# 允许访问的域名(填服务器IP/域名,多个用逗号分隔)
ALLOWED_HOSTS = ['*'] # ['your-domain.com', '服务器IP']. 正常这样配,我这里直接*了
# 以下三个静态文件配置
STATIC_URL = '/static/' # 静态文件访问url配置
# 静态文件存储路径,只需要配置全局的static文件夹即可,不需要配置每个app下的static文件夹,django会自动去app下寻找
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static')
]
STATIC_ROOT = os.path.join(PARENT_BASE_DIR, 'staticfiles') # 这个是收集静态文件后存放文件的目录
# 媒体文件配置
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(PARENT_BASE_DIR, 'media')
# 密钥(生产环境务必替换为随机字符串,建议从环境变量读取)
# export DJANGO_SECRET_KEY='一长串随机字符'. 这个在linux服务器上配置环境变量
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY') # 然后读取环境变量
# 用 Django 自带命令生成一个安全的 SECRET_KEY
# python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())'
# 数据库配置,修改为生产环境的配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'your_db_name',
'USER': 'your_db_user',
'PASSWORD': 'your_db_pwd',
'HOST': '127.0.0.1',
'PORT': '3306',
'OPTIONS': {
'charset': 'utf8mb4'
}
}
}
# 日志配置
# 日志配置
# ===================== 日志目录创建 =====================
LOG_DIR = os.path.join(PARENT_BASE_DIR, 'log')
os.makedirs(LOG_DIR, exist_ok=True) # 自动创建日志目录(不存在时)
# ===================== 核心日志配置 =====================
LOGGING = {
'version': 1,
'disable_existing_loggers': False, # 不禁用其他已存在的 logger
'formatters': {
'verbose': {
'format': '{asctime} 【{levelname}】{module} 进程ID-{process:d} 线程ID-{thread:d} {message} {exc_info}',
'style': '{',
"datefmt": "%Y-%m-%d %H:%M:%S",
},
# 业务日志格式:侧重业务语义,无技术细节
"business": {
"format": "[{asctime}] {levelname} {name} {module}.{funcName}:{lineno} - {message}",
"style": "{",
"datefmt": "%Y-%m-%d %H:%M:%S",
},
},
'handlers': {
'prod_file': {
'level': 'WARNING',
'class': 'logging.handlers.TimedRotatingFileHandler',
'when': 'D', # 按天轮转(可选S/秒、M/分、H/时、D/天、W0-6/周)
'backupCount': 30,
'filename': os.path.join(LOG_DIR, 'django_error.log'), # 错误日志文件路径
'formatter': 'verbose',
'encoding': 'utf-8',
},
"business_file": { # 业务日志处理器
"level": "INFO",
"class": "logging.handlers.TimedRotatingFileHandler", # 按时间轮转
"filename": os.path.join(LOG_DIR , "business.log"), # 业务日志文件路径
"when": "D", # 每天轮转
"backupCount": 30, # 保留30天
"encoding": "utf-8",
"formatter": "business",
},
"mail": { # 严重错误邮件告警(生产),可选
"level": "ERROR",
"class": "django.utils.log.AdminEmailHandler",
"formatter": "verbose",
"include_html": False, # 不发送HTML格式,避免泄露细节
},
},
'root': {
'handlers': ['prod_file'],
},
'loggers': {
# ---------------- Django核心日志器 ----------------
"django.request": { # HTTP请求日志(4xx/5xx)
"handlers": ["prod_file"],
"level": "WARNING",
"propagate": False,
},
"django.db.backends": { # 数据库日志
"handlers": ["prod_file"],
"level": "WARNING",
"propagate": False,
},
"django.security": {
"handlers": ["prod_file"],
"level": "WARNING",
"propagate": False,
},
'django': { # django框架核心日志器
'handlers': ['prod_file'],
'level': 'WARNING',
'propagate': False,
},
# ---------------- 业务日志器,可以为每个app都配置一个单独的,实现精细化的日志管理,也可以共用这一个 ----------------
'common_app': {
'handlers': ['business_file'],
'level': 'INFO',
'propagate': False,
},
},
}
2、收集静态文件
# 切换到www用户
su - www
# 激活虚拟环境(如果没激活)
source /data/www/your_project/env/bin/activate
# 进入项目源码目录
cd /data/www/wms_project/warehouse_management
# 收集静态文件到STATIC_ROOT目录下
python3 manage.py collectstatic --noinput # --noinput 跳过确认
3、创建超级管理员(可选),供后续使用
python3 manage.py createsuperuser
2.3、uWSGI配置
su - www # 切到www用户下执行
1. 创建 uWSGI 配置文件
# 注意!!!:写配置文件时,要把这些注释都去掉,uWSGI配置文件不支持这些注释,会把他们当作配置
cat > /data/www/wms_project/conf/uwsgi.ini <<EOF
[uwsgi]
# 核心配置
chdir = /data/www/wms_project/warehouse_management # 项目源码根目录(manage.py所在目录)
module =warehouse_management.wsgi:application # Django 项目里的 WSGI 应用程序
home = /data/www/wms_project/wms_venv # 虚拟环境路径
listen = 100 # 监听请求队列大小,即最多允许100个请求排队等待,django会把同一时刻超出workers * threads 的请求放到队列里,根据自己的并发调整
socket-timeout = 30 # 请求队列中排队超时30秒,避免用户无限等待
# 进程/线程配置(根据服务器CPU核数调整)
master = true # 主进程
workers = 4 # 工作进程数(建议:CPU核数*2+1),这是利用多核cpu的关键
threads = 2 # 每个进程的线程数,建议最大不要超过4,单个cpu在多个线程之间切换调用
max-requests = 10000 # 每个进程最大处理请求数(防止内存泄漏),每个 worker 进程处理完 10000 个请求后,uWSGI 会自动杀死这个 worker,并由主进程重启一个全新的 worker
# harakiri = 60 # 请求开始后执行过程超时时间(秒)
# 如果没用 nginx,只想自己启动一个 http 界面,用这个调测
# http = 0.0.0.0:8000 # 直接作为 web 服务器,uwsgi也可以直接做服务器,只是对静态文件来说太慢,所以要配合nginx
# 网络配置(与Nginx通信)
socket = /data/www/wms_project/conf/uwsgi.sock # UNIX Socket(比TCP更高效),只适用于nginx和uWSGI在同一个服务器机器的情况,如果nginx是单独的机器,需要用TCP, socket = 0.0.0.0:8000
uid = www # 运行用户
gid = www # 运行用户组
# 日志配置
daemonize = /data/www/wms_project/log/uwsgi.log # 后台运行并输出日志
log-maxsize = 10485760 # 日志文件达到这个大小会生成下一个新的文件(对daemonize的日志文件同样起作用)
# 注释/删除 logto = xxx.log(避免冲突)
pidfile = /data/www/wms_project/conf/uwsgi.pid # PID文件(用于重启/停止)
# 清理
vacuum = true # 退出时自动清理Socket文件
disable-logging = true # 关闭默认请求日志(避免冗余)
# 环境变量,告诉 Django运行时使用哪个配置文件,是多环境(开发 / 测试 / 生产)隔离的核心配置,我这里前面搞django配置文件时偷懒了,原地修改配置文件的,所以注释掉不用
# 注意env 是 uWSGI 配置文件里的固定指令,不是python的虚拟环境,就这样写,只修改最后的配置文件的路径即可
# env = DJANGO_SETTINGS_MODULE=warehouse_management.settings.production
EOF
2、启动 / 停止 uWSGI 命令
# 如果用这些命令要切换到www用户下
su - www
# 激活虚拟环境(若未激活)
source /data/www/wms_project/wms_venv/bin/activate
# 启动uWSGI
/data/www/wms_project/wms_venv/bin/uwsgi --ini /data/www/wms_project/conf/uwsgi.ini
# 停止uWSGI
/data/www/wms_project/wms_venv/bin/uwsgi --stop /data/www/wms_project/conf/uwsgi.pid
# 重启uWSGI
/data/www/wms_project/wms_venv/bin/uwsgi --reload /data/www/wms_project/conf/uwsgi.pid
3、配置 systemd 服务(管理 uWSGI)
# 要在root用户下配置systemd
# 注意这里有个坑,如果要配置systemd,则uwsgi配置文件中的daemonize参数要给去掉,systemd不支持后台守护进程
# 日志使用logto 参数配置
cat > /etc/systemd/system/uwsgi_wms_project.service <<EOF
[Unit]
Description=uWSGI Service for Django Project
After=network.target
[Service]
User=www
Group=www
# 指定「服务启动后切换到的工作目录」,可以把它理解为:系统启动这个服务时,先 “cd 到该目录”,再执行后续的启动命令(比如启动 uWSGI)。
WorkingDirectory=/data/www/wms_project
ExecStart=/data/www/wms_project/wms_venv/bin/uwsgi --ini /data/www/wms_project/conf/uwsgi.ini
ExecReload=/data/www/wms_project/wms_venv/bin/uwsgi --reload /data/www/wms_project/conf/uwsgi.ini
ExecStop=/data/www/wms_project/wms_venv/bin/uwsgi --stop /data/www/wms_project/conf/uwsgi.pid
# 进程守护参数,作用是:当服务进程因「异常原因」退出时,systemd 会自动重启该服务,以此保证服务的高可用性
Restart=on-failure
# 重启间隔时间参数,默认0.1秒
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
# 授权
sudo chown -R www:www /data/www
sudo chmod -R 755 /data/www
chmod 755 /etc/systemd/system/uwsgi_wms_project.service
# 配置好后下面是运维命令
# 重新加载systemd配置
systemctl daemon-reload
# 开机自启
systemctl enable uwsgi_wms_project
# 启动服务
systemctl start uwsgi_wms_project
# 查看状态
systemctl status uwsgi_wms_project
# 停止服务
systemctl stop uwsgi_wms_project
# 重启服务
systemctl restart uwsgi_wms_project
2.4、Nginx 配置
在www用户下配置nginx
su - www
1、编辑nginx配置文件
mv /usr/local/nginx/conf/nginx.conf /usr/local/nginx/conf/nginx.conf.bak
cat > /usr/local/nginx/conf/nginx.conf <<EOF
user www;
worker_processes auto; # 启动的工作子进程数量,auto表示和cpu核数一致
pid /usr/local/nginx/logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
# 字符集
charset utf-8;
# 客户端上传文件大小限制
client_max_body_size 100M;
server {
listen 80; # http端口,也可以自定义端口,如1000
server_name your_domain.com 192.168.1.100; # 你的域名/服务器IP,多个域名 / IP 用空格分隔,不支持网段
# 网段访问,拒绝所有其他IP
# allow 192.168.1.0/24; # 支持 CIDR 网段格式(/24=/255.255.255.0)
# deny all; # 拒绝除上述网段外的所有IP
##### 以下是自己部署用,只有ip,且不限制访 ###########
# 1. 监听80端口,设为默认服务器(匹配所有未被其他server块接管的请求)
listen 80 default_server;
# 2. server_name 配置为通配符/空值(匹配所有Host头,包括直接用IP访问的情况)
server_name _; # 官方推荐的“匹配所有”写法,替代空字符串 ""
# 3. 允许所有IP访问(默认就是允许,显式配置更清晰,避免误加限制)
allow all;
# (如果之前配过deny all,务必删掉,否则会冲突)
##### 以上是自己部署用,只有ip,且不限制访 ###########
# 日志
access_log /usr/local/nginx/logs/nginx_access.log;
error_log /usr/local/nginx/logs/nginx_error.log;
# 托管静态文件(核心:Nginx直接处理,不经过uWSGI)
location /static/ { # /static/ 对应 Django 的 STATIC_URL
alias /data/www/wms_project/staticfiles/; # 指向STATIC_ROOT
expires 30d; # 缓存30天
add_header Cache-Control "public"; # 配合 expires,缓存规则:公开缓存(浏览器本地缓存、CDN、代理服务器)
}
# 托管媒体文件
location /media/ { # /media/ 对应 Django 的 STATIC_URL
alias /data/www/wms_project/media/; # 指向MEDIA_ROOT
expires 7d;
add_header Cache-Control "public";
}
# 反向代理到uWSGI(动态请求)
location / {
uwsgi_pass unix:/data/www/wms_project/conf/uwsgi.sock; # 与uWSGI的socket路径一致
include /usr/local/nginx/conf/uwsgi_params; # 加载uWSGI参数(Nginx默认提供),这个是nginx中uwsgi_params的路径
uwsgi_read_timeout 300; # Nginx 与 uWSGI 通信的超时指令,单位秒
# 传递请求头(解决Django获取真实IP、域名问题),即透传功能
uwsgi_param Host $host;
uwsgi_param X-Real-IP $remote_addr;
uwsgi_param X-Forwarded-For $proxy_add_x_forwarded_for;
uwsgi_param X-Forwarded-Proto $scheme;
}
# 禁止访问隐藏文件(安全)
location ~ /\.ht {
deny all;
}
}
}
EOF
2、运维命令
#检查配置文件是否有问题
检查 nginx.conf 配置文件及其引用的所有包含文件的语法是否正确,
同时还会验证配置文件中所引用的文件和目录是否存在并且可访问
/usr/local/nginx/sbin/nginx -t
在root用户下执行运维命令
# 启动
/usr/local/nginx/sbin/nginx
# 重启
/usr/local/nginx/sbin/nginx -s reload
# 停止
/usr/local/nginx/sbin/nginx -s stop
# 优雅的停止 处理完请求后停止
/usr/local/nginx/sbin/nginx -s quit # -s 是signal
3、配置systemd 服务
cat > /etc/systemd/system/nginx.service << EOF
[Unit]
Description=nginx web service
Documentation=http://nginx.org/en/docs
After=network.target
[Service]
User=root
Group=root
Type=forking
PIDFile=/usr/local/nginx/logs/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 stop
PrivateTmp=true
[Install]
WantedBy=multi-user.target
EOF
# 授权
chmod 755 /etc/systemd/system/nginx.service
# 配置好后下面是运维命令
# 重新加载systemd配置
systemctl daemon-reload
# 设置开机自启
systemctl enable nginx
# 启动nginx
systemctl start nginx
# 查看状态
systemctl status nginx
# 重启Nginx
systemctl restart nginx
# 停止nginx
systemctl stop nginx
2.5、安全组/防火墙
如果有防火墙要记得设置或者关闭
三、验证
1、访问 http://你的域名/ 或 http://服务器IP:port/,确认项目正常访问;
注意:有些浏览器强制使用https访问,可以换几个浏览器尝试如果确认部署没问题而又访问不了,也可以打开无痕浏览试试
2、查看日志文件,确认无报错:
# 查看uWSGI日志
tail -f /data/www/wms_project/log/uwsgi.log
# 查看Nginx错误日志
tail -f /usr/local/nginx/logs/nginx_error.log
# 查看Django日志
tail -f /data/www/wms_project/logs/django_error.log
四、项目更新
# 1. 拉取最新代码或者手动上传也可以
cd /data/www/wms_project/
git pull
# 2. 安装新依赖(若有)
pip install -r requirements.txt
# 3. 数据库迁移(企业里面ddl要走数据库工单,不要用应用直接变更)
python manage.py migrate --noinput
# 4. 收集静态文件
python manage.py collectstatic --noinput
# 5. 重启uWSGI
systemctl restart uwsgi_your_project
五、配置https访问
以上配置只适用于http访问,如果想配置https访问,还需要继续配置https
# 1、安装Certbot
sudo yum install -y epel-release
sudo yum install -y certbot python3-certbot-nginx # 注意如果你是redhat7,python3-certbot-nginx要改成python2-certbot-nginx
# 2、获取 SSL 证书并自动配置 Nginx,
# -d 指定域名(多个域名用 -d 追加,如主域名+www子域名)
sudo certbot --nginx --nginx-ctl /usr/local/nginx/sbin/nginx --nginx-server-root /usr/local/nginx/conf -d your-domain.com
执行后会出现交互提示:
输入你的邮箱(用于证书到期提醒);
同意服务条款(选 Y);
是否共享邮箱(选 N);
是否强制 HTTP 跳转 HTTPS(选 2,推荐,所有 HTTP 请求自动转 HTTPS)
# 3、验证 nginx配置文件中的HTTPS 配置
Certbot 会自动修改你的 Nginx 配置文件
最终配置会新增以下核心内容(无需手动改,Certbot 已完成,查看下是否正确即可)
server {
# 1. 监听80端口,设为默认服务器(匹配所有未被其他server块接管的请求)
# 2. server_name 配置为通配符/空值(匹配所有Host头,包括直接用IP访问的情况)
server_name djangosun.cn; # managed by Certbot
# 3. 允许所有IP访问(默认就是允许,显式配置更清晰,避免误加限制)
allow all;
# (如果之前配过deny all,务必删掉,否则会冲突)
# 日志
access_log /usr/local/nginx/logs/nginx_access.log;
error_log /usr/local/nginx/logs/nginx_error.log;
# 托管静态文件(核心:Nginx直接处理,不经过uWSGI)
location /static/ {
alias /data/www/wms_project/staticfiles/;
expires 30d; # 缓存30天
add_header Cache-Control "public";
}
# 托管媒体文件
location /media/ {
alias /data/www/wms_project/media/;
expires 7d;
add_header Cache-Control "public";
}
# 反向代理到uWSGI(动态请求)
location / {
uwsgi_pass unix:/data/www/wms_project/conf/uwsgi.sock;
include /usr/local/nginx/conf/uwsgi_params;
uwsgi_param Host $host;
uwsgi_param X-Real-IP $remote_addr;
uwsgi_param X-Forwarded-For $proxy_add_x_forwarded_for;
uwsgi_param X-Forwarded-Proto $scheme;
}
# 禁止访问隐藏文件(安全)
location ~ /\.ht {
deny all;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/djangosun.cn/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/djangosun.cn/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
if ($host = djangosun.cn) {
# http 强制跳转https
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80 ;
server_name djangosun.cn;
return 404; # managed by Certbot
}
# 4、验证 HTTPS 是否可访问
sudo systemctl restart nginx # 重启 Nginx 确保配置生效
访问 https://your-domain.com,浏览器地址栏会显示「锁形图标」,说明 HTTPS 生效;
访问 http://your-domain.com,会自动跳转到 https://your-domain.com(验证跳转规则)
# 5、配置证书自动续期(Let's Encrypt 证书有效期 90 天)
# 第一、创建一个专门用于验证的目录,注意一定要在你的静态文件目录下(/data/www/wms_project/staticfiles), 确保路由能被nginx正确找到
sudo mkdir -p /data/www/wms_project/staticfiles/.well-known/acme-challenge
sudo chown -R www:www /data/www/wms_project/staticfiles/.well-known
# 第二、修改你的server块
server {
# -------------步骤 1: 粘贴下面的 location 块-----------------------
# 专门为 Let's Encrypt 验证请求开的“后门”
location /.well-known/acme-challenge/ {
# 使用 alias 将 URL 路径映射到服务器上的实际文件路径
alias /data/www/wms_project/staticfiles/.well-known/acme-challenge/;
# (可选) 增加一些安全和性能设置
location ~ /\. {
deny all;
}
access_log off;
expires -1;
add_header Pragma "no-cache";
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
# ------------步骤 1: 粘贴上面的 location 块-------------
# 步骤 2: 保留 Certbot 生成的 if 判断和跳转
if ($host = djangosun.cn) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
server_name djangosun.cn;
return 404; # managed by Certbot
}
# 测试自动续期命令是否正常(无报错则正常)
sudo certbot renew --dry-run --webroot --webroot-path /data/www/wms_project/staticfiles
# 添加 crontab 定时任务,每月自动检查续期
sudo crontab -e
# 新增以下内容(每天凌晨 2 点检查,需续期则自动续期)
0 2 * * * certbot renew --quiet --webroot --webroot-path /data/www/wms_project/staticfiles
# 6、关键补充:Django 适配 HTTPS
即使 Nginx 配置了 HTTPS,Django 仍可能识别为 HTTP 请求(导致重定向 / 登录失效),
需在 django配置文件 中添加以下配置:
# 告诉 Django 前端是 HTTPS(Nginx 已传递 X-Forwarded-Proto 头)
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
# 强制使用 HTTPS(可选,Django 层面拦截 HTTP 请求)
SECURE_SSL_REDIRECT = True
# Cookie 仅通过 HTTPS 传输(安全)
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
# HSTS 配置(强制浏览器后续仅用 HTTPS 访问,有效期 1 年)
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True

浙公网安备 33010602011771号