Editor – Este post faz parte de uma série de 10 partes:
Você também pode baixar o conjunto completo de blogs como um e-book gratuito – Levando o Kubernetes do teste à produção .
À medida que mais e mais empresas executam aplicativos em contêineres em produção, o Kubernetes continua a solidificar sua posição como a ferramenta padrão para orquestração de contêineres. Ao mesmo tempo, a demanda por computação em nuvem foi antecipada em alguns anos porque as iniciativas de trabalho em casa motivadas pela pandemia da COVID-19 aceleraram o crescimento do tráfego da Internet. As empresas estão trabalhando rapidamente para atualizar sua infraestrutura porque seus clientes estão enfrentando grandes interrupções e sobrecargas de rede.
Para atingir o nível de desempenho necessário em ambientes de microsserviços baseados em nuvem, você precisa de um software rápido e totalmente dinâmico que aproveite a escalabilidade e o desempenho dos data centers de hiperescala de última geração. Muitas organizações que usam o Kubernetes para gerenciar contêineres dependem de um controlador Ingress baseado em NGINX para entregar seus aplicativos aos usuários.
Neste blog, relatamos os resultados dos nossos testes de desempenho em três controladores NGINX Ingress em um ambiente multinuvem realista, medindo a latência das conexões do cliente pela Internet:
Usamos o programa de geração de carga wrk2
para emular um cliente, fazendo solicitações contínuas via HTTPS durante um período definido. O controlador Ingress em teste – o controlador Ingress da comunidade, NGINX Ingress Controller baseado no NGINX Open Source ou NGINX Ingress Controller baseado no NGINX Plus – encaminhou solicitações para aplicativos de backend implantados em Kubernetes Pods e retornou a resposta gerada pelos aplicativos para o cliente. Geramos um fluxo constante de tráfego de clientes para testar o estresse dos controladores do Ingress e coletamos as seguintes métricas de desempenho:
Para todos os testes, usamos o utilitário wrk2
em execução em uma máquina cliente na AWS para gerar solicitações. O cliente AWS se conectou ao endereço IP externo do controlador Ingress, que foi implantado como um Kubernetes DaemonSet no GKE-node-1 em um ambiente do Google Kubernetes Engine (GKE). O controlador Ingress foi configurado para terminação SSL (referenciando um Kubernetes Secret ) e roteamento de Camada 7, e exposto por meio de um Kubernetes Service do Tipo
LoadBalancer
. O aplicativo de backend foi executado como uma implantação do Kubernetes no GKE-node-2 .
Para obter detalhes completos sobre os tipos de máquinas em nuvem e as configurações de software, consulte o Apêndice .
Executamos o seguinte script wrk2
(versão 4.0.0) na máquina cliente da AWS. Ele gera 2 threads wrk
que, juntos, estabelecem 1.000 conexões com o controlador Ingress implantado no GKE. Durante cada execução de teste de 3 minutos, o script gera 30.000 solicitações por segundo (RPS), o que consideramos uma boa simulação da carga em um controlador Ingress em um ambiente de produção.
wrk -t2 -c1000 -d180s -L -R30000 https://app.example.com:443/
onde:
-t
– Define o número de threads (2)-c
– Define o número de conexões TCP (1000)-d
– Define a duração da execução do teste em segundos (180 ou 3 minutos)-L
– Gera informações detalhadas de percentil de latência para exportação para ferramentas de análise-R
– Define o número de RPS (30.000)Para criptografia TLS, usamos RSA com um tamanho de chave de 2048 bits e Perfect Forward Secrecy.
Cada resposta do aplicativo back-end (acessado em https://app.example.com:443) consiste em cerca de 1 KB de metadados básicos do servidor, juntamente com o 200
OK
Código de status HTTP.
Realizamos testes com uma implantação estática e dinâmica do aplicativo de back-end.
Na implantação estática, havia cinco réplicas de Pod e nenhuma alteração foi aplicada usando a API do Kubernetes.
Para a implantação dinâmica, usamos o seguinte script para dimensionar periodicamente a implantação do nginx de backend de cinco réplicas de Pod para sete e depois reduzir para cinco. Isso emula um ambiente Kubernetes dinâmico e testa a eficácia com que o controlador Ingress se adapta às alterações do endpoint.
enquanto [ 1 -eq 1 ]
faça
implantação de escala kubectl nginx --replicas=5
sleep 12
implantação de escala kubectl nginx --replicas=7
sleep 10
feito
Conforme indicado no gráfico, todos os três controladores Ingress obtiveram desempenho semelhante com uma implantação estática do aplicativo de back-end. Isso faz sentido, já que todos eles são baseados no NGINX Open Source e a implantação estática não requer reconfiguração do controlador Ingress.
O gráfico mostra a latência incorrida por cada controlador Ingress em uma implantação dinâmica em que dimensionamos periodicamente o aplicativo de back-end de cinco Pods de réplica para sete e vice-versa (consulte Implantação de aplicativo de back-end para obter detalhes).
Está claro que apenas o controlador Ingress baseado em NGINX Plus tem bom desempenho neste ambiente, sofrendo praticamente nenhuma latência até o 99,99º percentil. Tanto a comunidade quanto os controladores Ingress baseados em NGINX Open Source apresentam latência perceptível em percentis bastante baixos, embora em um padrão bastante diferente. Para o controlador Ingress da comunidade, a latência sobe suave, mas constantemente, até o 99º percentil, onde se estabiliza em cerca de 5.000 ms (5 segundos). Para o controlador Ingress baseado em NGINX Open Source, a latência aumenta drasticamente para cerca de 32 segundos no 99º percentil e novamente para 60 segundos no 99,99º.
Conforme discutimos mais detalhadamente em Resultados de tempo limite e erro para a implantação dinâmica , a latência experimentada com a comunidade e os controladores Ingress baseados em código aberto NGINX é causada por erros e tempos limite que ocorrem após a configuração do NGINX ser atualizada e recarregada em resposta às alterações de endpoints para o aplicativo de back-end.
Aqui está uma visão mais detalhada dos resultados para o controlador Ingress da comunidade e o controlador Ingress baseado em NGINX Plus na mesma condição de teste do gráfico anterior. O controlador Ingress baseado no NGINX Plus não apresenta praticamente nenhuma latência até o 99,99º percentil, onde começa a subir para 254 ms no 99,9999º percentil. O padrão de latência para o controlador Ingress da comunidade cresce continuamente para uma latência de 5000 ms no 99º percentil, ponto em que a latência se estabiliza.
Esta tabela mostra a causa dos resultados de latência com mais detalhes.
NGINX Open Source | Comunidade | NGINX Plus | |
---|---|---|---|
Erros de conexão | 33365 | 0 | 0 |
Tempo limite de conexão | 309 | 8809 | 0 |
Erros de leitura | 4650 | 0 | 0 |
Com o controlador Ingress baseado em código aberto NGINX, a necessidade de atualizar e recarregar a configuração do NGINX para cada alteração nos pontos de extremidade do aplicativo de back-end causa muitos erros de conexão, tempos limite de conexão e erros de leitura. Erros de conexão/socket ocorrem durante o breve período em que o NGINX leva para recarregar, quando os clientes tentam se conectar a um socket que não está mais alocado ao processo NGINX. Tempos limite de conexão ocorrem quando os clientes estabelecem uma conexão com o controlador Ingress, mas o ponto de extremidade de backend não está mais disponível. Tanto erros quanto tempos limite impactam severamente a latência, com picos de até 32 segundos no 99º percentil e novamente de até 60 segundos no 99,99º.
Com o controlador Ingress da comunidade, houve 8.809 tempos limite de conexão devido às alterações nos endpoints conforme o aplicativo de back-end era ampliado e reduzido. O controlador Ingress da comunidade usa código Lua para evitar recarregamentos de configuração quando os endpoints mudam . Os resultados mostram que executar um manipulador Lua dentro do NGINX para detectar alterações de endpoint aborda algumas das limitações de desempenho da versão baseada em código aberto do NGINX, que resultam da necessidade de recarregar a configuração após cada alteração nos endpoints. No entanto, tempos limite de conexão ainda ocorrem e resultam em latência significativa em percentis mais altos.
Com o controlador Ingress baseado em NGINX Plus não houve erros ou tempos limite – o ambiente dinâmico praticamente não teve efeito no desempenho. Isso ocorre porque ele usa a API NGINX Plus para atualizar dinamicamente a configuração do NGINX quando os endpoints mudam. Como mencionado, a latência mais alta foi de 254 ms e ocorreu apenas no percentil 99,9999.
Os resultados de desempenho mostram que, para eliminar completamente os tempos limite e erros em um ambiente de nuvem Kubernetes dinâmico, o controlador Ingress deve se ajustar dinamicamente às alterações nos endpoints de back-end sem manipuladores de eventos ou recarregamentos de configuração. Com base nos resultados, podemos dizer que a API NGINX Plus é a solução ideal para reconfigurar dinamicamente o NGINX em um ambiente dinâmico. Em nossos testes, apenas o controlador Ingress baseado em NGINX Plus obteve o desempenho perfeito em ambientes Kubernetes altamente dinâmicos que você precisa para manter seus usuários satisfeitos.
Máquina | Provedor de Nuvem | Tipo de máquina |
---|---|---|
Cliente | AWS | m5a.4xgrande |
GKE-nó-1 | GCP | e2-padrão-32 |
GKE-nó-2 | GCP | e2-padrão-32 |
apiVersão: apps/v1
tipo: DaemonSet
metadados:
nome: nginx-ingress
namespace: nginx-ingress
especificação:
seletor:
matchLabels:
aplicativo: nginx-ingress
modelo:
metadados:
rótulos:
aplicativo: nginx-ingress
#anotações:
#prometheus.io/scrape: "true"
#prometheus.io/port: "9113"
especificação:
serviceAccountName: nginx-ingress
nodeSelector:
kubernetes.io/hostname: gke-rawdata-cluster-default-pool-3ac53622-6nzr
hostNetwork: true
containers:
-imagem: gcr.io/nginx-demos/nap-ingress:edge
imagePullPolicy: Sempre
nome: nginx-plus-ingress
portas:
- nome: http
containerPort: 80
porta do host: 80
- nome: https
containerPort: 443
porta do host: 443
- nome: readiness-port
containerPort: 8081
#- nome: prometheus
#containerPort: 9113
readinessProbe:
httpGet:
caminho: /nginx-ready
porta: readiness-port
periodSeconds: 1
securityContext:
allowPrivilegeEscalation: verdadeiro
runAsUser: 101 #nginx
capacidades:
drop:
- ALL
add:
- NET_BIND_SERVICE
env:
- name: POD_NAMESPACE
valorDe:
fieldRef:
fieldPath: metadata.namespace
- nome: POD_NAME
valorDe:
fieldRef:
fieldPath: metadata.name
argumentos:
- -nginx-plus
- -nginx-configmaps=$(POD_NAMESPACE)/nginx-config
- -default-server-tls-secret=$(POD_NAMESPACE)/default-server-secret
Notas:
nginx‑plus
foram ajustadas conforme necessário na configuração do NGINX Open Source.gcr.io/nginx-demos/nap-ingress:edge
), mas foi desabilitado (o sinalizador -enable-app-protect
foi omitido).tipo: ConfigMap
apiVersion: v1
metadados:
nome: nginx-config
namespace: nginx-ingress
dados:
worker-connections: "10000"
trabalhador-rlimit-nofile: "10240"
manter ativo: "100"
requisições keepalive: "100000000"
apiVersão: apps/v1
tipo: DaemonSet
metadados:
rótulos:
helm.sh/chart: ingress-nginx-2.11.1
app.kubernetes.io/nome: ingress-nginx
app.kubernetes.io/instância: ingress-nginx
app.kubernetes.io/versão: 0.34.1
app.kubernetes.io/gerenciado por: Helm
app.kubernetes.io/component: controlador
nome: ingress-nginx-controller
namespace: ingress-nginx
spec:
seletor:
matchLabels:
app.kubernetes.io/nome: ingress-nginx
app.kubernetes.io/instância: ingress-nginx
app.kubernetes.io/component: controlador
template:
metadados:
rótulos:
app.kubernetes.io/nome: ingress-nginx
app.kubernetes.io/instância: ingress-nginx
app.kubernetes.io/component: controlador
spec:
nodeSelector:
kubernetes.io/nome do host: gke-rawdata-cluster-default-pool-3ac53622-6nzr
hostNetwork: true
contêineres:
- nome: controlador
imagem: us.gcr.io/k8s-artifacts-prod/ingress-nginx/controller:v0.34.1@sha256:0e072dddd1f7f8fc8909a2ca6f65e76c5f0d2fcfb8be47935ae3457e8bbceb20
imagePullPolicy: IfNotPresent
ciclo de vida:
preStop:
exec:
comando:
- /wait-shutdown
argumentos:
- /nginx-ingress-controller
- --election-id=ingress-controller-leader
- --ingress-class=nginx
- --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
- --validating-webhook=:8443
- --validating-webhook-certificate=/usr/local/certificates/cert
- --validating-webhook-key=/usr/local/certificates/key
securityContext:
capacidades:
drop:
- ALL
add:
- NET_BIND_SERVICE
runAsUser: 101
allowPrivilegeEscalation: verdadeiro
env:
- nome: POD_NAME
valorDe:
fieldRef:
fieldPath: metadata.name
- nome: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
readinessProbe:
httpGet:
path: /healthz
port: 10254
esquema: HTTP
períodoSegundos: 1
portas:
- nome: http
containerPort: 80
protocolo: TCP
- nome: https
containerPort: 443
protocolo: TCP
- nome: webhook
containerPort: 8443
protocolo: TCP
volumeMounts:
- nome: webhook-cert
mountPath: /usr/local/certificates/
readOnly: true
serviceAccountName: ingress-nginx
términoGracePeriodSeconds: 300
volumes:
- nome: webhook-cert
segredo:
secretName: ingress-nginx-admission
apiVersão: v1
tipo: ConfigMap
metadados:
nome: ingress-nginx-controller
namespace: ingress-nginx
dados:
max-worker-connections: "10000"
max-worker-arquivos-abertos: "10204"
upstream-keepalive-conexões: "100"
requisições keep-alive: "100000000"
apiVersão: apps/v1
tipo: Implantação
metadados:
nome: nginx
especificação:
seletor:
matchLabels:
aplicativo: nginx
modelo:
metadados:
rótulos:
aplicativo: nginx
especificação:
nodeSelector:
kubernetes.io/hostname: gke-rawdata-cluster-default-pool-3ac53622-t2dz
contêineres:
-nome: nginx
imagem: nginx
portas:
-containerPort: 8080
volumeMounts:
- nome: main-config-volume
mountPath: /etc/nginx
- nome: app-config-volume
mountPath: /etc/nginx/conf.d
readinessProbe:
httpGet:
caminho: /healthz
porta: 8080
períodoSegundos: 3
volumes:
- nome: main-config-volume
configMap:
nome: main-conf
- nome: app-config-volume
configMap:
nome: app-conf
---
apiVersão: v1
tipo: ConfigMap
metadados:
nome: main-conf
namespace: default
dados:
nginx.conf: |+
usuário nginx;
worker_processes 16;
worker_rlimit_nofile 102400;
worker_cpu_affinity auto 11111111111111111;
error_log /var/log/nginx/error.log aviso;
pid /var/run/nginx.pid;
eventos {
worker_connections 100000;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
sendfile em;
tcp_nodelay ligado;
access_log desligado;
include /etc/nginx/conf.d/*.conf;
}
---
apiVersion: v1
tipo: ConfigMap
metadados:
nome: app-conf
namespace: default
dados:
app.conf: "server {listen 8080;location / {default_type text/plain;expires -1;return 200 'Endereço do servidor: $server_addr:$server_port\nNome do servidor:$hostname\nData: $time_local\nURI: $request_uri\nID da solicitação: $request_id\n';}location /healthz {return 200 'Estou feliz e saudável :)';}}"
---
apiVersão: v1
tipo: Serviço
metadados:
nome: app-svc
especificação:
portas:
- porta: 80
Porta de destino: 8080
protocolo: TCP
nome: http
seletor:
aplicativo: nginx
---
"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."