208.4 Implementando o NGINX como WebServer e Proxy Reverso
Introdução
O NGINX é um servidor web de código aberto, assim como o Apache2, o NGINX é mais conhecido por sua alta performance e eficiência em lidar com grandes volumes de tráfego web, enquanto o Apache é mais conhecido por sua flexibilidade e recursos avançados de configuração.
Seu maior uso pelo que pude notar é como proxy_reverso, as pessoas acabam optando por ele. Proxy reverso é um servidor que recebe requisições em nome de um ou mais servidores de destino, permitindo que o cliente acesse esses servidores (que estão atrás do Proxy) de forma transparente. Ele é usado para balanceamento de carga, cache, segurança e outras finalidades.
Segue um diagrama que exemplifica melhor o cenário com proxy reverso:
Veja uma explicação do fluxograma acima
2 - O Servidor Proxy reverso recebe a solicitação e refaz a consulta para o servidor Web que está na rede Interna.
3 - O Servidor Web responde a solicitação do Servidor Proxy achando que é ele quem está solicitando e não o cliente que consultou.
4 - Com a resposta em mão, o servidor Proxy Reverso envia essa resposta para o cliente.
Instalação do NGINX
Vamos começar instalando o NGINX:
- CentOS 7
- Ubuntu 20.04
$ sudo yum install nginx
$ sudo apt install nginx
Entendendo o arquivo de configuração
O arquivo de configuração fica localizado em /etc/nginx/nginx.conf
, vamos conferir algumas configurações, não tudo. Vejamos a configuração abaixo, essa configuração é do Ubuntu 20.04 e pode ser um pouco diferente em outros sistemas:
1 user www-data;
2 worker_processes auto;
3 pid /run/nginx.pid;
4 include /etc/nginx/modules-enabled/*.conf;
5
6 events {
7 worker_connections 768;
8 # multi_accept on;
9 }
10
11 http {
12
13 ##
14 # Basic Settings
15 ##
16
17 sendfile on;
18 tcp_nopush on;
19 tcp_nodelay on;
20 keepalive_timeout 65;
21 types_hash_max_size 2048;
22 # server_tokens off;
23
24 # server_names_hash_bucket_size 64;
25 # server_name_in_redirect off;
26
27 include /etc/nginx/mime.types;
28 default_type application/octet-stream;
29
30 ##
31 # SSL Settings
32 ##
33
34 ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
35 ssl_prefer_server_ciphers on;
36
37 ##
38 # Logging Settings
39 ##
40
41 access_log /var/log/nginx/access.log;
42 error_log /var/log/nginx/error.log;
43
44 ##
45 # Gzip Settings
46 ##
47
48 gzip on;
49
50 # gzip_vary on;
51 # gzip_proxied any;
52 # gzip_comp_level 6;
53 # gzip_buffers 16 8k;
54 # gzip_http_version 1.1;
55 # gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
56
57 ##
58 # Virtual Host Configs
59 ##
60
61 include /etc/nginx/conf.d/*.conf;
62 include /etc/nginx/sites-enabled/*;
63 }
Veja uma explicação da configuração acima
2 - Define o número de processos que o servidor web deve iniciar para atender as solicitações. O "auto" significa que o servidor determinará automaticamente o número de processos a serem iniciados com base na configuração do servidor e nas características do hardware do servidor.
3 - Define o caminho para o arquivo que armazena o ID do processo (PID) do processo principal do servidor web.
4 - Inclui quaisquer arquivos de configuração adicionais que estejam localizados no diretório "/etc/nginx/modules-enabled/" e que terminem com a extensão ".conf".
6 até 9 - Cria-se um bloco, esse é o bloco de "events" (eventos) que define as configurações de eventos do servidor web, especificando o número máximo de conexões simultâneas que o servidor web pode manipular (A configuração 'worker_connections 768;' define que o servidor pode lidar com até 768 conexões simultâneas).
11 até 63 - Define as configurações globais do servidor Web, elas se aplicam para qualquer Virtual Host que venham a ser criados, mas fique atento, se for declarado uma configuração diferentes no Virtual Host, essa configuração irá sobrescrever a configuração global.
A linha 17, ativa a funcionalidade do Nginx para enviar arquivos para o cliente diretamente do disco, em vez de carregar o arquivo na memória do servidor e, em seguida, enviá-lo ao cliente.
A linha 18, ativa o mecanismo de otimização do TCP para reduzir o número de pacotes de rede enviados pelo servidor.
A linha 19, ativa a opção TCP_NODELAY, que melhora o desempenho do servidor TCP para pequenas solicitações de dados.
A linha 20, define o tempo máximo que uma conexão pode permanecer ociosa antes que o servidor a feche.
A linha 34, define os protocolos SSL/TLS aceitos pelo servidor.
A linha 41 e 42, definem os arquivos de log para acesso e erros, respectivamente.
A linha 48, ativa a compressão gzip para reduzir o tamanho dos arquivos enviados ao cliente.
As linhas 61 e 62 incluem quaisquer arquivos de configuração adicionais que estejam localizados no diretório "/etc/nginx/conf.d/" e nos hosts virtuais que são criados em "/etc/nginx/sites-enabled/" por questões de boas práticas. Esses arquivos de configuração adicionais são usados para configurar servidores virtuais e outras configurações personalizadas.
Por padrão o servidor Web já vem com um Virtual Host ativo, ele fica localizado em /etc/nginx/sites-enabled/default
, vejamos a configuração dele:
# As primeiras linhas são apenas comentários, por isso não coloquei aqui!
21 server {
22 listen 80 default_server;
23 listen [::]:80 default_server;
24
25 # SSL configuration
26 #
27 # listen 443 ssl default_server;
28 # listen [::]:443 ssl default_server;
29 #
30 # Note: You should disable gzip for SSL traffic.
31 # See: https://bugs.debian.org/773332
32 #
33 # Read up on ssl_ciphers to ensure a secure configuration.
34 # See: https://bugs.debian.org/765782
35 #
36 # Self signed certs generated by the ssl-cert package
37 # Don't use them in a production server!
38 #
39 # include snippets/snakeoil.conf;
40
41 root /var/www/html;
42
43 # Add index.php to the list if you are using PHP
44 index index.html index.htm index.nginx-debian.html;
45
46 server_name _;
47
48 location / {
49 # First attempt to serve request as file, then
50 # as directory, then fall back to displaying a 404.
51 try_files $uri $uri/ =404;
52 }
53
54 location /imagens {
55 autoindex on;
56 alias /var/imagens;
57 try_files $uri $uri/ =404;
58 }
59
60 }
Veja uma explicação da configuração acima
As linhas 22 e 23, o servidor virtual está configurado para escutar nas portas 80 do endereço IPv4 e IPv6, respectivamente.
A diretiva 'default_server' é um parâmetro de configuração usado no Nginx para definir um servidor padrão que responderá às solicitações recebidas quando não houver uma correspondência exata com um servidor virtual existente, por exemplo, quando usamos um nome como 'www.exemplo.com.br', só podemos ter um servidor virtual declarado como padrão.
A linha 41 especifica o diretório raiz do servidor virtual, onde os arquivos do site serão servidos a partir.
A linha 44 especifica quais arquivos o servidor deve tentar servir se o caminho da URL não for especificado completamente. O servidor tentará servir arquivos como "index.html", "index.htm", "index.nginx-debian.html" e, por último, tentará retornar um erro 404.
A linha 46 declara o nome do servidor, que neste caso é definida como "_", o que significa que o servidor virtual será usado como padrão para todas as solicitações que não correspondem a nenhum outro servidor virtual.
A linha 48 declara a diretiva "location", ela é definida para especificar como lidar com solicitações feitas ao servidor quando digitado na URL, nesse caso é quando não é colocado mais nenhuma informação na URL.
A linha 54 declara a diretiva location apontando para '/imagens', ou seja, quando temos algo parecido como http://www.exemplo.com.br/imagens.
A linha 55 habilita a exibição da lista de arquivos em um diretório se não houver um arquivo especificado na solicitação (como index.html por exemplo). Se o usuário acessar '/imagens/', uma lista de arquivos em '/var/imagens' será exibida.
A linha 56 muda o diretório raiz para servir o diretório que contenha as imagens.
A linha 57 define uma lista de arquivos ou diretórios a serem pesquisados para atender a solicitação. Neste caso, o Nginx tentará atender a solicitação do arquivo especificado na solicitação, seguido pelo diretório especificado na solicitação e, em seguida, retornará um erro 404 se não puder encontrar o arquivo ou diretório solicitado.
O root
é usado para definir o diretório raiz padrão do servidor para todas as solicitações, enquanto que a diretiva alias
é usado para definir um diretório raiz específico para uma solicitação.
Por exemplo, se você usar a diretiva root
para definir o diretório raiz do seu servidor como /var/www/html
, todas as solicitações serão tratadas como se estivessem sendo feitas a partir deste diretório. Se um cliente fizer uma solicitação para /images
(usando o root
), o servidor procurará por um arquivo ou diretório chamado /var/www/html/images
.
No entanto, se você usar a diretiva alias
para definir o diretório raiz para uma solicitação específica, como /imagens
, o servidor tratará a solicitação como se ela estivesse sendo feita a partir do diretório especificado no alias. Por exemplo, se você usar a diretiva alias /var/www/html/images
, uma solicitação para /imagens
será tratada como se estivesse sendo feita a partir do diretório /var/www/html/images
.
FastCGI/PHP
Vamos configurar o NGINX para servir uma página PHP, diferente do Apache que é só configurar o módulo PHP, já aqui vamos servir esse conteúdo através do FastCGI. O FastCGI é uma tecnologia que permite que servidores web, como o Apache ou Nginx, rodem aplicativos web escritos em várias linguagens de programação, como: PHP, Python e Shell, C entre outras.
Quando um aplicativo da web é executado com o FastCGI, ele é iniciado como um processo separado e fica em execução continuamente, aguardando solicitações do servidor web. Quando uma solicitação é recebida, o servidor web envia a solicitação para o processo do aplicativo FastCGI em vez de iniciar um novo processo. O processo do aplicativo pode então processar a solicitação e retornar a resposta para o servidor web. Isso reduz significativamente o tempo de inicialização do aplicativo e o consumo de recursos do servidor, permitindo que o servidor possa gerenciar um grande número de solicitações simultâneas.
Vamos instalar o php-fpm
(ele fornece o serviço do FastCGI para PHP), ele é um gerenciador de processos para gerenciar o FastCGI SAPI (Server API) em PHP, lembre-se que O PHP-FPM é um serviço e não um módulo (diferente de como seria no Apache). Ele é executado independente do servidor web que está rodando, sendo um processo à parte, mas só é possível usar ele com Servidor Web que suporte FastCGI (Fast Common Gateway Interface).
- CentOS 7
- Ubuntu 20.04
$ sudo yum install php-fpm
$ sudo apt install php-fpm
Veja se o serviço subiu:
# Verifique se está em execução:
$ sudo systemctl status php-fpm
● php8.1-fpm.service - The PHP 8.1 FastCGI Process Manager
Loaded: loaded (/lib/systemd/system/php8.1-fpm.service; enabled; vendor preset: enabled)
Active: active (running) since Mon 2023-04-10 22:11:03 UTC; 1min 1s ago
Docs: man:php-fpm8.1(8)
Process: 15584 ExecStartPost=/usr/lib/php/php-fpm-socket-helper install /run/php/php-fpm.sock /etc/php/8.1/fpm/pool.d/www.conf 81 (code=exited, status=0/SUCCESS)
Main PID: 15581 (php-fpm8.1)
Status: "Processes active: 0, idle: 2, Requests: 0, slow: 0, Traffic: 0req/sec"
Tasks: 3 (limit: 2237)
Memory: 7.2M
CPU: 57ms
CGroup: /system.slice/php8.1-fpm.service
├─15581 "php-fpm: master process (/etc/php/8.1/fpm/php-fpm.conf)" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
# No caso do Ubuntu 20.04 ele vem por padrão para escutar via Socket (socket por arquivo), então vamos configurar para escutar por IP/PORTA (é um socket por porta):
$ sudo vim /etc/php/8.1/fpm/pool.d/www.conf
### Adicione ###
;listen = /run/php/php8.1-fpm.sock
listen = 127.0.0.1:9000
# Reinicie o Serviço:
$ sudo systemctl restart php8.1-fpm
Vamos configurar o NGINX para quando for PHP ele se conectar na porta 9000 do localhost para conseguir servir o conteúdo PHP.
# Acesse a conf atual do NGINX:
$ sudo vim /etc/nginx/sites-enabled/default
### Adicione abaixo do /images ###
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
# Se quiser usar socket, use o exemplo abaixo:
#fastcgi_pass unix:<caminho para o arquivo>;
fastcgi_param QUERY_STRING $query_string;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include /etc/nginx/fastcgi_params;
}
### Agora reinicie o NGINX
# Verifique se não existem erros no arquivo de configuração:
$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
# Recarregue as mudanças de configuração:
$ sudo nginx -s reload
Lembre-se que é usado fastcgi_pass
para passar a requisição PHP para o FastCGI e fastcgi_param
para passar os parâmetros do script para o FastCGI, isso varia muito de script então deve-se conhecer o ambiente.
Segue um PHP para usar como exemplo!
<?php
header("Content-Type: text/html; charset=UTF-8");
echo "<h1>Hello, world from FastCGI!</h1>";
?>
Proxy Reverso
O Proxy Reverso é configurado para "redirecionar" uma requisição normalmente para outro endereço IP/Porta, eles podem estar rodando na mesma máquina ou estar em máquinas diferentes. Para esse teste eu vou subir um Docker com Apache e usado o NGINX para direcionar a requisição para ele.
Para instalar o Docker veja o Documento oficial.
# Execute o container com Apache:
$ sudo docker run -dit --name apache_lpic2 -p 8080:80 -v /tmp:/usr/local/apache2/htdocs/ httpd:2.4
# /tmp = Diretório da máquina que será mapeado para o Container em '/usr/local/apache2/htdocs/',
# dentro de '/tmp' eu tenho um 'index.html'.
# 8080 = Porta local do servidor que será redirecionada para porta 80 do Container.
Agora vamos configurar o NGINX, para isso vou zerar a configurar atual.
# Edite o arquivo abaixo:
$ sudo vim /etc/nginx/sites-enabled/default
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host;
try_files $uri $uri/ =404;
}
}
# Verifique se existem erros de configuração:
$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
# Recarregue as configurações:
$ sudo systemctl restart nginx
O parâmetro proxy_pass
define para onde a requisição vai ser encaminhada enquanto que proxy_set_header
muda informações do cabeçalho HTTP, essa configuração é a padrão e a mais usada.
O X-Real-IP
é o endereço IP real do cliente que está sendo enviado ao servidor atrás do Proxy.
O X-Forwarded-For
é o endereço IP do cliente que está sendo encaminhado para o servidor backend.
O Host
é o nome do host usado na solicitação do cliente.
Segue um HTML para usar como exemplo!
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<title>LPIC-2</title>
</head>
<body>
<h1>Docker com Apache</h1>
</body>
</html>
Agora é só acessar o IP do servidor do NGINX que consigo ver a mensagem Docker com Apache que está sendo gerada pelo Container Docker e não pelo NGINX.
Load Balance
Vamos configurar o NGINX como um LoadBalance para fazer o balanceamento entre servidores Web. Vou começar subindo outro container.
# Execute outro container com Apache:
$ sudo docker run -dit --name apache_lpic2-1 -p 8081:80 -v /home/vagrant/website:/usr/local/apache2/htdocs/ httpd:2.4
db481650d5479092ae59242c47dfb98516a12e9faf5184d61cd81807493bb7d8
# Edite o arquivo abaixo:
$ sudo vim /etc/nginx/sites-enabled/default
upstream backend {
server 127.0.0.1:8080;
server 127.0.0.1:8081;
}
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
location / {
proxy_pass http://backend;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host;
try_files $uri $uri/ =404;
}
}
# Verifique se existem erros de configuração:
$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
# Recarregue as configurações:
$ sudo systemctl restart nginx
Eu configurei o segundo index.html
para exibir Docker com Apache 1
, agora é só fazer o Refresh do site e ver que vai aparecer e sumir o 1, indicando que o balanceamento está funcionando.
Fazer esse tipo de load balancer não faz nenhum sentido, já que se a máquina que hospeda os container ficarem indisponíveis, os servidores Web também vão ficar.
O correto seria ter servidores Web rodando em máquinas separadas, mas fiz assim para exemplificar.
Comandos importantes
Comando | Descrição |
---|---|
nginx -t | Testa a configuração, para verificar se existe algum erro. |
nginx -s stop/start/reload/status/quit | Comando usado para Parar, Iniciar, Recarregar as configurações, Ver o Status e por fim encerrar o processo do daemon de forma hard |
Fontes importantes
https://w3techs.com/technologies/history_overview/web_server