Como configurar uWSGI e Nginx para servir aplicativos Python no Ubuntu 14.04

Introdução

Neste guia, estaremos configurando uma aplicação WSGI simples servida pelo uWSGI. Utilizaremos o servidor web Nginx como um proxy reverso para o servidor de aplicativos para fornecer um manuseio de conexão mais robusto. Estaremos instalando e configurando esses componentes em um servidor Ubuntu 14.04.

Definições e Conceitos

Esclarecendo Alguns Termos

Antes de começarmos, devemos abordar alguns termos confusos associados aos conceitos inter-relacionados com os quais estaremos lidando. Três termos separados que parecem intercambiáveis, mas na verdade têm significados distintos:

  • WSGI: Uma especificação em Python que define uma interface padrão para comunicação entre uma aplicação ou framework e um servidor de aplicativos/web. Isso foi criado para simplificar e padronizar a comunicação entre esses componentes para consistência e intercambialidade. Basicamente, isso define uma interface de API que pode ser utilizada sobre outros protocolos.
  • : Um contêiner de servidor de aplicativos que visa fornecer uma pilha completa para desenvolver e implantar aplicativos e serviços da web. O principal componente é um servidor de aplicativos que pode lidar com aplicativos de diferentes linguagens. Ele se comunica com o aplicativo usando os métodos definidos pela especificação WSGI e com outros servidores da web por meio de uma variedade de outros protocolos. Esta é a peça que traduz solicitações de um servidor da web convencional para um formato que o aplicativo pode processar.
  • : Um protocolo binário rápido implementado pelo servidor uWSGI para se comunicar com um servidor da web mais completo. Este é um protocolo de fio, não um protocolo de transporte. É a maneira preferida de falar com servidores da web que estão encaminhando solicitações para o uWSGI.

Requisitos da Aplicação WSGI

A especificação WSGI define a interface entre as porções de servidor da web e aplicativo da pilha. Neste contexto, “servidor da web” refere-se ao servidor uWSGI, que é responsável por traduzir as solicitações do cliente para o aplicativo usando a especificação WSGI. Isso simplifica a comunicação e cria componentes fracamente acoplados para que você possa facilmente trocar qualquer lado sem muito problema.

O servidor web (uWSGI) deve ter a capacidade de enviar solicitações para a aplicação, acionando um “callable” definido. O callable é simplesmente um ponto de entrada na aplicação onde o servidor web pode chamar uma função com alguns parâmetros. Os parâmetros esperados são um dicionário de variáveis ambientais e um callable fornecido pelo componente do servidor web (uWSGI).

Em resposta, a aplicação retorna um iterável que será usado para gerar o corpo da resposta ao cliente. Também chamará o callable do componente do servidor web que recebeu como parâmetro. O primeiro parâmetro ao acionar o callable do servidor web será o código de status HTTP e o segundo será uma lista de tuplas, cada uma definindo um cabeçalho de resposta e valor para enviar de volta ao cliente.

Com o componente “servidor web” desta interação fornecido pelo uWSGI nesta instância, só precisaremos garantir que nossas aplicações tenham as qualidades descritas acima. Também configuraremos o Nginx para lidar com as solicitações reais dos clientes e encaminhá-las para o servidor uWSGI.

Instalar os Componentes

Para começar, precisaremos instalar os componentes necessários em nosso servidor Ubuntu 14.04. Podemos fazer isso principalmente usando apt e pip.

Primeiro, atualize seu índice de pacotes apt e depois instale as bibliotecas e cabeçalhos de desenvolvimento Python, o gerenciador de pacotes Python pip, e o servidor web e proxy reverso Nginx:

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

Assim que a instalação do pacote estiver completa, você terá acesso ao gerenciador de pacotes Python pip. Podemos usar isso para instalar o pacote virtualenv, que usaremos para isolar o ambiente Python de nossa aplicação de qualquer outro que possa existir no sistema:

sudo pip install virtualenv

Uma vez concluído isso, podemos começar a criar a estrutura geral para nossa aplicação. Criaremos o ambiente virtual mencionado acima e instalaremos o servidor de aplicativos uWSGI dentro deste ambiente.

Configurar um Diretório de Aplicativos e um Virtualenv

Começaremos criando uma pasta para nosso aplicativo. Isso pode conter uma pasta aninhada contendo o código de aplicativo real em um aplicativo mais completo. Para nossos propósitos, este diretório simplesmente conterá nosso ambiente virtual e nosso ponto de entrada WSGI:

mkdir ~/myapp/

Em seguida, mova-se para o diretório para que possamos configurar o ambiente para nossa aplicação:

cd ~/myapp

Crie um ambiente virtual com o comando virtualenv. Chamaremos isso de myappenv para simplificar:

virtualenv myappenv

A new Python environment will be set up under a directory called myappenv. We can activate this environment by typing:

source myappenv/bin/activate

Sua linha de comando deve mudar para indicar que você está agora operando dentro do ambiente virtual. Deverá se parecer com algo assim:

(myappenv)username@host:~/my_app$

Se desejar sair deste ambiente a qualquer momento, você pode simplesmente digitar:

deactivate

Se você desativou seu ambiente, reative-o novamente para continuar com o guia.

Com este ambiente ativo, quaisquer pacotes Python instalados estarão contidos nesta hierarquia de diretórios. Eles não interferirão no ambiente Python do sistema. Com isso em mente, agora podemos instalar o servidor uWSGI em nosso ambiente usando pip. O pacote para isso é chamado de uwsgi (este ainda é o servidor uWSGI e não o protocolo uwsgi):

pip install uwsgi

Você pode verificar que agora está disponível digitando:

uwsgi --version

Se retornar um número de versão, o servidor uWSGI está disponível para uso.

Criar uma Aplicação WSGI

Em seguida, criaremos uma aplicação WSGI incrivelmente simples usando os requisitos de especificação WSGI que discutimos anteriormente. Para reiterar, o componente da aplicação que devemos fornecer deve ter as seguintes propriedades:

  • Deve fornecer uma interface por meio de um invocável (uma função ou outro construtor de linguagem que pode ser chamado)
  • O invocável deve receber como parâmetros um dicionário contendo pares chave-valor semelhantes a variáveis ambientais e um invocável que é acessível no servidor (uWSGI).
  • O invocável da aplicação deve retornar um iterável que produzirá o corpo para enviar ao cliente.
  • A aplicação deve chamar o invocável do servidor da web com o status HTTP e os cabeçalhos da solicitação.

Vamos escrever nossa aplicação em um arquivo chamado wsgi.py em nosso diretório de aplicativos:

nano ~/myapp/wsgi.py

Dentro deste arquivo, iremos criar a aplicação mais simples compatível com WSGI que podemos. Como em todo código Python, certifique-se de prestar atenção à indentação:

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return ["<h1 style='color:blue'>Hello There!</h1>"]

O código acima constitui uma aplicação WSGI completa. Por padrão, o uWSGI procurará por uma função chamada application, razão pela qual chamamos nossa função de application. Como você pode ver, ela recebe dois parâmetros.

O primeiro chamamos de environ porque será um dicionário de chave-valor semelhante a uma variável de ambiente. O segundo é chamado de start_response e é o nome que a aplicação usará internamente para se referir ao callable do servidor web (uWSGI) que é enviado. Ambos os nomes dos parâmetros foram selecionados simplesmente por causa de seu uso nos exemplos na especificação PEP 333 que define as interações WSGI.

Nossa aplicação deve pegar essas informações e fazer duas coisas. Primeiro, ela deve chamar o callable que recebeu com um código de status HTTP e quaisquer cabeçalhos que deseje enviar de volta. Neste caso, estamos enviando uma resposta “200 OK” e configurando o cabeçalho Content-Type para text/html.

Em segundo lugar, precisa retornar um iterável para ser usado como corpo da resposta. Aqui, apenas usamos uma lista contendo uma única string de HTML. Strings também são iteráveis, mas dentro de uma lista, o uWSGI será capaz de processar toda a string com uma única iteração.

Em um cenário do mundo real, este arquivo provavelmente seria usado como um link para o restante do código da sua aplicação. Por exemplo, projetos Django incluem um arquivo wsgi.py por padrão que traduz requisições do servidor web (uWSGI) para a aplicação (Django). A interface simplificada do WSGI permanece a mesma, independentemente de quão complexo seja o código da aplicação real. Esta é uma das vantagens da interface.

Salve e feche o arquivo quando terminar.

Para testar o código, podemos iniciar o uWSGI. Vamos dizer a ele para usar HTTP por enquanto e escutar na porta 8080. Passaremos a ele o nome do script (sufixo removido):

uwsgi --socket 0.0.0.0:8080 --protocol=http -w wsgi

Agora, se você visitar o endereço IP do seu servidor ou nome de domínio em seu navegador da web seguido por :8080, você deverá ver o texto do cabeçalho de primeiro nível que passamos como corpo em nosso arquivo wsgi.py:

Pare o servidor com CTRL-C quando tiver verificado que isso funciona.

Terminamos de projetar nossa aplicação real neste ponto. Você pode desativar nosso ambiente virtual se desejar:

deactivate

Configurar um Arquivo de Configuração uWSGI

No exemplo acima, iniciamos manualmente o servidor uWSGI e passamos alguns parâmetros na linha de comando. Podemos evitar isso criando um arquivo de configuração. O servidor uWSGI pode ler configurações em uma variedade de formatos, mas usaremos o formato .ini por simplicidade.

Para continuar com a nomenclatura que temos usado até agora, vamos chamar o arquivo myapp.ini e colocá-lo na pasta da nossa aplicação:

nano ~/myapp/myapp.ini

Dentro dele, precisamos estabelecer uma seção chamada [uwsgi]. Esta seção é onde todos os nossos itens de configuração viverão. Vamos começar identificando nossa aplicação. O servidor uWSGI precisa saber onde está a chamada da aplicação. Podemos fornecer o arquivo e a função dentro dele:

[uwsgi]
module = wsgi:application

Queremos marcar o processo inicial do uwsgi como um mestre e depois iniciar um número de processos trabalhadores. Vamos começar com cinco trabalhadores:

[uwsgi]
module = wsgi:application

master = true
processes = 5

Na verdade, vamos mudar o protocolo que o uWSGI usa para se comunicar com o mundo exterior. Quando estávamos testando nossa aplicação, especificamos --protocol=http para que pudéssemos vê-la de um navegador da web. Como vamos configurar o Nginx como um proxy reverso na frente do uWSGI, podemos mudar isso. O Nginx implementa um mecanismo de proxy uwsgi, que é um protocolo binário rápido que o uWSGI pode usar para se comunicar com outros servidores. O protocolo uwsgi é na verdade o protocolo padrão do uWSGI, então, simplesmente omitindo uma especificação de protocolo, ele voltará para o uwsgi.

Já que estamos projetando essa configuração para uso com o Nginx, também vamos mudar de usar uma porta de rede para usar um socket Unix. Isso é mais seguro e mais rápido. O socket será criado no diretório atual se usarmos um caminho relativo. Vamos chamá-lo de myapp.sock. Vamos alterar as permissões para “664” para que o Nginx possa escrever nele (vamos iniciar o uWSGI com o grupo www-data que o Nginx usa). Também adicionaremos a opção vacuum, que removerá o socket quando o processo parar:

[uwsgi]
module = wsgi:application

master = true
processes = 5

socket = myapp.sock
chmod-socket = 664
vacuum = true

Precisamos de uma opção final, já que estaremos criando um arquivo Upstart para iniciar nossa aplicação no boot. Upstart e uWSGI têm ideias diferentes sobre o que o sinal SIGTERM deve fazer com uma aplicação. Para resolver essa discrepância e garantir que os processos sejam tratados conforme o esperado com o Upstart, só precisamos adicionar uma opção chamada die-on-term, para que o uWSGI mate o processo em vez de recarregá-lo:

[uwsgi]
module = wsgi:application

master = true
processes = 5

socket = myapp.sock
chmod-socket = 664
vacuum = true

die-on-term = true

Salve e feche o arquivo quando terminar. Este arquivo de configuração está agora pronto para ser usado com um script Upstart.

Crie um Arquivo Upstart para Gerenciar a Aplicação

Podemos iniciar uma instância uWSGI no boot para que nossa aplicação esteja sempre disponível. Vamos colocar isso no diretório /etc/init que o Upstart verifica. Vamos chamá-lo de myapp.conf:

sudo nano /etc/init/myapp.conf

Primeiro, podemos começar com uma descrição do serviço e selecionar os níveis de execução do sistema nos quais ele deve ser executado automaticamente. Os níveis de execução padrão do usuário são de 2 a 5. Vamos dizer ao Upstart para parar o serviço quando estiver em qualquer nível de execução fora desse grupo (como quando o sistema está reiniciando ou no modo de usuário único):

description "uWSGI instance to serve myapp"

start on runlevel [2345]
stop on runlevel [!2345]

Em seguida, diremos ao Upstart sobre qual usuário e grupo executar o processo. Queremos executar a aplicação sob nossa própria conta (estamos usando demo neste guia, mas você deve substituir pelo seu próprio usuário). Queremos definir o grupo como o usuário www-data que o Nginx utiliza, no entanto. Isso é necessário porque o servidor web precisa poder ler e escrever no socket que nosso arquivo .ini irá criar:

description "uWSGI instance to serve myapp"

start on runlevel [2345]
stop on runlevel [!2345]

setuid demo
setgid www-data

Em seguida, executaremos os comandos reais para iniciar o uWSGI. Como instalamos o uWSGI em um ambiente virtual, temos um trabalho extra para fazer. Poderíamos simplesmente fornecer o caminho completo para o executável do uWSGI, mas em vez disso, iremos ativar o ambiente virtual. Isso facilitaria se estivéssemos dependendo de software adicional instalado no ambiente.

Para fazer isso, usaremos um bloco script. Dentro dele, iremos mudar para o diretório da nossa aplicação, ativar o ambiente virtual (devemos usar . em scripts em vez de source), e iniciar a instância do uWSGI apontando para nosso arquivo .ini:

description "uWSGI instance to serve myapp"

start on runlevel [2345]
stop on runlevel [!2345]

setuid demo
setgid www-data

script
    cd /home/demo/myapp
    . myappenv/bin/activate
    uwsgi --ini myapp.ini
end script

Com isso, nosso script do Upstart está completo. Salve e feche o arquivo quando terminar.

Agora, podemos iniciar o serviço digitando:

sudo start myapp

Podemos verificar se ele foi iniciado digitando:

ps aux | grep myapp
demo   14618  0.0  0.5  35868  5996 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   14619  0.0  0.5  42680  5532 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   14620  0.0  0.5  42680  5532 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   14621  0.0  0.5  42680  5532 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   14622  0.0  0.5  42680  5532 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   14623  0.0  0.5  42680  5532 ?        S    15:02   0:00 uwsgi --ini myapp.ini
demo   15520  0.0  0.0  11740   936 pts/0    S+   15:53   0:00 grep --color=auto myapp

Isso iniciará automaticamente na inicialização. Você pode parar o serviço a qualquer momento digitando:

sudo stop myapp

Configurar o Nginx para Proxificar para uWSGI

Neste ponto, temos um aplicativo WSGI e verificamos que o uWSGI pode ler e servir. Criamos um arquivo de configuração e um script Upstart. Nosso processo uWSGI ouvirá em um socket e se comunicará usando o protocolo uwsgi.

Agora estamos no ponto em que podemos trabalhar na configuração do Nginx como um proxy reverso. O Nginx tem a capacidade de fazer proxy usando o protocolo uwsgi para se comunicar com o uWSGI. Este é um protocolo mais rápido que o HTTP e terá melhor desempenho.

A configuração do Nginx que estaremos configurando é extremamente simples. Crie um novo arquivo dentro do diretório sites-available dentro da hierarquia de configuração do Nginx. Chamaremos nosso arquivo de myapp para combinar com o nome do aplicativo que estamos usando:

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

Dentro deste arquivo, podemos especificar o número da porta e o nome de domínio para o qual este bloco de servidor deve responder. No nosso caso, estaremos usando a porta padrão 80:

server {
    listen 80;
    server_name server_domain_or_IP;
}

Uma vez que desejamos enviar todas as solicitações neste domínio ou endereço IP para nossa aplicação WSGI, criaremos um único bloco de localização para solicitações que começam com /, que deve corresponder a tudo. Dentro dele, usaremos a diretiva include para incluir uma série de parâmetros com valores padrão razoáveis de um arquivo em nosso diretório de configuração do Nginx. O arquivo que contém esses é chamado uwsgi_params. Em seguida, passaremos o tráfego para nossa instância uWSGI através do protocolo uwsgi. Usaremos o socket Unix que configuramos anteriormente:

server {
    listen 80;
    server_name server_domain_or_IP;

    location / {
        include         uwsgi_params;
        uwsgi_pass      unix:/home/demo/myapp/myapp.sock;
    }
}

Na verdade, isso é tudo o que precisamos para uma aplicação simples. Existem algumas melhorias que poderiam ser feitas para uma aplicação mais completa. Por exemplo, poderíamos definir uma série de servidores uWSGI upstream fora desse bloco e então passá-los para lá. Poderíamos incluir alguns parâmetros uWSGI adicionais. Também poderíamos lidar com quaisquer arquivos estáticos diretamente do Nginx e passar apenas solicitações dinâmicas para a instância uWSGI.

Não precisamos de nenhuma dessas funcionalidades em nosso aplicativo de três linhas, então podemos salvar e fechar o arquivo.

Habilitar a configuração do servidor que acabamos de fazer, vinculando-a ao diretório sites-enabled:

sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled

Verificar o arquivo de configuração em busca de erros de sintaxe:

sudo service nginx configtest

Se ele relatar que nenhum problema foi detectado, reinicie o servidor para implementar suas alterações:

sudo service nginx restart

Assim que o Nginx reiniciar, você deverá conseguir acessar o nome de domínio ou endereço IP do seu servidor (sem número de porta) e visualizar a aplicação que você configurou:

Conclusão

Se você chegou até aqui, criou um aplicativo WSGI simples e tem uma ideia de como aplicativos mais complexos precisariam ser projetados. Instalamos o contêiner/servidor de aplicativos uWSGI em um ambiente virtual feito sob medida para servir nosso aplicativo. Criamos um arquivo de configuração e um script Upstart para automatizar esse processo. À frente do servidor uWSGI, configuramos um proxy reverso Nginx que pode se comunicar com o processo uWSGI usando o protocolo de rede uwsgi.

Você pode facilmente ver como isso pode ser expandido ao configurar um ambiente de produção real. Por exemplo, o uWSGI tem a capacidade de gerenciar vários aplicativos usando algo chamado “modo imperador”. Você pode expandir a configuração do Nginx para balancear a carga entre instâncias do uWSGI ou para lidar com arquivos estáticos para seu aplicativo. Ao servir vários aplicativos, pode ser do seu interesse instalar o uWSGI globalmente em vez de em um ambiente virtual, dependendo de suas necessidades. Os componentes são todos bastante flexíveis, então você deve ser capaz de ajustar sua configuração para acomodar muitos cenários diferentes.

Source:
https://www.digitalocean.com/community/tutorials/how-to-set-up-uwsgi-and-nginx-to-serve-python-apps-on-ubuntu-14-04