Home Comunidade Eventos YouTube Networks Desafios // produtos Academy News CTI | CVE Contato

01 - L4 (Transport Layer) vs L7 (Application Layer)

#http#l4#l7#load-balancer#tcp
video explicativo
Video explicativoEspaco reservado para o video desta anotação — adicione video: https://youtu.be/... no frontmatter da nota.

L4 (Transport Layer) vs L7 (Application Layer)

Balanceadores de carga distribuem requisições entre múltiplos destinos para escalar horizontalmente e sobreviver a falhas, e a divisão mais fundamental dessa tecnologia é a camada em que o balanceador enxerga o tráfego. Um balanceador de camada 4 opera sobre TCP e UDP, tomando decisões de roteamento com base em IP de origem, IP de destino, porta e, em alguns casos, protocolo transport; ele nunca interpreta a aplicação. Um balanceador de camada 7 termina a conexão de aplicação, lê HTTP completo, headers, path, cookies e corpo, e decide o destino a partir de regras semânticas. A escolha entre os dois não é hierárquica, é um trade-off: L4 compra throughput brutal e transparência com inteligência mínima, L7 compra roteamento fino e recursos de aplicação pagando custo de processamento por requisição. Em arquiteturas de produção reais, os dois coexistem, e saber em que ponto da cadeia cada um atua é o que permite desenhar camadas de tráfego coerentes.

Conceito e o modelo de atuação

O balanceador L4 se comporta como um comutador de conexões: recebe o segmento TCP ou o datagrama UDP e encaminha para um destino escolhido, sem abrir a conversa. Existem duas técnicas subjacentes, e a diferença entre elas importa em operação.

  • NAT (DNAT): o balanceador reescreve o IP de destino do pacote e o pacote volta por ele no caminho de retorno; é transparente para o cliente e funciona com qualquer protocolo TCP/UDP.
  • DSR (Direct Server Return): só o fluxo de entrada passa pelo balanceador, e o servidor responde diretamente ao cliente com o IP virtual próprio do balanceador; elimina o gargalo do retorno, mas limita recursos que exigem visibilidade da conexão.

O balanceador L7 é um proxy explícito: o cliente abre a conexão TCP com o balanceador, e o balanceador abre outra conexão com o destino. Essa terminação dupla é o que lhe dá poder: para escolher o backend, ele precisa ter lido a requisição inteira, e lendo a requisição inteira pode modificá-la, filtrá-la, comprimí-la e observá-la.

A consequência estrutural é o fluxo de conexões: em L4, uma conexão cliente é uma conexão backend, e o mapeamento persiste até o fim; em L7, muitas conexões e requisições de clientes multiplexam sobre um pequeno pool de conexões para o backend, o que reduz uso de porta e de memória nos servidores de aplicação e permite keepalive eficiente.

O problema que cada um resolve

O L4 resolve escala bruta e protocolos que não são HTTP. Bancos de dados replicados, Redis, brokers MQTT, servidores de jogos sobre UDP e tráfego TCP proprietário precisam de distribuição sem que haja semântica de aplicação para interpretar. Nesses cenários, um L7 é inútil: não existe path para rotear. O L4 também resolve sobrecarga: decidir por IP e porta custa poucas operações por pacote, e appliances e serviços dedicados de L4 atingem milhões de conexões por segundo com latência mínima.

O L7 resolve direcionamento inteligente. Em uma arquitetura de microsserviços atrás de um único endpoint público, é o L7 que envia /api/pedidos para o serviço de pedidos, /api/relatorios para o serviço de leitura e /admin para o cluster interno, tudo na mesma porta 443. É também o L7 que aplica autenticação no edge, limita requisição por API key, injeta headers de rastreamento e faz canary roteando 2% das requisições com header específico para a versão nova.

O problema que ambos resolvem em conjunto é disponibilidade: health checks removem destinos doentes do pool, e a remoção automática é o que transforma redundância em tolerância a falhas real. Sem verificação de saúde, balanceamento é só distribuição; com ela, passa a ser recuperação.

Sticky sessions e afinidade

A afinidade de sessão existe porque aplicações mantêm estado em memória, e é uma solução de mitigação, não de arquitetura.

  • Por cookie aplicativo: a aplicação emite o cookie e o balanceador o lê para fixar o destino; funciona em L7, sobrevive a reinício do balanceador e é o padrão do cookie JSESSIONID em Java.
  • Por cookie gerado pelo balanceador: o próprio load balancer emite um cookie de duração fixa que mapeia para o destino; o cliente não precisa saber nada e o backend não muda.
  • Por IP de origem: o L4 garante que o mesmo IP sempre cai no mesmo destino; quebra atrás de NAT corporativo, onde milhares de usuários compartilham o mesmo IP, e gera distribuição desigual.

A crítica arquitetural é o alvo certo: afinidade é paliativo para estado local. O desenho saudável externaliza a sessão em Redis ou em store dedicado, torna cada instância stateless, e a afinidade deixa de ser requisito; aí o balanceador escolhe livremente o destino mais saudável e mais próximo da capacidade, e o canary e o auto scaling funcionam sem cordas. Afinidade também corrompe o balanceamento: um usuário de API pesada fixado em um nó gera hot spot que nenhum algoritmo corrige.

Tráfego de WebSocket e gRPC de streaming aprofunda o problema. Uma vez estabelecida, a conexão persiste por tempo indeterminado e todo o volume dela cai no destino sorteado no handshake; em round robin puro, poucas conexões longas concentram carga que deveria estar distribuída. A resposta operacional é reconnection no cliente com jitter, limites de vida de conexão no edge e migração do estado de sessão para camada externa, de modo que a queda de um nó seja transparente para o usuário.

A conscientização de zona completa o quadro: balanceadores modernos mantêm tráfego dentro da mesma zona de disponibilidade do cliente, reduzindo latência e custo de transferência entre zonas, e redistribuem automaticamente quando um destino de uma zona fica insalubre. Esse comportamento só funciona bem com destinos uniformes e sem afinidade, mais um argumento para o backend stateless.

TLS termination

A terminação de TLS é o ponto onde L4 e L7 divergem de forma prática. O balanceador L7 termina o handshake TLS nele mesmo, mantém a sessão criptografada com o cliente, descriptografa, decide e recriptografa ou não para o backend. Isso centraliza gestão de certificados, habilita renovação automática, permite inspeção de conteúdo para roteamento e desloca o custo criptográfico do servidor de aplicação para o edge, que é dimensionado para isso.

O L4 típico não enxerga dentro do TLS: pode fazer passthrough, entregando o fluxo criptografado ao destino, que precisa ter o certificado e a chave. Essa é a exigência de quem precisa preservar identidade e conformidade fim a fim, ou de quem quer que o servidor real veja o certificado do cliente para autenticação mútua. O passthrough é também o modo de maior throughput, porque ninguém descriptografa nada no caminho.

Aspecto L4 (TCP/UDP) L7 (HTTP)
Camada de decisão Transporte: IP, porta, protocolo Aplicação: path, headers, cookies, corpo
Modelo de conexão Passagem de fluxo ou NAT Proxy com terminação dupla
Throughput Altíssimo, decisão barata por pacote Menor, processamento por requisição
Visibilidade de conteúdo Nenhuma, não descriptografa Total, lê e modifica requisição
TLS Passthrough, certificado no destino Terminação no balanceador, centralizada
Roteamento por conteúdo Impossível Path, host, método, header, query
Sticky sessions Por IP de origem Por cookie, mais robusto
Protocolos servidos Qualquer TCP ou UDP HTTP, HTTPS, HTTP/2, gRPC, WebSocket
Custo típico Menor por unidade de tráfego Maior, cobra por LCU ou regra
Caso ideal Banco, UDP, tráfego de alto volume APIs, microsserviços, roteamento fino

Funcionamento interno e algoritmos

O coração do balanceador é a escolha do destino por conexão ou por requisição, e os algoritmos base se repetem nas duas camadas.

  • Round robin: distribui ciclicamente de forma igual; simples e previsível, mas cego à carga real de cada destino.
  • Least connections: envia ao destino com menos conexões ativas, corrigindo desequilíbrio quando o tempo de atendimento varia muito entre requisições.
  • Weighted variants: atribui pesos por capacidade, permitindo misturar instâncias grandes e pequenas ou conduzir migração gradual de versão.
  • Source IP hash: determinístico por origem, fornece afinidade sem estado no balanceador.

Em L7, o mesmo algoritmo opera por requisição, e não por conexão, o que permite que um único keepalive de navegador distribua entre vários pods. Em L4, a decisão acontece uma vez por conexão e vale enquanto ela durar; conexões longas criam desequilíbrio persistente que só least connections corrige parcialmente, porque a conexão estabelecida não migra.

Aplicação prática: a pilha combinada

A arquitetura comum de produção empilha as duas camadas de forma complementar, e o exemplo abaixo ilustra o desenho típico.

# Camada L7: Nginx como roteador de aplicação atrás de um NLB (L4)
# O NLB entrega o TCP bruto na porta 443 e o Nginx decide por path
upstream servico_pedidos {
    least_conn;                     # menos conexões ativas: bom para latência variável
    server pedidos-1:8080 max_fails=3 fail_timeout=15s;
    server pedidos-2:8080 max_fails=3 fail_timeout=15s;
    keepalive 64;                   # reutiliza conexões com o upstream
}

upstream servico_relatorios {
    least_conn;
    server relatorios-1:8080;
    server relatorios-2:8080;
    keepalive 32;
}

server {
    listen 443 ssl;
    server_name api.exemplo.com.br;

    ssl_certificate     /etc/nginx/certs/api.crt;   # terminação TLS centralizada
    ssl_certificate_key /etc/nginx/certs/api.key;

    location /v1/pedidos/ {
        proxy_pass http://servico_pedidos;
        proxy_set_header X-Request-Id $request_id;  # rastreio ponta a ponta
        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }

    location /v1/relatorios/ {
        proxy_pass http://servico_relatorios;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }
}

O desenho explora o melhor dos dois mundos: o NLB da camada de rede absorve picos, ataques volumétricos e flutuação de porta com IP estático e throughput elástico, e o Nginx aplica semântica, TLS e roteamento. Em Kubernetes, o par é exatamente o mesmo conceito: Service do tipo LoadBalancer como L4 e Ingress como L7.

Health checks e drenagem de conexões

O health check da camada 4 testa a abertura de conexão TCP no endereço e porta do destino: se o handshake completa, o destino conta como saudável. É barato, rápido e independe de aplicação, mas tem falso positivo clássico: o processo escuta a porta com a fila de accept cheia e o check passa enquanto requisições reais estagnam. Em L4 puro, por não haver como perguntar à aplicação, a mitigação combina intervalo curto, limiar de falha agressivo e supervisão de processo no host.

O health check da camada 7 executa uma requisição HTTP real, em geral GET em /healthz, e pode exigir código 200, validação de corpo e presença de dependência crítica. Ele enxerga o que o usuário enxerga, e é o único check que valida o caminho completo do proxy ao backend. O desenho correto distingue liveness de readiness: o readiness do balanceador responde se o destino deve receber tráfego, enquanto a dependência opcional degradada não deve tirar o destino do pool, apenas disparar alarme.

A drenagem de conexões é o mecanismo que impede erro visível em operação de rotina. Ao retirar um destino do pool, durante deploy ou scale in, o balanceador para de enviar conexões novas e aguarda o término das conexões existentes dentro de um prazo de drain; em L7, o limite é por requisição e as conexões esvaziam rápido, enquanto em L4 conexões longas, como WebSocket e banco, podem segurar o drain por horas. Sem drenagem, cada deploy interrompe requisições em voo, e o sintoma típico é taxa de erro 502 de poucos por cento exatamente no fim da janela de publicação.

Critério de decisão

A decisão entre L4 e L7 se apoia em quatro perguntas, nesta ordem. Primeiro, o protocolo é HTTP ou algo sobre TCP/UDP genérico? Não sendo HTTP, a resposta é L4, sem discussão. Segundo, existe necessidade de rotear pelo conteúdo, como path, header, cookie ou API key? Existindo, é L7, porque a informação só está visível na camada de aplicação. Terceiro, o requisito de throughput e latência é extremo, na casa de milhões de conexões ou tráfego UDP volumétrico? Sendo, o L4 dedicado na frente reduz custo e risco. Quarto, há exigência de inspecionar ou preservar o TLS fim a fim? Passthrough pede L4, e inspeção pede L7.

O desenho resultante maduro é híbrido e em camadas: um L4 na borda, robusto e barato, protegendo e absorvendo a conexão, com um ou mais L7 atrás, aplicando semântica por domínio e serviço. A fronteira entre eles deve ser documentada em termos de responsabilidade sobre TLS, health check e timeout, pois duplicar essas responsabilidades entre camadas cria comportamento imprevisível em falha.

Erros comuns e boas práticas

  • Usar L7 onde só existe TCP: aplicar balanceador de aplicação em banco ou MQTT não funciona; sem HTTP não há path para decidir, e a conexão falha.
  • Depender de sticky session como projeto: afinidade mascara estado local e impede escalonamento real; externalize a sessão e use afinidade apenas como transição.
  • Desligar keepalive entre proxy e backend: abrir conexão TCP por requisição consome porta e latência; configure pool de conexões no upstream.
  • Ignorar conexões longas em L4: round robin com conexões de minutos gera hot spots; use least connections ou limite o tempo de vida da conexão.
  • Terminar TLS sem recriptografar para dentro: tráfego interno em texto claro viola requisitos de conformidade; criptografe de novo ou use mTLS entre serviços.
  • Health check por TCP aberto: porta aberta não significa aplicação saudável; valide endpoint real com resposta esperada na camada correspondente.

A boa prática final é decidir por requisito, e não por modismo: se o protocolo não é HTTP, a resposta é L4; se o roteamento precisa ler a requisição, é L7; quando o tráfego é misto, empilhe os dois com fronteiras claras. Documente qual camada é responsável por TLS, por afinidade e por health check, porque a sobreposição dessas responsabilidades entre camadas é a origem da maioria dos incidentes de balanceamento.