Este guia fornece as práticas recomendadas para projetar, implementar, testar e implantar um serviço de exibição Knative. 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 de veiculação Knative.
Como evitar atividades em segundo plano
Quando um aplicativo em execução no Knative termina de processar uma solicitação, o acesso da instância do contêiner à CPU é desativado ou severamente 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:
- Iniciar o 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
Na exibição do Knative, não é possível presumir que o estado do serviço seja preservado entre as solicitações. No entanto, a exibição do Knative reutiliza instâncias de contêiner individuais para exibir tráfego contínuo. Assim, você pode declarar uma variável no escopo global para permitir que o valor seja reutilizado em invocações 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 objetos em cache na memória se a recriação for cara a cada solicitação de serviço. Mover isso da lógica de solicitação para o escopo global resulta em um melhor 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 de exibição do Knative podem atender a várias solicitações simultaneamente,
"simultaneamente", até uma simultaneidade máxima configurável.
Isso é diferente do Cloud Functions, 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 o serviço;
- Defina a simultaneidade de veiculação do Knative para seu serviço como igual ou menor que qualquer configuração no nível de código. Se não houver configuração no nível de código, use a 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.
Na exibição do Knative, 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 é contabilizado na 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 para criar contêineres
- Práticas recomendadas do Kubernetes: como e por que criar imagens de contêiner pequenas
- Sete práticas recomendadas para a criação de contêineres