configuração apache + mod_wsgi para projeto (s) django em um quad core

2

Eu tenho experimentado bastante tempo com uma configuração "típica" de django em nginx + apache2 + mod_wsgi + memcached (+ postgresql) (lendo o documento e algumas questões sobre SO e SF, veja os comentários)

Desde que eu ainda estou insatisfeito com o comportamento (definitivamente por causa de uma má má configuração da minha parte), eu gostaria de saber como seria uma boa configuração com essas hipóteses:

  • Quad-Core Xeon 2,8 GHz
  • 8 GB de memória
  • vários projetos de django (algo especial relacionado a isso?)

Estes são trechos dos meus confs atuais:

EDIT: Eu adicionei mais coisas para completar, mas seguindo a sugestão de Graham, eu irei acompanhar a lista de discussão do 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

Atualmente usando a versão 3.3 criada a partir da fonte

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)'

estrutura de pastas

caso seja importante (ligeiramente encurtado)

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

Por favor, perdoe-me com antecedência se eu negligenciei algo óbvio.

Minha principal questão é sobre as configurações do wsgi apache2. Estão bem? 25 threads são / ok / number com um quad core para um único projeto django? Ainda está tudo bem com vários projetos de django em diferentes hosts virtuais? Devo especificar 'process'? Qualquer outra diretriz que devo acrescentar? Há algo realmente ruim no arquivo wsgi.py?

Eu tenho lido sobre possíveis problemas com o arquivo wsgi.py padrão, devo mudar para isso?

Ou .. este conf só deveria estar funcionando bem, e eu deveria procurar por problemas em algum outro lugar?

Então, o que quero dizer com "insatisfeito": bem, muitas vezes fico com CPU WAIT bastante alta; mas o que é pior, é que, com relativa frequência, o apache2 fica preso. Ele simplesmente não responde mais e precisa ser reiniciado. Eu configurei um monitor para cuidar disso, mas não é uma solução real. Eu tenho me perguntado se é um problema com o acesso ao banco de dados (postgresql) sob carga pesada, mas mesmo se fosse, por que os processos do apache2 ficariam presos?

Além desses dois problemas, o desempenho é ótimo no geral. Eu até tentei New Relic e obtive resultados médios muito bons.

edit Eu mesmo não poderei responder, pois mudei temporariamente para um ambiente nginx + gunicorn.

Acompanhe também os grupos do Google para minha situação pessoal e problemas! Parece que o Graham está, é claro, muito ocupado (o mod_wsgi é um projeto paralelo gratuito!), Mas mudar para o Read The Docs parece ótimo, e resolver esse problema seria totalmente incrível. Isso e o novo Apache 2.4 podem me fazer reconsiderar o melhor combo (atualmente nginx + gunicorn, então eu poderia abandonar o nginx para uma configuração de verniz + apache + mod_wsgi)

    
por Stefano 29.11.2011 / 02:27

1 resposta

2

Ative mod_headers no Apache e adicione ao seu VirtualHost:

RequestHeader add X-Queue-Start "%t"

A New Relic mostrará seu tempo de espera no gráfico da visão geral principal.

Este é o tempo entre quando a solicitação é aceita pela primeira vez pelo processo de trabalho filho do Apache e quando o processo do daemon mod_wsgi consegue manipular a solicitação. Isso pode ser usado como um indicador de solicitações de backlog que, por sua vez, pode indicar falta de threads no processo do daemon devido a encadeamentos em deadlock ou encadeamentos que aguardam um recurso externo.

Infelizmente, a New Relic depende de solicitações concluídas para relatar dados para essa solicitação. Então, se um pedido ficar preso, você não saberá sobre isso. Se todos os encadeamentos ficarem presos, o processo do daemon parará de manipular mais solicitações.

O problema é que, se o número de processos / encadeamentos em processos de trabalho filho do Apache for menor que 100, o backlog do ouvinte do processo do daemon, todos esses encadeamentos também poderão ficar presos e você não saberá dos logs de erros do Apache, porque eles lá esperando por daemon para aceitar conexão que nunca acontece. Somente o cliente do navegador HTTP saberá como a conexão será recusada quando o backlog do soquete do trabalhador filho do Apache ficar cheio.

No mod_wsgi 4.0, estarei adicionando a capacidade de configurar o backlog do listener para o processo do daemon, de modo que possa ser reduzido para que você possa obter algum tipo de erro. Já existem novas opções no mod_wsgi 4.0 para procurar encadeamentos bloqueados e para reiniciar processos do daemon automaticamente e também descarregar um rastreamento de pilha de onde os encadeamentos bloqueados estavam no código no momento.

Para conseguir isso, você precisaria usar o código dev mod_wsgi 4.0 do mod_wsgi repo. Em seguida, você pode definir o tempo limite bloqueado em WSGIDaemonProcess para 60 segundos e, quando todos os threads ficarem presos, ele fará a reinicialização e a recuperação, além de rastreamentos de pilha de despejo. Ainda estou aprimorando isso e existem outras opções de configuração relacionadas a isso porque não descrevemos aqui.

O código mod_wsgi 4.0 também tem alguns outros recursos que podem ser usados com gráficos personalizados no New Relic para acompanhar o número crescente de encadeamentos bloqueados. Não estou feliz com isso e precisa ser mudado um pouco, mas é estável.

De qualquer forma, entre na lista de discussão mod_wsgi para discutir mais.

    
por 29.11.2011 / 04:19