Este guia apresenta as práticas recomendadas para projetar, implementar, testar e implantar um serviço do Knative serving. Para mais dicas, consulte Como migrar um serviço atual.
Como gravar serviços eficazes
Esta seção descreve as práticas recomendadas gerais para projetar e implementar um serviço Knative serving.
Como evitar atividades em segundo plano
Quando um aplicativo em execução no Knative serving termina de processar uma solicitação, o acesso da instância do contêiner à CPU é desativado ou gravemente limitado. Portanto, não inicie linhas de execução ou rotinas em segundo plano que sejam executadas fora do escopo dos gerenciadores de solicitações.
O uso de linhas de execução em segundo plano pode resultar em um comportamento inesperado, porque qualquer solicitação seguinte à mesma instância de contêiner retoma qualquer atividade em segundo plano suspensa.
Uma atividade em segundo plano é qualquer evento ocorrido depois que sua resposta HTTP foi entregue. Revise seu código para certificar-se de que todas as operações assíncronas terminem antes de entregar sua resposta.
Se você suspeita que pode haver uma atividade em segundo plano no serviço que não seja imediatamente aparente, é possível verificar seus registros: procure por qualquer coisa que esteja registrada após a entrada para a solicitação HTTP.
Como excluir arquivos temporários
No ambiente do Cloud Run, o armazenamento em disco é um sistema de arquivos na memória. Os arquivos gravados no disco consomem memória de outra forma disponível para seu serviço e podem persistir entre as chamadas. Deixar de excluir esses arquivos pode resultar em um erro de memória insuficiente e uma inicialização a frio subsequente.
Como otimizar o desempenho
Nesta seção, você verá as práticas recomendadas para otimizar o desempenho.
Como iniciar os serviços rapidamente
Como as instâncias de contêiner são escalonadas conforme necessário, um método tradicional é inicializar o ambiente de execução completamente. Esse tipo de inicialização é chamado de "inicialização a frio". Se uma solicitação do cliente acionar uma inicialização a frio, a inicialização da instância do contêiner resultará em latência adicional.
A rotina de inicialização engloba estas etapas:
- Início do serviço
- Inicialização do contêiner
- Execução do comando entrypoint para iniciar seu servidor.
- Verificação da porta de serviço aberta
A otimização da velocidade de inicialização do serviço minimiza a latência que atrasa uma instância de contêiner no atendimento às solicitações.
Como usar dependências com sabedoria
Se você usar uma linguagem dinâmica com bibliotecas dependentes, como a importação de módulos no Node.js, o tempo de carregamento desses módulos adicionará latência durante uma inicialização a frio. Reduza a latência de inicialização das maneiras a seguir:
- Minimize o número e o tamanho das dependências para criar um serviço enxuto.
- Desacelere o código de carregamento que é usado com pouca frequência, se a linguagem for compatível com ele.
- Use otimizações de carregamento de código, como a otimização do carregador automático do composer do PHP.
Como usar variáveis globais
No Knative serving, não é possível presumir que o estado do serviço seja preservado entre solicitações. No entanto, o Knative serving reutiliza instâncias de contêiner individuais para veicular o tráfego em andamento, para você declarar uma variável no escopo global e permitir que o valor seja reutilizado em chamadas subsequentes. Não é possível saber com antecedência se alguma solicitação individual recebe o benefício dessa reutilização.
Também é possível armazenar em cache objetos na memória, se eles são caros para recriar em cada solicitação de serviço. Mover isso da lógica de solicitação para o escopo global melhora o desempenho.
Node.js
Python
Go
Java
Como realizar a inicialização lenta de variáveis globais
A inicialização de variáveis globais sempre ocorre durante a inicialização, o que aumenta o tempo de inicialização a frio. Use a inicialização lenta para objetos usados com pouca frequência para adiar a cobrança relacionada ao tempo e diminuir os tempos de inicialização a frio.
Node.js
Python
Go
Java
Como otimizar a simultaneidade
As instâncias do Knative serving podem atender a várias solicitações ao mesmo tempo,
"simultaneamente", até uma simultaneidade máxima configurável.
Isso é diferente das funções do Cloud Run, que usa concurrency = 1
.
Continue usando a configuração de simultaneidade máxima padrão, a menos que seu código tenha requisitos específicos de simultaneidade.
Como ajustar a simultaneidade para seu serviço
É possível que o número de solicitações simultâneas que cada instância de contêiner pode exibir seja limitado pela pilha de tecnologia e pelo uso de recursos compartilhados, como variáveis e conexões de banco de dados.
Para otimizar seu serviço e ter máxima simultaneidade estável:
- otimize seu desempenho de serviço;
- defina o nível esperado de suporte de simultaneidade em qualquer configuração de simultaneidade no nível de código. Nem todas as pilhas de tecnologia exigem essa configuração;
- implante seu serviço;
- Defina a simultaneidade do Knative serving no seu serviço como igual ou menor que qualquer outra configuração no nível do código. Se não houver configuração no nível de código, use sua simultaneidade esperada;
- Use ferramentas de teste de carga que sejam compatíveis com uma simultaneidade configurável. Confirme que seu serviço permanece estável sob a carga e simultaneidade esperadas;
- se o serviço não funcionar bem, vá para a etapa 1 para melhorá-lo ou para a etapa 2 para reduzir a simultaneidade. Se o serviço funcionar bem, volte para a etapa 2 e aumente a simultaneidade.
Continue iterando até encontrar a simultaneidade estável máxima.
Como corresponder a memória com a simultaneidade
Cada solicitação que o serviço gerencia requer uma quantidade adicional de memória. Então, quando você ajusta a simultaneidade para cima ou para baixo, ajuste seu limite de memória também.
Como evitar o estado global mutável
Se você quiser aproveitar o estado global mutável em um contexto simultâneo, execute etapas adicionais no código para garantir que isso seja feito com segurança. Minimize a contenção limitando as variáveis globais à inicialização única e reutilize conforme descrito acima em Desempenho.
Se você usar variáveis globais mutáveis em um serviço que atenda a várias solicitações ao mesmo tempo, certifique-se de usar bloqueios ou mutexes para evitar disputas.
Segurança do contêiner
Muitas práticas de segurança de software de uso geral se aplicam a aplicativos em contêiner. Há algumas práticas que são específicas para contêineres ou que se alinham à filosofia e arquitetura deles.
Para melhorar a segurança do contêiner, siga estas recomendações:
Use imagens de base seguras e mantidas ativamente, como as imagens de base do Google ou as imagens oficiais do Docker Hub.
Aplique atualizações de segurança aos serviços, reconstruindo regularmente as imagens de contêiner e reimplantando os serviços.
Inclua no contêiner apenas o que for necessário para executar seu serviço. Códigos, pacotes e ferramentas extras podem deixar a segurança mais vulnerável. Confira acima o impacto relacionado ao desempenho.
Implemente um processo de compilação determinista que inclua versões específicas de software e biblioteca. Isso impede que códigos não verificados sejam incluídos no contêiner.
Defina o contêiner para ser executado como um usuário diferente de
root
com a instruçãoUSER
do Dockerfile (em inglês). Algumas imagens de contêiner talvez já tenham um usuário específico configurado.
Como automatizar a verificação de segurança
Ative a verificação de vulnerabilidades para a verificação de segurança de imagens de contêiner armazenadas no Artifact Registry.
Também é possível usar a autorização binária para garantir que apenas imagens de contêiner seguras sejam implantadas.
Como criar imagens mínimas de contêiner
É possível que imagens grandes de contêiner aumentem as vulnerabilidades de segurança porque elas contêm mais do que o código precisa.
No Knative serving, o tamanho da imagem do contêiner não afeta a inicialização a frio ou o tempo de processamento da solicitação e não conta para a memória disponível do contêiner.
Para criar um contêiner mínimo, utilize uma imagem básica enxuta, como estas:
O Ubuntu é maior em tamanho, mas é uma imagem de base comumente usada com um ambiente de servidor pronto para uso mais completo.
Se seu serviço tem um processo de criação de ferramentas pesadas, considere o uso de compilações em vários estágios para manter o contêiner leve no ambiente de execução.
Estes recursos fornecem informações adicionais sobre a criação de imagens de contêineres enxutas:
- Práticas recomendadas do Kubernetes: como e por que criar pequenas imagens de contêiner
- Sete práticas recomendadas para a compilação de contêineres