如何在Debian8上使用uWSGI和Nginx服务Django应用程序

来自菜鸟教程
跳转至:导航、​搜索

介绍

Django 是一个强大的 Web 框架,可以帮助您启动 Python 应用程序或网站。 Django 包含一个简化的开发服务器,用于在本地测试您的代码,但对于任何与生产相关的事情,都需要一个更安全、更强大的 Web 服务器。

在本指南中,我们将演示如何在 Debian 8 上安装和配置一些组件以支持和服务 Django 应用程序。 我们将配置 uWSGI 应用程序容器服务器以与我们的应用程序交互。 然后,我们将设置 Nginx 以反向代理到 uWSGI,使我们能够访问其安全性和性能特性来为我们的应用程序提供服务。

先决条件和目标

为了完成本指南,您应该拥有一个全新的 Debian 8 服务器实例,该实例具有配置了 sudo 权限的非 root 用户。 您可以通过我们的 初始服务器设置指南 来了解如何设置它。

我们将在两个不同的虚拟环境中安装 Django。 这将允许单独处理您的项目及其要求。 我们将创建两个示例项目,以便我们可以在多项目环境中运行这些步骤。

一旦我们有了我们的应用程序,我们将安装和配置 uWSGI 应用程序服务器。 这将作为我们应用程序的接口,它将使用 HTTP 的客户端请求转换为我们的应用程序可以处理的 Python 调用。 然后,我们将在 uWSGI 前面设置 Nginx,以利用其高性能的连接处理机制和易于实现的安全特性。

让我们开始吧。

安装和配置 VirtualEnv 和 VirtualEnvWrapper

我们将在他们自己的虚拟环境中安装我们的 Django 项目,以隔离每个项目的需求。 为此,我们将安装 virtualenv,它可以创建 Python 虚拟环境,以及 virtualenvwrapper,它为 virtualenv 工作流程增加了一些可用性改进。

我们将使用 Python 包管理器 pip 安装这两个组件。 我们可以从 Debian 存储库安装此实用程序。

如果您使用 Python 2 构建 Django 项目,请输入:

sudo apt-get update
sudo apt-get install python-pip

如果您使用的是 Python 3,请键入:

sudo apt-get update
sudo apt-get install python3-pip

现在您已经安装了 pip,我们可以全局安装 virtualenvvirtualenvwrapper

如果您使用 Python 2,请键入:

sudo pip install virtualenv virtualenvwrapper

如果您使用的是 Python 3,请键入:

sudo pip3 install virtualenv virtualenvwrapper

安装这些组件后,我们现在可以使用 virtualenvwrapper 脚本所需的信息来配置我们的 shell。 我们的虚拟环境都将放置在我们的主文件夹中名为 Env 的目录中,以便于访问。 这是通过一个名为 WORKON_HOME 的环境变量来配置的。 我们可以将它添加到我们的 shell 初始化脚本中,并且可以获取虚拟环境包装器脚本。

如果您使用 Python 3pip3 命令,您还必须在 shell 初始化脚本中添加一行:

echo "export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3" >> ~/.bashrc

无论您使用的是哪个版本的 Python,您都需要运行以下命令:

echo "export WORKON_HOME=~/Env" >> ~/.bashrc
echo "source /usr/local/bin/virtualenvwrapper.sh" >> ~/.bashrc

现在,获取您的 shell 初始化脚本,以便您可以在当前会话中使用此功能:

source ~/.bashrc

您现在应该在您的主文件夹中有一个名为 Env 的目录,该目录将保存虚拟环境信息。

创建 Django 项目

现在我们有了虚拟环境工具,我们将创建两个虚拟环境,在每个环境中安装 Django,并启动两个项目。

创建第一个项目

我们可以使用 virtualenvwrapper 脚本提供给我们的一些命令轻松创建虚拟环境。

通过键入以下内容,使用您的第一个站点或项目的名称创建您的第一个虚拟环境:

mkvirtualenv firstsite

这将创建一个虚拟环境,在其中安装 Python 和 pip,并激活环境。 您的提示将更改以指示您现在正在新的虚拟环境中操作。 它看起来像这样:(firstsite)user@hostname:~$。 括号中的值是您的虚拟环境的名称。 通过 pip 安装的任何软件现在都将安装到虚拟环境中,而不是安装在全局系统中。 这允许我们在每个项目的基础上隔离我们的包。

我们的第一步是安装 Django 本身。 我们可以在没有 sudo 的情况下使用 pip,因为我们是在虚拟环境中本地安装它:

pip install django

安装 Django 后,我们可以通过键入以下内容创建我们的第一个示例项目:

cd ~
django-admin.py startproject firstsite

这将在您的主目录中创建一个名为 firstsite 的目录。 其中有一个用于处理项目各个方面的管理脚本和另一个用于存放实际项目代码的同名目录。

移动到第一级目录,以便我们可以开始设置示例项目的最低要求。

cd ~/firstsite

首先迁移数据库以初始化我们项目将使用的 SQLite 数据库。 如果您愿意,您可以为您的应用程序设置一个备用数据库,但这超出了本指南的范围:

./manage.py migrate

您现在应该在项目目录中有一个名为 db.sqlite3 的数据库文件。 现在,我们可以通过键入以下内容来创建管理用户:

./manage.py createsuperuser

您必须选择用户名,提供联系电子邮件地址,然后选择并确认密码。

接下来,使用文本编辑器打开项目的设置文件:

nano ~/firstsite/firstsite/settings.py

首先找到 ALLOWED_HOSTS 指令。 这定义了可用于连接到 Django 实例的地址或域名的白名单。 任何带有不在此列表中的 Host 标头的传入请求都将引发异常。 Django 要求您设置它以防止某一类安全漏洞。

在方括号中,列出与您的 Django 服务器关联的 IP 地址或域名。 每个项目都应列在引号中,条目之间用逗号分隔。 如果您希望请求整个域和任何子域,请在条目的开头添加句点。 在下面的代码片段中,有一些注释掉的示例用于演示:

~/myproject/myproject/settings.py

. . .
# The simplest case: just add the domain name(s) and IP addresses of your Django server
# ALLOWED_HOSTS = [ 'example.com', '203.0.113.5']
# To respond to 'example.com' and any subdomains, start the domain with a dot
# ALLOWED_HOSTS = ['.example.com', '203.0.113.5']
ALLOWED_HOSTS = ['your_server_domain_or_IP', 'second_domain_or_IP', . . .]

由于我们将设置 Nginx 来为我们的网站提供服务,我们需要配置一个目录来保存我们网站的静态资产。 这将允许 Nginx 直接为这些服务,这将对性能产生积极影响。 我们将告诉 Django 将它们放入项目基目录中名为 static 的目录中。 将此行添加到文件底部以配置此行为:

~/firstsite/firstsite/settings.py

. . .
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')

完成后保存并关闭文件。 现在,收集我们网站的静态元素并将它们放在该目录中,输入:

./manage.py collectstatic

您可以键入“是”以确认操作并收集静态内容。 在您的项目目录中将有一个名为 static 的新目录。

您可能需要调整防火墙设置以允许流量到我们的 Django 开发服务器,我们将在端口 8080 上运行该服务器。

如果您正在运行 ufw 防火墙,您可以通过键入以下内容来允许到端口 8080 的流量:

sudo ufw allow 8080

如果您正在运行 iptables,您需要的确切命令取决于您当前的防火墙配置。 对于 大多数配置 ,此命令应该可以工作:

sudo iptables -I INPUT -p tcp --dport 8080 -j ACCEPT

完成所有这些后,我们可以通过临时启动开发服务器来测试我们的项目。 要启动开发服务器,请键入:

./manage.py runserver 0.0.0.0:8080

这将在端口 8080 上启动开发服务器。 在浏览器中访问您服务器的域名或 IP 地址,后跟 8080

http://server_domain_or_IP:8080

您应该会看到如下所示的页面:

/admin 添加到浏览器地址栏中 URL 的末尾,您将被带到管理员登录页面:

使用您通过 createsuperuser 命令选择的管理登录凭据,登录到服务器。 然后,您将可以访问管理界面:

测试此功能后,在终端中键入 CTRL-C 停止开发服务器。 我们现在可以继续我们的第二个项目。

创建第二个项目

第二个项目的创建方式与第一个项目完全相同。 我们将在本节中删减解释,看看您是如何完成这一次的。

回到您的主目录并为您的新项目创建第二个虚拟环境。 激活后在这个新环境中安装 Django:

cd ~
mkvirtualenv secondsite
pip install django

将创建新环境 并将 更改为,保留您以前的虚拟环境。 这个 Django 实例与您配置的另一个实例完全分开。 这使您可以独立管理它们并根据需要进行自定义。

创建第二个项目并进入项目目录:

django-admin.py startproject secondsite
cd ~/secondsite

初始化数据库并创建一个管理用户:

./manage.py migrate
./manage.py createsuperuser

打开设置文件:

nano ~/secondsite/secondsite/settings.py

ALLOWED_HOSTS 设置为您的第二个项目的域、服务器的 IP 地址或两者,就像您对第一个项目所做的那样:

ALLOWED_HOSTS = ['second_project_domain_or_IP', 'another_domain_or_IP', . . .]

添加静态文件的位置,就像您在上一个项目中所做的那样:

~/secondsite/secondsite/settings.py

. . .
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')

保存并关闭文件。 现在,通过键入以下命令将静态元素收集到该目录中:

./manage.py collectstatic

最后,启动开发服务器来测试站点:

./manage.py runserver 0.0.0.0:8080

您应该在以下网址查看常规网站:

http://server_domain_or_IP:8080

同时登录管理站点:

http://server_domain_or_IP:8080/admin

当您确认一切都按预期工作时,在您的终端中键入 CTRL-C 以停止开发服务器。

退出虚拟环境

由于我们现在已经完成了指南的 Django 部分,我们可以停用我们的第二个虚拟环境:

deactivate

如果您需要再次在任一 Django 站点上工作,您应该重新激活它们各自的环境。 您可以使用 workon 命令来做到这一点:

workon firstsite

或者:

workon secondsite

同样,在您完成网站工作后停用:

deactivate

设置 uWSGI 应用服务器

现在我们已经设置了两个 Django 项目并准备就绪,我们可以配置 uWSGI。 uWSGI 是一个应用服务器,可以通过称为 WSGI 的标准接口与应用程序进行通信。 要了解更多信息,请阅读我们在 Ubuntu 14.04 上设置 uWSGI 和 Nginx 指南的 本节

安装 uWSGI

与上面链接的指南不同,在本教程中,我们将全局安装 uWSGI。 这将在处理多个 Django 项目时减少摩擦。 在我们安装 uWSGI 之前,我们需要软件所依赖的 Python 开发文件。 我们可以直接从 Debian 的存储库中安装它。

如果您一直在使用带有 Python 2 的 Django,请输入:

sudo apt-get install python-dev

如果您一直在使用 Python 3,请输入:

sudo apt-get install python3-dev

现在开发文件已经可用,我们可以通过pip全局安装uWSGI。

如果您使用 Python 2,请键入:

sudo pip install uwsgi

如果您使用的是 Python 3,请键入:

sudo pip3 install uwsgi

我们可以通过将我们的站点之一的信息传递给它来快速测试这个应用程序服务器。 例如,我们可以通过键入以下命令告诉它为我们的第一个项目服务:

uwsgi --http :8080 --home /home/sammy/Env/firstsite --chdir /home/sammy/firstsite -w firstsite.wsgi

在这里,我们告诉 uWSGI 使用位于我们的 ~/Env 目录中的虚拟环境,切换到我们的项目目录,并使用存储在我们内部 [X186X 中的 wsgi.py 文件] 目录来提供文件。 对于我们的演示,我们告诉它在端口 8080 上提供 HTTP。 如果您在浏览器中访问服务器的域名或IP地址,然后输入:8080,您将再次看到您的站点(/admin界面中的静态元素,如CSS,将不起作用然而)。 完成此功能的测试后,在终端中键入 CTRL-C。

创建配置文件

从命令行运行 uWSGI 对于测试很有用,但对于实际部署并不是特别有用。 相反,我们将在“Emperor 模式”下运行 uWSGI,它允许主进程在给定一组配置文件的情况下自动管理单独的应用程序。

创建一个存放配置文件的目录。 由于这是一个全局进程,我们将创建一个名为 /etc/uwsgi/sites 的目录来存储我们的配置文件。 创建后进入目录:

sudo mkdir -p /etc/uwsgi/sites
cd /etc/uwsgi/sites

在这个目录中,我们将放置我们的配置文件。 我们需要为我们服务的每个项目提供一个配置文件。 uWSGI 进程可以采用多种格式的配置文件,但由于其简单性,我们将使用 .ini 文件。

为您的第一个项目创建一个文件并在文本编辑器中打开它:

sudo nano firstsite.ini

在内部,我们必须从 [uwsgi] 节标题开始。 我们所有的信息都将放在此标题下方。 我们还将使用变量来使我们的配置文件更可重用。 在标题之后,使用您的第一个项目的名称设置一个名为 project 的变量。 添加一个名为 uid 的变量,其中包含您的 sudo 用户名。

我们还将添加一个名为 base 的变量,其中包含用户主目录的路径。 这将从我们使用 %(variable_name) 语法设置的用户名构造。 读取配置时,这将被变量的值替换:

/etc/uwsgi/sites/firstsite.ini

[uwsgi]
project = firstsite
uid = sammy
base = /home/%(uid)

接下来,我们需要配置 uWSGI 以便它正确处理我们的项目。 我们需要通过设置 chdir 选项进入项目根目录。 我们可以使用相同的变量语法组合主目录和项目名称。

以类似的方式,我们将为我们的项目指明虚拟环境。 通过设置模块,我们可以准确地指示如何与我们的项目交互(通过从项目目录中的 wsgi.py 文件中导入可调用的“应用程序”)。 这些项目的配置将如下所示:

/etc/uwsgi/sites/firstsite.ini

[uwsgi]
project = firstsite
uid = sammy
base = /home/%(uid)

# Next, add the lines below
chdir = %(base)/%(project)
home = %(base)/Env/%(project)
module = %(project).wsgi:application

我们想创建一个有 5 个工作人员的主进程。 我们可以通过添加这个来做到这一点:

/etc/uwsgi/sites/firstsite.ini

[uwsgi]
project = firstsite
uid = sammy
base = /home/%(uid)

chdir = %(base)/%(project)
home = %(base)/Env/%(project)
module = %(project).wsgi:application

# Next, add these lines
master = true
processes = 5

接下来我们需要指定 uWSGI 应该如何监听连接。 在我们对 uWSGI 的测试中,我们使用了 HTTP 和一个网络端口。 但是,由于我们将使用 Nginx 作为反向代理,因此我们有更好的选择。

我们可以使用 Unix 套接字,而不是使用网络端口,因为所有组件都在单个服务器上运行。 这更安全并提供更好的性能。 这个套接字不会使用 HTTP,而是会实现 uWSGI 的 uwsgi 协议,这是一种用于与其他服务器通信的快速二进制协议。 Nginx 可以使用 uwsgi 协议进行原生代理,所以这是我们最好的选择。

我们还将修改套接字的所有权和权限,因为我们将授予 Web 服务器写入权限。 我们将设置 vacuum 选项,以便在服务停止时自动清理套接字文件:

/etc/uwsgi/sites/firstsite.ini

[uwsgi]
project = firstsite
uid = sammy
base = /home/%(uid)

chdir = %(base)/%(project)
home = %(base)/Env/%(project)
module = %(project).wsgi:application

master = true
processes = 5

# Finish off the configuration with the following lines
socket = /run/uwsgi/%(project).sock
chown-socket = %(uid):www-data
chmod-socket = 660
vacuum = true

至此,我们第一个项目的uWSGI配置就完成了。 保存并关闭文件。

使用变量设置文件的优点是它使重用变得非常简单。 复制您的第一个项目的配置文件以用作您的第二个配置文件的基础:

sudo cp /etc/uwsgi/sites/firstsite.ini /etc/uwsgi/sites/secondsite.ini

使用文本编辑器打开第二个配置文件:

sudo nano /etc/uwsgi/sites/secondsite.ini

我们只需要更改此文件中的单个值即可使其适用于我们的第二个项目。 使用您在第二个项目中使用的名称修改 project 变量:

/etc/uwsgi/sites/secondsite.ini

[uwsgi]
project = secondsite
uid = sammy
base = /home/%(uid)

chdir = %(base)/%(project)
home = %(base)/Env/%(project)
module = %(project).wsgi:application

master = true
processes = 5

socket = /run/uwsgi/%(project).sock
chown-socket = %(uid):www-data
chmod-socket = 660
vacuum = true

完成后保存并关闭文件。 您的第二个项目现在应该可以开始了。

为 uWSGI 创建一个 systemd 单元文件

我们现在有了为 Django 项目提供服务所需的配置文件,但我们还没有自动化这个过程。 接下来,我们将创建一个 systemd 单元文件以在启动时自动启动 uWSGI。

我们将在 /etc/systemd/system 目录中创建单元文件,保存用户创建的单元文件。 我们将调用我们的文件 uwsgi.service

sudo nano /etc/systemd/system/uwsgi.service

[Unit] 部分开始,用于指定元数据和排序信息。 我们将在这里简单地描述我们的服务:

/etc/systemd/system/uwsgi.service

[Unit]
Description=uWSGI Emperor service

接下来,我们将打开 [Service] 部分。 我们将使用 ExecStartPre 指令来设置运行服务器所需的部分。 这将确保创建 /run/uwsgi 目录,并且我们的普通用户拥有它,并且 www-data 组作为组所有者。 带有 -p 标志的 mkdirchown 命令即使不需要它们的操作也会成功返回。 这就是我们想要的。

对于由 ExecStart 指令指定的实际启动命令,我们将指向 uwsgi 可执行文件。 我们将告诉它以“Emperor 模式”运行,允许它使用在 /etc/uwsgi/sites 中找到的文件来管理多个应用程序。 我们还将添加 systemd 正确管理流程所需的部分。 这些取自 uWSGI 文档 这里

/etc/systemd/system/uwsgi.service

[Unit]
Description=uWSGI Emperor service

[Service]
ExecStartPre=/bin/bash -c 'mkdir -p /run/uwsgi; chown sammy:www-data /run/uwsgi'
ExecStart=/usr/local/bin/uwsgi --emperor /etc/uwsgi/sites
Restart=always
KillSignal=SIGQUIT
Type=notify
NotifyAccess=all

现在,我们需要做的就是添加 [Install] 部分。 这允许我们指定何时应该自动启动服务。 我们将把我们的服务绑定到多用户系统状态。 每当系统为多个用户设置(正常运行条件)时,我们的服务将被激活:

/etc/systemd/system/uwsgi.service

[Unit]
Description=uWSGI Emperor service

[Service]
ExecStartPre=/bin/bash -c 'mkdir -p /run/uwsgi; chown sammy:www-data /run/uwsgi'
ExecStart=/usr/local/bin/uwsgi --emperor /etc/uwsgi/sites
Restart=always
KillSignal=SIGQUIT
Type=notify
NotifyAccess=all

[Install]
WantedBy=multi-user.target

完成后,保存并关闭文件。

此时我们将无法成功启动服务,因为它依赖于可用的 www-data 用户。 我们将不得不等到安装 Nginx 之后才能启动 uWSGI 服务。

安装和配置 Nginx 作为反向代理

配置好 uWSGI 并准备就绪后,我们现在可以安装和配置 Nginx 作为我们的反向代理。 这可以从 Debian 的默认存储库下载:

sudo apt-get install nginx

安装 Nginx 后,我们可以继续为每个项目创建一个服务器块配置文件。 通过创建服务器块配置文件从第一个项目开始:

sudo nano /etc/nginx/sites-available/firstsite

在内部,我们可以通过指示我们的第一个项目应该可以访问的端口号和域名来启动我们的服务器块。 我们假设您有一个域名:

/etc/nginx/sites-available/firstsite

server {
    listen 80;
    server_name firstsite.com www.firstsite.com;
}

接下来,如果找不到 favicon,我们可以告诉 Nginx 忽略错误。 我们还将它指向我们收集站点静态元素的静态文件目录的位置:

/etc/nginx/sites-available/firstsite

server {
    listen 80;
    server_name firstsite.com www.firstsite.com;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/sammy/firstsite;
    }
}

接下来我们可以创建一个包罗万象的位置块,它将所有额外的查询直接传递给我们的应用程序。 我们将包含 /etc/nginx/uwsgi_params 中的 uwsgi 参数,并将流量传递到 uWSGI 服务器设置的套接字:

/etc/nginx/sites-available/firstsite

server {
    listen 80;
    server_name firstsite.com www.firstsite.com;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/sammy/firstsite;
    }

    location / {
        include         uwsgi_params;
        uwsgi_pass      unix:/run/uwsgi/firstsite.sock;
    }
}

这样,我们的第一个服务器块就完成了。 您可以保存并退出文件。

我们将以此作为第二个项目的 Nginx 配置文件的基础。 现在复制过来:

sudo cp /etc/nginx/sites-available/firstsite /etc/nginx/sites-available/secondsite

在文本编辑器中打开新文件:

sudo nano /etc/nginx/sites-available/secondsite

在这里,您必须将对 firstsite 的任何引用更改为对 secondsite 的引用。 您还必须修改 server_name 以便您的第二个项目响应不同的域名。 完成后,它将如下所示:

/etc/nginx/sites-available/secondsite

server {
    listen 80;
    server_name secondsite.com www.secondsite.com;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/sammy/secondsite;
    }

    location / {
        include         uwsgi_params;
        uwsgi_pass      unix:/run/uwsgi/secondsite.sock;
    }
}

完成后保存并关闭文件。

接下来,将两个新配置文件链接到 Nginx 的 sites-enabled 目录以启用它们:

sudo ln -s /etc/nginx/sites-available/firstsite /etc/nginx/sites-enabled
sudo ln -s /etc/nginx/sites-available/secondsite /etc/nginx/sites-enabled

通过键入以下内容检查配置语法:

sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

如果未检测到语法错误,您可以重新启动 Nginx 服务以加载新配置:

sudo systemctl restart nginx

如果您还记得之前的内容,我们实际上从未启动过 uWSGI 服务器。 现在通过键入:

sudo systemctl start uwsgi

我们需要再次通过防火墙进行调整。 我们不再需要打开 8080 端口,因为我们通过 Nginx 进行代理,因此我们可以删除该规则。 然后我们可以添加一个例外来允许 Nginx 进程的流量。

如果您使用 ufw,您可以通过键入:

sudo ufw delete allow 8080
sudo ufw allow 'Nginx Full'

如果您使用 iptables,则相应的命令将如下所示:

sudo iptables -D INPUT -p tcp --dport 8080 -j ACCEPT
sudo iptables -I INPUT -p tcp --dport 80 -j ACCEPT

您现在应该能够通过访问它们各自的域名来访问您的两个项目。 公共和管理界面都应该按预期工作。

如果一切顺利,您可以通过键入以下命令启用这两个服务在启动时自动启动:

sudo systemctl enable nginx
sudo systemctl enable uwsgi

笔记

配置 Nginx 后,下一步应该是使用 SSL/TLS 保护到服务器的流量。 这很重要,因为没有它,包括密码在内的所有信息都会以纯文本形式通过网络发送。

如果您有域名,获得 SSL 证书以保护您的流量的最简单方法是使用 Let's Encrypt。 按照 本指南 在 Debian 8 上设置 Let's Encrypt with Nginx。

如果您没有域名,您仍然可以使用 自签名 SSL 证书 保护您的站点以进行测试和学习。


结论

在本指南中,我们设置了两个 Django 项目,每个项目都在自己的虚拟环境中。 我们已经将 uWSGI 配置为使用为每个项目配置的虚拟环境独立地为每个项目提供服务。 之后,我们设置 Nginx 作为反向代理来处理客户端连接并根据客户端请求为正确的项目提供服务。

Django 通过提供许多通用部分使创建项目和应用程序变得简单,让您可以专注于独特的元素。 通过利用本文中描述的通用工具链,您可以轻松地为您从单个服务器创建的应用程序提供服务。