Назад | Перейти на главную страницу

Конфигурация apache + mod_wsgi для проекта (ов) django на четырехъядерном процессоре

Я довольно давно экспериментировал с "типичной" настройкой django на nginx + apache2 + mod_wsgi + memcached (+ postgresql) (чтение документ и некоторые вопросы по SO и SF, см. комментарии)

Поскольку я все еще не удовлетворен поведением (определенно из-за некоторой неправильной конфигурации с моей стороны), я хотел бы знать, как будет выглядеть хорошая конфигурация с этими гипотезами:

Это выдержки из моих текущих сообщений:

РЕДАКТИРОВАТЬ: Я добавил больше вещей, чтобы завершить это, но, следуя предложению Грэма, я буду следить за списком рассылки wsgi

apache 2 (> apache2 -v)

Server version: Apache/2.2.12 (Ubuntu)
Server built:   Nov 18 2010 21:16:51
Server's Module Magic Number: 20051115:23
Server loaded:  APR 1.3.8, APR-Util 1.3.9
Compiled using: APR 1.3.8, APR-Util 1.3.9
Architecture:   64-bit
Server MPM:     Worker
  threaded:     yes (fixed thread count)
    forked:     yes (variable process count)
Server compiled with....
 -D APACHE_MPM_DIR="server/mpm/worker"
 -D APR_HAS_SENDFILE
 -D APR_HAS_MMAP
 -D APR_HAVE_IPV6 (IPv4-mapped addresses enabled)
 -D APR_USE_SYSVSEM_SERIALIZE
 -D APR_USE_PTHREAD_SERIALIZE
 -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT
 -D APR_HAS_OTHER_CHILD
 -D AP_HAVE_RELIABLE_PIPED_LOGS
 -D DYNAMIC_MODULE_LIMIT=128
 -D HTTPD_ROOT=""
 -D SUEXEC_BIN="/usr/lib/apache2/suexec"
 -D DEFAULT_PIDLOG="/var/run/apache2.pid"
 -D DEFAULT_SCOREBOARD="logs/apache_runtime_status"
 -D DEFAULT_ERRORLOG="logs/error_log"
 -D AP_TYPES_CONFIG_FILE="/etc/apache2/mime.types"
 -D SERVER_CONFIG_FILE="/etc/apache2/apache2.conf"

apache2 conf

PidFile ${APACHE_PID_FILE}
Timeout 60
KeepAlive Off

ServerSignature Off
ServerTokens Prod
#MaxKeepAliveRequests 100
#KeepAliveTimeout 15
# worker MPM
<IfModule mpm_worker_module>
    StartServers          2
    ServerLimit           4
    MinSpareThreads       2
    MaxSpareThreads       4
    ThreadLimit          32
    ThreadsPerChild      16
    MaxClients          64#128
    MaxRequestsPerChild   10000
</IfModule>

...

SetEnv VHOST null 
#WSGIPythonOptimize 2

<VirtualHost *:8082>
    ServerName subdomain.domain.com
    ServerAlias www.domain.com
    SetEnv VHOST subdomain.domain
    AddDefaultCharset UTF-8
    ServerSignature Off

    LogFormat "%{X-Real-IP}i %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" custom
    ErrorLog  /home/project1/var/logs/apache_error.log
    CustomLog /home/project1/var/logs/apache_access.log custom

    AllowEncodedSlashes On

    WSGIDaemonProcess subdomain.domain user=www-data group=www-data threads=25
    WSGIScriptAlias / /home/project1/project/wsgi.py
    WSGIProcessGroup %{ENV:VHOST}
</VirtualHost>

wsgi.py

В настоящее время используется версия 3.3, собранная из исходников

import os
import sys

# setting all the right paths....


_realpath = os.path.realpath(os.path.dirname(__file__))
_public_html = os.path.normpath(os.path.join(_realpath, '../'))    

sys.path.append(_realpath)
sys.path.append(os.path.normpath(os.path.join(_realpath, 'apps')))
sys.path.append(os.path.normpath(_public_html))
sys.path.append(os.path.normpath(os.path.join(_public_html, 'libs')))
sys.path.append(os.path.normpath(os.path.join(_public_html, 'django')))


os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'

import django.core.handlers.wsgi

_application = django.core.handlers.wsgi.WSGIHandler()

def application(environ, start_response):
    """
    Launches django passing over some environment (domain name) settings
    """

    application_group = environ['mod_wsgi.application_group']
    """
    wsgi application group is required. It's also used to generate the
    HOST.DOMAIN.TLD:PORT parameters to pass over
    """
    assert application_group
    fields = application_group.replace('|', '').split(':')
    server_name = fields[0]
    os.environ['WSGI_APPLICATION_GROUP'] = application_group
    os.environ['WSGI_SERVER_NAME'] = server_name
    if len(fields) > 1 :
        os.environ['WSGI_PORT'] = fields[1]
    splitted = server_name.rsplit('.', 2)    
    assert splitted >= 2
    splited.reverse()
    if len(splitted) > 0 :
        os.environ['WSGI_TLD'] = splitted[0]
    if len(splitted) > 1 :
        os.environ['WSGI_DOMAIN'] = splitted[1]
    if len(splitted) > 2 :
        os.environ['WSGI_HOST'] = splitted[2]
    return _application(environ, start_response)`

структура папок

в случае, если это имеет значение (на самом деле немного сокращено)

/home/www-data/projectN/var/logs
                       /project (contains manage.py, wsgi.py, settings.py)
                       /project/apps (all the project ups are here)
                       /django
                       /libs

Пожалуйста, заранее простите меня, если я упустил из виду что-то очевидное.

Мой главный вопрос касается настроек apache2 wsgi. Это нормально? Является ли 25 потоков / ok / числом с четырехъядерным процессором только для одного проекта django? Все еще нормально с несколькими проектами django на разных виртуальных хостах? Следует указать «процесс»? Любая другая директива, которую я должен добавить? Есть ли что-нибудь действительно плохое в файле wsgi.py?

Я читал о потенциальные проблемы со стандартным файлом wsgi.py, мне следует переключиться на него?

Или ... должна ли эта конфигурация работать нормально, и я должен искать проблемы в другом месте?

Итак, что я имею в виду под словом «неудовлетворен»: у меня часто бывает довольно высокая загрузка CPU WAIT; но что еще хуже, apache2 довольно часто зависает. Он просто больше не отвечает, и его нужно перезапустить. Я установил монитор, чтобы позаботиться об этом, но это не настоящее решение. Мне было интересно, не связана ли проблема с доступом к базе данных (postgresql) при большой нагрузке, но даже если это так, почему процессы apache2 застревают?

Помимо этих двух проблем, производительность в целом отличная. Я даже попробовал New Relic и получил очень хорошие средние результаты.

редактировать Я не смогу ответить сам, так как временно перешел в среду nginx + gunicorn.

Также следите за группы Google по моей личной ситуации и вопросам! Похоже, что Грэм, конечно, очень занят (mod_wsgi - это бесплатный сторонний проект!), Но переход на Read The Docs звучит здорово, и решение этой одной проблемы с невыполненной работой было бы совершенно потрясающим. Это и новый Apache 2.4 могут заставить меня пересмотреть лучшую комбинацию (в настоящее время nginx + gunicorn, затем я могу отказаться от nginx для настройки varnish + apache + mod_wsgi)

Включите mod_headers в Apache, а затем добавьте в свой VirtualHost:

RequestHeader add X-Queue-Start "%t"

New Relic покажет вам время ожидания в очереди на основной обзорной диаграмме.

Это время между первым принятием запроса дочерним рабочим процессом Apache и моментом, когда процесс демона mod_wsgi получает возможность обработать запрос. Это можно использовать как один из индикаторов задержки запросов, что, в свою очередь, может указывать на нехватку потоков в процессе демона из-за тупиковых потоков или потоков, ожидающих на внешнем ресурсе.

К сожалению, New Relic полагается на завершение запросов, чтобы сообщить данные для этого запроса. Поэтому, если запрос застрянет, вы не узнаете об этом. Если все потоки застревают, процесс-демон перестанет обрабатывать больше запросов.

Проблема в том, что если количество процессов / потоков в дочерних рабочих процессах Apache меньше 100, то все эти потоки также могут застрять, и вы не узнаете из журналов ошибок Apache, потому что они просто будут сидеть и ждать демон принять соединение, которое никогда не происходит. Только клиент HTTP-браузера будет знать, поскольку он получит отказ в соединении, когда заполнится невыполненный журнал дочернего рабочего сокета Apache.

В mod_wsgi 4.0 я добавлю возможность настраивать журнал ожидания слушателя для процесса демона, чтобы его можно было уменьшить, чтобы вы могли получить какую-то ошибку. В mod_wsgi 4.0 уже есть новые параметры для поиска заблокированных потоков и автоматического перезапуска процессов демона, а также для вывода трассировки стека, где в то время были заблокированы потоки в коде.

Для этого вам нужно будет использовать код разработчика mod_wsgi 4.0 из репозитория mod_wsgi. Затем вы можете установить время ожидания блокировки для WSGIDaemonProcess равным 60 секундам, и когда все потоки застрянут, он выполнит перезапуск и восстановление, а также трассировку стека дампа. Я все еще настраиваю это, и есть другие параметры конфигурации, связанные с этим, почему бы не описать здесь.

Код mod_wsgi 4.0 также имеет некоторые другие функции, которые можно использовать с настраиваемыми диаграммами в New Relic для отслеживания растущего числа заблокированных потоков. Меня это не устраивает и нужно немного изменить, но работает стабильно.

В любом случае, перейдите в список рассылки mod_wsgi для дальнейшего обсуждения.