Este tutorial é um dos quatro que colocam em prática conceitos do Microservices de março de 2022: Rede Kubernetes :
Quer orientação detalhada sobre como usar o NGINX para ainda mais casos de uso de rede Kubernetes? Baixe nosso e-book gratuito, Gerenciando o tráfego do Kubernetes com NGINX: Um guia prático .
Sua organização acaba de lançar seu primeiro aplicativo e API no Kubernetes. Foi dito para você esperar altos volumes de tráfego (e o dimensionamento automático já foi implementado para garantir que o NGINX Ingress Controller possa rotear o tráfego rapidamente), mas há preocupações de que a API possa ser alvo de um ataque malicioso. Se a API receber um alto volume de solicitações HTTP – uma possibilidade com tentativas de adivinhação de senha por força bruta ou ataques DDoS – tanto a API quanto o aplicativo podem ficar sobrecarregados ou até mesmo travar.
Mas você está com sorte! A técnica de controle de tráfego chamada limitação de taxa é um caso de uso de gateway de API que limita a taxa de solicitações de entrada a um valor típico para usuários reais. Você configura o NGINX Ingress Controller para implementar uma política de limitação de taxa, o que evita que o aplicativo e a API sejam sobrecarregados por muitas solicitações. Bom trabalho!
Este blog acompanha o laboratório da Unidade 2 de Microsserviços de março de 2022 – Expondo APIs no Kubernetes , demonstrando como combinar vários controladores de entrada NGINX com limitação de taxa para evitar que aplicativos e APIs fiquem sobrecarregados.
Para executar o tutorial, você precisa de uma máquina com:
Para aproveitar ao máximo o laboratório e o tutorial, recomendamos que antes de começar você:
Assista à gravação da visão geral conceitual transmitida ao vivo
Revise os blogs de fundo, webinar e vídeo
Assista ao resumo em vídeo de 18 minutos do laboratório:
Este tutorial usa estas tecnologias:
As instruções para cada desafio incluem o texto completo dos arquivos YAML usados para configurar os aplicativos. Você também pode copiar o texto do nosso repositório GitHub . Um link para o GitHub é fornecido junto com o texto de cada arquivo YAML.
Este tutorial inclui três desafios:
Neste desafio, você implanta um cluster minikube e instala o Podinfo como um aplicativo de amostra e API. Em seguida, você implanta o NGINX Ingress Controller , configura o roteamento de tráfego e testa a configuração do Ingress .
Crie um cluster minikube . Após alguns segundos, uma mensagem confirma que a implantação foi bem-sucedida.
$ minikube start 🏄 Pronto! O kubectl agora está configurado para usar o cluster "minikube" e o namespace "default" por padrão
Podinfo é um “aplicativo web feito com Go que demonstra as melhores práticas de execução de microsserviços no Kubernetes”. Estamos usando-o como um aplicativo de exemplo e API devido ao seu tamanho reduzido.
Usando o editor de texto de sua escolha, crie um arquivo YAML chamado 1-apps.yaml com o seguinte conteúdo (ou copie do GitHub ). Ele define uma Implantação que inclui:
apiVersão: apps/v1
tipo: Implantação
metadados:
nome: api
especificação:
seletor:
matchLabels:
aplicativo: api
modelo:
metadados:
rótulos:
aplicativo: api
especificação:
contêineres:
-nome: api
imagem: stefanprodan/podinfo
portas:
-containerPort: 9898
---
apiVersão: v1
tipo: Serviço
metadados:
nome: api
especificação:
portas:
- porta: 80
targetPort: 9898
nóPorta: 30001
seletor:
aplicativo: api
tipo: Balanceador de carga
---
apiVersion: apps/v1
tipo: Implantação
metadados:
nome: frontend
especificação:
seletor:
matchLabels:
aplicativo: frontend
modelo:
metadados:
rótulos:
aplicativo: frontend
especificação:
contêineres:
-nome: frontend
imagem: stefanprodan/podinfo
portas:
-containerPort: 9898
---
apiVersão: v1
tipo: Serviço
metadados:
nome: frontend
especificação:
portas:
- porta: 80
targetPort: 9898
nóPorta: 30002
seletor:
aplicativo: frontend
tipo: Balanceador de carga
Implante o aplicativo e a API:
$ kubectl apply -f 1-apps.yaml deployment.apps/api criado service/api criado deployment.apps/frontend criado service/frontend criado
Confirme se os pods para a API do Podinfo e o Frontend do Podinfo foram implantados com sucesso, conforme indicado pelo valor Em execução
na coluna STATUS
.
$ kubectl get pods NOME PRONTO STATUS REINÍCIOS IDADE api-7574cf7568-c6tr6 1/1 Em execução 0 87s frontend-6688d86fc6-78qn7 1/1 Em execução 0 87s
A maneira mais rápida de instalar o NGINX Ingress Controller é com o Helm .
Instale o NGINX Ingress Controller em um namespace separado ( nginx ) usando o Helm.
Crie o namespace:
$ kubectl criar namespace nginx
Adicione o repositório NGINX ao Helm:
$ helm repo adicionar nginx-stable https://helm.nginx.com/stable
Baixe e instale o NGINX Ingress Controller no seu cluster:
$ helm install main nginx-stable/nginx-ingress \ --set controller.watchIngressWithoutClass=true \ --set controller.ingressClass=nginx \ --set controller.service.type=NodePort \ --set controller.service.httpPort.nodePort=30010 \ --set controller.enablePreviewPolicies=true \ --namespace nginx
Confirme se o pod do NGINX Ingress Controller foi implantado, conforme indicado pelo valor Em execução
na coluna STATUS
(para legibilidade, a saída é distribuída em duas linhas).
$ kubectl get pods -namespace nginx NOME PRONTO STATUS ... main-nginx-ingress-779b74bb8b-d4qtc 1/1 Executando ... ... RECOMEÇA IDADE ... 0 92s
apiVersão: networking.k8s.io/v1
tipo: Ingress
metadados:
nome: primeiro
especificação:
ingressClassName: nginx
regras:
- host: "example.com"
http:
caminhos:
- backend:
serviço:
nome: frontend
porta:
número: 80
caminho: /
caminhoType: Prefixo
- host: "api.example.com"
http:
paths:
- backend:
service:
name: api
port:
number: 80
caminho: /
caminhoType: Prefixo
$ kubectl apply -f 2-ingress.yaml ingress.networking.k8s.io/primeiro criado
Para garantir que sua configuração do Ingress esteja funcionando conforme o esperado, teste-a usando um pod temporário. Inicie um pod BusyBox descartável no cluster:
$ kubectl run -ti --rm=true busybox --image=busybox Se você não vir um prompt de comando, tente pressionar enter. / #
Teste a API do Podinfo emitindo uma solicitação ao pod do NGINX Ingress Controller com o nome de host api.example.com . A saída mostrada indica que a API está recebendo tráfego.
/ # wget --header="Host: api.example.com" -qO- main-nginx-ingress.nginx { "nome do host": "api-687fd448f8-t7hqk", "versão": "6.0.3", "revisão": "", "cor": "#34577c", "logotipo": "https://raw.githubusercontent.com/stefanprodan/podinfo/gh-pages/cuddle_clap.gif", "mensagem": "saudações do podinfo v6.0.3", "goos": "linux", "goarch": "arm64", "tempo de execução": "go1.16.9", "num_goroutine": "6", "num_cpu": "4" }
/ # wget --header="Host: example.com" --header="Agente do Usuário: Mozilla" -qO- main-nginx-ingress.nginx <!DOCTYPE html>
<html>
<cabeçalho>
<título>frontend-596d5c9ff4-xkbdc</título>
# ...
Em outro terminal, abra o Podinfo em um navegador. As saudações da página podinfo indicam que o Podinfo está em execução.
$ minikube serviço podinfo
Parabéns! O NGINX Ingress Controller está recebendo solicitações e as encaminhando para o aplicativo e a API.
No terminal original, encerre a sessão do BusyBox:
/ # sair $
Neste desafio, você instala o Locust , uma ferramenta de geração de carga de código aberto, e a usa para simular um aumento de tráfego que sobrecarrega a API e faz com que o aplicativo trave.
Usando o editor de texto de sua escolha, crie um arquivo YAML chamado 3-locust.yaml com o seguinte conteúdo (ou copie do GitHub ).
O objeto ConfigMap
define um script chamado locustfile.py que gera solicitações a serem enviadas ao pod, completas com os cabeçalhos corretos. O tráfego não é distribuído uniformemente entre o aplicativo e a API – as solicitações são direcionadas para a API do Podinfo , com apenas 1 em cada 5 solicitações indo para o Podinfo Frontend .
Os objetos de implantação
e serviço
definem o pod Locust.
apiVersão: v1
tipo: ConfigMap
metadados:
nome: script-locust
dados:
locustfile.py: |-
de locust importar HttpUser, tarefa, entre
classe QuickstartUser(HttpUser):
tempo_de_espera = entre(0.7, 1.3)
@tarefa(1)
def visit_website(self):
com self.client.get("/", headers={"Host": "exemplo.com", "Agente-do-usuário": "Mozilla"}, timeout=0.2, catch_response=True) como resposta:
if response.request_meta["response_time"] > 200:
response.failure("Falha no frontend")
else:
response.success()
@task(5)
def visit_api(self):
with self.client.get("/", headers={"Host": "api.example.com"}, timeout=0.2) como resposta:
if response.request_meta["response_time"] > 200:
response.failure("Falha na API")
else:
response.success()
---
apiVersion: apps/v1
kind: Implantação
metadados:
nome: gafanhoto
especificação:
seletor:
matchLabels:
aplicativo: gafanhoto
modelo:
metadados:
rótulos:
aplicativo: gafanhoto
especificação:
contêineres:
-nome: gafanhoto
imagem: locustio/gafanhoto
portas:
-containerPort: 8089
volumeMounts:
- mountPath: /home/locust
nome: locust-script
volumes:
-nome: locust-script
configMap:
nome: locust-script
---
apiVersion: v1
tipo: Serviço
metadados:
nome: locust
especificação:
portas:
- porta: 8089
targetPort: 8089
nóPorta: 30015
seletor:
aplicativo: gafanhoto
tipo: Balanceador de carga
Implantar Locust:
$ kubectl apply -f 3-locust.yaml configmap/locust-script criado deployment.apps/locust criado serviço/locust criado
kubectl
apply
e, portanto, a instalação ainda está em andamento, conforme indicado pelo valor ContainerCreating
para o pod Locust no campo STATUS
. Aguarde até que o valor esteja em execução
antes de prosseguir para a próxima seção. (A saída é distribuída em duas linhas para maior legibilidade.)$ kubectl get pods NOME PRONTO STATUS ... api-7574cf7568-c6tr6 1/1 Em execução ... frontend-6688d86fc6-78qn7 1/1 Em execução ... locust-77c699c94d-hc76t 0/1 ContainerCreating ... ... RECOMEÇA IDADE ... 0 33m ... 0 33m ... 0 4s
Abra o Locust em um navegador.
$ serviço minikube gafanhoto
Insira os seguintes valores nos campos:
Clique no botão Iniciar swarming para enviar tráfego para a API do Podinfo e o Frontend do Podinfo . Observe os padrões de tráfego nas guias Locust Charts e Failures :
Isso é problemático porque um único agente mal-intencionado usando a API pode derrubar não apenas a API, mas todos os aplicativos atendidos pelo NGINX Ingress Controller!
No desafio final, você implanta dois NGINX Ingress Controllers para eliminar as limitações da implantação anterior, criando um namespace separado para cada um, instalando instâncias separadas do NGINX Ingress Controller para o Podinfo Frontend e o Podinfo API , reconfigura o Locust para direcionar o tráfego do aplicativo e da API para seus respectivos NGINX Ingress Controllers e verifica se a limitação de taxa é eficaz . Primeiro, vamos ver como abordar o problema arquitetônico. No desafio anterior, você sobrecarregou o NGINX Ingress Controller com solicitações de API, o que também impactou o aplicativo. Isso aconteceu porque um único Ingress controller era responsável por rotear o tráfego para o aplicativo da web ( Podinfo Frontend ) e para a API ( Podinfo API ).
Executar um pod NGINX Ingress Controller separado para cada um dos seus serviços evita que seu aplicativo seja afetado por muitas solicitações de API. Isso não é necessariamente necessário para todos os casos de uso, mas em nossa simulação é fácil ver os benefícios de executar vários controladores NGINX Ingress.
A segunda parte da solução, que evita que a API do Podinfo fique sobrecarregada, é implementar a limitação de taxa usando o NGINX Ingress Controller como um gateway de API.
A limitação de taxa restringe o número de solicitações que um usuário pode fazer em um determinado período de tempo. Para mitigar um ataque DDoS, por exemplo, você pode usar a limitação de taxa para limitar a taxa de solicitações de entrada a um valor típico para usuários reais. Quando a limitação de taxa é implementada com o NGINX, os clientes que enviam muitas solicitações são redirecionados para uma página de erro para que não possam impactar negativamente a API. Saiba como isso funciona na documentação do NGINX Ingress Controller .
Um gateway de API roteia solicitações de API de clientes para os serviços apropriados. Um grande erro de interpretação dessa definição simples é que um gateway de API é uma peça única de tecnologia. Não é. Em vez disso, “gateway de API” descreve um conjunto de casos de uso que podem ser implementados por meio de diferentes tipos de proxies – mais comumente um ADC ou balanceador de carga e proxy reverso e, cada vez mais, um controlador Ingress ou malha de serviço. A limitação de taxa é um caso de uso comum para implantar um gateway de API. Saiba mais sobre casos de uso de gateway de API no Kubernetes em Como escolher? Gateway de API vs. Controlador de entrada vs. Service Mesh em nosso blog.
Antes de implementar a nova arquitetura e a limitação de taxa, você deve excluir a configuração anterior do NGINX Ingress Controller.
$ kubectl delete -f 2-ingress.yaml ingress.networking.k8s.io "primeiro" excluído
Crie um namespace chamado nginx‑web para o Podinfo Frontend :
$ kubectl criar namespace nginx-web namespace/nginx-web criado
Crie um namespace chamado nginx‑api para a API do Podinfo :
$ kubectl criar namespace nginx-api namespace/nginx-api criado
Instalar o NGINX Ingress Controller:
$ helm install web nginx-stable/nginx-ingress --set controller.ingressClass=nginx-web \ --set controller.service.type=NodePort \ --set controller.service.httpPort.nodePort=30020 \ --namespace nginx-web
Crie um manifesto do Ingress chamado 4-ingress-web.yaml para o Podinfo Frontend (ou copie do GitHub ).
apiVersion: k8s.nginx.org/v1 tipo: Metadados da política: nome: especificação da política de limite de taxa: rateLimit: taxa: Chave 10r/s: ${binary_remote_addr} zoneSize: 10M --- apiVersão: k8s.nginx.org/v1 tipo: Metadados do VirtualServer: nome: api-vs spec: ingressClassName: nginx-api host: api.example.com políticas: - nome: rate-limit-policy upstreams: - nome: api service: api port: 80 rotas: - caminho: / ação: senha: api
Implante o novo manifesto:
$ kubectl apply -f 4-ingress-web.yaml ingress.networking.k8s.io/frontend criado
Agora, reconfigure o Locust e verifique se:
Execute estas etapas:
Altere o script do Locust para que:
Como o Locust suporta apenas uma única URL no painel, codifique o valor no script Python usando o arquivo YAML 6-locust.yaml com o seguinte conteúdo (ou copie do GitHub ). Anote os URLs em cada tarefa
.
apiVersion: v1
tipo: ConfigMap
metadados:
nome: script-locust
dados:
arquivo-locust.py: |-
de locust importar HttpUser, tarefa, entre
classe QuickstartUser(HttpUser):
tempo_de_espera = entre(0.7, 1.3)
@tarefa(1)
def visitar_site(self):
com self.client.get("http://web-nginx-ingress.nginx-web/", headers={"Host": "exemplo.com", "Agente-do-usuário": "Mozilla"}, timeout=0.2, catch_response=True) como resposta:
if response.request_meta["response_time"] > 200:
response.failure("Falha no frontend")
else:
response.success()
@task(5)
def visit_api(self):
with self.client.get("http://api-nginx-ingress.nginx-api/", headers={"Host": "api.example.com"}, timeout=0.2) como resposta:
if response.request_meta["response_time"] > 200:
response.failure("Falha na API")
else:
response.success()
---
apiVersion: apps/v1
kind: Implantação
metadados:
nome: gafanhoto
especificação:
seletor:
matchLabels:
aplicativo: gafanhoto
modelo:
metadados:
rótulos:
aplicativo: gafanhoto
especificação:
contêineres:
-nome: gafanhoto
imagem: locustio/gafanhoto
portas:
-containerPort: 8089
volumeMounts:
- mountPath: /home/locust
nome: locust-script
volumes:
-nome: locust-script
configMap:
nome: locust-script
---
apiVersion: v1
tipo: Serviço
metadados:
nome: locust
especificação:
portas:
- porta: 8089
targetPort: 8089
nóPorta: 30015
seletor:
aplicativo: gafanhoto
tipo: Balanceador de carga
Implante a nova configuração do Locust. A saída confirma que o script mudou, mas os outros elementos permanecem inalterados.
$ kubectl apply -f 6-locust.yaml configmap/locust-script configurado deployment.apps/locust inalterado service/locust inalterado
Exclua o pod Locust para forçar o recarregamento do novo ConfigMap. Para identificar o pod a ser removido, o argumento para o comando kubectl
delete
pod
é expresso como comandos canalizados que selecionam o pod Locust da lista de todos os pods.
$ kubectl delete pod `kubectl obter pods | grep locust | awk {'imprimir $1'}`
Verifique se o Locust foi recarregado (o valor do pod Locust na coluna AGE
é de apenas alguns segundos).
$ kubectl get pods NOME PRONTO STATUS ... api-7574cf7568-jrlvd 1/1 Em execução ... frontend-6688d86fc6-vd856 1/1 Em execução ... locust-77c699c94d-6chsg 0/1 Em execução ... ... RECOMEÇA IDADE ... 0 9m57s ... 0 9m57s ... 0 6s
Retorne ao Locust e altere os parâmetros nestes campos:
Clique no botão Iniciar swarming para enviar tráfego para a API do Podinfo e o Frontend do Podinfo .
Na barra de título do Locust, no canto superior esquerdo, observe como, à medida que o número de usuários aumenta na coluna STATUS , o mesmo ocorre com o valor na coluna FALHAS . No entanto, os erros não estão mais vindo do Podinfo Frontend, mas sim da Podinfo API, porque o limite de taxa definido para a API significa que solicitações excessivas estão sendo rejeitadas. No rastreamento no canto inferior direito você pode ver que o NGINX está retornando a mensagem503
Serviço
Temporariamente
Indisponível
, que faz parte do recurso de limitação de taxa e pode ser personalizado. A API tem taxa limitada e o aplicativo web está sempre disponível. Bom trabalho!
No mundo real, a limitação de taxa por si só não é suficiente para proteger seus aplicativos e APIs de agentes mal-intencionados. Você precisa implementar pelo menos um ou dois dos seguintes métodos para proteger aplicativos, APIs e infraestrutura do Kubernetes:
Abordamos esses tópicos e muito mais na Unidade 3 de Microservices de março de 2022 – Padrão de segurança de microsserviços no Kubernetes . Para experimentar o NGINX Ingress Controller para Kubernetes com NGINX Plus e NGINX App Protect, comece seu teste gratuito de 30 dias hoje mesmo ou entre em contato conosco para discutir seus casos de uso . Para testar o NGINX Ingress Controller com o NGINX Open Source, você pode obter o código-fonte da versão ou baixar um contêiner pré-criado do DockerHub .
"Esta postagem do blog pode fazer referência a produtos que não estão mais disponíveis e/ou não têm mais suporte. Para obter as informações mais atualizadas sobre os produtos e soluções F5 NGINX disponíveis, explore nossa família de produtos NGINX . O NGINX agora faz parte do F5. Todos os links anteriores do NGINX.com redirecionarão para conteúdo semelhante do NGINX no F5.com."