Como servir aplicações Flask com Gunicorn e Nginx no Ubuntu 22.04

A previous version of this tutorial was written by Kathleen Juell.

Introdução

Neste guia, você irá construir uma aplicação Python utilizando o microframework Flask no Ubuntu 22.04. A maior parte deste tutorial será sobre como configurar o servidor de aplicação Gunicorn e como lançar a aplicação e configurar o Nginx para atuar como um proxy reverso de front-end.

Pré-requisitos

Antes de iniciar este guia, você deve ter:

  • Um servidor com Ubuntu 22.04 instalado e um usuário não-root com privilégios sudo. Siga nosso guia de configuração inicial do servidor para orientação.

  • O Nginx instalado, seguindo os Passos 1 e 2 de Como Instalar o Nginx no Ubuntu 22.04.

  • Um nome de domínio configurado para apontar para o seu servidor. Você pode comprar um no Namecheap ou obter um gratuitamente no Freenom. Você pode aprender como apontar domínios para o DigitalOcean seguindo a documentação relevante sobre domínios e DNS. Certifique-se de criar os seguintes registros DNS:

    • Um registro A com seu_domínio apontando para o endereço IP público do seu servidor.
    • Um registro A com www.seu_domínio apontando para o endereço IP público do seu servidor.
  • Familiaridade com a especificação WSGI, que o servidor Gunicorn usará para se comunicar com sua aplicação Flask. Esta discussão cobre o WSGI com mais detalhes.

Passo 1 — Instalando os Componentes dos Repositórios do Ubuntu

O primeiro passo será instalar todos os componentes necessários dos repositórios do Ubuntu. Isso inclui o pip, o gerenciador de pacotes Python, que gerenciará os componentes Python. Você também obterá os arquivos de desenvolvimento Python necessários para compilar alguns dos componentes do Gunicorn.

Primeiro, atualize o índice de pacotes local e instale os pacotes que permitirão que você construa seu ambiente Python. Estes incluirão python3-pip, juntamente com alguns pacotes e ferramentas de desenvolvimento adicionais necessários para um ambiente de programação robusto:

  1. sudo apt update
  2. sudo apt install python3-pip python3-dev build-essential libssl-dev libffi-dev python3-setuptools

Com esses pacotes em vigor, o próximo passo é criar um ambiente virtual para o seu projeto.

Passo 2 — Criando um Ambiente Virtual Python

Em seguida, você configurará um ambiente virtual para isolar a aplicação Flask dos outros arquivos Python em seu sistema.

Comece instalando o pacote python3-venv, que instalará o módulo venv:

  1. sudo apt install python3-venv

Em seguida, crie um diretório pai para o seu projeto Flask. Mova-se para o diretório com o comando cd após criá-lo:

  1. mkdir ~/myproject
  2. cd ~/myproject

Crie um ambiente virtual para armazenar os requisitos do projeto Python do Flask digitando:

  1. python3 -m venv myprojectenv

Isso irá instalar uma cópia local do Python e do pip em um diretório chamado myprojectenv dentro do seu diretório de projeto.

Antes de instalar aplicativos dentro do ambiente virtual, você precisa ativá-lo. Faça isso digitando:

  1. source myprojectenv/bin/activate

Seu prompt mudará para indicar que você está operando dentro do ambiente virtual. Parecerá algo assim: (myprojectenv)user@host:~/myproject$.

Passo 3 — Configurando uma Aplicação Flask

Agora que você está no seu ambiente virtual, pode instalar o Flask e o Gunicorn e começar a projetar sua aplicação.

Primeiro, instale o wheel com a instância local do pip para garantir que seus pacotes sejam instalados mesmo se estiverem faltando arquivos de roda:

  1. pip install wheel

Nota

Independentemente da versão do Python que estiver usando, quando o ambiente virtual estiver ativado, você deve usar o comando pip (não pip3).

Em seguida, instale o Flask e o Gunicorn:

  1. pip install gunicorn flask

Criando um Aplicativo de Exemplo

Agora que você tem o Flask disponível, você pode criar um aplicativo simples. Flask é um microframework. Ele não inclui muitas das ferramentas que frameworks mais completos podem ter, e existe principalmente como um módulo que você pode importar em seus projetos para ajudá-lo a inicializar um aplicativo da web.

Embora seu aplicativo possa ser mais complexo, vamos criar nosso aplicativo Flask em um único arquivo, chamado myproject.py:

  1. nano ~/myproject/myproject.py

O código do aplicativo ficará neste arquivo. Ele irá importar o Flask e instanciar um objeto Flask. Você pode usar isso para definir as funções que devem ser executadas quando uma rota específica é solicitada:

~/myproject/myproject.py
from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "<h1 style='color:blue'>Hello There!</h1>"

if __name__ == "__main__":
    app.run(host='0.0.0.0')

Isso basicamente define qual conteúdo apresentar quando o domínio raiz é acessado. Salve e feche o arquivo quando terminar.

Se você seguiu o guia de configuração inicial do servidor, deve ter um firewall UFW habilitado. Para testar o aplicativo, você precisa permitir o acesso à porta 5000:

  1. sudo ufw allow 5000

Agora você pode testar seu aplicativo Flask digitando:

  1. python myproject.py

Você verá uma saída como a seguinte, incluindo um aviso útil lembrando-o de não usar esta configuração de servidor em produção:

Output
* Serving Flask app "myproject" (lazy loading) * Environment: production WARNING: Do not use the development server in a production environment. Use a production WSGI server instead. * Debug mode: off * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)

Visite o endereço IP do seu servidor seguido por :5000 em seu navegador da web:

http://your_server_ip:5000

Você deverá ver algo assim:

Quando terminar, pressione CTRL-C na janela do terminal para parar o servidor de desenvolvimento do Flask.

Criando o Ponto de Entrada WSGI

Em seguida, crie um arquivo que servirá como ponto de entrada para sua aplicação. Isso dirá ao servidor Gunicorn como interagir com a aplicação.

Chame o arquivo de wsgi.py:

  1. nano ~/myproject/wsgi.py

Neste arquivo, importe a instância Flask de nossa aplicação e então execute-a:

~/myproject/wsgi.py
from myproject import app

if __name__ == "__main__":
    app.run()

Salve e feche o arquivo quando terminar.

Passo 4 — Configurando o Gunicorn

Sua aplicação agora está escrita com um ponto de entrada estabelecido. Agora você pode prosseguir para configurar o Gunicorn.

Antes de prosseguir, verifique se o Gunicorn pode servir a aplicação corretamente.

Você pode fazer isso passando a ele o nome do ponto de entrada da aplicação. Isso é construído como o nome do módulo (menos a extensão .py), mais o nome do chamável dentro da aplicação. Neste caso, é wsgi:app.

Também especifique a interface e a porta a serem vinculadas usando o argumento 0.0.0.0:5000 para que a aplicação seja iniciada em uma interface disponível publicamente:

  1. cd ~/myproject
  2. gunicorn --bind 0.0.0.0:5000 wsgi:app

Você deverá ver uma saída como a seguinte:

Output
[2020-05-20 14:13:00 +0000] [46419] [INFO] Starting gunicorn 20.0.4 [2020-05-20 14:13:00 +0000] [46419] [INFO] Listening at: http://0.0.0.0:5000 (46419) [2020-05-20 14:13:00 +0000] [46419] [INFO] Using worker: sync [2020-05-20 14:13:00 +0000] [46421] [INFO] Booting worker with pid: 46421

Visite novamente o endereço IP do seu servidor com :5000 adicionado ao final no seu navegador da web:

http://your_server_ip:5000

Você deverá ver a saída da sua aplicação:

Quando tiver confirmado que está funcionando corretamente, pressione CTRL-C na janela do seu terminal.

Quando terminar de usar o ambiente virtual, você pode desativá-lo:

  1. deactivate

Quaisquer comandos Python agora utilizarão novamente o ambiente Python do sistema.

Em seguida, crie o arquivo de unidade de serviço systemd. Criar um arquivo de unidade systemd permitirá que o sistema de inicialização do Ubuntu inicie automaticamente o Gunicorn e sirva a aplicação Flask sempre que o servidor for inicializado.

Crie um arquivo de unidade com extensão .service dentro do diretório /etc/systemd/system para começar:

  1. sudo nano /etc/systemd/system/myproject.service

Dentro dele, você começará com a seção [Unit], que é usada para especificar metadados e dependências. Adicione uma descrição do seu serviço aqui e informe ao sistema de inicialização para iniciar apenas após o destino de rede ter sido alcançado:

/etc/systemd/system/myproject.service
[Unit]
Description=Gunicorn instance to serve myproject
After=network.target

Em seguida, adicione uma seção [Service]. Isso especificará o usuário e o grupo nos quais você deseja que o processo seja executado. Dê propriedade da conta de usuário regular ao processo, já que ela possui todos os arquivos relevantes. Também dê propriedade do grupo ao grupo www-data para que o Nginx possa se comunicar facilmente com os processos do Gunicorn. Lembre-se de substituir o nome de usuário aqui pelo seu nome de usuário:

/etc/systemd/system/myproject.service
[Unit]
Description=Gunicorn instance to serve myproject
After=network.target

[Service]
User=sammy
Group=www-data

Em seguida, mapeie o diretório de trabalho e defina a variável de ambiente PATH para que o sistema de inicialização saiba que os executáveis do processo estão localizados dentro do nosso ambiente virtual. Também especifique o comando para iniciar o serviço. Este comando fará o seguinte:

  • Iniciar 3 processos trabalhadores (embora você deva ajustar isso conforme necessário)
  • Criar e vincular a um arquivo de socket Unix, myproject.sock, dentro do diretório do nosso projeto. Vamos definir um valor de umask de 007 para que o arquivo de socket seja criado dando acesso ao proprietário e ao grupo, enquanto restringe outro acesso
  • Especificar o nome do arquivo de entrada WSGI, juntamente com o Python chamável dentro desse arquivo (wsgi:app)

O Systemd requer que você forneça o caminho completo para o executável do Gunicorn, que está instalado dentro do seu ambiente virtual.

Lembre-se de substituir o nome de usuário e os caminhos do projeto com suas próprias informações:

/etc/systemd/system/myproject.service
[Unit]
Description=Gunicorn instance to serve myproject
After=network.target

[Service]
User=sammy
Group=www-data
WorkingDirectory=/home/sammy/myproject
Environment="PATH=/home/sammy/myproject/myprojectenv/bin"
ExecStart=/home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app

Por fim, adicione uma seção [Install]. Isso dirá ao systemd a que serviço vincular se você o habilitar para iniciar durante a inicialização. Você desejará que este serviço seja iniciado quando o sistema multiusuário regular estiver funcionando:

/etc/systemd/system/myproject.service
[Unit]
Description=Gunicorn instance to serve myproject
After=network.target

[Service]
User=sammy
Group=www-data
WorkingDirectory=/home/sammy/myproject
Environment="PATH=/home/sammy/myproject/myprojectenv/bin"
ExecStart=/home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app

[Install]
WantedBy=multi-user.target

Com isso, seu arquivo de serviço systemd está completo. Salve e feche-o agora.

Agora você pode iniciar o serviço Gunicorn que criou e habilitá-lo para que ele inicie na inicialização:

  1. sudo systemctl start myproject
  2. sudo systemctl enable myproject

Vamos verificar o status:

  1. sudo systemctl status myproject

Você deverá ver uma saída como esta:

Output
● myproject.service - Gunicorn instance to serve myproject Loaded: loaded (/etc/systemd/system/myproject.service; enabled; vendor preset: enabled) Active: active (running) since Tue 2022-05-10 19:40:41 UTC; 9s ago Main PID: 17300 (gunicorn) Tasks: 4 (limit: 2327) Memory: 56.0M CPU: 514ms CGroup: /system.slice/myproject.service ├─17300 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app ├─17301 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app ├─17302 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app └─17303 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app May 10 19:40:41 r systemd[1]: Started Gunicorn instance to serve myproject. . . .

Se você encontrar algum erro, certifique-se de resolvê-lo antes de continuar com o tutorial.

Passo 5 — Configurando o Nginx para Encaminhar Requisições

O seu servidor de aplicação Gunicorn deve estar agora em execução, aguardando solicitações no arquivo de soquete no diretório do projeto. Agora você pode configurar o Nginx para encaminhar solicitações web para esse soquete fazendo algumas pequenas adições ao seu arquivo de configuração.

Comece criando um novo arquivo de configuração de bloco de servidor no diretório sites-available do Nginx. Chame isso de meuprojeto para manter a coerência com o restante do guia:

  1. sudo nano /etc/nginx/sites-available/myproject

Abra um bloco de servidor e informe ao Nginx para ouvir na porta padrão 80. Informe também para usar este bloco para solicitações para o nome de domínio do nosso servidor:

/etc/nginx/sites-available/myproject
server {
    listen 80;
    server_name your_domain www.your_domain;
}

Em seguida, adicione um bloco de localização que corresponda a todas as solicitações. Dentro deste bloco, você incluirá o arquivo proxy_params que especifica alguns parâmetros gerais de proxy que precisam ser definidos. Em seguida, passe as solicitações para o soquete que você definiu usando a diretiva proxy_pass:

/etc/nginx/sites-available/myproject
server {
    listen 80;
    server_name your_domain www.your_domain;

    location / {
        include proxy_params;
        proxy_pass http://unix:/home/sammy/myproject/myproject.sock;
    }
}

Salve e feche o arquivo quando terminar.

Para habilitar a configuração de bloco de servidor do Nginx que acabou de criar, vincule o arquivo ao diretório sites-enabled:

  1. sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled

Com o arquivo nesse diretório, você pode testar por erros de sintaxe:

  1. sudo nginx -t

Se isso retornar sem indicar problemas, reinicie o processo do Nginx para ler a nova configuração:

  1. sudo systemctl restart nginx

Finalmente, ajuste o firewall novamente. Você não precisa mais de acesso pela porta 5000, então pode remover essa regra. Em seguida, permita acesso total ao servidor Nginx:

  1. sudo ufw delete allow 5000
  2. sudo ufw allow 'Nginx Full'

Agora você deverá conseguir navegar até o nome de domínio do seu servidor em seu navegador da web:

http://your_domain

Você deverá ver a saída do seu aplicativo:

Observação: Você receberá um erro de gateway HTTP 502 se o Nginx não puder acessar o arquivo de soquete do gunicorn. Normalmente isso acontece porque o diretório home do usuário não permite que outros usuários acessem arquivos dentro dele.

Se o seu arquivo de soquete for chamado /home/sammy/myproject/myproject.sock, certifique-se de que /home/sammy tenha um mínimo de permissões 0755. Você pode usar uma ferramenta como chmod para alterar as permissões assim:

  1. sudo chmod 755 /home/sammy

Depois recarregue a página para ver se o erro HTTP 502 desaparece.

Se você encontrar algum erro, tente verificar o seguinte:

  • sudo less /var/log/nginx/error.log: verifica os logs de erro do Nginx.
  • sudo less /var/log/nginx/access.log: verifica os logs de acesso do Nginx.
  • sudo journalctl -u nginx: verifica os logs de processo do Nginx.
  • sudo journalctl -u myproject: verifica os logs do Gunicorn do seu aplicativo Flask.

Passo 6 — Segurança da Aplicação

Para garantir que o tráfego para o seu servidor permaneça seguro, vamos obter um certificado SSL para o seu domínio. Existem várias maneiras de fazer isso, incluindo obter um certificado gratuito do Let’s Encrypt, gerar um certificado autoassinado, ou comprar um de outro provedor e configurar o Nginx para usá-lo seguindo as Etapas 2 a 6 de Como Criar um Certificado SSL Autoassinado para o Nginx no Ubuntu 22.04. Vamos usar a opção um (Let’s Encrypt) por uma questão de rapidez.

Instale o pacote do Nginx do Certbot com apt:

  1. sudo apt install python3-certbot-nginx

O Certbot oferece uma variedade de maneiras de obter certificados SSL por meio de plugins. O plugin do Nginx cuidará de reconfigurar o Nginx e recarregar a configuração sempre que necessário. Para usar este plugin, digite o seguinte:

  1. sudo certbot --nginx -d your_domain -d www.your_domain

Isso executa o certbot com o plugin --nginx, usando -d para especificar os nomes para os quais gostaríamos que o certificado fosse válido.

Se esta for a primeira vez que você executa o certbot, você será solicitado a inserir um endereço de e-mail e concordar com os termos de serviço. Após fazer isso, o certbot irá se comunicar com o servidor Let’s Encrypt e, em seguida, executar um desafio para verificar que você controla o domínio para o qual está solicitando um certificado.

Se isso for bem-sucedido, o certbot perguntará como você gostaria de configurar suas configurações HTTPS:

Output
Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access. ------------------------------------------------------------------------------- 1: No redirect - Make no further changes to the webserver configuration. 2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for new sites, or if you're confident your site works on HTTPS. You can undo this change by editing your web server's configuration. ------------------------------------------------------------------------------- Select the appropriate number [1-2] then [enter] (press 'c' to cancel):

Selecione sua escolha e pressione ENTER. A configuração será atualizada, e o Nginx será recarregado para pegar as novas configurações. O certbot irá concluir com uma mensagem informando que o processo foi bem-sucedido e onde seus certificados estão armazenados:

Output
IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/your_domain/fullchain.pem Your key file has been saved at: /etc/letsencrypt/live/your_domain/privkey.pem Your cert will expire on 2020-08-18. To obtain a new or tweaked version of this certificate in the future, simply run certbot again with the "certonly" option. To non-interactively renew *all* of your certificates, run "certbot renew" - Your account credentials have been saved in your Certbot configuration directory at /etc/letsencrypt. You should make a secure backup of this folder now. This configuration directory will also contain certificates and private keys obtained by Certbot so making regular backups of this folder is ideal. - If you like Certbot, please consider supporting our work by: Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate Donating to EFF: https://eff.org/donate-le

Se você seguiu as instruções de instalação do Nginx nos pré-requisitos, você não precisará mais da permissão redundante de perfil HTTP:

  1. sudo ufw delete allow 'Nginx HTTP'

Para verificar a configuração, navegue novamente para o seu domínio, usando https://:

https://your_domain

Você deverá ver novamente a saída do seu aplicativo, juntamente com o indicador de segurança do seu navegador, que deve indicar que o site está seguro.

Conclusão

Neste guia, você criou e protegeu um aplicativo Flask simples dentro de um ambiente virtual Python. Você criou um ponto de entrada WSGI para que qualquer servidor de aplicativos capaz de WSGI possa se comunicar com ele e, em seguida, configurou o servidor de aplicativos Gunicorn para fornecer essa função. Em seguida, você criou um arquivo de serviço systemd para iniciar automaticamente o servidor de aplicativos durante a inicialização. Você também criou um bloco de servidor Nginx que encaminha o tráfego do cliente da web para o servidor de aplicativos, transmitindo solicitações externas e protegendo o tráfego para o seu servidor com Let’s Encrypt.

O Flask é um framework muito simples, mas extremamente flexível, destinado a fornecer funcionalidades para suas aplicações sem ser muito restritivo quanto à estrutura e design. Você pode usar a pilha geral descrita neste guia para servir as aplicações Flask que você desenvolve.

Source:
https://www.digitalocean.com/community/tutorials/how-to-serve-flask-applications-with-gunicorn-and-nginx-on-ubuntu-22-04