补充说明:关于项目部署,历来是开发和运维人员的痛点。造成部署困难的主要原因是大家的Linux环境不同,这包括发行版、解释器、插件、运行库、配置、版本级别等等太多太多的细节。因此,一个成功的部署案例,往往很难移植到别的环境下,总是要填许多坑。那么,别人的案例就没有参考价值了么?当然不是,部署的过程其实就是参考很多的成功案例,摸索出自己适用的方式!这过程中需要什么?熟练的Linux技能是最基本的!一些部署的经验和灵活的思维也是要有的。不惧怕满屏的英文错误信息,能从中抓住问题的耐心更是要有的。
鉴于以上原因,我在这一节并没有介绍得太详细,因为多说多错。但肯定是一个成功的案例,不能说你按照我的方案没有部署成功,那么我这个就是错误的,这有失偏颇。
那么有没有比较好的大家都能用起来的部署方案呢?Docker了解一下!
基本概念
首先介绍一些Django部署的基本概念。
本节不涉及ASGI部署,在前面的章节有介绍,其实两者类同。
Django 的主要部署的方式是 WSGI,它是 Web 服务器和 Web 应用的 Python 标准,也是所谓的同步服务和默认服务。
Django支持:
- Gunicorn
- uWSGI
- Apache
- Nginx
它们可以混搭。个人推荐Nginx配合uWSGI。
application
对象
用 WSGI 部署的关键是一个叫做application
的可调用对象,应用服务器用它与你的代码交互,是所有请求的接口和入口。
application
一般位于一个 Python 模块中,以名为 application
的对象的形式出现,且对服务器可见。
我们使用startproject
命令创建项目时会自动创建 /wsgi.py
文件,其中就包含了 application
对象,直接使用即可。Django 开发服务器和生产环境的 WSGI 部署都使用它。
WSGI 服务器从其配置文件中获取 application
对象的路径。Django 的开发服务器( runserver
),从配置项 WSGI_APPLICATION
中获取。默认值是 .wsgi.application
,指向 /wsgi.py
中的 application
。
配置 settings 模块
当 WSGI 服务器(uWSGI、Gunicorn等)加载应用时,Django 需要导入配置模块。
Django 利用 DJANGO_SETTINGS_MODULE
环境变量来决定使用哪个settings.py模块,它的值是一个圆点路径字符串。开发环境和生产环境可以配置不同的settings.py。
若未设置该变量, wsgi.py
默认将其设置为 your_site.settings
, your_site
即工程名字。
注意:由于环境变量是进程级的,所以如果在同一进程运行多个 Django 站点将出错。为了避免此问题,可以为每个站点使用 mod_wsgi
的daemon模式。或者在 wsgi.py
中强制设置 os.environ["DJANGO_SETTINGS_MODULE"] = "mysite.settings"
,重写来自环境变量的值。
WSGI 中间件
如果你想使用WSGI 中间件,可以如下将原来的application包裹起来:
from helloworld.wsgi import HelloWorldApplication application = HelloWorldApplication(application)
部署架构
本节将重点介绍基于下面的架构部署Django:
Python3.8 + ubuntu 16.04 + Nginx + uWSGI + Django3.1
首先,你得有一台ubuntu机器,真实物理机和虚拟机都行,如果是阿里云ECS主机并且带有独立公网IP,那是最好不过。
我这里和大家一样,什么都没有,只有虚拟机,囧。
至于如何安装ubutun、Python3.8和Django3.1,不是本节的内容,请自行解决。下面我假定你已将安装好了这三者。
在部署之前请务必按照部署清单检查完毕后,收集静态文件和媒体文件,并将DEBGU设置为False。
一、安装Nginx
我不太推荐Apache2,偏爱Nginx。
Ubuntu默认源里面的Nginx版本比较旧,需要先添加一个Nginx的源,再通过apt-get安装Nginx。
sudo add-apt-repository ppa:nginx/stable apt-get update apt-get install nginx
一般这个都没问题,Nginx是居家必备软件,各家Linux下都可以顺利安装。
使用service --status-all
命令查看一下,安装好后,服务应该会自动启动:
...... [ + ] network-manager [ + ] networking [ + ] nginx [ + ] ondemand [ - ] plymouth [ - ] plymouth-log ......
如果能看到带+号的nginx,表明一切ok!
然后,通过ifconfig
,查看一下你的ubuntu虚拟机的ip地址,我这里是192.168.1.121
。使用同一局域网内的主机,通过浏览器访问192.168.1.121
,如果能看到下面的界面,说明Nginx服务正常。
二、安装uWSGI
Django的主要部署平台是WSGI,它也是Python的标准web服务器和应用。
uWSGI是实现了WSGI协议的WSGI服务器。
uWSGI 是一个快速的、自我驱动的、对开发者和系统管理员友好的应用容器服务器,完全由 C 编写。
uWSGI的官网地址:https://uwsgi-docs.readthedocs.io/en/latest/index.html
根据血和泪的经验教训,请确保安装的是最新版本的uwsgi,否则可能出现各种坑。
所以不建议使用:pip3 install uwsgi
(不一定是最新版)
不建议使用:pip install https://projects.unbit.it/downloads/uwsgi-lts.tar.gz
(也不一定是最新版)
而是建议到https://uwsgi-docs.readthedocs.io/en/latest/Download.html页面,下载Stable/LTS
版本的源文件。
为什么要最新版?因为现在的官方教程和相关技术文章全是以新版编写的,很多参数名,用法有较大改变。用旧版,你可能连跑都跑不起来。
我这里下载的是uwsgi-2.0.14.tar.gz
,可能等到你看到此文时,已经不是最新的了。
在ubuntu中,解压源码,然后指定安装位置,将uWSGI 安装好:
# 解压文件 tar -zxvf uwsgi # 进入解压目录 sudo python3 setup.py install
注意!注意!一定要注意!如果你使用了虚拟环境,那么你必须使用虚拟环境的Python解释器安装uWSGI!!!也就是说,你的Django代码使用的哪个Python解释器,你的uWSGI服务器也必须使用同一个解释器。
安装完毕后,尝试运行一下uwsgi:
[feixue@feixue-VirtualBox: ~/soft]$ uwsgi *** Starting uWSGI 2.0.14 (64bit) on [Wed Dec 20 22:41:46 2017] *** compiled with version: 5.4.0 20160609 on 20 December 2016 12:48:11 os: Linux-4.4.0-101-generic #124-Ubuntu SMP Fri Nov 10 18:29:59 UTC 2017 nodename: feixue-VirtualBox machine: x86_64 clock source: unix detected number of CPU cores: 2 current working directory: /home/feixue/soft detected binary path: /usr/local/bin/uwsgi !!! no internal routing support, rebuild with pcre support !!! *** WARNING: you are running uWSGI without its master process manager *** your processes number limit is 15648 your memory page size is 4096 bytes detected max file descriptor number: 1024 lock engine: pthread robust mutexes thunder lock: disabled (you can enable it with --thunder-lock) The -s/--socket option is missing and stdin is not a socket.
虽然运行出错了,但至少表明你的uWSGI在系统可执行命令路径中。
如果出现找不到命令的提示,那么建议创建一个指向/usr/local/bin
目录的软链接。这些都是Linux管理员的业务领域,不展开介绍了。
需要提醒大家注意的是权限的问题,该sudo的时候要sudo。还有读写权限,看看当前用户是否具备相关文件的读写能力。很多时候部署失败,都不是Python和Django层面的原因,而是你的Linux管理业务还不够熟练。
三、配置uwsgi
软件安装好了后,首先是要拷贝项目代码。
因为我这里是在Windows下使用Pycharm编写的代码,不是在ubuntu虚拟机内编写的代码,所以需要将项目文件先整体拷贝到虚拟机中。(当然,最佳的方式是通过Pycharm进行远程开发。堡垒机视频中有教学。)
这个过程,也是八仙过海,各有奇招,我就当你将项目文件拷贝过去了。
在项目的根目录下,也就是有manage.py
的目录下,新建一个uwsgi.ini
文件。文件名可以随便,但后缀必须是ini
。
在里面写入下面的配置内容:
[uwsgi] chdir = /home/feixue/python/www/for_test //项目根目录 module = for_test.wsgi:application // 指定wsgi模块下的application对象 socket = 127.0.0.1:8000 //对本机8000端口提供服务 master = true //主进程 # 以上4个是核心配置项 #vhost = true //多站模式 #no-site = true //多站模式时不设置入口模块和文件 #workers = 2 //子进程数 #reload-mercy = 10 #vacuum = true //退出、重启时清理文件 #max-requests = 1000 #limit-as = 512 #buffer-size = 30000 #pidfile = /var/run/uwsgi9090.pid //pid文件,用于下脚本启动、停止该进程 daemonize = /home/feixue/python/www/for_test/run.log // 日志文件 disable-logging = true //不记录正常信息,只记录错误信息
详细说明:
- 配置项中以‘#’开头的都是被注释的项目,不起作用;
- 以双斜杠开头,表示注释;
- chdir是你的项目根目录。我这里的项目名叫for_test;
- moudule是你的入口wsgi模块,将for_test替换成你的项目名称;
- socket是通信端口设置,和我一样就行;
- master=True表示以主进程模式运行;
- demonize是你的日志文件,会自动建立
- disable-logging = true 表示不记录正常信息,只记录错误信息。否则你的日志可能很快就爆满了。
- env: 指定
DJANGO_SETTINGS_MODULE
的值 - home:可选的项目虚拟环境路径
也可以通过命令参数的方式,不过就显得复杂可不可修改了:
uwsgi --chdir=/path/to/your/project \ --module=mysite.wsgi:application \ --env DJANGO_SETTINGS_MODULE=mysite.settings \ --master --pidfile=/tmp/project-master.pid \ --socket=127.0.0.1:49152 \ # can also be a file --processes=5 \ # number of worker processes --uid=1000 --gid=2000 \ # if root, uwsgi can drop privileges --harakiri=20 \ # respawn processes taking more than 20 seconds --max-requests=5000 \ # respawn processes after serving 5000 requests --vacuum \ # clear environment on exit --home=/path/to/virtual/env \ # optional path to a virtual environment --daemonize=/var/log/uwsgi/yourproject.log # background the process
四、配置Nginx
步骤1:备份默认配置文件
首先,在进行任何更改之前创建配置文件的备份始终是一个好习惯。这样,您可以根据需要轻松恢复到原始设置。
sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/default.backup
此命令将default
文件复制到同一目录中命名的新文件中default.backup
,作为备份。
步骤 2:编辑默认配置文件
接下来,您需要编辑该default
文件以自定义 Nginx 服务器配置。您可以使用系统上可用的任何文本编辑器,例如nano
或vim
。为简单起见,我将nano
在本示例中使用:
sudo nano /etc/nginx/sites-available/default
在此文件中,找到server
您要修改的块。根据您的要求,您将进行以下更改:
- 将 更改
server_name
为您服务器的实际 IP 地址或域名。 - 确保
include uwsgi_params;
线路保持不变。 - 修改
uwsgi_pass
指令以匹配您的 uWSGI 配置的套接字。 - 调整
location /static
块的alias
指令以指向静态文件的正确位置。
## # You should look at the following URL's in order to grasp a solid understanding # of Nginx configuration files in order to fully unleash the power of Nginx. # http://wiki.nginx.org/Pitfalls # http://wiki.nginx.org/QuickStart # http://wiki.nginx.org/Configuration # # Generally, you will want to move this file somewhere, and start with a clean # file but keep this around for reference. Or just disable in sites-enabled. # # Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples. ## # Default server configuration # server { listen 80; listen [::]:80; # SSL configuration # # listen 443 ssl default_server; # listen [::]:443 ssl default_server; # # Note: You should disable gzip for SSL traffic. # See: https://bugs.debian.org/773332 # # Read up on ssl_ciphers to ensure a secure configuration. # See: https://bugs.debian.org/765782 # # Self signed certs generated by the ssl-cert package # Don't use them in a production server! # # include snippets/snakeoil.conf; # root /var/www/html; # Add index.php to the list if you are using PHP # index index.html index.htm index.nginx-debian.html; server_name 192.168.1.121; location / { # First attempt to serve request as file, then # as directory, then fall back to displaying a 404. # try_files $uri $uri/ =404; include uwsgi_params; uwsgi_pass 127.0.0.1:8000; } location /static { alias /home/feixue/python/www/for_test/static; } # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # #location ~ \.php$ { # include snippets/fastcgi-php.conf; # # # With php7.0-cgi alone: # fastcgi_pass 127.0.0.1:9000; # # With php7.0-fpm: # fastcgi_pass unix:/run/php/php7.0-fpm.sock; #} # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /\.ht { # deny all; #} } # Virtual Host configuration for example.com # # You can move that to a different file under sites-available/ and symlink that # to sites-enabled/ to enable it. # #server { # listen 80; # listen [::]:80; # # server_name example.com; # # root /var/www/example.com; # index index.html; # # location / { # try_files $uri $uri/ =404; # } #}
关键是这一部分:
server { listen 80; listen [::]:80; server_name 192.168.1.121; location / { include uwsgi_params; uwsgi_pass 127.0.0.1:8000; } location /static { alias /home/feixue/python/www/for_test/static; } }
请将server_name
改成你的实际IP。include uwsgi_params
一定要一样。uwsgi_pass
和你uWSGI配置中的socket
要一样。location /static
的alias改成你的实际情况,让静态文件得以部署。
/home/feixue/python/www/for_test/static与setting.py中
STATIC_ROOT路径一直
进行必要的编辑后,保存并关闭文件。如果您使用的是nano
,则可以按Ctrl + O
,然后Enter
按 保存,然后按Ctrl + X
退出来执行此操作。
第 3 步:测试 Nginx 配置并重新加载
在应用更改之前,测试 Nginx 配置是否存在任何语法错误至关重要:
sudo nginx -t
如果测试成功,您将看到一条消息,指示语法正确且测试成功。最后,重新加载 Nginx 以应用更改:
sudo systemctl reload nginx
此命令会重新加载 Nginx,应用新配置,而无需完全重新启动服务,从而最大限度地减少停机时间。
通过执行这些步骤,您将成功备份和自定义 Nginx 服务器配置,以更好地满足您的部署需求。请记住,调整服务器配置需要仔细注意细节,以确保您的 Web 应用程序保持可访问性和安全性。
五、启动服务
下面我们可以尝试启动服务了! 进入项目的根目录,也就是有uwsgi.ini
文件的地方,运行:
sudo uwsgi uwsgi.ini
系统提示:
[uWSGI] getting INI configuration from uwsgi.ini
到主机浏览器中访问‘192.168.1.121’,却看见下面的错误提示页面:
DisallowedHost at / Invalid HTTP_HOST header: '192.168.1.121'. You may need to add '192.168.1.121' to ALLOWED_HOSTS. Request Method: GET Request URL: http://192.168.1.121/ Django Version: 1.11.3 Exception Type: DisallowedHost Exception Value: Invalid HTTP_HOST header: '192.168.1.121'. You may need to add '192.168.1.121' to ALLOWED_HOSTS. Exception Location: /usr/local/lib/python3.5/dist-packages/django/http/request.py in get_host, line 113 Python Executable: /usr/local/bin/uwsgi Python Version: 3.5.2 ......
不要被它们吓到!很多人都不愿意仔细看错误信息,其实解决办法,人家已经提示得非常清楚了,需要在ALLOWED_HOSTS配置项目中添加'192.168.1.121'
。
进入相应目录,编辑settings.py文件:
DEBUG = False ALLOWED_HOSTS = ['192.168.1.121']
同时将DEBUG设置为False。
在ubuntu中,运行下面的命令:
sudo killall -9 uwsgi
这会删除先前的uWSGI进程。
过几秒,一定要过几秒,数5下,然后:
sudo uwsgi uwsgi.ini
为什么要过几秒?因为端口释放有延迟啦。
再次在浏览器中访问192.168.1.121
,看到欢迎信息则说明部署成功了。
上面的信息是因为我配置了一条url和一个简单的视图:
# 根urls.py from django.urls import path from django.contrib import admin from app1 import views urlpatterns = [ url('admin/', admin.site.urls), url('', views.index), ] # app1/views.py from django.shortcuts import HttpResponse def index(request): return HttpResponse("恭喜你,成功部署了DJango!")
到此一个基本的Django项目就部署好了。
注意:如果改动manage.py中的
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'laoyang_blog.settings.dev')
则wsgi.py也要改动为相同的
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'laoyang_blog.settings.dev')