Este guia fornece práticas recomendadas para conceber, implementar, testar e implementar um serviço de fornecimento do Knative. Para mais sugestões, consulte o artigo Migrar um serviço existente.
Escrever serviços eficazes
Esta secção descreve as práticas recomendadas gerais para conceber e implementar um serviço de publicação do Knative.
Evitar atividades em segundo plano
Quando uma aplicação em execução no Knative serving termina o processamento de um pedido, o acesso da instância do contentor ao CPU é desativado ou severamente limitado. Por conseguinte, não deve iniciar threads ou rotinas em segundo plano que sejam executadas fora do âmbito dos controladores de pedidos.
A execução de threads em segundo plano pode resultar num comportamento inesperado, uma vez que qualquer pedido subsequente à mesma instância do contentor retoma qualquer atividade em segundo plano suspensa.
A atividade em segundo plano é tudo o que acontece depois de a resposta HTTP ter sido enviada. Reveja o seu código para se certificar de que todas as operações assíncronas terminam antes de enviar a resposta.
Se suspeitar que pode existir atividade em segundo plano no seu serviço que não seja facilmente visível, pode verificar os registos: procure tudo o que seja registado após a entrada do pedido HTTP.
Eliminar ficheiros temporários
No ambiente do Cloud Run, o armazenamento em disco é um sistema de ficheiros na memória. Os ficheiros escritos no disco consomem memória que, de outra forma, estaria disponível para o seu serviço e podem persistir entre invocações. Se não eliminar estes ficheiros, pode ocorrer um erro de falta de memória e um arranque a frio subsequente.
Otimizar o desempenho
Esta secção descreve as práticas recomendadas para otimizar o desempenho.
Iniciar serviços rapidamente
Uma vez que as instâncias de contentores são dimensionadas conforme necessário, um método típico consiste em inicializar completamente o ambiente de execução. Este tipo de inicialização é denominado "arranque a frio". Se um pedido do cliente acionar um início a frio, o início da instância do contentor resulta numa latência adicional.
A rotina de arranque consiste no seguinte:
- A iniciar o serviço
- A iniciar o contentor
- Executar o comando entrypoint para iniciar o servidor.
- Verificar a porta de serviço aberta.
A otimização para a velocidade de arranque do serviço minimiza a latência que atrasa uma instância de contentor a servir pedidos.
Usar as dependências de forma inteligente
Se usar uma linguagem dinâmica com bibliotecas dependentes, como importar módulos no Node.js, o tempo de carregamento desses módulos adiciona latência durante um arranque a frio. Reduza a latência do arranque das seguintes formas:
- Minimize o número e o tamanho das dependências para criar um serviço simples.
- Carregue em diferido o código que é usado com pouca frequência, se o seu idioma o suportar.
- Use otimizações de carregamento de código, como a otimização do carregamento automático do Composer do PHP.
Usar variáveis globais
No Knative serving, não pode assumir que o estado do serviço é preservado entre pedidos. No entanto, o Knative serving reutiliza instâncias de contentores individuais para publicar tráfego contínuo, pelo que pode declarar uma variável no âmbito global para permitir que o respetivo valor seja reutilizado em invocações subsequentes. Não é possível saber antecipadamente se um pedido individual vai receber o benefício desta reutilização.
Também pode colocar objetos em cache na memória se for dispendioso recriá-los em cada pedido de serviço. Mover isto da lógica de pedido para o âmbito global resulta num melhor desempenho.
Node.js
Python
Ir
Java
Execução da inicialização tardia de variáveis globais
A inicialização de variáveis globais ocorre sempre durante o arranque, o que aumenta o tempo de início a frio. Use a inicialização tardia para objetos usados com pouca frequência para adiar o custo de tempo e diminuir os tempos de início a frio.
Node.js
Python
Ir
Java
Otimizar a simultaneidade
As instâncias de publicação do Knative podem publicar vários pedidos em simultâneo, "em simultâneo", até uma simultaneidade máxima configurável.
Isto é diferente das funções do Cloud Run, que usam concurrency = 1.
Deve manter a predefinição de simultaneidade máxima, a menos que o seu código tenha requisitos de simultaneidade específicos.
Ajustar a simultaneidade do seu serviço
O número de pedidos simultâneos que cada instância do contentor pode publicar pode ser limitado pela pilha de tecnologia e pela utilização de recursos partilhados, como variáveis e ligações à base de dados.
Para otimizar o seu serviço em função da simultaneidade estável máxima:
- Otimize o desempenho do seu serviço.
- Defina o nível esperado de suporte de simultaneidade em qualquer configuração de simultaneidade ao nível do código. Nem todas as plataformas tecnológicas requerem esta definição.
- Implemente o seu serviço.
- Defina a simultaneidade de publicação do Knative para o seu serviço igual ou inferior a qualquer configuração ao nível do código. Se não existir uma configuração ao nível do código, use a concorrência esperada.
- Use ferramentas de teste de carga que suportem uma concorrência configurável. Tem de confirmar que o seu serviço permanece estável sob a carga e a concorrência esperadas.
- Se o serviço tiver um desempenho fraco, avance para o passo 1 para melhorar o serviço ou para o passo 2 para reduzir a simultaneidade. Se o serviço tiver um bom desempenho, regresse ao passo 2 e aumente a simultaneidade.
Continue a iterar até encontrar a concorrência estável máxima.
Fazer corresponder a memória à concorrência
Cada pedido que o seu serviço processa requer uma certa quantidade de memória adicional. Por isso, quando ajustar a concorrência para cima ou para baixo, certifique-se de que também ajusta o limite de memória.
Evitar o estado global mutável
Se quiser tirar partido do estado global mutável num contexto concorrente, tome medidas adicionais no seu código para garantir que isto é feito em segurança. Minimize a contenção limitando as variáveis globais à inicialização única e à reutilização, conforme descrito acima em Desempenho.
Se usar variáveis globais mutáveis num serviço que processa vários pedidos ao mesmo tempo, certifique-se de que usa bloqueios ou exclusões mútuas para evitar condições de corrida.
Segurança do contentor
Muitas práticas de segurança de software de fins gerais aplicam-se a aplicações contentorizadas. Existem algumas práticas específicas dos contentores ou que se alinham com a filosofia e a arquitetura dos contentores.
Para melhorar a segurança do contentor:
Use imagens base seguras e ativamente mantidas, como as imagens base da Google ou as imagens oficiais do Docker Hub.
Aplique atualizações de segurança aos seus serviços recriando regularmente imagens de contentores e reimplementando os seus serviços.
Inclua no contentor apenas o que for necessário para executar o seu serviço. O código, os pacotes e as ferramentas adicionais são potenciais vulnerabilidades de segurança. Consulte acima o impacto no desempenho relacionado.
Implemente um processo de compilação determinístico que inclua versões específicas de software e bibliotecas. Isto impede a inclusão de código não validado no seu contentor.
Defina o contentor para ser executado como um utilizador diferente de
rootcom a declaração DockerfileUSER. Algumas imagens de contentores podem já ter um utilizador específico configurado.
Automatizar a análise de segurança
Ative a análise de vulnerabilidades para a análise de segurança de imagens de contentores armazenadas no Artifact Registry.
Também pode usar a autorização binária para garantir que apenas são implementadas imagens de contentores seguras.
Criar imagens de contentores mínimas
As imagens de contentores grandes aumentam provavelmente as vulnerabilidades de segurança porque contêm mais do que o código precisa.
No Knative Serving, o tamanho da imagem do contentor não afeta o arranque a frio nem o tempo de processamento de pedidos e não conta para a memória disponível do contentor.
Para criar um contentor minimalista, considere trabalhar a partir de uma imagem de base simples, como:
O Ubuntu tem um tamanho maior, mas é uma imagem base usada com frequência com um ambiente de servidor mais completo pronto a usar.
Se o seu serviço tiver um processo de compilação com muitas ferramentas, considere usar compilações de várias fases para manter o seu contentor leve no tempo de execução.
Estes recursos fornecem mais informações sobre a criação de imagens de contentores otimizadas:
- Práticas recomendadas do Kubernetes: como e porquê criar imagens de contentores pequenas
- 7 práticas recomendadas para criar contentores