使用gunicorn和nginx部署Django应用程序

85

这是一个广泛的问题,但我想得到一个权威回答。我一直在尝试使用 gunicornnginxDjango 中部署站点。阅读了大量教程后,我已经成功了,但我无法确定我遵循的步骤足够好,可以没有问题地运行站点,或者有更好的方法来完成它。这种不确定性很烦人。

这就是为什么我正在寻找一个非常详细和清晰的新手指南的原因。我不想过多地解释我知道什么和我不知道什么,因为这可能会使答案有所偏差,其他人可能会从你的回答中受益较少。但是,我希望看到提到一些内容:

  • 你见过哪些“设置”效果最好?我使用了 virtualenv 并将我的 Django 项目移到了这个环境中,但是我见过其他设置,其中有一个文件夹用于虚拟环境,另一个文件夹用于项目。

  • 如何设置以允许在单个服务器上托管多个站点?

  • 为什么有些人建议使用 gunicorn_django -b 0.0.0.0:8000,而另一些人建议使用 gunicorn_django -b 127.0.0.1:8000?我在 Amazon EC2 实例中测试了后者,但它没有工作,而前者则没有问题。

  • nginx 的配置文件背后的逻辑是什么?有很多使用截然不同的配置文件的教程,让我对哪个更好感到困惑。例如,有些人使用 alias /path/to/static/folder,而其他人则使用 root /path/to/static/folder。也许你可以分享你喜欢的配置文件。

  • 为什么我们在 /etc/nginx 中创建 site-availablesites-enabled 之间的符号链接?

  • 始终欢迎一些最佳实践 :-)

谢谢


你能否在git上发布一个关于nginx和gunicorn/uwsgi的示例?这对像我这样的新手会更有用。 - Shiva
@Shiva 实际上,miki725的答案包含了一个非常完整的配置文件示例。如果你想要一个非常详细的介绍nginx是如何工作的,我推荐你阅读<a href="http://www.amazon.com/Nginx-HTTP-Server-Cl%C3%A9ment-Nedelcu/dp/1849510865">this book</a>。gunicorn的集成非常简单。可以在<a href="https://docs.djangoproject.com/en/dev/howto/deployment/wsgi/gunicorn/">这里</a>找到相关信息。 - r_31415
4个回答

108
你看过哪种"设置"效果最好?我使用virtualenv,并将我的django项目放在这个环境中,但我看到其他设置中有一个用于虚拟环境的文件夹和另一个用于项目的文件夹。
virtualenv是一种隔离Python环境的方法;因此,在部署期间它并没有发挥太大作用 - 但在开发和测试期间,如果不是强烈建议,那么就是必须的。 virtualenv的价值在于它允许您确保为应用程序安装了正确的库版本。 所以,虚拟环境本身放在哪里都没关系。只需确保不将其包含在源代码版本控制系统中即可。
文件系统布局并不是关键。 您会看到很多文章赞扬目录布局甚至还有可以克隆的框架项目。 我认为这更具个人偏好而不是严格要求。当然拥有这个很好,但除非您知道为什么,否则它不会为部署流程增加任何价值-因此,除非对您的情况有意义,否则不要因为某些博客推荐它而这么做。例如-如果您没有私有PyPi服务器是部署工作流程的一部分,则无需创建setup.py文件。
如何设置以允许在单个服务器上托管多个站点?
您需要两件事来进行多站点设置:
1. 一个正在公共IP上监听端口80和/或端口443(如果有SSL)的服务器。 2. 大量运行实际django源代码的“进程”。
人们使用nginx作为#1,因为它是非常快速的代理,并且不带有像Apache这样的综合服务器的开销。 如果您熟悉Apache,则可以自由使用它。 没有“多个站点,请使用nginx”的要求; 您只需要一个服务来监听该端口,知道如何重定向(代理)到运行实际django代码的进程即可。
对于#2,有几种启动这些进程的方法。 gevent / uwsgi是最流行的。 这里唯一需要记住的是在生产中不要使用runserver。这些是绝对最低要求。通常人们会添加一些进程管理器来控制所有运行的“django服务器”(#2)。在这里,你会看到提到了upstartsupervisor。我更喜欢supervisor,因为它不需要接管整个系统(不像upstart)。然而,再次强调-这不是一个硬性要求。你可以 perfectly 运行一堆screen 会话并将它们分离。缺点是,如果您的服务器重新启动,则必须重新启动screen会话。
个人建议如下:
  1. Nginx #1
  2. 选择使用uwsgi或gunicorn之一-我使用uwsgi。
  3. supervisor 管理后端进程。
  4. 为您托管的每个应用程序使用单独的系统帐户(用户)
我建议使用第4步是为了隔离权限;同样,这不是必需的。
一些人为什么建议使用gunicorn_django -b 0.0.0.0:8000 和其他人建议使用 gunicorn_django -b 127.0.0.1:8000? 我在一个Amazon EC2实例中测试了后者,但它没有起作用,而前者则没有问题。 0.0.0.0表示“所有IP地址”,这是一个元地址(即占位符地址)。 127.0.0.1是一个保留地址,始终指向本地机器。这就是为什么它被称为"localhost"。它只能由运行在同一系统上的进程访问。
通常,您将在具有公共IP地址的前端服务器(#1在上面的列表中)上进行侦听。您应该明确地将服务器绑定到一个IP地址。但是,如果由于某种原因您正在使用DHCP,或者您不知道IP地址是什么(例如,它是新建立的系统), 您可以告诉nginx/apache/任何其他进程绑定到0.0.0.0这应该是一个临时的权宜之计
对于生产服务器,您将拥有静态IP。如果您有动态IP(DHCP),那么您可以留下0.0.0.0。然而,您的生产机器很少使用DHCP。在生产环境中,不建议将gunicorn/uwsgi绑定到此地址。如果将后端进程(gunicorn/uwsgi)绑定到0.0.0.0,它可能会通过绕过前端代理(nginx/apache/等)“直接”被访问;如果您的前端服务器(nginx)和后端进程(django/uwsgi/gevent)在同一台机器上运行,某人可以通过请求http://your.public.ip.address:9000/ 直接访问您的应用程序特别是

如果您不想运行前端代理服务器,也可以自由地这样做。

nginx配置文件背后的逻辑是什么?有这么多教程使用完全不同的配置文件,我对哪个更好感到困惑。例如,有些人使用"alias /path/to/static/folder",而其他人则使用"root /path/to/static/folder"。 或许你可以分享你喜欢的配置文件。

您需要了解关于nginx的第一件事就是,它与Apache或IIS等不同,不是一个Web服务器,它是一个代理服务器。因此,您将看到不同的术语,例如“上游”/“下游”和多个“服务器”被定义,花点时间去阅读nginx手册。

有许多不同的设置nginx的方法,但以下是对于您关于aliasroot的问题的一个答案。 root 是一个明确的指令,将nginx的文档根目录(“主目录”)绑定到指定目录。这是在没有路径的请求时(例如:http://www.example.com/)它将查看的目录。

alias 的意思是“将名称映射到目录”。别名目录可能不是文档根目录的子目录。

为什么我们需要在/etc/nginx中的site-available和sites-enabled之间创建符号链接?

这是Debian(以及类似Ubuntu的基于Debian的系统)所特有的。 sites-available 列出系统中所有虚拟主机/站点的配置文件。从sites-enabledsites-available的符号链接“激活”该网站或虚拟主机。这是一种分离配置文件并轻松启用/禁用主机的方法。


2
非常好的回答!解决了很多问题。您能否详细说明一下(或添加一个示例),您所说的显式绑定服务器到IP地址以及gunicorn/uwsgi应该绑定到0.0.0.0是什么意思?不幸的是,我认为这就是我正在做的。谢谢! - r_31415
8
一台普通计算机至少会有两个IP地址:127.0.0.1和由网络分配给它的IP地址;这是最小值 - 您的计算机可能具有多个接口和多个IP地址。您应该将您的网络服务器(或任何进程,真的)配置为侦听“一个”IP地址 - 这就是我所说的明确。当您绑定到0.0.0.0时,您告诉程序侦听所有IP地址,包括可能分配给您的机器的任何新IP地址。出于各种原因(其中安全性是之一),这不是一个好的做法。 - Burhan Khalid
1
明白了。我已经正确配置了Gunicorn。非常感谢! - r_31415
nginx可以提供静态内容服务。 - Marcin
服务器如何知道我们在/etc/nginx/sites-enabled中配置了服务器地址的哪个文件。 - Shiva
@Shiva - 请创建一个新问题,因为评论区不适合进行长时间讨论。 - Burhan Khalid

11

我不是部署专家,但会分享一些使用gevent(应该与gunicorn类似)部署Django的实践经验。

virtualenv非常好用,我不会在这里详细介绍原因。但我发现当你同时处理多个项目时,virtualenv-wrapper (文档) 特别有用,因为它允许你轻松切换不同的虚拟环境。虽然这并不适用于部署环境,但当我需要通过SSH进行故障排除时,我发现这非常有用。使用它的另一个好处是它可以管理virtualenv目录,减少了手动操作。Virtualenv应该是可丢弃的,以便在出现版本问题或任何其他安装问题时,您可以删除当前环境并创建新的环境。因此,最佳实践是不要将任何项目代码包含在virtualenv中,应该保持独立。

至于设置多个站点,virtualenv就足够了。每个项目都应该有一个单独的虚拟环境。仅此一项就可以解决许多问题。然后,在部署时,不同的Python进程将运行不同的站点,避免了部署之间可能存在的冲突。我特别发现在同一台服务器上管理多个站点非常有用的工具是supervisor (文档)。它提供了一个易于使用的界面,可以启动、停止和重启不同的Django实例。它还能够在进程失败或计算机启动时自动重新启动进程。例如,如果出现异常并且没有任何东西捕获它,整个网站就会崩溃。Supervisor将捕获该异常并自动重启Django实例。以下是一个示例supervisor程序(单个进程)配置:

[program:foo]
command=/path/toviertualenv/bin/python deploy.py
directory=/path/where/deploy.py/is/located/
autostart=true
autorestart=true
redirect_stderr=True
user=www

对于Nginx,一开始可能会感到压力很大。我发现Nginx书籍非常有用。它解释了所有主要的nginx指令。
在我的nginx安装中,我发现最佳实践是只在nginx.conf文件中设置核心配置,然后我有一个单独的sites文件夹,其中包含我托管的每个站点的nginx配置。然后我只需在核心配置文件中包含该文件夹中的所有文件。我使用指令include sites/+*.conf;。这样它只包括以+符号开头的文件在sites文件夹中。通过文件名,我可以控制哪些配置文件被加载。因此,如果我想禁用某个站点,我只需要重命名配置文件并重新启动nginx即可。不确定您在问题中所说的“在/etc/nginx中的site-available和sites-enabled之间创建符号链接”是什么意思,因为那些是Apache命名的文件夹,但它们完成与include指令类似的任务。
至于rootalias指令,它们基本相同,只是它们计算根目录的位置不同。在alias中,location中的任何内容都被删除,而在根目录中则没有。假设您有以下nginx配置:
location /static {
    alias /some/path/;
}
location /static2 {
    root /some/other/path/;
}

如果用户访问这些URL,nginx将会在以下位置查找系统中的文件:
/static/hello/world.pdf => /some/path/hello/world.pdf
/static2/hello/world.pdf => /some/other/path/static2/hello/world.pdf

这是一个针对nginx站点的简单配置:

server {
    server_name .foodomain.com;
    listen 80;

    access_log logs/foodomain.log;

    gzip                on;
    gzip_http_version   1.0;
    gzip_comp_level     2;
    gzip_proxied        any;
    gzip_min_length     1100;
    gzip_buffers        16 8k;
    gzip_types          text/plain text/html text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript;

    # Some version of IE 6 don't handle compression well on some mime-types, so just disable for them
    gzip_disable "MSIE [1-6].(?!.*SV1)";

    # Set a vary header so downstream proxies don't send cached gzipped content to IE6
    gzip_vary on;

    location / {
        proxy_read_timeout      30s;
        proxy_pass              http://localhost:8000;
        proxy_set_header        Host                 $host;
        proxy_set_header        User-Agent           $http_user_agent;
        proxy_set_header        X-Real-IP            $remote_addr;
    }

    location /media {
        alias   /path/to/media/;
        expires 1y;
    }

    location /static {
        autoindex on;
        expires   1y;
        alias     /path/to/static/;
    }

     location /favicon.ico {
        alias /path/to/favicon.ico;
    }
}

希望这能对您有所帮助。


实际上,你的回答帮了很大的忙!Supervisor听起来很不错,这是博客作者中似乎存在共识的少数几件事之一。关于虚拟环境和其包装器的建议非常好。我曾想将virtualenv-wrapper添加到其中,但我不想在这个问题中不必要地增加复杂性。至于site-available和sites-enabled,nginx包含这些目录。你在哪里为nginx创建配置文件?在你的Django项目内部吗? - r_31415
我个人将它们放在nginx配置文件夹中。在我的情况下,它是/usr/local/nginx/config/sites。但不确定这是否是正确或更好的方法。我将它们放在那里的原因是,如果我将其移出,则必须通过手动包含“include”指令或创建符号链接来在nginx中包含它。在任何一种情况下,都需要手动操作,所以我只是将其保留在主配置位置。 - miki725
我正在阅读你推荐的书 :-) 它很棒,正如你可能记得的那样,/sites/*.conf 是一种建议的方法。无论如何,谢谢你的回答。 - r_31415
欢迎。我认为书中一个不太有用的部分是如何使用Django与nginx。书中建议使用fastcgi,但与使用proxy pass相比并不那么整洁。因此,您可以跳过第6章。 - miki725
我刚刚读完了这本书,感觉非常棒。实际上,我读了第六章,因为我想知道fastcgi是如何工作的,但你说得对...它并没有什么用。谢谢! - r_31415

2

2

关于最佳实践,我想分享一个对我非常有用的工具!在配置多个站点的gunicorn、nginx和supervisorD等多个配置文件时,我经常感到困惑。但是我渴望自动化整个过程,以便我可以即时更改我的应用/站点并部署它。这个工具名叫django-fagungis。你可以在Django Deployment automation here找到我的使用经验。我只需要配置一次fabfile.py(django-fagungis使用fabric自动化整个过程,并在远程服务器上创建一个虚拟环境,非常方便管理单个服务器上托管的多个站点的依赖项。它使用nginx、gunicorn和supervisorD来处理Django项目/站点的部署),然后django-fagungis就会从bitbucket克隆我的最新项目并将其部署在我的远程服务器上,我只需在本地机器的shell中输入三个命令,就完成了!对我来说,这已经成为了Django部署的最佳实践。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接