Tuning do Nginx

Pessoal vou compartilhar com vocês como eu faço para tunar o Nginx. Vamos abordar o tuning do sistema operacional e o tuning do Nginx.

Tuning do Sistema Operacional

O tuning do sistema operacional é configurado alterando-se parâmetros do kernel através do sysctl.

Configuração do sysctl

Edite o arquivo /etc/sysctl.conf
Ou crie um arquivo de configuração “.conf” em: /etc/sysctl.d/ (recomendado).
Exemplo: /etc/sysctl.d/tuninginx.conf

Abaixo seguem os parâmetros e uma breve descrição de cada um.

Prevenção de SYN Flood e ataques DDOS.
net.ipv4.tcp_syncookies = 1

Número de conexões aguardando a confirmação “ACK” do cliente.
Aumente conforme a necessidade do servidor.
net.ipv4.tcp_max_syn_backlog = 65535

Quantas vezes tentar retransmitir uma resposta SYN-ACK.
net.ipv4.tcp_synack_retries = 2

Quantas vezes tentar retransmitir uma resposta SYN.
net.ipv4.tcp_syn_retries = 2

Tempo de espera pela resposta de encerramento FIN-WAIT. Em resumo tempo máximo para esperar
o encerramento de uma conexão.
net.ipv4.tcp_fin_timeout = 15

Número máximo de conexões enfileiradas. Isto afeta o número máximo de conexões e pacotes de entrada.
Tem que ser menor que a quantidade de file descriptors.
net.core.somaxconn = 65535

Número máximo de pacotes que podem ser enfileirados na placa de rede antes do processamento. Quanto maior a banda de rede melhor.
net.core.netdev_max_backlog = 65535

File Descriptors controlam a quantidade de processos e arquivos que um usuário pode trabalhar no Linux.
Configuração global, ou seja afeta todo o sistema.
fs.file-max = 185032

[root@lemp-01 sysctl.d]# cat /etc/sysctl.d/tuninginx.conf 
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_synack_retries = 2 
net.ipv4.tcp_syn_retries = 2
net.ipv4.tcp_fin_timeout = 15
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 65535
fs.file-max = 185032
Configuração do file descriptor por usuário

Pode ser configurado tanto na pasta /etc/security/limits.d/ no arquivo limits.conf.

#vi /etc/security/limits.conf
nginx                soft   nofile      8192
nginx                hard   nofile      65835

Tuning do Nginx

O tuning do Nginx é configurado alterando-se parâmetros de configuração no arquivo nginx.conf. Também é possível configurar alguns parâmetros no virtual host.

#vi /etc/nginx/nginx.conf

worker_rlimit_nofile – contexto: main – Limita o número máximo de openfiles.
worker_rlimit_nofile 65535;

worker_processes – contexto: main – Quantos processos serão carregados. O ideal é um processo por cpu.
Por ex: 8 cpus=worker_processes=8. A opção “auto” configura automaticamente os processos.
worker_processes auto;

worker_connections – contexto: events – quantidade de conexões por processo.
#não pode exceder a quantidade de File Descriptors.
#aumente conforme a necessidade e capacidade.
worker_connections = 8190;

accept_mutex – contexto: events – Economiza recursos do sistema destinado aos worker processes.
Se ativo worker process receberão as conexões por turno, caso contrário todos os worker processes serão notificados sobre novas conexões, e atenderá aquele que tiver livre.
accept_mutex off;

Epoll – contexto: events – Método de conexão para kernel >= 2.6. Epoll é utilizado em ambientes que precisam escalar com eficiência, que precisam de muitos file descriptors abertos.
use epoll;

multi_accept – contexto: events – Se desabilitado o worker process vai aceitar uma conexão por vez, caso contrário o worker process vai aceitar todas as conexões ao mesmo tempo.
multi_accept on;

Parâmetros sendfile, tcp_nopush e tcp_nodelay

Não tenha dúvidas que no tuning do Nginx os parâmetros sendfile, tcp_nodelay e tcp_nopush são os que geram maior confusão.

Sendfile pode ser utilizado ou não, depende do ambiente.

Sendfile – contexto http, server, location, if in location – Permite a transferência de dados de um File Descriptor a outro diretamente no Kernel.
Substitui a operação de leitura e escrita em um File Descriptor.
Execução diretamente no kernel sem a troca de contexto.
Escrita diretamente no Buffer do Kernel (DMA).
Recomendado para servir arquivos estáticos (cache).
Se o servidor for um proxy reverso ou apenas web server pode ser desativado.
sendfile on;

tcp_nodelay – contexto http, server, location – Elimina ou ativa o delay de 200ms na pilha TCP para que um dado possa ser enviado. Quando ativado “On” um pacote será enviado assim que tiver disponível e não mais após o tempo de 200ms.
tcp_nodelay on;

tcp_nopush – contexto http, server, location – Controla o tamanho de um pacote antes do envio. Quando ativado “on” – o pacote será enviado somente com o tamanho MSS (MSS = MTU – IP header).
Quando “on” a opção sendfile precisa obrigatoriamente estar ativa “sendfile on”.
Quando desativado “off” o pacote será enviado assim que tiver pronto para ser enviado independentemente do tamanho, sem passar pelo bloqueio de tamanho MSS.
Quando Nginx configurado como servidor de Cache é interessante ligar o tcp_nopush, já que ao enviar apenas pacotes completos, diminui o tráfego de pequenos pacotes.
tcp_nopush off;

Combinações:
sendfile on + tcp_nopush on = Vai garantir um pacote completo antes do envio diminuindo o overhead de rede. Ideal para o Ngix como servidor de cache.

sendfile on + tcp_nopush on + tcp_nodelay on = Garante um pacote completo e menos overhead na rede. O pacote será enviado assim que disponível sem o bloqueio de 200ms.

tcp_nopush off + tcp_nodelay on = Um pacote será enviado assim que tiver disponível. Recomendado caso o Nginx seja apenas um web server ou proxy server (Sem qualquer função de cache).

sendfile off + tcp_nodelay on = o pacote será enviado assim que tiver disponível, sem delay de 200ms, melhor para web server ou proxy server, sem a troca de arquivos entre File Descriptors.

sendfile on + tcp_nodelay on = o pacote será enviado assim que tiver pronto, sem delay, melhor para web server ou proxy server, com a troca de arquivos entre File Descriptors.

Sugestões:
Para servidor de Cache = sendfile on + tcp_nopush on + tcp_nodelay on
Proxy Server = sendfile off + tcp_nopush off + tcp_nodelay on
Web server = sendfile (talvez on ou off) + tcp_nopush off + tcp_nodelay on
Para Web server é interessante testar a performance com sendfine on ou off.

O interessante é você combinar as opções e ir testando, no geral tcp_nopush e tcp_nodelay não combinam devido à natureza da coisa, o tcp_nopush controla o tamanho do pacote, e tcp_nodelay o tempo de envio do pacote.

Sendfile on combina bem tanto com o “tcp_nopush on” como o “tcp_nodelay on” porém dependendo do tipo de servidor (Proxy server) pode ser necessário desativar.

KeepAlive
keepalive_requests – contexto: http, server, location
– Quantas requisições um cliente pode fazer dentro de uma conexão keepalive.
keepalive_requests 100;

keepalive_timeout – contexto: http, server, location – Quanto tempo uma conexão com status idle em keepalive deve ser mantida aberta.
keepalive_timeout 65;

Desativar o Log, manter ligado ou deixar no buffer?

O log é o seguinte, a cada hit http no Nginx é logado sendo assim o Log consome muitos ciclos de I/O e processamento, em um ambiente de cache isto pode ser complicado, já que um servidor de cache utiliza muito I/O, então existirá concorrência.

Você tem que saber se precisa desligar o log ou não, em termos de performance com certeza desligar o log é o ideal.

Para melhor performance
access_log off;

Porém se mesmo assim você quer manter o log ativo, eu particularmente gosto de log, então você pode tentar minimizar o impacto de I/O e colocar o log para ser gravado no buffer, e assim que o buffer tiver cheio o log será gravado no disco em uma tacada só, e assim sucessivamente.
access_log /var/log/nginx/access.log main buffer=64k;

Ou simplesmente deixe o log padrão
access_log /var/log/nginx/access.log main;

#cat /etc/nginx/nginx.conf

user  nginx;
worker_processes  auto;
worker_rlimit_nofile 65535;

error_log  /var/log/nginx/error.log debug;
pid        /var/run/nginx.pid;

events {
    worker_connections  8190;
    accept_mutex  off;
    use epoll;
    multi_accept on;
    
}

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

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    
    access_log  /var/log/nginx/access.log main buffer=64k;

    sendfile     on;
    tcp_nopush   off;
    tcp_nodelay  on;
    keepalive_timeout  65;
    keepalive_requests  100;

    include /etc/nginx/conf.d/*.conf;
}

Tem muiiito mais coisas possíveis para se fazer, obviamente eu jamais conseguiria esgotar todo o assunto de tuning do Nginx, até mesmo por que cada ambiente precisa de configurações particulares, mas fica aqui mais algumas sugestões:

->Cache em memória RAM: se você está configurando um servidor de cache de imagens, considere montar o volume de cache na memória Ram (tmpfs). Escrever direto na memória RAM é rápido demais.

->Gzip on: Utilização de compressão de dados com gzip.

Print Webinar Nginx
Segue um print que eu tirei de um Webinar que tem no próprio site do Nginx https://www.nginx.com/resources/webinars/installing-tuning-nginx/
Assunto: instalação e Tuning do Nginx. Veja que tem mais algumas opções de Kernel e Nginx que podem ser tunadas.

webinar-nginxtuning

Nginx Benchmarking

Não adianta você fazer o tuning e não medir para ver se obteve melhoria ou piora de desempenho.
Existem várias ferramentas, das simples as complexas para medir o desempenho, eu vou sugerir uma bem simples de usar que é a “AB – ApacheBench” para a simulação de acessos web.

Também é interessante gerar gráficos para uma melhor mensuração, você pode experimentar a Gnuplot.
Com “AB – Apachebench + Gnuplot” você já consegue ter uma boa visão do seu tuning.

Bom é isto, o segredo do tuning é você ir alterando aos poucos e medindo com as ferramentas de Benchmarking, eu nunca vi na minha vida uma receita perfeita de tuning, as vezes algo que funciona bem em um ambiente, em outros já não funciona de forma tão eficiente.

Referências:
https://www.nginx.com/resources/webinars/installing-tuning-nginx/
https://www.nginx.com/blog/tuning-nginx/
https://www-01.ibm.com/support/knowledgecenter/linuxonibm/liaag/wp64bit/l0wpbt00_ds_linux_kernel_settings.htm
https://access.redhat.com/sites/default/files/attachments/20150325_network_performance_tuning.pdf
http://www.revsys.com/12days/nginx-tuning/
https://t37.net/nginx-optimization-understanding-sendfile-tcp_nodelay-and-tcp_nopush.html
https://www.litespeedtech.com/support/wiki/doku.php/litespeed_wiki:config:mitigating-syn-floods
https://klaver.it/linux/sysctl.conf

Puppet – Tuning do Puppet Server

Entendendo o Funcionamento do Puppet Server

Neste artigo estou utilizando o Puppet Server 4.3.
Antes de fazer o tuning vamos entender um pouco sobre o funcionamento do Puppet. O Puppet Server trabalha com o JRuby, permitindo a utilização de todos os recursos do Ruby dentro da plataforma Java, a ideia é aproveitar o melhor dos 2 mundos, ou seja, utilizar recursos tanto do Ruby como do Java.
Quando você inicia o Puppet Server ele carrega um pool de interpretadores JRuby para poder executar os códigos em Ruby, pense neste pool como se fossem pequenas máquinas virtuais “VM” executando os códigos em Ruby.

Por padrão o Puppet Server carrega apenas uma instância JRuby (max-active-instances), isto quer dizer que o puppet Server pode tratar apenas uma requisição de clientes puppet por vez; por exemplo seu puppet server está configurado na forma padrão, ou seja max-active-instances=1, sendo assim com apenas uma instância JRuby UP, se você tiver 2 clientes solicitando configurações no Puppet Server ao mesmo tempo, um deles entrará na fila até que o outro termine.

Análise de logs.

tail –f /var/log/puppetlabs/puppetserver/puppetserver-access.log
10.0.0.161 - - - 23/Feb/2016:13:28:17 -0300 "GET /puppet/v3/node/puppetclient-01.devopslab.com.br?environment=production&transaction_uuid=c2cba296-f2e3-4f8e-90fe-1d52dfa4e0ad&fail_on_404=true HTTP/1.1" 200 9386 10.0.0.161 10.0.0.161 8140 93
10.0.0.162 - - - 23/Feb/2016:13:28:17 -0300 "GET /puppet/v3/node/puppetclient-02.devopslab.com.br?environment=production&transaction_uuid=b12a202a-81b6-414b-82db-22289d59c372&fail_on_404=true HTTP/1.1" 200 9400 10.0.0.162 10.0.0.162 8140 58
10.0.0.161 - - - 23/Feb/2016:13:28:17 -0300 "GET /puppet/v3/file_metadatas/pluginfacts?environment=production&links=follow&recurse=true&source_permissions=use&ignore=.svn&ignore=CVS&ignore=.git&checksum_type=md5 HTTP/1.1" 200 242 10.0.0.161 10.0.0.161 8140 16
10.0.0.162 - - - 23/Feb/2016:13:28:17 -0300 "GET /puppet/v3/file_metadatas/pluginfacts?environment=production&links=follow&recurse=true&source_permissions=use&ignore=.svn&ignore=CVS&ignore=.git&checksum_type=md5 HTTP/1.1" 200 242 10.0.0.162 10.0.0.162 8140 17
tail –f /var/log/puppetlabs/puppetserver/puppetserver.log
2016-02-23 13:28:17,644 INFO  [qtp955200837-68] [puppet-server] Puppet Caching node for puppetclient-01.devopslab.com.br
2016-02-23 13:28:17,742 INFO  [qtp955200837-69] [puppet-server] Puppet Caching node for puppetclient-02.devopslab.com.br

2016-02-23 13:28:20,790 INFO  [qtp955200837-67] [puppet-server] Puppet Caching node for puppetclient-01.devopslab.com.br
2016-02-23 13:28:20,906 INFO  [qtp955200837-67] [puppet-server] Puppet Compiled catalog for puppetclient-01.devopslab.com.br in environment production in 0.09 seconds

2016-02-23 13:28:21,253 INFO  [qtp955200837-68] [puppet-server] Puppet Caching node for puppetclient-02.devopslab.com.br
2016-02-23 13:28:21,354 INFO  [qtp955200837-68] [puppet-server] Puppet Compiled catalog for puppetclient-02.devopslab.com.br in environment production in 0.08 seconds

Perceba que cada requisição dos clientes são tratadas de forma sequencial, uma de cada vez, a diferença é de milissegundos, no entanto dependendo do tamanho do seu ambiente passará de milissegundos para segundos, e de segundos para minuto, e quando você perceber seu Puppet Server estará com um overhead imenso, causando lentidão na aplicação de configurações nos clientes.

Tuning parte 1 – Cálculo de CPUs

Uma instância definida no parâmetro “max-active-instances” aloca 1 CPU.

Não é recomendável ter mais instâncias definidas no “max-active-instances” do que CPUs. Se você tem 8 CPUs, logo a quantidade de “max-active-instances” tem que ser no máximo 8, se o número de instâncias forem maior que as CPUs logo sobrará instâncias carregadas em memória porém sem trabalhar, no final das contas, mais instâncias que a quantidade de CPU, vai aumentar o consumo de memória porém sem benefício algum.

Uma fórmula razoável para cálculo de “max-active-instances”:
max-active-instances = CPUs-1.

CPUs = a quantidade de CPUs no servidor.
CPUs-1 = a Quantidade de CPUs-1, pois precisa sobrar uma CPU para o restante do sistema, é possível utilizar todas as CPUs para o Puppet Server, max-active-instances=Número-de-CPUs, mas seu sistema também tem outras coisas rodando, como systemd, ssh, postfix, e outros vários processos, então é recomendável deixar uma CPU sobrando.

Para aumentar a quantidade de instâncias edite o arquivo puppetserver.conf.

vi /etc/puppetlabs/puppetserver/conf.d/puppetserver.conf

# (optional) maximum number of JRuby instances to allow
max-active-instances: 7

Reinicie o Puppet Server para a aplicação das alterações.

Tuning parte 2 – Cálculo de Memória RAM do JRuby e do Sistema Operacional

Cálculo de Memória RAM do JRuby
Cada instância JRuby/JVM definida no “max-active-instances” consome 512MB em média, podendo ser um pouco mais dependendo da quantidade de módulos, manifestos e etc, mas em regras gerais é 512MB.

Para calcular a memória do JRuby/JVM podemos fazer:
Memória RAM = (max-active-instances * 512)MB.
Se eu tenho 7 CPUs destinadas ao max-active-instances, então minha memória RAM para o JRuby/JVM seria: (7 * 512) = 3584 MB RAM.

Altere a variável JAVA_ARGS no arquivo /etc/sysconfig/puppetserver.

vi /etc/sysconfig/puppetserver

JAVA_ARGS="-Xms3584m -Xmx3584m -XX:MaxPermSize=256m"

Xms e Xmx é recomendável serem iguais; o Xms é a memória inicial alocada pelo JVM e Xmx é o máximo que pode ser alocado pelo JVM.

Reinicie o Puppet Server para a aplicação das alterações.

Cálculo de Memória RAM do Sistema Operacional.
Destine para o Sistema Operacional algo em torno de 512MB, sendo assim a memória RAM total do seu servidor deve ser igual a memória calculada para o JRuby/JVM + 512MB.

Mem RAM do sistema Operacional = (max-active-instances * 512)MB + 512MB
No nosso exemplo, precisaríamos de um servidor com 4GB RAM pois (7*512)MB+512MB = 4096MB.

Tuning parte 3 – File Descriptors

File Descriptors controlam a quantidade de processos e arquivos que um usuário pode trabalhar no Linux, por padrão vem configurado em 1024 open files.

Vamos alterar isto para um número maior, já que o usuário “puppet” responsável por todos os processos do Puppet Perver pode precisar de algo acima de 1024 open files.

Edite o arquivo: /etc/security/limits.conf

vi /etc/security/limits.conf


*                soft   nofile      4096
*                hard   nofile      65535

Esta configuração se aplicará globalmente afetando todos os usuários do sistema.

Se você quiser mais segurança, você pode especificar o usuário “puppet” de forma explicita.

vi /etc/security/limits.conf

puppet                soft   nofile      4096
puppet                hard   nofile      65535

Verifique também o File Descriptor “fs.file-max” configurado no Sysctl.

sysctl -a | grep fs.file-max

fs.file-max = 150216

Se for algum número menor do que você configurou no arquivo limits.conf, ajuste para um número no mínimo igual ao “puppet hard nofile 65535”.
Edite o arquivo “/etc/sysctl.conf”.

vi /etc/sysctl.conf

fs.file-max = 150216

No meu caso o “fs.file-max” já estava como 150216, então não precisei mexer em nada, porém é sempre bom saber que no sysctl.conf também pode ser configurado o File Descriptor.

Dúvidas, sugestões e correções entre em contato.
Muito brigado.
Leonardo