Neste guia, descrevemos otimizações para serviços do Cloud Run escritos na linguagem de programação Python, além de informações básicas para ajudar você a entender as contrapartidas envolvidas em algumas das otimizações. As informações desta página complementam as dicas gerais de otimização, que também se aplicam ao Python.
Muitas das práticas recomendadas e otimizações desses aplicativos tradicionais em Python baseados na Web giram em torno de:
- como processar solicitações simultâneas (E/S com base em linhas de execução e sem bloqueio);
- como reduzir a latência de resposta usando o pool de conexões e agrupando em lote funções não críticas, como o envio de traces e métricas para tarefas em segundo plano.
Otimizar a imagem do contêiner
Ao otimizar a imagem do contêiner, é possível reduzir os tempos de carregamento e de inicialização. É possível otimizar a imagem:
- Colocar no contêiner apenas o que o aplicativo precisar no ambiente de execução
- Como otimizar o servidor WSGI
Só coloque no contêiner o que o aplicativo precisar no ambiente de execução
Considere quais componentes estão incluídos no contêiner e se eles são necessários para a execução do serviço. Há várias maneiras de minimizar a imagem do contêiner:
- Usar uma imagem de base menor
- Mova arquivos grandes para fora do contêiner
Usar uma imagem de base menor
O Docker Hub fornece várias imagens de base oficiais do Python que podem ser usadas se optar por não instalar o Python pela origem nos contêineres. Elas são baseadas no sistema operacional Debian.
Se você estiver usando a imagem python
do Docker Hub, use a versão slim
.
O tamanho dessas imagens é menor porque elas não vêm com vários
pacotes que seriam usados para criar indicadores, por exemplo, que podem não ser necessários
para o aplicativo. Por exemplo, a imagem Python vem com o compilador
GNU C, pré-processador e utilitários principais.
Para identificar os dez maiores pacotes em uma imagem base, execute este comando:
DOCKER_IMAGE=python # or python:slim
docker run --rm ${DOCKER_IMAGE} dpkg-query -Wf '${Installed-Size}\t${Package}\t${Description}\n' | sort -n | tail -n10 | column -t -s $'\t'
Como há menos desses pacotes de baixo nível, as imagens baseadas em slim
também
oferecem menos superfície de ataque para possíveis vulnerabilidades. Essas imagens
podem não incluir os elementos necessários para criar indicadores a partir da origem.
É possível adicionar pacotes específicos novamente adicionando uma linha RUN apt install
ao
Dockerfile. Veja mais sobre como usar os pacotes do sistema no Cloud Run.
Também há opções para contêineres não baseados no Debian. A opção
python:alpine
pode resultar em um contêiner muito menor, mas muitos pacotes do Python podem não
ter indicadores pré-compilados compatíveis com sistemas baseados em alpino. O suporte está melhorando
(consulte PEP-656), mas continua
variado. Também é possível usar o
distroless base image
,
que não contém nenhum gerenciador de pacotes, shell ou qualquer outro programa.
Mover arquivos grandes para fora do contêiner
Arquivos grandes, como recursos de mídia etc., não precisam ser incluídos no contêiner de base.
O Google Cloud oferece várias opções de hospedagem, como o Cloud Storage, para armazenar esses itens grandes. Mova grandes recursos para esses serviços e faça referência a eles a partir do aplicativo no ambiente de execução.
Otimizar o servidor WSGI
O Python padronizou a maneira como os aplicativos podem interagir com servidores da Web
pela implementação do padrão WSGI,
PEP-3333. Um dos servidores WSGI
mais comuns é gunicorn
, usado em grande parte da documentação de amostra.
Otimizar o Gunicorn
Adicione o seguinte CMD
a Dockerfile
para otimizar a invocação de
gunicorn
:
CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app
Se você pretende alterar essas configurações, ajuste o número de workers e linhas de execução por aplicativo. Por exemplo, tente usar um número de workers igual aos núcleos disponíveis e verifique se há uma melhoria no desempenho e ajuste o número de linhas de execução. A configuração de muitos workers ou linhas de execução pode ter um impacto negativo, como latência de inicialização a frio mais longa, mais memória consumida, solicitações menores por segundo etc.
Por padrão, gunicorn
gera workers e ouve a porta especificada ao
iniciar, mesmo antes de avaliar o código do aplicativo. Nesse caso, você
precisa configurar sondas de inicialização personalizadas
para seu serviço, já que a sonda de inicialização padrão do Cloud Run marca
uma instância de contêiner como saudável assim que começa a detectar em $PORT
.
Se você quiser mudar esse comportamento, invoque gunicorn
com o
Configuração --preload
para avaliar o código do aplicativo antes de escutá-lo. Estas são algumas das vantagens:
- Identificar bugs graves no ambiente de execução no momento da implantação
- Economizar recursos de memória
Considere o que o aplicativo está pré-carregando antes de realizar essa adição.
Outros servidores WSGI
Não há restrição de uso gunicorn
para executar Python em contêineres.
É possível usar qualquer servidor da Web WSGI ou ASGI, desde que o contêiner detecte na
porta HTTP $PORT
, de acordo com o
contrato de ambiente de execução do contêiner.
Alternativas comuns incluem uwsgi
,
uvicorn
e waitress
.
Por exemplo, com o arquivo main.py
contendo o objeto app
, as
seguintes invocações iniciariam um servidor WSGI:
# uwsgi: pip install pyuwsgi
uwsgi --http :$PORT -s /tmp/app.sock --manage-script-name --mount /app=main:app
# uvicorn: pip install uvicorn
uvicorn --port $PORT --host 0.0.0.0 main:app
# waitress: pip install waitress
waitress-serve --port $PORT main:app
Elas podem ser adicionadas como uma linha CMD exec
em um Dockerfile
ou como uma entrada web:
em Procfile
, quando os buildpacks do Google Cloud são usados.
Otimizar aplicativos
No código de serviço do Cloud Run, também é possível otimizar para tempos de inicialização e uso de memória mais rápidos.
Reduzir linhas de execução
Para otimizar a memória, reduza o número de linhas de execução. Para isso, use estratégias reativas sem bloqueios e evite atividades em segundo plano. Evite gravar no sistema de arquivos, conforme mencionado na página de dicas gerais.
Se você quiser oferecer suporte a atividades em segundo plano no serviço do Cloud Run, defina a CPU do serviço do Cloud Run como sempre alocada para que você possa executar atividades em segundo plano fora das solicitações e ainda assim tenha acesso à CPU.
Reduzir tarefas de inicialização
Os aplicativos em Python baseados na Web podem ter muitas tarefas a serem concluídas durante a inicialização, como pré-carregamento de dados, aquecimento do cache, estabelecimento de pools de conexão etc. Essas tarefas, quando executadas sequencialmente, podem ficar lentas. No entanto, se você quiser que elas sejam executadas em paralelo, aumente o número de núcleos de CPU.
No momento, o Cloud Run envia uma solicitação de usuário real para acionar uma instância de inicialização a frio. Os usuários que têm uma solicitação atribuída a uma instância recém-iniciada podem enfrentar atrasos demorados. O Cloud Run não tem uma verificação de "prontidão" para evitar o envio de solicitações a aplicativos que não estão prontos.
A seguir
Veja mais dicas em