kb.erickguedes.com
Nginx: Performance e Proxy Reverso

Tuning e Monitoramento

Aula 6 de 6

Performance Tuning

worker_processes e worker_connections

# /etc/nginx/nginx.conf
user www-data;

# Auto: detecta numero de CPUs
worker_processes auto;

# Fixo: 2 workers por CPU
# worker_processes 4;

# PID
pid /run/nginx.pid;

events {
    # Maximo de conexoes por worker
    worker_connections 2048;

    # Aceitar multiplas conexoes simultaneamente
    multi_accept on;

    # Metodo de multiplexacao (linux: epoll)
    use epoll;
}
# Calculo de capacidade maxima
# Max Clients = worker_processes * worker_connections
# Exemplo: 4 * 2048 = 8192 conexoes simultaneas

# Verificar CPU disponivel
nproc
lscpu | grep "^CPU(s)"

sendfile, tcp_nopush, tcp_nodelay

http {
    # sendfile: zero-copy, envia arquivos diretamente do disco para socket
    sendfile on;

    # tcp_nopush: envia headers e inicio do arquivo juntos (com sendfile on)
    tcp_nopush on;

    # tcp_nodelay: desabilita Nagle algorithm para respostas em tempo real
    tcp_nodelay on;

    # Sendfile max chunk
    sendfile_max_chunk 512k;
}

KeepAlive Tuning

http {
    # Habilitar keepalive
    keepalive_requests 1000;
    keepalive_timeout 15;

    # Para proxy reverso
    proxy_http_version 1.1;
    proxy_set_header Connection "";

    upstream backend {
        server 10.0.0.1:8080;
        server 10.0.0.2:8080;

        # Keepalive connections para o upstream
        keepalive 32;
        keepalive_requests 100;
        keepalive_timeout 60s;
    }
}

Buffer Tuning

http {
    # Buffers de requisição
    client_body_buffer_size 128k;
    client_header_buffer_size 1k;
    client_max_body_size 100m;
    large_client_header_buffers 4 8k;

    # Buffers de resposta
    output_buffers 8 32k;
    postpone_output 1460;

    # Proxy buffers
    proxy_buffer_size 4k;
    proxy_buffers 8 4k;
    proxy_busy_buffers_size 8k;
    proxy_temp_file_write_size 8k;
}

Open File Cache

http {
    # Cache de descritores de arquivo
    open_file_cache max=2000 inactive=20s;
    open_file_cache_valid 60s;
    open_file_cache_min_uses 2;
    open_file_cache_errors on;
}

Configuração Completa de Performance

# /etc/nginx/nginx.conf
user www-data;
worker_processes auto;
pid /run/nginx.pid;
worker_rlimit_nofile 65535;

events {
    worker_connections 4096;
    multi_accept on;
    use epoll;
}

http {
    # Basic
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    sendfile_max_chunk 512k;

    # Timeouts
    keepalive_timeout 15;
    keepalive_requests 1000;
    client_body_timeout 10;
    client_header_timeout 10;
    send_timeout 10;

    # Buffers
    client_body_buffer_size 128k;
    client_header_buffer_size 1k;
    client_max_body_size 100m;
    large_client_header_buffers 4 16k;
    output_buffers 4 32k;

    # File cache
    open_file_cache max=4000 inactive=20s;
    open_file_cache_valid 60s;
    open_file_cache_min_uses 2;

    # Types
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # Logging
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log warn;

    # Gzip
    gzip on;
    gzip_comp_level 6;
    gzip_vary on;
    gzip_types text/plain text/css application/javascript application/json image/svg+xml;

    # Rate limiting zones
    limit_req_zone $binary_remote_addr zone=general:10m rate=100r/s;

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

Logs e Formatos

Formatos Customizados

http {
    # Formato padrao combined
    log_format combined '$remote_addr - $remote_user [$time_local] '
                        '"$request" $status $body_bytes_sent '
                        '"$http_referer" "$http_user_agent"';

    # Formato com tempo de resposta
    log_format timed '$remote_addr - $remote_user [$time_local] '
                     '"$request" $status $body_bytes_sent '
                     '$request_time $upstream_response_time';

    # Formato JSON para analise estruturada
    log_format json escape=json '{'
        '"time_local":"$time_local",'
        '"remote_addr":"$remote_addr",'
        '"request":"$request",'
        '"status":$status,'
        '"body_bytes":$body_bytes_sent,'
        '"request_time":$request_time,'
        '"upstream_time":"$upstream_response_time",'
        '"referer":"$http_referer",'
        '"user_agent":"$http_user_agent",'
        '"x_forwarded_for":"$http_x_forwarded_for"'
    '}';

    # Aplicar formatos
    access_log /var/log/nginx/access.log timed;
    access_log /var/log/nginx/access.json.log json;

    # Log condicional (ignorar health checks)
    map $request_uri $loggable {
        /health 0;
        /status 0;
        default 1;
    }
    access_log /var/log/nginx/access.log combined if=$loggable;
}

Log Rotation (logrotate)

# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
    daily
    missingok
    rotate 30
    compress
    delaycompress
    notifempty
    create 0640 www-data adm
    sharedscripts
    postrotate
        if [ -f /var/run/nginx.pid ]; then
            kill -USR1 `cat /var/run/nginx.pid`
        fi
    endscript
}
# Testar logrotate
sudo logrotate -d /etc/logrotate.d/nginx
sudo logrotate -f /etc/logrotate.d/nginx

Logs Separados por Site

server {
    server_name site1.com;
    access_log /var/log/nginx/site1-access.log timed;
    error_log /var/log/nginx/site1-error.log warn;
}

server {
    server_name site2.com;

    # Log apenas de erros
    access_log off;
    error_log /var/log/nginx/site2-error.log;

    # Log lento (requisicoes > 5s)
    if ($request_time > 5) {
        access_log /var/log/nginx/slow-requests.log timed;
    }
}

Monitoramento com stub_status

# Verificar se modulo esta compilado
nginx -V 2>&1 | grep stub_status
server {
    listen 127.0.0.1:80;
    server_name localhost;

    location /nginx_status {
        stub_status on;
        access_log off;
        allow 127.0.0.1;
        deny all;
    }
}
# Verificar status
curl http://127.0.0.1/nginx_status

# Saida esperada:
# Active connections: 291
# server accepts handled requests
#  16630948 16630948 31070465
# Reading: 6 Writing: 179 Waiting: 106

Métricas do stub_status

MétricaSignificado
Active connectionsConexões ativas totais
acceptsTotal de conexões aceitas
handledConexões manipuladas com sucesso
requestsTotal de requisições
ReadingRequisições sendo lidas
WritingRespostas sendo escritas
WaitingConexões keepalive aguardando

Script de Monitoramento

#!/bin/bash
# monitor.sh - Monitoramento basico do Nginx

HOST="http://127.0.0.1"
STATUS_URL="$HOST/nginx_status"

echo "=== Nginx Status ==="
curl -s $STATUS_URL

echo -e "\n=== Teste HTTP ==="
curl -s -o /dev/null -w "HTTP Code: %{http_code}\nTempo Total: %{time_total}s\n" $HOST

echo -e "\n=== Conexoes TCP ==="
ss -tlnp | grep 80

echo -e "\n=== Processos Nginx ==="
ps aux | grep nginx | grep -v grep

echo -e "\n=== Logs recentes (erros) ==="
tail -5 /var/log/nginx/error.log
# Monitoramento continuo
watch -n 5 'curl -s http://127.0.0.1/nginx_status'

# Coleta de metricas para grafana/prometheus
echo "nginx_connections_active $(curl -s http://127.0.0.1/nginx_status | awk '/Active/{print $3}')"

Nginx Amplify

# Instalar agente Nginx Amplify
sudo apt install python3 python3-pip -y
sudo pip3 install nginx-amplify-agent

# Configurar agente
sudo nginx-amplify-agent.py --setup

# Iniciar agente
sudo systemctl start amplify-agent
sudo systemctl enable amplify-agent

# Verificar logs do agente
sudo tail -f /var/log/amplify-agent/agent.log

Troubleshooting

curl e openssl

# Testar resposta basica
curl -I http://localhost

# Seguir redirects
curl -IL http://site.com

# Testar com metodo especifico
curl -X POST -d '{"key":"value"}' -H "Content-Type: application/json" http://api.local

# Testar SSL/TLS
curl -vk https://site.com
openssl s_client -connect site.com:443 -servername site.com

# Testar HTTP/2
curl --http2 -I https://site.com

# Testar gzip
curl -H "Accept-Encoding: gzip" -I http://site.com

# Verificar headers completos
curl -s -D - http://site.com -o /dev/null

# Medir tempo de resposta
curl -s -w "\nTempo: %{time_total}s\n" http://site.com

Logs de Erro

# Logs em tempo real
sudo tail -f /var/log/nginx/error.log
sudo tail -f /var/log/nginx/access.log

# Filtrar erros especificos
sudo tail -f /var/log/nginx/error.log | grep -E "emerg|alert|crit"

# Buscar 5xx no access log
sudo awk '$9 ~ /5[0-9][0-9]/' /var/log/nginx/access.log

# Buscar requisicoes lentas (> 5s)
sudo awk '$NF > 5' /var/log/nginx/access.log

# Top 10 IPs por requisicao
sudo awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10

# Top 10 URLs mais acessadas
sudo awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10

Debug com Error Log

# Niveis de log: debug | info | notice | warn | error | crit | alert | emerg
error_log /var/log/nginx/error.log debug;

# Debug especifico para location
location /api/ {
    error_log /var/log/nginx/api-debug.log debug;
    proxy_pass http://backend;
}

Lua Scripting (OpenResty)

# Instalar OpenResty (Nginx com Lua)
sudo apt install openresty -y
# Ou compilar com modulo lua-nginx-module
# Exemplo de script Lua no Nginx
location /lua {
    default_type 'text/plain';

    content_by_lua_block {
        local args = ngx.req.get_uri_args()
        local name = args["name"] or "Mundo"
        ngx.say("Ola, " .. name .. "!")
    }
}

location /auth-lua {
    access_by_lua_block {
        local token = ngx.req.get_headers()["X-Auth-Token"]
        if not token or token ~= "secret123" then
            ngx.status = 401
            ngx.say("Nao autorizado")
            ngx.exit(401)
        end
    }
    proxy_pass http://backend;
}

location /rate-limit-lua {
    content_by_lua_block {
        local redis = require "resty.redis"
        local red = redis:new()
        local ok, err = red:connect("127.0.0.1", 6379)
        if not ok then
            ngx.say("Redis connection error")
            return
        end

        local key = "rate:" .. ngx.var.binary_remote_addr
        local count, err = red:incr(key)
        if count == 1 then
            red:expire(key, 60)
        end

        if count > 100 then
            ngx.status = 429
            ngx.say("Rate limit exceeded")
            return
        end

        ngx.say("OK: " .. count)
    }
}

Arquivos Lua Externos

# Carregar script de arquivo externo
location /api {
    access_by_lua_file /etc/nginx/lua/auth.lua;
    content_by_lua_file /etc/nginx/lua/handler.lua;
}
-- /etc/nginx/lua/auth.lua
local function validate_jwt(token)
    -- Implementar validacao JWT
    return true
end

local token = ngx.req.get_headers()["Authorization"]
if token then
    token = token:gsub("^Bearer%s+", "")
    if not validate_jwt(token) then
        ngx.status = 401
        ngx.exit(401)
    end
else
    ngx.status = 401
    ngx.exit(401)
end

Lab: Nginx Otimizado com Monitoramento

Configure um servidor Nginx completo com tuning de performance e monitoramento.

# 1. Backup da configuracao original
sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.backup

# 2. Aplicar configuracao de performance otimizada
cat << 'EOF' | sudo tee /etc/nginx/nginx.conf
user www-data;
worker_processes auto;
pid /run/nginx.pid;
worker_rlimit_nofile 65535;

events {
    worker_connections 4096;
    multi_accept on;
    use epoll;
}

http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 15;
    keepalive_requests 1000;
    client_body_timeout 10;
    client_header_timeout 10;
    send_timeout 10;
    client_body_buffer_size 128k;
    client_max_body_size 100m;
    open_file_cache max=4000 inactive=20s;
    open_file_cache_valid 60s;
    open_file_cache_min_uses 2;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    log_format timed '$remote_addr - $remote_user [$time_local] '
                     '"$request" $status $body_bytes_sent '
                     '$request_time $upstream_response_time';

    access_log /var/log/nginx/access.log timed;
    error_log /var/log/nginx/error.log warn;

    gzip on;
    gzip_comp_level 6;
    gzip_vary on;
    gzip_types text/plain text/css application/javascript application/json;

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}
EOF

# 3. Configurar stub_status
cat << 'EOF' | sudo tee /etc/nginx/conf.d/status.conf
server {
    listen 127.0.0.1:80;
    server_name localhost;

    location /nginx_status {
        stub_status on;
        access_log off;
        allow 127.0.0.1;
        deny all;
    }
}
EOF

# 4. Configurar logrotate para logs JSON
cat << 'EOF' | sudo tee /etc/nginx/conf.d/json-log.conf
log_format json escape=json '{'
    '"time":"$time_local",'
    '"remote":"$remote_addr",'
    '"request":"$request",'
    '"status":$status,'
    '"bytes":$body_bytes_sent,'
    '"rt":$request_time,'
    '"upstream_rt":"$upstream_response_time"'
'}';
server {
    listen 127.0.0.1:81;
    server_name localhost;
    access_log /var/log/nginx/access.json.log json;
    location / { return 200 "OK"; }
}
EOF

# 5. Testar e recarregar
sudo nginx -t && sudo systemctl reload nginx

# 6. Testar status
echo "=== Stub Status ==="
curl http://127.0.0.1/nginx_status

echo -e "\n=== Performance Test ==="
ab -n 1000 -c 10 http://localhost/ 2>/dev/null | grep -E "Requests per second|Time per request|Failed requests"

echo -e "\n=== Log Format ==="
curl -s http://localhost/ > /dev/null
sudo tail -1 /var/log/nginx/access.log

echo -e "\n=== File Descriptors ==="
cat /proc/$(cat /var/run/nginx.pid)/limits | grep "open files"

Performance no Nginx e sobre configurar buffers adequados, escolher o metodo de multiplexacao correto (epoll), ajustar worker_processes ao numero de CPUs e monitorar constantemente com stub_status. Nenhum tuning substitui monitoramento continuo.