Introdução
Neste guia, iremos configurar uma aplicação WSGI simples servida pelo uWSGI. Usaremos o servidor web Nginx como um proxy reverso para o servidor de aplicação, a fim de fornecer um tratamento 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. Estes três termos separados que parecem intercambiáveis, na verdade, têm significados distintos:
- WSGI: Uma especificação do Python que define uma interface padrão para comunicação entre uma aplicação ou framework e um servidor de aplicação/web. Isso foi criado para simplificar e padronizar a comunicação entre esses componentes para consistência e intercambiabilidade. Basicamente, isso define uma interface de API que pode ser usada sobre outros protocolos.
- uWSGI: Um contêiner de servidor de aplicativos que tem como objetivo fornecer uma pilha completa para desenvolver e implantar aplicativos e serviços da web. O componente principal é um servidor de aplicativos que pode lidar com aplicativos de diferentes linguagens. Ele se comunica com a aplicação 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 web convencional para um formato que a aplicação pode processar.
- uwsgi: Um protocolo binário rápido implementado pelo servidor uWSGI para se comunicar com um servidor da web mais completo em recursos. Este é um protocolo de fio, não um protocolo de transporte. É a maneira preferida de se comunicar com servidores da web que estão encaminhando solicitações para uWSGI.
Requisitos da Aplicação WSGI
A especificação WSGI define a interface entre o servidor web e as partes da aplicação da pilha. Neste contexto, “servidor web” refere-se ao servidor uWSGI, que é responsável por traduzir as solicitações do cliente para a aplicação usando a especificação WSGI. Isso simplifica a comunicação e cria componentes fracamente acoplados para que você possa facilmente trocar qualquer lado sem muitos problemas.
O servidor web (uWSGI) deve ter a capacidade de enviar solicitações para o aplicativo ao acionar um “chamável” definido. O chamável é simplesmente um ponto de entrada no aplicativo 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 chamável fornecido pelo componente do servidor web (uWSGI).
Em resposta, o aplicativo retorna um iterável que será usado para gerar o corpo da resposta do cliente. Ele também chamará o chamável do componente do servidor web que recebeu como parâmetro. O primeiro parâmetro ao acionar o chamável do servidor web será o código de status HTTP e o segundo será uma lista de tuplas, cada uma das quais define um cabeçalho de resposta e valor a serem enviados de volta ao cliente.
Com o componente “servidor web” desta interação fornecido por uWSGI neste caso, só precisaremos garantir que nossos aplicativos tenham as qualidades descritas acima. Também configuraremos o Nginx para lidar com as solicitações de clientes reais e as encaminhará 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 o índice de pacotes apt
e, em seguida, instale as bibliotecas e cabeçalhos de desenvolvimento do 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
Depois 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 quaisquer outros que possam 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 discutido acima e instalaremos o servidor de aplicativos uWSGI dentro desse ambiente.
Configurar um Diretório de Aplicativos e um Virtualenv
Vamos começar 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 simplicidade:
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 isso:
(myappenv)username@host:~/my_app$
Se desejar sair deste ambiente a qualquer momento, basta digitar:
deactivate
Se você desativou seu ambiente, reative-o novamente para continuar com o guia.
Com este ambiente ativo, quaisquer pacotes Python instalados serão contidos dentro desta 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 uwsgi
(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.
Crie uma Aplicação WSGI
Em seguida, vamos criar uma aplicação WSGI incrivelmente simples usando os requisitos de especificação WSGI que discutimos anteriormente. Para reiterar, o componente de aplicativo que devemos fornecer deve ter as seguintes propriedades:
- – Deve fornecer uma interface através de um chamável (uma função ou outro construto de linguagem que pode ser chamado)
- – O chamável deve receber como parâmetros um dicionário contendo pares chave-valor semelhantes a variáveis ambientais e um chamável que é acessível no servidor (uWSGI).
- – O chamável da aplicação deve retornar um iterável que produzirá o corpo para enviar ao cliente.
- – A aplicação deve chamar o chamável do servidor web com o status HTTP e cabeçalhos de 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, criaremos a aplicação mais simples compatível com WSGI que pudermos. Como em todo código Python, certifique-se de prestar atenção à indentação:
O código acima constitui uma aplicação WSGI completa. Por padrão, o uWSGI procurará por uma função chamada application
, que é por isso que chamamos nossa função de application
. Como pode ver, ela recebe dois parâmetros.
O primeiro chamamos de environ
porque será um dicionário chave-valor semelhante a variáveis de ambiente. O segundo é chamado de start_response
e é o nome que a aplicação usará internamente para se referir à função chamável do servidor web (uWSGI) que é enviada. Ambos os nomes de parâmetros foram simplesmente selecionados por causa de seu uso nos exemplos na especificação PEP 333 que define as interações WSGI.
Nossa aplicação precisa pegar essas informações e fazer duas coisas. Primeiro, ela precisa chamar a função chamável que recebeu com um código de status HTTP e quaisquer cabeçalhos que deseja enviar de volta. Neste caso, estamos enviando uma resposta “200 OK” e definindo o cabeçalho Content-Type
para text/html
.
Em segundo lugar, ela precisa retornar um iterável para usar 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 poderá processar toda a string com uma iteração.
Num cenário real, este arquivo provavelmente seria usado como um link para o resto 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 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 ouvir na porta 8080
. Passaremos a ele o nome do script (sem sufixo):
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 verificar que isso funciona.
Concluímos o projeto de nossa aplicação real neste ponto. Você pode desativar nosso ambiente virtual, se desejar:
deactivate
Configure um Arquivo de Configuração do 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, chamaremos o arquivo myapp.ini
e o colocaremos em nossa pasta de aplicativos:
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á o ponto de entrada 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 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 estaremos configurando 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 usará uwsgi
por padrão.
Uma vez que estamos projetando esta configuração para uso com o Nginx, também vamos mudar de usar uma porta de rede e usar um socket Unix em vez disso. 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, pois criaremos um arquivo Upstart para iniciar nossa aplicação na inicialização. O Upstart e o 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á pronto para ser usado com um script Upstart.
Criar um Arquivo Upstart para Gerenciar o Aplicativo
Podemos iniciar uma instância do uWSGI na inicialização 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 iniciado 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 em modo de usuário único):
description "uWSGI instance to serve myapp"
start on runlevel [2345]
stop on runlevel [!2345]
Em seguida, vamos informar 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 para o usuário www-data
que o Nginx usa, no entanto. Isso é necessário porque o servidor web precisa ser capaz de ler e escrever no soquete que nosso arquivo .ini
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 a fazer. Poderíamos simplesmente fornecer o caminho completo para o executável do uWSGI, mas em vez disso, ativaremos o ambiente virtual. Isso facilitaria se estivéssemos dependendo de software adicional instalado no ambiente.
Para fazer isso, usaremos um bloco script
. Dentro dele, mudaremos para o diretório de nossa aplicação, ativaremos o ambiente virtual (devemos usar .
em scripts em vez de source
) e iniciaremos 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 no boot. Você pode parar o serviço a qualquer momento digitando:
sudo stop myapp
Configure o Nginx para Proxar para uWSGI
Neste ponto, temos um aplicativo WSGI e verificamos que o uWSGI pode ler e servi-lo. 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 proxy usando o protocolo uwsgi
para se comunicar com o uWSGI. Este é um protocolo mais rápido do que o HTTP e terá melhor desempenho.
A configuração do Nginx que iremos configurar é 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 que este bloco de servidor deve responder. Em 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 estes é chamado uwsgi_params
. Em seguida, encaminharemos 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;
}
}
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 um número de servidores uWSGI upstream fora deste bloco e então passá-los para isso. Poderíamos incluir alguns parâmetros uWSGI adicionais. Também poderíamos lidar com quaisquer arquivos estáticos diretamente do Nginx e passar apenas as solicitações dinâmicas para a instância uWSGI.
Não precisamos de nenhuma dessas funcionalidades em nossa aplicação de três linhas, então podemos salvar e fechar o arquivo.
Habilite 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
Verifique o arquivo de configuração em busca de erros de sintaxe:
sudo service nginx configtest
Se ele retornar que nenhum problema foi detectado, reinicie o servidor para implementar suas alterações:
sudo service nginx restart
Depois que o Nginx reiniciar, você deverá ser capaz de acessar o nome de domínio do seu servidor ou endereço IP (sem um número de porta) e ver a aplicação que você configurou:
Conclusão
Se você chegou até aqui, você criou uma aplicação WSGI simples e tem alguma compreensão de como aplicações mais complexas precisariam ser projetadas. Nós instalamos o contêiner/servidor de aplicação uWSGI em um ambiente virtual feito sob medida para servir nossa aplicação. Criamos um arquivo de configuração e um script Upstart para automatizar esse processo. Em frente ao servidor uWSGI, configuramos um proxy reverso Nginx que pode se comunicar com o processo uWSGI usando o protocolo de fio 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 múltiplas aplicações usando algo chamado “modo imperador”. Você pode expandir a configuração do Nginx para balancear a carga entre as instâncias uWSGI ou para lidar com arquivos estáticos para sua aplicação. Ao servir múltiplas aplicações, pode ser do seu melhor 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ê deverá ser capaz de ajustar sua configuração para acomodar muitos cenários diferentes.