Preparando para uma posição de engenheiro de aprendizado de máquina? Este artigo foi escrito para você! Por que?
Porque criaremos o protótipo do frasco e criaremos um serviço totalmente funcional e escalável. Especificamente, estaremos configurando um aplicativo de aprendizado profundo servido por Uwsgi e Nginx. Vamos explorar tudo passo a passo: de como começar a partir de um simples aplicativo de frasco, conectar -se Uwsgi Para agir como um servidor Web completo e escondê -lo atrás Nginx (como proxy reverso) para fornecer um manuseio de conexão mais robusto. Tudo isso será feito no topo do projeto de aprendizado profundo que construímos até agora, que executa segmentação semântica em imagens usando um modelo e tensorflow personalizado.
Até esse momento da série, pegamos um notebook colab, convertido em um projeto altamente otimizado com Testes de unidade e aprimoramentos de desempenho, treinou o modelo no Google Cloud, e desenvolveu um protótipo de frasco Portanto, pode ser servido aos usuários.
Aumentaremos esse protótipo de frasco e criaremos um serviço totalmente funcional e escalável.
O que é UWSGI?
De acordo com o site oficial:
O UWSGI é um servidor de aplicativos que visa fornecer uma pilha completa para o desenvolvimento e implantação de aplicativos e serviços da Web.
Como a maioria dos servidores de aplicativos, é agnóstico da linguagem, mas seu uso mais popular é para servir aplicativos Python. O UWSGI é construído sobre a especificação WSGI e se comunica com outros servidores em um protocolo de baixo nível chamado UWSGI.
Ok, isso é muito confuso e precisamos esclarecer isso, então vamos começar com algumas definições.
-
WSGI (Interface do gateway do servidor da web): um interface Especificação que define a comunicação entre um servidor da Web e um aplicativo da Web. Em termos simples, ele nos informa quais métodos devem ser implementados para passar solicitações e respostas entre o servidor e o aplicativo. É basicamente uma interface da API que está sendo usada para padronizar a comunicação e faz parte da biblioteca padrão do Python.
-
Uwsgi: Um aplicativo Servidor que se comunica com aplicativos com base nas especificações WSGI e com outros servidores da Web em vários outros protocolos (como o HTTP). Na maioria das vezes, age como um middleware, pois traduz solicitações de um servidor da web convencional em um formato que o aplicativo entende (WSGI)
-
Uwsgi: um nível baixo/binário protocolo Isso permite a comunicação entre servidores da Web. O UWSGI é um protocolo de fio que foi implementado pelo servidor UWSGI como sua maneira preferida de falar com outros servidores da Web e instâncias UWSGI. Em essência, ele define o formato dos dados enviados entre servidores e instâncias.
Vejamos o quadro completo agora: temos nosso software exposto a um aplicativo da web usando o Flask. Este aplicativo será servido usando o servidor UWSGI e se comunicará com ele usando a especificação WSGI. Além disso, o servidor UWSGI agora estará oculto atrás de outro servidor da web (no nosso caso nginx) com quem ele se comunicará usando o protocolo UWSGI. Isso faz sentido? Sério, não sei por que eles nomearam tudo isso com as mesmas iniciais!
Se eu fosse você, ainda teria mais duas perguntas.
Por que precisamos do servidor UWSGI em primeiro lugar (o Flask não é suficiente?) E por que precisamos de outro servidor da Web, como o Nginx, na frente do UWSGI?
E ambos são válidos.
Por que precisamos de UWSGI? O frasco não é adequado?
Embora o Flask possa atuar como um servidor da Web HTTP, ele não foi desenvolvido e otimizado para segurançaAssim, escalabilidadee eficiência. É antes uma estrutura construir aplicativos da web, conforme explicado no Artigo anterior. O UWSGI, por outro lado, foi criado como um servidor da Web totalmente funcional e resolve muitos problemas fora da caixa que o frasco nem sequer toca.
Exemplos são:
-
Gerenciamento de processos: Ele lida com a criação e manutenção de vários processos, para que possamos ter um aplicativo simultâneo em um ambiente e poder escalar para vários usuários.
-
Clustering: pode ser usado em um conjunto de instâncias
-
Balanceamento de carga: equilibra a carga de solicitações para diferentes processos
-
Monitoramento: Ele fornece funcionalidade fora da caixa para monitorar o desempenho e a utilização de recursos
-
Limitação de recursos: pode ser configurado para limitar a CPU e o uso de memória até um ponto específico
-
Configuração: Ele tem uma enorme variedade de opções configuráveis que nos dão controle total sobre sua execução
Dito isto, vamos inspecionar nossa próxima ferramenta: nginx.
O que é Nginx e por que precisamos?
O NGINX é um servidor web de alto desempenho, altamente escalável e altamente disponível (muito aqui). Ele atua como um balanceador de carga, um proxy reverso e um mecanismo de cache. Também pode ser usado para servir arquivos estáticos, para fornecer segurança e criptografia sobre os pedidos, para limitar-os e supostamente pode lidar com mais do que 10000 conexões simultâneas (Na verdade, na minha experiência, não é uma suposição). É basicamente UWSGI em esteróides.
O NGINX é extremamente popular e faz parte da pilha de tecnologia de muitas grandes empresas. Então, por que devemos usá -lo na frente do UWSGI? Bem, a principal razão é que simplesmente queremos o melhor dos dois mundos. Queremos que esses recursos UWSGI sejam específicos do Python, mas também gostamos de todas as funcionalidades extras que o Nginx fornece. Ok, se não esperamos que nosso aplicativo seja dimensionado para milhões de usuários, pode ser desnecessário, mas nesta série de artigos, esse é o nosso objetivo final. Além disso, é um conhecimento útil incrível para ter como engenheiro de aprendizado de máquina. Ninguém espera que sejamos especialistas, mas saber que os fundamentos não podem realmente nos machucar.
Neste exemplo, usaremos o NGINX como proxy reverso na frente do UWSGI. Um proxy reverso é simplesmente um sistema que encaminha todas as solicitações da Web para o nosso servidor da Web e voltam. É um único ponto de comunicação com o mundo exterior e vem com alguns recursos incrivelmente úteis. Primeiro de tudo, ele pode equilibrar a carga de milhões de solicitações e distribuir o tráfego uniformemente em muitas instâncias do UWSGI. Em segundo lugar, fornece um nível de segurança que pode impedir ataques e usa criptografia nas comunicações. Por último, mas não menos importante, também pode cache o conteúdo e as respostas, resultando em desempenho mais rápido.
Espero que você já esteja convencido agora. Mas o suficiente com a teoria. Eu acho que é hora de sujar as mãos e ver como todas essas coisas podem ser configuradas na prática.
Configure um servidor UWSGI com frasco
No artigo anterior, desenvolvemos um aplicativo de frasco. Ele recebe uma imagem como uma solicitação, prevê sua máscara de segmentação usando o modelo TensorFlow UNET que criamos e a devolve ao cliente.
import os
import traceback
from flask import Flask, jsonify, request
from executor.unet_inferrer import UnetInferrer
app = Flask(__name__)
APP_ROOT = os.getenv('APP_ROOT', '/infer')
HOST = "0.0.0.0"
PORT_NUMBER = int(os.getenv('PORT_NUMBER', 8080))
u_net = UnetInferrer()
@app.route(APP_ROOT, methods=("POST"))
def infer():
data = request.json
image = data('image')
return u_net.infer(image)
@app.errorhandler(Exception)
def handle_exception(e):
return jsonify(stackTrace=traceback.format_exc())
if __name__ == '__main__':
app.run(host=HOST, port=PORT_NUMBER)
O código acima permanecerá intacto porque, para utilizar o UWSGI, precisamos apenas executar algumas pequenas etapas em cima do aplicativo de frasco.
Depois de instalar UWSGI com PIP,
pip install uwsgi
Podemos simplesmente aumentar uma instância com o seguinte comando:
uwsgi --http 0.0.0.0:8080 --wsgi-file service.py --callable app
Isso diz ao UWSGI para executar um servidor em 0.0.0.0 e a porta 8080 usando o aplicativo localizado no arquivo Service.py, que é onde mora nosso código de frasco. Também precisamos fornecer um parâmetro chamável (deve ser uma função) que pode ser chamado usando a especificação WSGI. No nosso caso, é a instância do frasco que criamos e ligamos todas as rotas.
app = Flask(__name__)
Quando pressionamos Enter, um servidor UWSGI completo será gerado e podemos acessá -lo em nossa localhost.
É seriamente tão fácil! E, é claro, se executarmos o script do cliente que construímos no artigo anterior, ele atingirá o servidor UWSGI e retornará a máscara de segmentação de nosso Little Yorkshire Terrier.
Dica: observe que, em vez de passar todos os parâmetros usando a linha de comando, podemos Crie um arquivo de configuração simples e faça o servidor ler diretamente a partir dele.
De fato, geralmente é da maneira preferida, especialmente porque mais tarde implantaremos o servidor na nuvem e é muito mais fácil alterar uma opção de configuração do que alterar o comando do terminal.
Um arquivo de configuração de amostra (App.ini) pode ser assim:
(uwsgi)
http = 0.0.0.0:8080
module = app.service
callable = app
die-on-term = true
chdir = /home/aisummer/src/soft_eng_for_dl/
virtualenv = /home/aisummer/miniconda3/envs/Deep-Learning-Production-Course/
processes = 1
master = false
vacuum = true
Aqui, definimos nosso URL “HTTP” e “Callable” como antes e usamos a opção “Module” para indicar onde está localizado o módulo Python com nosso aplicativo. Além disso, precisamos especificar algumas outras coisas para evitar incorretamente o servidor, como o caminho completo do diretório do aplicativo (“chdir”), bem como o caminho do ambiente virtual (se usarmos um).
Observe também que não usamos multiprocessamento aqui (processo = 1) e temos apenas um processo mestre (mais sobre processos nos documentos). “Die-a termo” é uma opção útil que nos permite matar o servidor do terminal e o “vácuo” determina UWSGI para limpar arquivos gerados não utilizados periodicamente.
A configuração de um servidor UWSGI não é direta e precisa ser cuidadosamente examinada, porque existem muitas opções e muitos parâmetros a serem levados em consideração. Aqui não vou analisar ainda mais todas as diferentes opções e detalhes, mas sugiro procurar nos documentos oficiais. Como sempre, forneceremos links adicionais no final.
Para executar o servidor, podemos fazer:
uwsgi app.ini
Faça um fio Nginx como um proxy reverso
A próxima tarefa na lista TODO é conectar o servidor nginx, que novamente é tão simples quanto criar um arquivo de configuração.
Primeiro, instalamos-o usando “instalação apt-get”
sudo apt-get install nginx
Em seguida, queremos criar o arquivo de configuração que deve viver dentro do diretório “/etc/nginx/sites e disponível” (se você estiver no Linux) e ele deve ser nomeado após o nosso aplicativo.
sudo nano /etc/nginx/sites-available/service.conf
Em seguida, criamos um arquivo de configuração muito simples que contém apenas o mínimo absoluto para executar o proxy. Novamente, para descobrir todas as opções de configuração disponíveis, verifique a documentação oficial.
server {
listen 80;
server_name 0.0.0.0;
location {
include uwsgi_params;
uwsgi_pass unix: /home/aisummer/src/soft_eng_for_dl/app/service.sock;
}
}
Aqui, dizemos ao nginx para ouvir a porta 80 padrão para solicitações provenientes do servidor localizado em 0.0.0.0 O bloco de localização está identificando todas as solicitações provenientes da Web para o servidor UWSGI, incluindo “UWSGI_PARAMS”, que os especifica os parâmetros de UWSGI genéricos e “UWSGI_Pass” para encaminhá -los para os encaminhá -los para os sinistros e “uwsgi_pass”.
Que soquete você pode se perguntar? Não criamos um soquete. Isso mesmo. É por isso que precisamos declarar o soquete em nosso arquivo de configuração UWSGI adicionando as 2 linhas a seguir:
socket = service.sock
chmod-socket = 660
Isso instrui o servidor a ouvir no soquete 660. E lembre -se de que o Nginx fala com você UWSGI através do soquete usando o protocolo UWSGI.
Soquete: Um soquete da web é uma conexão segura bidirecional que permite uma sessão de comunicação interativa bidirecional entre um usuário e um cliente. Dessa forma, podemos enviar mensagens para um servidor e receber respostas orientadas a eventos sem precisar pesquisar o servidor para uma resposta.
Por fim, habilitamos a configuração acima, executando o comando abaixo que vincula nossa configuração no diretório “sites disponíveis” com o diretório “habilitado para sites”
sudo ln -s /etc/nginx/sites-available/service /etc/nginx/sites-enabled
E iniciamos o nginx
sudo nginx -t
Se tudo correr bem, devemos ver nosso aplicativo na localhost e podemos usar nosso cliente mais uma vez para verificar se tudo funciona conforme o esperado.
Conclusão
Usamos o UWSGI para criar um servidor a partir do nosso aplicativo Flask e ocultamos o servidor atrás do proxy reverso do NGINX para lidar com coisas como segurança e balanceamento de carga. Como resultado, temos oficialmente um aplicativo de aprendizado profundo que pode ser dimensionado para milhões de usuários sem nenhum problema. E a melhor parte é que ele pode ser implantado exatamente como está na nuvem e ser usado pelos usuários no momento. Ou você pode configurar seu próprio servidor em seu porão, mas sugerirei não fazer isso.
Devido a todas as etapas e otimização que realizamos, podemos ter certeza sobre o desempenho de nosso aplicativo. Portanto, não precisamos nos preocupar muito com coisas como latência, eficiência, segurança. E para provar que isso é realmente verdade nos próximos artigos, vamos implantar nosso aplicativo de aprendizado profundo no Google Cloud usando contêineres e Kubernetes. Mal posso esperar para te ver lá.
Como material lateral, sugiro fortemente o Tensorflow: especialização em técnicas avançadas Curso de Deeplearning.ai hospedado no Coursera, o que lhe dará um entendimento fundamental sobre o TensorFlow
Tchau…
Recursos
* Divulgação: Observe que alguns dos links acima podem ser links de afiliados e, sem nenhum custo adicional, ganharemos uma comissão se você decidir fazer uma compra depois de clicar.