isaacalves7

Isaac Alves Pinheiro

Posted on May 24, 2023

Nginx

O NGINX é um servidor web/proxy reverso rápido, de alto rendimento e um proxy para correio eletrônico (IMAP/POP3), é um software livre e de código-aberto. Ele é um serviço, é um programa que roda e que serve para responder requisições web.

Ele não usa somente aquela ideia de processos do servidor Apache, threads e programação paralela, ele usa um outro conceito de programação assíncrona muito interessante:

Então, temos aqui uma base do funcionamento do NGINX. Quando você inicia um serviço do NGINX, ele cria um processo principal como se fosse o patrão desses colaboradores aqui. No qual esse patrão vai criar alguns processos colaboradores, alguns worker process. Esses worker process são criados baseado no número de núcleos (cores) que o seu processador tem. Suponha que eu tenho um servidor que tem um processador com quatro núcleos. Então eu poderia criar, por exemplo, 4 processos worker process para tratar requisições. Porque assim eu tenho um maior número de processos tratando cada número de requisições, então eu consigo tratar mais requisições.

Só que a sacada é: Não é como se cada processo tratasse uma requisição. Não é isso! Cada um desses processos trata um número grande de requisições, várias requisições, e para que ele consiga tratar mais de uma requisição, ele usa um conceito de Multiplexing I/O. Ou seja, ele faz mais de uma coisa de forma assíncrona.

Então, imagine que chegou uma requisição nesse worker aqui e depois chega uma nova requisição que caiu nesse mesmo worker. O que ele vai fazer? Ele vai colocar para executar o primeiro. Enquanto esse processo está esperando essa tarefa ser executada - por exemplo: o servidor de aplicação responder e o arquivo ser carregado - ele já trata outra requisição, coloca para carregar imagem e manda requisição para servidor de imagem. Depois ele pega a resposta da primeira requisição e devolve, continua tratando a segunda e devolve. Por isso o NGINX veio com a proposta de ser o servidor web mais rápido.

Existem benchmarks que colocam realmente o NGINX lá no topo, ou seja, é um servidor muito performático. Mas obviamente não existe bala de prata e ele tem seus cenários onde ele trabalha muito bem e não é ferramenta ideal.

Funcionamento do NGINX: Ele inicia um servidor, um processo. Esse processo cria outros processos colaboradores, e cada um desses processos consegue tratar várias requisições utilizando o conceito de programação assíncrona, garantindo assim uma grande performance.

Difere-se muito do servidor Apache, porque ele transfere a responsabilidade de um servidor web para um servidor de aplicação além de ser:

  • Tolerante a falhas;
  • Compatível com o IPv6;

Exemplo: Com Java você pode ter um Tomcat rodando, ou com PHP você vai ter um PHP FPM rodando. Enfim, você vai ter algum servidor de aplicação aqui, e o NGINX consegue se conectar à ele. Mas nós vamos focar só na parte do NGINX, sem nos conectarmos a algum servidor de aplicação.

Instalação do NGINX

Como baixar e configurar o NGINX server.

Windows

macOS

brew install nginx 
Enter fullscreen mode Exit fullscreen mode

Linux

sudo apt install nginx
Enter fullscreen mode Exit fullscreen mode

Para iniciar o nginx é simples, basta escrever o nome dele na linha de comando no terminal:

nginx
Enter fullscreen mode Exit fullscreen mode

Basta somente executa-lo e acessar o http://localhost:8080

welcome-screen-e1450116630667

[NGINX] Servidor HTTP

Exibe ajuda e lá podemos ver os caminhos do arquivo de configuração

nginx -h
Enter fullscreen mode Exit fullscreen mode

Arquivos de configuração do servidor (nginx.conf)

Essa é a área que configuramos o nosso servidor Nginx:

NGINX

#user  nobody;
worker_processes  1; # auto

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024; # limite de processos em um sistema operacional de arquivos assíncronos (file descriptors = sockets, connections, etc...)
}


http { # Servidor HTTP
    include       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  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server { # Servidor Web
        listen       8080;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            root   html;
            index  index.html index.htm;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }


    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}


    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}
    include servers/*;
}
Enter fullscreen mode Exit fullscreen mode

Você pode usar o comando abaixo para retirar os comentários e visualizar somente o necessário, funciona como um filtro:

cat /opt/homebrew/etc/nginx/nginx.conf | grep -ve '^.\s*#' | grep -ve '^#' | sed '/^$/d' 
Enter fullscreen mode Exit fullscreen mode

Dessa forma, o arquivo de configuração fica assim:

NGINX

worker_processes  1;
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    server {
        listen       8080;
        server_name  localhost;
        location / {
            root   html;
            index  index.html index.htm;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
    include servers/*;
}
Enter fullscreen mode Exit fullscreen mode

Vamos editar nosso arquivo principal da página do Nginx. Para isso, acesse o comando abaixo:

nano /opt/homebrew/Cellar/nginx/1.23.2/html/index.html
Enter fullscreen mode Exit fullscreen mode

Se definirmos location /, significa / literalmente tudo, ou seja, podemos definir qual o diretório raiz do projeto, qual o arquivo padrão, regras de redirecionamento, etc. Percebe-se que o arquivo não mostra o preview dele no localhost.

Recarrega o arquivo nginx

nginx -s reload
Enter fullscreen mode Exit fullscreen mode

Testar o arquivo de configuração

nginx -t 
Enter fullscreen mode Exit fullscreen mode

Páginas de erro HTTP

Os códigos de erro de protocolo HTTP começam na faixa dos 400:

server {
    listen 80;
    server_name localhost;

    location / {
        root C:/Users/isaac/Dev/nginx;
        index index.html
    }
    error_page 404 400 401 402 /erro.html;
}
Enter fullscreen mode Exit fullscreen mode

[NGINX] Proxy Reverso

Um proxy de rede funciona basicamente como se fosse um "túnel" ou "filtro", quando acessamos uma determinada área, o proxy . Então é assim que funciona um proxy, ele literalmente pega as requisições e dispara na internet para um servidor web acessar as informações, ou sejam um proxy reverso ele pega as requisições feitas e redireciona ainda para outro servidor (Como se acrescentasse mais uma etapa no processo de proxy).

Então, o proxy reverso é um servidor web que recebe as requisições e distribui para outros servidores.

Porque normalmente um proxy fica no lado do cliente. O conceito padrão de proxy é algo que fica no lado do cliente interceptando os pacotes de rede. Como nesse caso o proxy está no lado do servidor, chamamos de proxy reverso.

location / {
    proxy_pass http://localhost;
}
Enter fullscreen mode Exit fullscreen mode

Com esse simples código, eu tenho o conceito de proxy reverso.

Por que realizar um proxy reverso e não apenas deixar o servidor de aplicação lidar com todas as requisições? Com nginx na frente podemos responder arquivos estáticos muito mais rapidamente. Esse é um dos principais motivos. O nginx é um servidor incrivelmente performático, então nós ganhamos muito ao não enviar todas as requisições para o servidor de aplicação. O nginx pode enviar diretamente os arquivos estáticos sem processar nada, além de poder definir cache, compressão, etc.

Servidor 2 em 1

Com o proxy reverso podemos receber uma requisição e devolver muito rápido se for um arquivo estático, fazer cash e etc. Já para fazer uma rota ou uma URL que precisa de lógica, que precisa ser processada, eu mando para o servidor de aplicação.

Exemplo (default.conf):

server {
    listen 80;
    server_name localhost;

    location / {
        root /Users/isaac/Dev/nginx;
        index index.html
    }

    location ~ \.php$ {
        proxy_pass http://localhost:8000;
    }

    error_page 404 400 401 402 /erro.html
}
Enter fullscreen mode Exit fullscreen mode
php -S localhost:8000
Enter fullscreen mode Exit fullscreen mode

Com nginx na frente podemos responder arquivos estáticos muito mais rapidamente. Esse é um dos principais motivos. O nginx é um servidor incrivelmente performático, então nós ganhamos muito ao não enviar todas as requisições para o servidor de aplicação. O nginx pode enviar diretamente os arquivos estáticos sem processar nada, além de poder definir cache, compressão, etc.

[NGINX] API Gateway

Monolítica, Microserviços e Múltiplos serviços

Resumidamente:

  • Monolithic (Monolítica): é nada mais e nada menos do que uma arquitetura para uma aplicação rodando em somente um serviço contendo uma estrutura com poucas seções de maneira estruturada para o servidor;

  • Microserviços (Microservices): é nada mais é do que uma arquitetura com muitos micro (pequenos) serviços rodando em uma ou várias aplicações, ideal para projetos de grande porte e com diversas áreas diferentes.

Vamos criar dois serviços dentro de (microserviços.conf):

server {
    listen 8001;
    server_name localhost;

    location / {
        root /Users/isaac/Dev/nginx/servico1;
        index index.html
    }

    error_page 404 400 401 /erro.html;
}

server {
    listen 8002;
    server_name localhost;

    location / {
        root /Users/isaac/Dev/nginx/servico2;
        index index.html
    }

    error_page 404 400 401 /erro.html;
}
Enter fullscreen mode Exit fullscreen mode

Agora vamos criar a index.html:

mkdir Dev/nginx/servico1 Dev/nginx/servico2
echo "Servico 1" > Dev/nginx/servico1/index.html
echo "Servico 2" > Dev/nginx/servico1/index.html
Enter fullscreen mode Exit fullscreen mode

Configurando um servidor web que faz o redirecionamento para outros múltiplos servidores baseados na URL recebida (ponto de entrada para centralizar o acesso a múltiplos serviços em um único host):

location /servico1 {
    proxy_pass http://localhost:8001/;
  }

location /servico2 {
    proxy_pass http://localhost:8002/;
  }
Enter fullscreen mode Exit fullscreen mode

Dessa forma, todas as requisições podem ser feitas para o mesmo servidor, facilitando a vida do cliente, dentre outras vantagens.

Um servidor web que faz o redirecionamento para outros múltiplos servidores baseado na URL recebida. Dessa forma, todas as requisições podem ser feitas para o mesmo servidor, facilitando a vida do cliente, dentre outras vantagens.

A partir do API Gateway é decidido onde essa requisição será direcionada.

  • Problema: Clientes acessando livremente os serviços geram caos
  • Gateway fornece um proxy, uma fachada, para as necessidades reais
  • Desvantagem: Esse portão de entrada pode se tornar um pouco central de falha

O comportamento do Gateway

  • Simplesmente autorizar e redirecionar os requests
  • Uso de Decorator para adicionar informações necessárias aos requests
  • Limitar o acesso ou conteúdo trafegado
  • Impedir que determinadas URLs sejam acessadas por completo

Agora que conhecemos o conceito de um API Gateway, temos uma base melhor para esse artigo fenomenal do próprio Nginx: https://www.nginx.com/blog/deploying-nginx-plus-as-an-api-gateway-part-1/

[NGINX] Load Balancing (Balanceamento de carga)

É um balanceador de carga (como um switch para serviços).

mv Dev/nginx/servico2/servico.html Dev/nginx/servico2/index.html
Enter fullscreen mode Exit fullscreen mode

Podemos configura-lo de forma muito simples, com o upstream:

upstream servicos {
     server localhost:8001;
     server localhost:8002;
}

server {
    listen 8003;
    server_name localhost;

    location / {
        proxy_pass http://servicos;
    }
}
Enter fullscreen mode Exit fullscreen mode

Na prática, através do nome definido em upstream, podemos acessar algum dos servidores deste grupo dependendo de algumas regras definidas.

Configuração de logs

access_log é qualquer log de acesso, já o error_log é um log somente de erros. Vamos ver um exemplo no (nginx.conf):

#user nobody;
worker_processes 1;

#error_log  logs/error.log;
#error_log  logs/error.log notice;
#error_log  logs/error.log info;

#pid        logs/nginx.pid


events {
    worker_connections  1024;
}


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

    logs_format main  '$remote_addr - $remote_user [$time_local] "$request"'
                      '$status $body_bytes_sent "$http_referer"
                      '"$http_user_agent" "$http_x_fowarded_for"';

   #access_log  logs/access.log  main;
}
Enter fullscreen mode Exit fullscreen mode

Logo:

mkdir Dev/nginx/logs
nginx -t
nginx -s reload
tail -f Dev/nginx/logs/servico1
tail -f Dev/nginx/logs/servico2
Enter fullscreen mode Exit fullscreen mode

Formato de logs (nginx.conf)

#user nobody;
worker_processes 1;

#error_log  logs/error.log;
#error_log  logs/error.log notice;
#error_log  logs/error.log info;

#pid        logs/nginx.pid


events {
    worker_connections  1024;
}


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

    logs_format main  'Remote Addr: $remote_addr - Time: [$time_local] "$request"'
                      'Status: $status, Referer: "$http_referer"

   #access_log  logs/access.log  main;
}

     sendfile          on;
Enter fullscreen mode Exit fullscreen mode

Formato de logs (microservicos.conf)

server {
    listen 8001;
    server_name localhost;
    access_log /Users/isaac/Dev/nginx/logs/servico1.log main;

    location / {
      root /Users/isaac/Dev/nginx/logs/servico1.log;
      index index.html;
    }
}

server {
    listen 8002;
    server_name localhost;
    access_log /Users/isaac/Dev/nginx/logs/servico2.log main;

    location / {
      root /Users/isaac/Dev/nginx/logs/servico2.log;
      index index.html;
    }
}
Enter fullscreen mode Exit fullscreen mode

Adicionando informações

Dentro do load-balancer.conf:

upstream servicos {
     server localhost:8001;
     server localhost:8002;
}

server {
    listen 8003;
    server_name localhost;

    location / {
        proxy_pass http://servicos;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
Enter fullscreen mode Exit fullscreen mode

No nginx.conf:

#user nobody;
worker_processes 1;

#error_log  logs/error.log;
#error_log  logs/error.log notice;
#error_log  logs/error.log info;

#pid        logs/nginx.pid


events {
    worker_connections  1024;
}


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

    logs_format main  'Remote Addr: $http_x_real_ip, Time: [$time_local] "$request"'
                      'Status: $status, Referer: "$http_referer"

   #access_log  logs/access.log  main;
}

     sendfile          on;
Enter fullscreen mode Exit fullscreen mode

Definindo pesos (load-balancer.conf)

Se não distriuimos corretamente o peso para cada um servidor, deixaremos um desses servidores sobrecarregados.

upstream servicos {
     server localhost:8001 weight=2;
     server localhost:8002;
}

server {
    listen 8003;
    server_name localhost;

    location / {
        proxy_pass http://servicos;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
Enter fullscreen mode Exit fullscreen mode

Aplicamos o cenário de quando temos servidores com capacidades diferentes.

Servidores de backup

Vamos fazer o seguinte, vamos utilizar o servidor1 como o servidor principal e o servidor2 como o nosso servidor de backup.

upstream servicos {
     server localhost:8001 fail_timeout=120s;
     server localhost:8002 backup;
}

server {
    listen 8003;
    server_name localhost;

    location / {
        proxy_pass http://servicos;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
Enter fullscreen mode Exit fullscreen mode

Utilizaremos o servidor backup somente para casos extremos, ou seja, se o servidor principal falhar e estiver em instabilidade, o servidor backup o substitue.

[NGINX] Fast CGI


Lógicas do lado do servidor, e para isso foi criado o CGI. Com a mesma ideia do CGI, surgiu então o FastCGI cujo você não precisa criar outro processo por cada requisição, o que melhora muito na performance.

A principal diferença entre o CGI e o FastCGI está no processo, o FastCGI permanece vivo após o encerramento de uma requisição. Ao iniciar um processo FastCGO, ele ouvirá novas conexões e não morrerá mais, ou seja, o mesmo processo continua gerenciando os recursos da aplicação. Usando CGI, a cada requisição um processo é criado e depois morre.

Configurando o proxy

touch index.php
echo '<?php phpinfo();' > index.php
docker run --rm -it -p 9000:9000 -v $(pwd):/caminho/projeto php:fpm
Enter fullscreen mode Exit fullscreen mode

Quero um serviço para rodar no localhost:8004, vamos criar um novo documento (fpm.conf):

server {
   listen 8004;

   location / {
       fastcgi_pass localhost:9000;
   }
}
Enter fullscreen mode Exit fullscreen mode

Parâmetros adicionais

Ainda dentro do (fpm.conf):

server {
    listen 8004;
    root /caminho/projeto;

    location / {
        include fastcgi.conf;
        fastcgi_pass localhost:9000;
    }
}
Enter fullscreen mode Exit fullscreen mode

É um protocolo mais enxuto com fendas comprimidas e dessa forma a requisição do app não precisa receber todo o protocolo HTTP

Performance

Cache HTTP

O navegador (browser) pode ignorar os cabeçalhos de cache e não cachear o recurso. Tanto isso é verdade que temos a opção de desabilitar o cache do nosso navegador. Os cabeçalhos de cache instruem o navegador sobre o quanto tempo ele pode/deve manter o recurso em cache, mas cabe a ele aceitar essa instrução ou não. Todos os navegadores modernos tendem a seguir a instrução a menos que configuremos de forma diferente.

server {
    listen 8005;
    root /Users/isaac/Dev/performance;
    index index.html;

    location ~ \.jpg$ {
       expires 30d;
       add_header Cache-Control public;
    }
}
Enter fullscreen mode Exit fullscreen mode

Compreensão

Modo de compreensão instalado no próprio NGINX:

server {
    listen 8005;
    root /Users/isaac/Dev/performance;
    index index.html
    gzip on;
    gzip_types image/jpg text/css;

    location ~ \.jpg$ {
         expires 30d;
         add_header Cache-Control public;
    }
}
Enter fullscreen mode Exit fullscreen mode

Os tipos de recursos que são mais beneficiados pela compressão com gzip na web são os arquivos de texto (estáticos): html, css, js, svg, etc...

Os arquivos de texto podem ser facilmente comprimidos e são os que mais levam vantagem desta técnica. Arquivos binários ou arquivos de imagem, por exemplo, naturalmente já são comprimidos, por isso o efeito seria bem menor (ou inexistente).

Conexões

server {
    listen 8005;
    root /Users/isaac/Dev/performance;
    index index.html;
    gzip on;
    gzip_types text/css;
    add_header Keep-Alive "timeout=5, max=1000";

    location ~ \.jpg$ {
         expires 30d;
         add_header Cache-Control public;
    }
}
Enter fullscreen mode Exit fullscreen mode

Cache

Caminho do Cache

É possível tranformar nosso servidor em um servidor cache, armazenando dados de resposta para não reprocessar determinadas requisições. Em um cenário em que URLs precisam de processamento e não mudam de usuário para usuário.

fastcgi_cache_path level=1.2 /tmp/cache keys_zone=fpm:10m;
proxy_cache_path /tmp/cache levels=1:2 keys_zone=proxy:1m;

server {
    listen 8004;
    root /caminho/projeto;

    location / {
        include fastcgi.conf;
        fastcgi_pass localhost:9000;
    }
}
Enter fullscreen mode Exit fullscreen mode

Com uma simples página web por exemplo, nós não precisamos realizar todas as queries de cursos, formações, etc o tempo todo. Podemos executar uma vez só e armazenar o html montado em cache.

Usando o cache

fastcgi_cache_path /tmp/cache level=1:2 keys_zone=fpm:10m;

server {
    listen 8004;
    root /caminho/projeto;

    location / {
        fastcgi_pass localhost:9000;
        include fastcgi.conf;
        fastcgi_cache_key $request_method$request_uri;
        fastcgi_cache fpm;
        fastcgi_cache_valid 1m;
    }
}
Enter fullscreen mode Exit fullscreen mode

Verificando o status

Para podermos depurar quando necessário, se precisarmos saber se um cache está sendo encontrado ou não, ter um cabeçalho na resposta é uma forma bem fácil de obter essa informação.

fastcgi_cache_path /tmp/cache level=1:2 keys_zone=fpm:10m;

server {
    listen 8004;
    root /caminho/projeto;

    location / {
        fastcgi_pass localhost:9000;
        include fastcgi.conf;
        fastcgi_cache_key $request_method$request_uri;
        fastcgi_cache fpm;
        fastcgi_cache_valid 1m;
        add_header X-Cache-Status $upstream_cache_status;
    }
}
Enter fullscreen mode Exit fullscreen mode

HTTPS


Um protocolo de Hipertexto seguro criptografado. Quanto utilizamos o HTTP os dados são transportados em texto puro para o servidor, visível para qualquer um. Nossos dados são enviados em um texto puro, ficando visível para qualquer um que consiga interceptar nossa conexão!

Gerando nosso próprio certificado

openssl req -x509 -nodes -days 30 -newkey rsa:2048 -keyout /tmp/localhost.key -out /tmp/localhost.crt
Enter fullscreen mode Exit fullscreen mode
security add-certificate /tmp/localhost.crt
security add-trusted-cert /tmp/localhost.crt
Enter fullscreen mode Exit fullscreen mode

Configurando o NGINX

server {
    listen 443 ssl;
    root /Users/isaac/Dev/performance;
    index index.html
    gzip on;
    gzip_types text/css;
    add_header Keep-Alive "timeout=5, max=1000";
    ssl_certificate /tmp/localhost.crt;
    ssl_certificate_key /tmp/localhost.key;

    location ~ \.jpg$ {
         expires 30d;
         add_header Cache-Control public;
    }
}

server {
    listen 8005;
    root /Users/isaac/Dev/performance;
    index index.html;
    gzip on;
    gzip_types text/css;
    add_header Keep-Alive "timeout=5, max=1000"

    location ~ \.jpg$ {
         expires 30d;
         add_header Cache-Control public;
    }
}
Enter fullscreen mode Exit fullscreen mode
💖 💪 🙅 🚩
isaacalves7
Isaac Alves Pinheiro

Posted on May 24, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Nginx
nginx Nginx

May 24, 2023