DevSecOps

10 mar, 2015

Apache Mod_Proxy como front-end de acesso e balanceamento de diversas aplicações web

Publicidade

Depois de um bom tempo sem escrever nada, eis-me aqui novamente com um artigo sobre um assunto que várias pessoas haviam me questionado “como fazer” e que mesmo eu estava precisando, mas por falta de tempo ainda não havia feito. Então vamos lá!

Já pensou em ter que disponibilizar uma porta 80 ou 443 para cada aplicação web? Não seria nada barato, nem prático. Como diriam: “Haja porta 80 hein!!!“. Mesmo que você tenha um range de IPs fixos para isso, não seria muito interessante.

O artigo que escrevi em 2013 sobre hospedagem de vários sites http/https em um único servidor resolve parte desse problema.

Então vem a pergunta: e se eu tiver vários servidores com características diferentes e que não podem estar no mesmo servidor por um motivo ou outro?

Eu respondo: Conheça (caso ainda não conheça) o Apache Mod_Proxy, ele resolve esse problema pra você!

Rápida apresentação do Mod_Proxy

O Mod_Proxy é um módulo que implementa um proxy/gateway de entrada para o Apache. Ele implementa a capacidade de proxy para ajp13 (Apache JServe Protocol versão 1.3), FTP, CONNECT (por SSL), HTTP/0.9, HTTP/1.0 e HTTP/1.1. O módulo pode ser configurado para se conectar a outros módulos de proxy para estes e outros protocolos.

Entre as funcionalidades que o Mod_Proxy pode proporcionar, utilizaremos duas neste artigo: o encaminhamento de requisições (proxy em si) e o balanceamento de carga nativo (pois há outras formas de balanceamento que podem ser acrescentadas ao Apache, como o mod_cluster e mod_jk).

Os módulos nativos podem ser identificados pelas linhas abaixo e que fazem parte do arquivo /etc/httpd/conf/httpd.conf:

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
LoadModule proxy_ftp_module modules/mod_proxy_ftp.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
LoadModule proxy_connect_module modules/mod_proxy_connect.so

Cenário utilizado

Para este artigo ,utilizaremos o cenário abaixo no qual demonstro a versatilidade do Mod_Proxy:

  1. 1 servidor Linux CentOS 6.6 com Apache + mod_ssl (nosso front-end) – IP 192.168.1.1;
  2. 1 servidor Windows 2008 R2 com IIS (back-end) – IP 192.168.1.2 – nome1.site.com;
  3. 2 servidores Linux CentOS 6.6 com Apache + PHP (ambos com o mesmo conteúdo para alta disponibilidade) – IPs 192.168.1.3 e 192.168.1.4 – site: nome2.site.com;
  4. 1 servidor CentOS 6.6 com Apache + mod + Mod_Cluster (balanceador para JBoss, este módulo é incompatível com o mod_proxy e por isso não pode estar no mesmo servidor) – IP 192.168.1.5 – nome3.site.com;
  5. 2 servidores CentOS 6.6 com JBoss (servidores de aplicação em modo domain que recebem a carga do balanceador acima) – IPs 192.168.1.6 e 192.168.1.7 (estes servidores recebem a carga do servidor anterior e portanto não são acessados diretamente pelo nosso front-end) são citados apenas para didática e entendimento do ambiente proposto como exemplo;
  6. 1 servidor CentOS 6.6 com JBoss (servidor de aplicação em modo standalone) – IP 192.168.1.8 – site: nome4.site.com.

Não vou entrar em detalhes sobre a configuração específica de cada servidor, nem a estrutura de rede utilizada, pois não é o foco deste artigo, exceto, é claro, nosso Front-End. Aqui também assumo que o Apache esteja instalado e com suporte para SSL.

Para todo caso, se não estiver instalado, instale-os com o comando “yum install -y httpd mod_ssl“.

Abaixo, a representação desse cenário proposto como exemplo didático:

 

front-end

 

Configuração inicial para o ambiente

Vamos ao que interessa, configurar nosso Apache Front-End!

Podemos aplicar as configurações diretamente nos arquivos httpd.conf e ssl.conf ou, para uma melhor organização, separar cada aplicação em diferentes arquivos no diretório “/etc/httpd/conf.d/“. Faça da forma que considerar melhor, ambas as opções funcionam normalmente.

Como nosso front-end precisa atender a requisições para vários sites diferentes, é necessário que nele seja habilitada essa funcionalidade.

Para requisições HTTP, insira a linha abaixo no arquivo “/etc/httpd/conf/httpd.conf“:

NameVirtualHost *:80

E para as requisições HTTPS, insira a linha abaixo no arquivo “/etc/httpd/conf.d/ssl.conf“:

NameVirtualHost *:443

Vamos iniciar pelas aplicações que utilizam apenas requisições HTTP, os servidores dos itens 2, 3 e 6.

a. O servidor IIS do item 2 hospeda um site simples na porta 80.
b. Os servidores Apache do item 3 hospedam 2 aplicações: app1 e app2 em PHP na porta 80.
c. O servidor JBoss do item 6 hospeda uma aplicação java na porta 8080.

Front-end para Microsoft IIS

Para o nosso servidor web IIS, temos a seguinte configuração comentada e que normalmente é alterada de acordo com a aplicação ou ambiente:

<VirtualHost *:80>
      ServerName nome1.site.com # Nome do site
      ServerAlias nome1.site.com # URL que o site é acessado
      ProxyRequests Off
      ProxyPreserveHost On
     ErrorLog logs/nome1_site_error_log # Arquivo de logs de erro especifico para o site
      TransferLog logs/nome1_site_access_log # Arquivo de logs de acessos especifico para o site
      LogLevel warn
     <Proxy 192.168.1.1:80> # Permissão para o servidor front-end (proxy)
           Order deny,allow
           Allow from all
      </Proxy>
     ProxyPass / http://192.168.1.2/ # A "/" após o ProxyPass é o path de acesso na URL (acessando apenas o nome do site)
      ProxyPassReverse / http://192.168.1.2/
 </VirtualHost>

Nesse caso, todas as requisições que chegarem ao nosso front-end através da URL http://nome1.site.com/ serão encaminhadas ao servidor IIS de forma transparente, assim como as respostas do servidor IIS ao cliente.

Front-end e balanceamento para aplicações PHP

Para o próximo exemplo, temos alguns detalhes a mais, pois são dois servidores back-end, ou seja, as requisições serão distribuídas entre ambos.

Veja a configuração do segundo exemplo:

<VirtualHost *:80>
      ServerName nome2.site.com # Nome do site
      ServerAlias nome2.site.com # URL que o site é acessado
      ProxyRequests Off
      ProxyPreserveHost On
     ErrorLog logs/nome2_site_error_log # Arquivo de logs de erro especifico para o site
      TransferLog logs/nome2_site_access_log # Arquivo de logs de acessos especifico para o site
      LogLevel warn
     <Proxy 192.168.1.1:80> # Permissão para o servidor front-end (proxy)
           Order deny,allow
           Allow from all
      </Proxy>
     ProxyPass /balancer-manager ! # Não é permitido acessar o balanceador através do front-end
     # APP1
      ProxyPass /app1 balancer://balancer/app1
      ProxyPassReverse /app1 balancer://balancer/app1
     <Proxy balancer://balancer/app1 stickysession=ROUTEID lbmethod=byrequests nofailover=off>
           BalancerMember http://192.168.1.3 min=10 max=100 loadfactor=1 route=1
           BalancerMember http://192.168.1.4 min=10 max=100 loadfactor=1 route=2
      </Proxy>
     # APP2
      ProxyPass /app2 balancer://balancer/app2
      ProxyPassReverse /app2 balancer://balancer/app2
     <Proxy balancer://balancer/app2 stickysession=ROUTEID lbmethod=byrequests nofailover=off>
           BalancerMember http://192.168.1.3 min=10 max=100 loadfactor=1 route=1
           BalancerMember http://192.168.1.4 min=10 max=100 loadfactor=1 route=2
      </Proxy>
     # Visualização do balanceamento entre os 2 servidores back-end
      <Location /balancer-manager>
           Order deny,allow
           Deny from all
           Allow from 192.168.1. # Quem pode acessar o balanceador para visualizar as estatísticas
           SetHandler balancer-manager
      </Location>
 </VirtualHost>

Observe que o acesso das duas aplicações que estão disponíveis nesses servidores é configurado dentro do mesmo VirtualHost.

Caso houvesse mais servidores back-end, bastaria incluí-los como BalancerMember’s – e não necessariamente para todas as aplicações, poderia ser apenas para uma delas.

Se tivéssemos uma terceira aplicação, esta poderia ser incluída dentro do mesmo VirtualHost, e não necessariamente ser distribuída por dois ou mais servidores back-end – poderia ser indicada para ser atendida apenas por um dos servidores back-ends, teríamos o mesmo caso do primeiro exemplo que mostramos com o servidor IIS.

Uma observação em relação as instruções “stickysession” e “route” no balanceamento é que, no exemplo que estamos mostrando, o balanceamento é realizado de forma simples. A “stickysession” (no caso de PHP) se utilizada com cookie de sessão pode ser alterada para “PHPSESSID”, e a instrução “route” pode ser alterada para a identificação do servidor. Mais detalhes sobre essas instruções podem ser encontradas na documentação oficial.

Front-end para servidores JBoss

Nosso terceiro caso segue o mesmo conceito do primeiro, apenas indicamos a porta na qual o servidor back-end responde, que neste caso é a porta 8080:

<VirtualHost *:80>
      ServerName nome4.site.com # Nome do site
      ServerAlias nome4.site.com # URL que o site é acessado
      ProxyRequests Off
      ProxyPreserveHost On
     ErrorLog logs/nome4_site_error_log # Arquivo de logs de erro especifico para o site
      TransferLog logs/nome4_site_access_log # Arquivo de logs de acessos especifico para o site
      LogLevel warn
     <Proxy 192.168.1.1:80> # Permissão para o servidor front-end (proxy)
           Order deny,allow
           Allow from all
      </Proxy>
     ProxyPass /app http://192.168.1.8:8080/app
      ProxyPassReverse /app http://192.168.1.8:8080/app
 </VirtualHost>

Com isso, dispensamos a necessidade de indicar a porta 8080 na URL de acesso à aplicação, pois nosso front-end atende às requisições na porta 80 e as repassa para o back-end na porta 8080.

Da mesma forma que citei no exemplo anterior, caso tivéssemos dois ou mais back-ends, a aplicação poderia ser distribuída normalmente com algumas pequenas alterações.

Front-End para SSL

Agora que tal utilizarmos o Mod_Proxy + SSL (HTTPS)? Sim, ele também pode ser utilizado para as requisições que trafegam em HTTPS.

Vamos ao exemplo do item 4, no qual já temos um balanceador de carga configurado com o Mod_Cluster que é bastante utilizado para front-end de servidores JBoss (a configuração desse módulo é assunto para outro artigo) e que não pode estar configurado diretamente como front-end no mesmo servidor que estamos utilizando por incompatibilidade entre os módulos. Nesse caso, vamos transformá-lo também em um back-end.

Veja como configuramos esse exemplo:

<VirtualHost _default_:443>
      ServerName nome3.site.com:443
      ServerAlias nome3.site.com:443
     ErrorLog logs/nome3_site_ssl_error_log
      TransferLog logs/nome3_site_ssl_access_log
      LogLevel warn
     SSLEngine on
      SSLProxyEngine on # Ativação de proxy SSL
     SSLProtocol all -SSLv2
     SSLCipherSuite ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW
     SSLCertificateFile "/etc/httpd/conf.d/ssl/app.cer" # Certificado válido assinado pela CA
      SSLCertificateKeyFile "/etc/httpd/conf.d/ssl/app.key" # Chave privada utilizada para gerar a CSR
     <Files ~ "\.(cgi|shtml|phtml|php3?)quot;>
           SSLOptions +StdEnvVars
      </Files>
      <Directory "/var/www/cgi-bin">
           SSLOptions +StdEnvVars
      </Directory>
     SetEnvIf User-Agent ".*MSIE.*" \
      nokeepalive ssl-unclean-shutdown \
      downgrade-1.0 force-response-1.0
     CustomLog logs/ssl_request_log \
      "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
     ProxyRequests Off
      ProxyPreserveHost On
     <Proxy 192.168.1.1:443>
           Order deny,allow
           Allow from all
      </Proxy>
     ProxyPass /app https://192.168.1.5/app # Redirecionamento em https
      ProxyPassReverse /app https://192.168.1.5/app # Redirecionamento em https
 </VirtualHost>

Observe as linhas comentadas – elas são, respectivamente, a ativação do modo proxy para SSL e as duas linhas que especificam o certificado SSL do site. Mais abaixo, as instruções ProxyPass e ProxyPassReverse para HTTPS. É importante que o back-end aceite requisições HTTPS, porém apenas o front-end precisa ter o certificado válido (apenas por conveniência e para não aparecer a tradicional mensagem de certificado inválido, não há qualquer problema em utilizar um certificado autoassinado).

Conclusão

Devemos sempre lembrar que para cada novo site no servidor front-end (seja HTTP ou HTTPS) uma nova sessão de VirtualHost deve ser inserida, mantendo apenas a característica de cada uma (padrões das portas 80 ou 443), seja utilizando balanceamento ou não.

Aconselho a lerem a documentação para entenderem melhor as funcionalidades e suas características, pois há várias opções para balanceamento para diferentes tipos de aplicações.

Ao final desses exemplos, temos quatro sites diferentes e cinco aplicações publicadas através de um único servidor front-end, e o melhor: utilizando para isso apenas um link (sem a necessidade de contratar mais links para ter mais portas 80 e 443 disponíveis).

Um ponto importante e que é necessário citar é sobre a capacidade do servidor front-end. Como ele atenderá praticamente a todas as requisições que são destinadas aos back-endss, pode ser necessária uma customização/tunning para que ele suporte a carga e não tenha um efeito contrário ao desejado, causando lentidão nos acessos. É possível encontrar como fazer isso facilmente na internet. Claro que isso depende da necessidade de cada aplicação, e é o administrador dos servidores é quem pode determinar se há ou não essa necessidade.

Outro ponto interessante é ativar a compactação das páginas (opcional), utilizando o mod_deflate. É possível encontrar facilmente como fazer isso em uma pesquisa rápida no Google.

#FicaaDica

Abraços.

Fonte: http://httpd.apache.org/docs/2.2/mod/mod_proxy.html