Qingquan-Li / blog

My Blog
https://Qingquan-Li.github.io/blog/
132 stars 16 forks source link

部署Django项目(Ubuntu + conda + uWSGI + Nginx) #137

Open Qingquan-Li opened 4 years ago

Qingquan-Li commented 4 years ago

环境:

注:生产环境上需要设置 MySQLhttps 等,这里不作介绍。

部署步骤:

  1. 配置SSH、安装Git;
  2. 传输代码到服务器;
  3. 在服务器上克隆开发时的 conda 环境;
  4. 安装并配置uWSGI;
  5. 安装并配置Nginx。

注:更优的部署方式是使用 Docker 容器实现自动化部署,这里不作介绍。

参考:

the web client <-> the web server(Nginx) <-> the socket <-> uwsgi <-> Django

整个部署的链路是 Nginx -> uWSGI -> Python Web 程序。

当一个访问进来的时候,首先到 Nginx,Nginx 会把请求(HTTP协议)转换 uwsgi 协议传递给 uWSGI ,uWSGI 通过 WSGI 规范和 Web Server 进行通信取到响应结果,再通过 uwsgi 协议发给 Nginx,最终 Nginx 以 HTTP 协议发送响应给用户。



一、配置SSH、安装Git

1.1 配置 SSH

用于 $ ssh username@server 登录服务器。

参考:SSH登录远程主机:https://github.com/FatliTalk/blog/issues/130

1.2 安装 Git

安装 Git 以使用 Git 传输代码。

macOS 安装 Git :参考 Git_1 了解 Git(git --version)

Ubuntu 安装 Git:参考 Ubuntu搭建Git服务器



二、传输代码到服务器

传输代码到服务的方式,例如:使用 FileZilla 图形化软件 的 SFTP 传输;或使用 scp 与服务器传输文件。

git部署项目代码至服务器的三种方式,这里使用第三种:「本地开发主机 => 提交代码到GitHub => 服务器Git拉取代码」的形式进行代码传输。

本地开发主机提交代码到 GitHub 后,服务器 $ git clone 克隆代码仓库,或 $ git pull (等价于 $ git fetch + $ git merge )拉取并合并代码。

拉取并合并本地代码实例:

# 查看远程仓库
fatli@fatli-vm-ubuntu:~/github/mysite$ git remote -v
origin  git@github.com:FatliTalk/mysite.git (fetch)
origin  git@github.com:FatliTalk/mysite.git (push)

# 拉取远程仓库
fatli@fatli-vm-ubuntu:~/github/mysite$ git fetch
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (1/1), done.
remote: Total 3 (delta 2), reused 3 (delta 2), pack-reused 0
Unpacking objects: 100% (3/3), done.
From github.com:FatliTalk/mysite
   c756d23..5a5494e  master     -> origin/master

# 对比本地分支和远程分支(这也是使用GitHub管理代码的好处,可以对比修改内容)
fatli@fatli-vm-ubuntu:~/github/mysite$ git diff master origin/master

# 合并分支
fatli@fatli-vm-ubuntu:~/github/mysite$ git merge

注意:

在服务器 $ git clone GitHub 上的代码前,先将 ssh 公钥复制到 GitHub 的 SSH keys 。参考:SSH登录远程主机 - SSH登录(公钥登录),免得每次要输入密码。

不要在服务器上直接修改代码,不然 $ git pull 会失败。假如真的修改过,下次更新代码时,请删除服务器仓库的代码,重新 $ git clone GitHub 上的代码。(在服务器项目文件下新增文件,并且不使用 $ git add 跟踪新文件,则使用 $ git pull 拉取合并代码时不会出现冲突。使用 $ git status 可查看新文件状态为未被跟踪。)



三、在服务器上克隆开发时的 conda 环境

3.1 安装 conda

在服务器上克隆开发时的 conda 环境,即使用 conda 安装相应的包。首先要安装conda。

此处使用安装 Miniconda 的形式安装 conda,参考:


3.2 克隆conda环境

有多种方式可以共享 conda 环境:Moving Conda Environments

这里使用 Environment.yml 的方式进行 conda 环境共享。

通过平台(platforms)和操作系统之间共享项目环境,可以使用 export 选项生成一个 environment.yml 文件。 environment.yml 文件不是特定于操作系统的,并且使用 YAML 进行了格式化。

该文件仅列出了软件包(package)的名称和一些说明。

另外, export 导出的软件包还包括使用 pip 安装的软件包(部分包 conda 无法安装,只能使用 pip 安装)。

3.2.1.在本地开发主机导出environment.yml文件

(conda-env) $ conda env export --file environment_<platform>.yml

# 推荐导出no-builds版本(Remove build specification规范 from dependencies):
(conda-env) $ conda env export --no-builds --file environment_<platform>.yml

3.2.2 在服务器(包含environment.yml的目录中)创建condo环境

$ conda env create --file environment_<platform>.yml

注意: 报错如下时,再次执行 $ conda env create --file environment_<platform>.yml 会报错 Segmentation fault (core dumped),此时等 5 分钟左右再次执行 $ conda env create --file environment_<platform>.yml 就可以把没安装成功的 Python 包安装好。

("read error: Error([('SSL routines', 'ssl3_read_n', 'unexpected eof while reading')])",)


3.3 附:创建环境 ResolvePackageNotFound 问题

参考:https://stackoverflow.com/questions/49154899/resolvepackagenotfound-create-env-using-conda-and-yml-file-on-macos

注意:


# 使用本地开发主机(这里是macOS)导出的environment.yml,在服务器上创建conda环境的实例
fatli@fatli-vm-ubuntu:~/github/mysite$ conda env create --file environment-from-macos.yml
Collecting package metadata (repodata.json): done
Solving environment: failed

ResolvePackageNotFound:
  - xz==5.2.4=h1de35cc_1001
  - openssl==1.1.1c=h01d97ff_0
  - bzip2==1.0.8=h01d97ff_1
  - python==3.7.3=h93065d6_1
  # ...

建议建立 2 个不同平台的 yml 文件:

# 服务器(这里是Ubuntu)environment-for-ubuntu.yml文件实例
# 此文件在macOS上导出,在Ubuntu上创建环境后,创建的环境默认会安装到miniconda3/envs的目录下

# 如果报ResolvePackageNotFound,请把该部分的包放在 `- pip:` 下
# 安装后使用conda list也可以查看到在 `- pip:` 下的包

# conda环境名称,可修改
name: py37_dj_env
# 自动使用conda-forge源进行安装(即使未配置conda-forge源:conda config --add channels conda-forge)
channels:
  - conda-forge
  - defaults
dependencies:
  # ...
  - django=2.2=py37_0
  # ...
  - pip:
    - xz==5.2.4=h1de35cc_1001
    - openssl==1.1.1c=h01d97ff_0
    - bzip2==1.0.8=h01d97ff_1
    - python==3.7.3=h93065d6_1
    # ...
prefix: /anaconda3/envs/py37_dj_env

修改 yml 文件后,在服务器再次执行 $ conda env create --file environment-for-ubuntu.yml 即可安装全部环境依赖包(dependencies),并成功创建好 conda 环境。


注意: 激活虚拟环境(否则使用conda安装在虚拟环境的包无法找到,后续部分命令无法被执行,影响项目部署):

fatli@fatli-vm-ubuntu:~/github/mysite$ conda info --envs
# conda environments:
#
base                  *  /home/fatli/miniconda3
py37_dj_env              /home/fatli/miniconda3/envs/py37_dj_env

fatli@fatli-vm-ubuntu:~/github/mysite$ conda activate py37_dj_env
(py37_dj_env) fatli@fatli-vm-ubuntu:~/github/mysite$



附:Django 项目配置 MySQL - pymysql 版本报错解决方案



四、安装并配置uWSGI

参考:

.ini 文件配置:

WSGI(Web Server Gateway Interface)规范,WSGI 规定了Python Web 应用和 Python Web 服务器之间的通讯方式。Django 的主要部署平台是 WSGI,它是 Web 服务器和 Web 应用的 Python 标准。

Django 的管理命令 $ django-admin startproject project_name 创建项目的同时,生成了一个简单的默认 WSGI 配置( wsgi.py )。

uWSGI是WSGI实现。uWSGI 是一个快速的,自我驱动的,对开发者和系统管理员友好的应用容器服务器(对比Apache Tomcat,在接受和转发的容器意义上,两者很相似),完全由 C 编写。uWSGI 是实现了 WSGI 的工具,可以用 uWSGI 托管(整合/集成) Django。除此之外,还可以使用 Gunicorn 托管 Django ,或使用 Apache 和 mod_wsgi 托管 Django 。

uWSGI 以客户端-服务端模式运行。Web 服务器(例如 Nginx,Apache)与 django-uwsgi "worker" 进程进行通信(交互)以提供动态内容。

4.1 安装uWSGI

# You can use pip to install uWSGI (it will build a binary with python support).
# Install the latest stable release:
$ pip install uwsgi
# ... or if you want to install the latest LTS (long term support) release,
$ pip install https://projects.unbit.it/downloads/uwsgi-lts.tar.gz

# 查看官网uwsgi-docs.readthedocs.io/en/latest/Download.html安装最新Stable/LTS版,此时是2.0.18
$ pip install https://projects.unbit.it/downloads/uwsgi-2.0.18.tar.gz

# Remember that you will need to have Python development packages installed. In the case of Debian, or Debian-derived systems such as Ubuntu, what you need to have installed is pythonX.Y-dev, where X.Y is your version of Python.

# ------------分割线------------

# 使用conda安装uWSGI,查看anaconda.org/conda-forge/uwsgi,此时uWSGI版本为2.0.18,与官网最新版一致
# 此处使用该安装方式(conda会顺便安装好uWSGI的依赖包)
$ conda install -c conda-forge uwsgi
# 安装完毕后,尝试运行一下uwsgi,输出类似以下信息则证明安装成功了。
$ uwsgi
*** Starting uWSGI 2.0.18 (64bit) on [Wed Nov 27 20:56:47 2019] ***
compiled with version: 7.3.0 on 22 July 2019 16:51:41
os: Linux-4.4.0-21-generic #37-Ubuntu SMP Mon Apr 18 18:33:37 UTC 2016
nodename: fatli-vm-ubuntu
machine: x86_64
clock source: unix
pcre jit disabled
detected number of CPU cores: 1
current working directory: /home/fatli/github/polls
detected binary path: /home/fatli/miniconda3/envs/py37_dj_env/bin/uwsgi
*** WARNING: you are running uWSGI without its master process manager ***
your processes number limit is 3737
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.


4.2 配置并启动用于托管Django的uWSGI服务器

作用类似于本地开发时使用 $ python manage.py runserver 命令启动服务器。

4.2.1 直接使用uwsgi命令运行项目

安装完之后,可以用 uwsgi 来测试网站是否能成功运行:

# 实例
$ uwsgi --http :8001 --chdir /home/fatli/github/mysite --module mysite.wsgi:application
the web client <-> uWSGI <-> Django

此时,在浏览器地址中输入 http://服务器IP地址:8001 。这里是: http://服务器IP地址:8001/pollshttp://服务器IP地址:8001/admin (对比访问原来本地开发环境的 8000 端口地址: http://localhost:8000/polls ),可以看到一个没有 css 样式的页面(页面内容和 http://localhost:8000/polls 相同)。

之所以看不到样式,是因为 uWSGI 只能提供动态连接服务,访问静态文件需要安装配置 Nginx 这类 Web 服务器。

注意:进行上述服务器访问,需要先将服务器 IP 地址配置到项目的 settings.pyALLOWED_HOSTS = [] 中。如果服务器有可视化界面(例如此处的 Ubuntu 桌面版),可以在服务器浏览器直接访问 http://localhost:8001/pollshttp://localhost:8001/admin


4.2.2 使用.ini 配置文件运行项目

注意:以下配置因为涉及到 web server(Nginx)通过 socket 与 uWSGI 通讯,所以需要先配置 Nginx ,请跳到“五、安装并配置Nginx”。

the web client <-> the web server <-> the socket <-> uWSGI <-> Django
# 运行socket可能失败
$ uwsgi --socket :8001 --chdir /home/fatli/github/mysite --module mysite.wsgi:application
# 运行socket成功。chmod-socket是设置套接字权限,以允许nginx使用它
# $ uwsgi --socket 0.0.0.0:8001 --chdir /home/fatli/github/mysite --module mysite.wsgi:application --chmod-socket=664

运行上面的 uwsgi 命令太麻烦了,还容易造成错误输入,可以将上面的命令,做成一个用于保存 uwsgi 配置选项的 yourfile.ini 配置文件。然后,只需要执行以下命令即可:

$ uwsgi --ini yourfile.ini

注意:该 .ini 文件不一定要放置在项目目录中。因为服务器使用的代码同步方式是:在服务器上 $ git pull 拉取 GitHub 上的代码。所以请不要把 .ini 文件放置在项目目录中,避免代码同步时与远程 GitHub 仓库代码冲突导致同步失败。

建议在项目的同级目录中,新建一个 djangoProject_uwsgi 目录(这里是 mysite_uwsgi ),把 .ini 文件(这里是 mysite.ini )还有后续生成的一些日志文件等放置在该目录中:

# 实例
(py37_dj_env) fatli@fatli-vm-ubuntu:~/github$ ls
mysite
(py37_dj_env) fatli@fatli-vm-ubuntu:~/github$ mkdir mysite_uwsgi
fatli@fatli-vm-ubuntu:~/github$ ls
mysite  mysite_uwsgi
(py37_dj_env) fatli@fatli-vm-ubuntu:~/github$ cd mysite_uwsgi
(py37_dj_env) fatli@fatli-vm-ubuntu:~/github/mysite_uwsgi$ vim mysite.ini
(py37_dj_env) fatli@fatli-vm-ubuntu:~/github/mysite_uwsgi$ ls
mysite.ini

uWSGI 配置文件 mysite.ini 实例:

.ini 文件配置:

文件中每个选项的作用:

[uwsgi]
# 项目根目录
chdir = /home/fatli/github/mysite

# 要使用的WSGI模块
module = mysite.wsgi:application

# the virtualenv (full path)
# 不在此处指定虚拟环境,而在命令行直接激活虚拟环境 $ conda activate
# home=/home/ubuntu/miniconda3/envs/lottery_env # 无效。

# 以主进程模式运行。 a master process (will respawn your processes when they die)
# uWSGI’s built-in prefork+threading multi-worker management mode, activated by flicking the master
# switch on. For all practical serving deployments it is generally a good idea to use master mode.
master = true

# 在失去权限前,创建pidfile文件,将pid写到指定的pidfile文件中。
# 通过pid该文件可以控制uwsgi的重启和停止。
# 实例,重启uwsgi:uwsgi --reload /home/ubuntu/github/project_lottery_uwsgi/project_lottery.pid
pidfile = /home/fatli/github/mysite_uwsgi/mysite.pid

# 退出、重启uwsgi是否清理(环境临时生成的)文件,包含pid、sock和status文件
vacuum = true

# 为每个工作进程(processes/workers)设置请求数的上限。达到这个值,该工作进程就会被回收重用(重启)。防止内存泄漏
# 你可以使用这个选项来默默地对抗内存泄漏(尽管这类情况使用reload-on-as和reload-on-rss选项更有用)。
# max-requests = 5000
max-requests = 1000

# daemonize:守护。使进程在后台运行,并将日志打印到指定的日志文件
daemonize = /home/fatli/github/mysite_uwsgi/mysite.log

# 如果上传的文件名包含非 ASCII 字符时,可能抛出 UnicodeEncodeError,进行修复:
# env = LANG=en_US.UTF-8

# ===========================================================================
# 以上配置参考:https://docs.djangoproject.com/zh-hans/2.2/howto/deployment/wsgi/uwsgi/
# 以下配置参考:https://uwsgi-docs.readthedocs.io/en/latest/WSGIquickstart.html#deploying-django
# https://uwsgi-docs.readthedocs.io/en/latest/tutorials/Django_and_nginx.html
# ===========================================================================

# maximum number of worker processes
# processes = workers, spawn the specified number of workers/processes. 能实现简单并且安全的并发能力,
# 简单理解:processes应对CPU-bound(计算密集型)场景;threads应对I/O bound场景。注意:进程数如果设置太多,有可能导致系统崩溃。
# 简单理解:uwsgi设置master=true后,processes=?是子进程,uwsgi可以Respawn子进程。uwsgi创建n个子进程,就有n个解释器实例在工作。
# There is no magic rule for setting the number of processes or threads to use. It is very much application and system
# dependent. Simple math like `processes = 2 * cpucores` will not be enough. 可以使用 uwsgitop (pip install)工具进行性能监控测试。
# processes = 10
processes = 4
# spawn 4 processes, each with 2 threads:
# threads = 2 # 不设置固定线程数。下面配置了 enable-threads = true,会自动产生线程。

# bind to the specified(指定) UNIX/TCP socket(套接字) using default protocol(协议,这里指的是uwsgi协议).
# 最多可以同时指定8个socket选项。
# socket = 127.0.0.1:8001
# socket = 0.0.0.0:8001
# A socket(套接字) consists of three things: A transport protocol, An IP address, A port number .
# e.g., (TCP , 10.1.1.2 , port 1030) is a socket. 1030 is a port.
# UNIX Socket 是一种进程间的通信机制,它允许在同一机器上进行进程之间的双向数据交换。
# TCP/IP Socket 是一种允许进程通过网络进行通信的机制(一般用于多台服务器之间的进程通信,也可用于同一服务器的不同进程间的通信)。
# 在同一机器内进行进程间的通信,使用 UNIX Socket 会比 TCP/IP Socket 更高效,因为前者可以避免进行某些检查和操作(like routing)。
# 此时 Nginx 配置的 server 也需更改为此 UNIX Socket file 。
socket = /home/fatli/github/mysite_uwsgi/mysite.sock

# 设置套接字权限,以允许nginx使用它,may be needed(根据文件对应的用户的权限判断)
# chmod-socket = 664

# others you ought to look at for a deployment in production include :
# set an environment variable(应该是Django<1.4才使用)
# env = DJANGO_SETTINGS_MODULE=project_lottery.settings
# 请求超时,这个请求会被丢弃,并且当前处理这个请求的工作进程会被回收再利用(即重启),避免阻塞:
harakiri = 30 # respawn processes taking more than 30 seconds
# 通过使用POSIX/UNIX的setrlimit()函数来限制每个uWSGI进程的虚拟内存使用数,
# 如果当前进程的虚拟内存已经达到128M,并继续申请虚拟内存则会使程序报内存错误,本次的http请求将返回500错误:
# limit-as = 128 # limit processes address space/vsz(虚拟内存)

# ===========================================================================
# 以下为参考其他配置方案,自定义的设置
# ===========================================================================

# 不记录请求信息的日志。只记录错误以及uWSGI内部消息到日志中,避免日志很快就爆满
disable-logging = true
# Disable built-in logging, but log 4xx's anyway, and 5xx's
log-4xx = true
log-5xx = true              

# 因为项目中有 app (lottery)使用了 APScheduler 定时调度任务(使用了线程),访问报错:
# The scheduler seems to be running under uWSGI, but threads have been disabled.
# You must run uWSGI with the --enable-threads option for the scheduler to work.
# 解决方案:启用(多)线程。参考:
# https://uwsgi-docs.readthedocs.io/en/latest/WSGIquickstart.html#a-note-on-python-threads
# 中文文档:https://uwsgi-docs-cn.readthedocs.io/zh_CN/latest/WSGIquickstart.html#python
# https://uwsgi-docs.readthedocs.io/en/latest/ThingsToKnow.html
# https://apscheduler.readthedocs.io/en/latest/faq.html#how-can-i-use-apscheduler-with-uwsgi
# Python 多线程与 GIL:https://github.com/FatliTalk/blog/issues/96
enable-threads = true

管理 uWSGI server:

# Managing the uWSGI server:
# https://uwsgi-docs.readthedocs.io/en/latest/Management.html
$ uwsgi --ini /home/fatli/github/mysite_uwsgi/mysite.ini     # 启动
[uWSGI] getting INI configuration from ../mysite_uwsgi/mysite.ini

$ ps -aux | grep uwsgi           # 查看uwsgi进程状态(Process Status)
# a: Show processes for all users
# u: Display the user who is using the process
# x: Show all processes. (Without this, ps won’t show processes running in a GUI environment.)
$ sudo lsof -i :8001  # 查看8001端口被哪个进程使用中。如果不显示内容,则未被使用

$ uwsgi --reload /home/fatli/github/mysite_uwsgi/mysite.pid  # 重启

$ uwsgi --stop /home/fatli/github/mysite_uwsgi/mysite.pid    # 关闭
$ kill -INT `cat /home/fatli/github/mysite_uwsgi/mysite.pid` # 关闭

查看 uWSGI (报错)日志路径:

/home/fatli/github/mysite_uwsgi/mysite.log



五、安装并配置Nginx

Nginx 是一个Web服务器,是一个反向代理工具,我们通常用它来部署静态文件(需要与数据库进行交互的动态内容由 Django 处理,图片、css、js 等静态内容则交由 Nginx 处理)。

uWSGI 通过 WSGI 规范和我们编写的服务进程通信,然后通过自带的高效的 uwsgi 协议和 Nginx 进行通信,最终 Nginx 通过 HTTP 协议将服务对外透出。

5.1 安装并运行Nginx

参考:

the web client <-> the web server
# Ubuntu官方列表的Nginx版本不一定是最新的。所以请添加Nginx安装列表:
# 这一步不是必须的,但建议执行,不然安装的Nginx版本会挺旧的。
$ sudo add-apt-repository ppa:nginx/stable
# 更新列表
$ sudo apt-get update
$ sudo apt-get install nginx
$ nginx -v       # 查看Nginx版本号
$ dpkg -L nginx  # 查看Nginx安装位置,输出/usr/share/doc/nginx。其他文件在/etc/nginx
$ dpkg --list    # 查看已安装的包
$ sudo apt-get remove nginx nginx-common  # 删除Nginx(保留配置文件)
$ sudo apt-get purge nginx nginx-common   # 删除Nginx(且不保留配置文件)
$ sudo apt-get autoremove                 # 删除依赖(dependencies)
$ service nginx status   # 查看Nginx状态,默认在运行ing(按q返回命令行)
# 此时访问服务器的IP地址即可看到"Welcome to nginx!"的页面
# the web client <-> the web server
# Nginx默认使用80端口,请确保80端口未被其他程序占用,使用 `sudo lsof -i:80` 查看
$ sudo /etc/init.d/nginx start   # start nginx
$ sudo /etc/init.d/nginx restart # Restart nginx
$ service nginx start    # Nginx启动命令
$ service nginx stop     # Nginx停止命令
$ service nginx restart  # Nginx重启命令



5.2 配置Nginx

参考:

the web client <-> the web server <-> the socket <-> uwsgi <-> Django

/etc/nginx/sites-available 目录中创建一个名为 mysite_nginx.conf 的文件,并写入以下内容(这个conf文件告诉nginx从文件系统提供媒体和静态文件,以及处理需要Django介入的请求):

# mysite_nginx.conf

# the upstream component nginx needs to connect to
upstream django {
    # 此处的 “django” 可以随意命名为其他名字,例如命名为 “django_02”,下面的配置也统一使用  “django_02” 即可,
    # 注意:如果有多个工程项目,此处的命名不能和其他的项目的配置命名一样,否则:
    # $ sudo nginx -t nginx 报错:[emerg] duplicate upstream "django" in /etc/nginx/sites-enabled/other.conf:4
    # server unix:///path/to/your/mysite/mysite.sock; # for a file socket
    # server 127.0.0.1:8001; # for a web port socket (we'll use this first)
    # 在同一机器内进行进程间的通信,使用 UNIX Socket 会比 TCP/IP Socket 更高效,因为前者可以避免进行某些检查和操作(like routing)。
    server unix:///home/fatli/github/mysite_uwsgi/mysite.sock;
}

# configuration of the server
server {
    # the port your site will be served on
    # nginx meanwhile has been configured to communicate with uWSGI on that port(8001), and with the outside world on port 8000.
    # 指定端口,nginx默认端口为80。使用80端口可以通过IP/域名直接访问,不需要在IP/域名后追加 `:<your_port>` 进行访问。
    listen 8000; # listen 80; # 正式上线后请使用 80 端口
    # the domain name it will serve for
    # server_name example.com;  # substitute your machine's IP address or FQDN
    # 因为在mysite/setting.py中的ALLOWED_HOST设置了这两个ip,所以设置任意一个,局域网内都可以访问到项目:
    server_name 127.0.0.1; # 局域网内服务器IP:192.168.31.126 # 正式上线后请使用域名如example.com
    charset utf-8;

    # max upload size
    client_max_body_size 75M;  # adjust to taste

    # Django media
    location /media  {
        # alias /path/to/your/mysite/media;    # your Django project's media files - amend as required
        alias /home/fatli/github/mysite/media; # 存放图片等媒体文件,对外使用Nginx析出。
    }

    location /static {
        # alias /path/to/your/mysite/static; # your Django project's static files - amend as required
        # alias /home/fatli/github/mysite/polls/static; # 此时,访问http://ip:8000/admin页面将不显示任何静态文件,例如CSS样式、图片等。因为此时的通信路径是:web client-web server(Nginx)-the socket-uwsgi-Django,静态文件由Nginx对外提供。
        alias /home/fatli/github/mysite/static; # 需要收集所有静态文件到static目录,才能如此配置。
    }

    # Finally, send all non-media requests to the Django server.动态文件交给uwsgi处理
    location / {
        # uwsgi_pass unix:///tmp/uwsgi.sock; # 设置uWSGI生成的Unix套接字文件的路径
        # uwsgi_pass 127.0.0.1:8001; # 或者使用TCP端口套接字
        uwsgi_pass django; # 复用upstream django的配置即可
        # include /path/to/your/mysite/uwsgi_params; # the uwsgi_params file you installed
        # uwsgi_params文件在/etc/nginx/目录中。如有需要,可以复制到项目路径下(此处不复制),避免产生读取权限问题。该文件也可从github.com/nginx/nginx/blob/master/conf/uwsgi_params下载。
        include /etc/nginx/uwsgi_params; # 此时权限为root用户
    }
}

创建软链接:Symlink to this file from /etc/nginx/sites-enabled so nginx can see it :

注意:请删除 /etc/nginx/sites-enabled 原有的软链接 default

$ sudo ln -s /etc/nginx/sites-available/mysite_nginx.conf /etc/nginx/sites-enabled/

测试 Nginx 配置文件是否正确:

$ nginx -t
$ sudo nginx -t

查看 Nginx 报错日志路径:

/var/log/nginx/error.log

nginx.conf 配置文件 /etc/nginx/nginx.conf 用户名默认为 www-data 不用改:

# uWSGI官方文档:add your user to nginx’s group (www-data), or vice-versa, so that nginx can read and write to your socket properly.
# 某博客:将启动nginx的用户改为root,要不然会出现403 forbidden的错误
# user www-data; ==> # 运行$ nginx -t:the "user" directive(指示) makes sense only if the master process runs with super-user privileges, ignored in /etc/nginx/nginx.conf
# www-data是debian中默认的nginx用户,其他发行版使用不同的用户(例如:http或nginx)。
user www-data; # 运行$ ps faux | grep nginx: root - nginx: master process ; www-data - nginx: worker process



5.3 管理静态文件

参考:

此时,访问 http://ip:8000/admin 进行测试时,页面将不显示任何静态文件,例如CSS样式、图片等。

因为此时的通信路径是:the web client <-> the web server <-> the socket <-> uwsgi <-> Django ,静态文件由 web server(这里是 Nginx )对外提供。

在运行 Nginx 对外提供服务之前,必须将所有 Django 静态文件收集到静态文件夹中。

开发时,因为默认使用了 INSTALLED_APPS = [django.contrib.staticfiles] 管理静态文件的框架,这会在 DEBUG = True 情况下由 `django-admin runserver [addrport] 自动完成静态文件管理。

该方法极度低效不怎么安全,所以这不适合生产环境

收集到静态文件:mysite/settings.py 设置静态文件(这里包括 polls APP 的静态文件、Django admin管理后台的静态文件)存放路径:

STATIC_ROOT = os.path.join(BASE_DIR, "static/")
# 然后运行 $ python manage.py collectstatic 收集所有静态文件到 static 目录中。
# 收集静态文件的时间如果过长,10分钟后还在收集ing,可中断,因为实际上已收集完成。

此时,重启 Nginx 服务器,访问 http://ip:8000/admin 即可访问到静态文件。如有其他修改,例如设置了 mysite/setting.py 中的 DEBUG = False 或修改了 ALLOWED_HOST ,需要也重启一下 uWSGI 后才能生效。

不再需要通过执行 $ python manage.py runserver 0.0.0.0:<your port> 的命令形式运行项目了。

附:部署成功后常用命令:

# 本地上传代码到 GitHub,服务器端拉取-合并:
$ git fetch
$ git merge

# 重启 uwsgi (需要先 cd 到项目路径下,激活虚拟环境 conda activate xxx_env 。这里请使用你自己具体的进程ID文件路径):
$ uwsgi --reload /home/fatli/github/mysite_uwsgi/mysite.pid
# uwsgi --ini /home/fatli/github/mysite_uwsgi/mysite.ini # 启动uwsgi

# 重启 nginx :
$ sudo /etc/init.d/nginx restart
# sudo /etc/init.d/nginx start # 启动nginx

# 测试 nginx :
$ sudo nginx -t