跨服务器部署Django:如何分离Django项目与Nginx/Gunicorn应用服务器?
嘿,我来帮你理清楚这个跨服务器部署的问题!首先得明确一个关键前提:Gunicorn必须和Django项目代码部署在同一服务器,因为它需要直接加载Django的WSGI应用模块。所以你想要的「应用服务器与Django项目分离」的正确架构应该是:
- 服务器1(172.30.10.92):托管所有Django项目,每个项目启动一个独立的Gunicorn进程,监听不同的内网端口
- 服务器2(172.30.10.93):部署Nginx作为统一的反向代理入口,根据请求的域名或URL路径,把流量转发到服务器1对应Django应用的Gunicorn端口
下面是一步步的配置指南,帮你搞定多应用部署的问题:
第一步:配置服务器1的Django + Gunicorn
每个Django应用都需要单独配置Gunicorn,确保它们监听不同的端口(比如从8000开始递增),并且绑定到服务器1的内网IP(这样服务器2才能访问)。
1.1 启动Gunicorn并后台运行
进入你的Django项目根目录(就是有manage.py的那个文件夹),运行以下命令:
# 第一个应用:绑定到172.30.10.92的8000端口,后台运行 gunicorn --bind 172.30.10.92:8000 myproject.wsgi:application --daemon # 第二个应用:绑定到172.30.10.92的8001端口 gunicorn --bind 172.30.10.92:8001 mysecondproject.wsgi:application --daemon
- 别绑定
127.0.0.1,否则服务器2根本连不上! - 为了避免服务器重启后要手动启动Gunicorn,建议用systemd管理进程。比如给第一个应用创建
/etc/systemd/system/myproject.service:
然后启用并启动服务:[Unit] Description=Gunicorn daemon for my Django app1 After=network.target [Service] User=你的用户名 Group=www-data WorkingDirectory=/home/你/myproject # 替换成你的项目路径 ExecStart=/usr/bin/gunicorn --bind 172.30.10.92:8000 myproject.wsgi:application [Install] WantedBy=multi-user.target
每个Django应用都要做类似的配置,只需要改端口、项目路径和服务名就行。sudo systemctl daemon-reload sudo systemctl enable myproject.service sudo systemctl start myproject.service
1.2 验证Gunicorn的可访问性
在服务器2上跑个curl测试,确认能连到服务器1的Gunicorn:
curl http://172.30.10.92:8000
如果能返回Django的默认页面或者你的应用内容,说明这一步没问题。
第二步:配置服务器2的Nginx反向代理
Nginx的作用就是当用户请求过来时,把不同的请求转发到对应的Django应用。这里分两种场景:按域名区分多应用,或者按URL路径区分。
2.1 按域名区分(推荐)
假设你有两个域名:app1.yourdomain.com和app2.yourdomain.com,分别对应服务器1的8000和8001端口。
编辑Nginx配置文件(比如/etc/nginx/sites-available/django_proxy):
# 定义上游服务器(也就是服务器1的Gunicorn端口) upstream app1 { server 172.30.10.92:8000; } upstream app2 { server 172.30.10.92:8001; } # 第一个应用的服务器块 server { listen 80; server_name app1.yourdomain.com; location / { proxy_pass http://app1; # 传递必要的请求头,让Django能正确获取客户端信息 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; } # 静态文件优化:让Nginx直接托管静态文件(需要提前把服务器1的静态文件同步到服务器2) location /static/ { alias /var/www/static/app1/; # 替换成你在服务器2存放app1静态文件的路径 expires 30d; # 缓存静态文件30天 } } # 第二个应用的服务器块 server { listen 80; server_name app2.yourdomain.com; location / { proxy_pass http://app2; 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; } location /static/ { alias /var/www/static/app2/; expires 30d; } }
2.2 按URL路径区分(同一域名下)
如果想用同一个域名,比如yourdomain.com/app1和yourdomain.com/app2,可以这么配置:
upstream app1 { server 172.30.10.92:8000; } upstream app2 { server 172.30.10.92:8001; } server { listen 80; server_name yourdomain.com; location /app1/ { proxy_pass http://app1/; # 注意末尾的斜杠,不然路径会出错! 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; } location /app2/ { proxy_pass http://app2/; 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; } # 静态文件对应路径 location /app1/static/ { alias /var/www/static/app1/; expires 30d; } location /app2/static/ { alias /var/www/static/app2/; expires 30d; } }
⚠️ 注意:这种情况下,每个Django应用的settings.py里要加FORCE_SCRIPT_NAME = '/app1'(对应各自的路径),不然URL反向解析会出问题。
2.3 启用配置并验证
把配置文件软链接到sites-enabled目录:
sudo ln -s /etc/nginx/sites-available/django_proxy /etc/nginx/sites-enabled/
测试Nginx配置是否合法:
sudo nginx -t
如果显示test is successful,就重启Nginx:
sudo systemctl restart nginx
现在访问对应的域名或路径,应该就能正常打开你的Django应用了。
常见问题排查
- 服务器2连不上服务器1的Gunicorn:检查服务器1的防火墙有没有开放对应的端口(比如8000、8001),可以用
sudo ufw allow 8000/tcp开放。 - 静态文件加载失败:要么把服务器1的静态文件同步到服务器2(用
scp或者rsync),要么在Django里用Whitenoise托管静态文件,让Gunicorn直接处理静态请求。 - Django获取不到客户端真实IP:确保Nginx配置里的
proxy_set_header都加了,并且在Django的settings.py里添加:USE_X_FORWARDED_HOST = True SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') # 如果用HTTPS的话
内容的提问来源于stack exchange,提问作者Essex

