Aula 5
Transformando em uma aplicação com Flask
e Docker
em um servidor HTTP estático com Nginx
Primeiro, devemos entender um pouco sobre o que é uma aplicação web e sobre o protocolo HTTP.
WSGI
-
CGI (Common Gateway Interface): CGI é uma especificação para transferir informações entre um servidor de informações e um programa de aplicação. Um programa CGI é qualquer programa projetado para aceitar e retornar dados que são transmitidos por meio de um servidor web.
Início da web
-
WSGI (Web Server Gateway Interface): WSGI é uma especificação para uma interface entre servidores web e aplicações web escritas em Python.
Os navegadores fazem uma requisição ao servidor web, então o WSGI deve procurar um módulo Python que realizará as operações e entregará a resposta.
Devemos então criar um servidor WSGI para lidar com as requisições da nossa aplicação. Uma opção é utilizarmos o pacote
gunicorn
, que é um servidor WSGI para Python, funcionando como uma camada por cima do Flask.
Comando de instalação:
pip install gunicorn
E configuração no Dockerfile:
FROM python:3.10.2
WORKDIR /usr/src/app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
ENTRYPOINT ["gunicorn", "-w", "4", "-b", "0.0.0.0:5000", "app:app"]
O gunicorn
deve funcionar como um servidor de PRODUÇÃO.
Por que não usar o Flask?
- O Flask é um servidor de desenvolvimento; ele não é recomendado para produção. Ele é lento e menos seguro em
comparação com um servidor WSGI como o
gunicorn
.
NGINX
O NGINX
é um servidor web de código aberto que também pode ser usado como um servidor de proxy reverso, balanceador de
carga, servidor de email e servidor de streaming de mídia.
- O que é um proxy reverso? Um proxy reverso é um servidor que fica entre os clientes e os servidores de backend, encaminhando as solicitações dos clientes para os servidores apropriados e retornando as respostas dos servidores aos clientes. Ele atua como um intermediário, gerenciando e distribuindo o tráfego de rede.
Primeiro, devemos fazer ambos rodarem no mesmo container e também hospedar a nossa página de cadastro junto com o
NGINX
(apesar de não ser recomendado justamente pela ideia dos containers ser desacoplamento).
O NGINX deve verificar de onde a request está vindo e realizar o redirecionamento para o serviço solicitado.
Exemplo: (url request com /api/... )
Podemos setar uma variável de ambiente dentro do Dockerfile com:
ENV VARIABLE_NAME value
Exemplo completo do Dockerfile combinando Flask, Gunicorn e Nginx:
FROM python:3.10.2-slim
# Instalação do nginx
# Vamos rodar apt update sem precisar interagir. Essa flag diz isso.
ENV DEBIAN_FRONTEND=noninteractive
# Agora vamos instalar nginx, passando a opção -y para aceitar os prompts
# Após a instalação, vamos remover os caches criados pelo apt update,
# Assim nossa imagem fica mais limpa
RUN apt update && apt install -y nginx && rm -rf /var/lib/apt/lists/*
# Vamos copiar os arquivos estáticos para www (pasta configurada para o nginx)
COPY webclient/* /www/
# E agora vamos copiar o arquivo de configuração do nginx
COPY default /etc/nginx/sites-available/default
# A porta 80 será a utilizada pelo nginx. Vamos expô-la
EXPOSE 80/tcp
# Agora vamos às configurações do nosso app original
WORKDIR /usr/src/app
# Aqui vamos copiar os arquivos um a um, pois agora temos outros
# arquivos que não fazem parte da aplicação e não faz sentido copiar
COPY requirements.txt ./
COPY model.sav ./
COPY app.py ./
RUN pip install --no-cache-dir -r requirements.txt
# Agora vamos copiar um script que roda as duas coisas
# nginx e gunicorn
COPY entrypoint.sh ./
RUN chmod 755 ./entrypoint.sh
CMD ["./entrypoint.sh"]
Também tem um novo arquivo, chamado default (sem extensão mesmo). Esse arquivo é a configuração do NGINX
. Veja seu
conteúdo:
server {
listen 80;
location / {
root /usr/src/app/static;
try_files $uri $uri/ =404;
}
location /api {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Com essas configurações, temos um ambiente Docker que executa a aplicação Flask usando Gunicorn como servidor WSGI e Nginx como proxy reverso, hospedando também arquivos estáticos.
Requisições que chegarem na URL raiz ("/") serão redirecionadas para a pasta www da imagem, que é onde mandamos copiar os arquivos estáticos. É o primeiro caso de uso que explicamos acima (arquivos estáticos que são tratados pelo nginx sozinho); e
Requisições que chegarem em URLs que tem "/api/" serão reescritas (rewrite) para suprimir esse trecho, e em seguida redirecionadas para o gunicorn, que vai rodar na porta 5000. É o segundo caso (requisições que serão repassadas para o gunicorn, para serem tratadas pelo nosso aplicativo Python).
#!/bin/bash
gunicorn -w 4 -b 127.0.0.1:5000 app:app &
nginx -g 'daemon off;' &
wait -n
exit $?