fbpx
NGINX Automation
Publicado em: domingo, 5 de fev de 2017
Tags: Docker | Nginx | NodeJS

Se você usa o NGINX em produção, como Reverse Proxy, já se perguntou se seria possível criar algum tipo de automação. Essa é uma demanda pois quanto mais apps passando por ele, mais endereços, mais configurações serão necessárias. Se você usa todo o potencial do NGINX, aí com toda certeza você ficará interessado na automação.

Exemplo de configuração do NGINX:

Para você entender o que estou falando, abaixo tenho um arquivo bem parecido com o que tenho em produção.

#user  nobody;
worker_processes  1;
pid        /run/nginx.pid;

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


events {
    worker_connections  4096;
}

stream {

	#############################################################
	# Stream binds for Container zimbra
	#############################################################
	server { 
    	listen 23; 
    	proxy_pass zimbra:22; 

    }				
	server { 
    	listen 53; 
    	proxy_pass zimbra:53; 

    }				
	server { 
    	listen 110; 
    	proxy_pass zimbra:110; 

    }				
	server { 
    	listen 143; 
    	proxy_pass zimbra:143; 

    }				
	server { 
    	listen 389; 
    	proxy_pass zimbra:389; 

    }				
	server { 
    	listen 465; 
    	proxy_pass zimbra:465; 

    }				
	server { 
    	listen 587; 
    	proxy_pass zimbra:587; 

    }				
	server { 
    	listen 993; 
    	proxy_pass zimbra:993; 

    }				
	server { 
    	listen 995; 
    	proxy_pass zimbra:995; 

    }				
	server { 
    	listen 3443; 
    	proxy_pass zimbra:3443; 

    }				
	server { 
    	listen 9071; 
    	proxy_pass zimbra:9071; 

    }				
	server { 
    	listen 444; 
    	proxy_pass zimbra:443; 

    }				

	#############################################################
	# Stream binds for Container MySQL
	#############################################################
	server { 
    	listen 3306; 
    	proxy_pass MySQL:3306; 

    }				

	#############################################################
	# Stream binds for Container Redis
	#############################################################
	server { 
    	listen 6379; 
    	proxy_pass Redis:6379; 

    }				

	#############################################################
	# Stream binds for Container MongoDB
	#############################################################
	server { 
    	listen 27017; 
    	proxy_pass MongoDB:27017; 

    }				

	#############################################################
	# Stream binds for Container RabbitMQ
	#############################################################
	server { 
    	listen 4369; 
    	proxy_pass RabbitMQ:4369; 

    }				
	server { 
    	listen 25672; 
    	proxy_pass RabbitMQ:25672; 

    }				
	server { 
    	listen 5671; 
    	proxy_pass RabbitMQ:5671; 

    }				
	server { 
    	listen 5672; 
    	proxy_pass RabbitMQ:5672; 

    }				
	server { 
    	listen 15672; 
    	proxy_pass RabbitMQ:15672; 

    }				
	server { 
    	listen 61613; 
    	proxy_pass RabbitMQ:61613; 

    }				
	server { 
    	listen 61614; 
    	proxy_pass RabbitMQ:61614; 

    }				
	server { 
    	listen 1883; 
    	proxy_pass RabbitMQ:1883; 

    }				
	server { 
    	listen 8883; 
    	proxy_pass RabbitMQ:8883; 

    }				
	
}


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;

    client_max_body_size 50M;

    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

	#############################################################
    #
    #		HTTP
    #
    #############################################################



	#############################################################
	# HTTP binds for Container LuizCarlosFariaBlog
	#############################################################
	server { 
    	listen 80; 
		server_name  endereco.com.br www.endereco.com.br docker.endereco.com.br;

		location / {
    		proxy_pass http://LuizCarlosFariaBlog:80; 

			#proxy_set_header X-Real-IP $remote_addr;
			#add_header  Feedback $host;
		}
    }				
			

	#############################################################
	# HTTP binds for Container javaApp
	#############################################################
	server { 
    	listen 80; 
		server_name  jira.endereco.com.br;

		location / {
    		proxy_pass http://javaApp:8080; 

			#proxy_set_header X-Real-IP $remote_addr;
			#add_header  Feedback $host;
		}
    }				
			

	#############################################################
	# HTTP binds for Container zimbra
	#############################################################
	server { 
    	listen 80; 
		server_name  mail.*;

		location / {
    		proxy_pass http://zimbra:80; 

			#proxy_set_header X-Real-IP $remote_addr;
			#add_header  Feedback $host;
		}
    }				
			
	


	#############################################################
    #
    #		HTTPS
    #
    #############################################################


	#############################################################
	# HTTPS binds for Container LuizCarlosFariaBlog
	#############################################################
	server { 
    	listen 443 ssl; 
		server_name  endereco.com.br www.endereco.com.br docker.endereco.com.br;

		#ssl_certificate xxxxxxxxxxxxxxxxxxxxxxxx;
    	#ssl_certificate_key  xxxxxxxxxxxxxxxxxxxxxxxx;
		#ssl_dhparam  xxxxxxxxxxxxxxxxxxxxxxxx;
        #ssl_protocols xxxxxxxxxxxxxxxxxxxxxxxx;
        #ssl_prefer_server_ciphers on;
        #ssl_ciphers  xxxxxxxxxxxxxxxxxxxxxxxx;
        #ssl_session_timeout  xxxxxxxxxxxxxxxxxxxxxxxx;	
        #ssl_session_cache  xxxxxxxxxxxxxxxxxxxxxxxx;
        #ssl_stapling  xxxxxxxxxxxxxxxxxxxxxxxx;
        #ssl_stapling_verify  xxxxxxxxxxxxxxxxxxxxxxxx;
        #add_header Strict-Transport-Security  xxxxxxxxxxxxxxxxxxxxxxxx;
		
		location / {			
    		proxy_pass http://LuizCarlosFariaBlog:80; 
			
			
			#proxy_set_header X-Real-IP $remote_addr;
			#add_header  Feedback $host;
		}
		
    }				

	#############################################################
	# HTTPS binds for Container zimbra
	#############################################################
	server { 
    	listen 443 ssl; 
		server_name  mail.*;

		#ssl_certificate xxxxxxxxxxxxxxxxxxxxxxxx;
    	#ssl_certificate_key  xxxxxxxxxxxxxxxxxxxxxxxx;
		#ssl_dhparam  xxxxxxxxxxxxxxxxxxxxxxxx;
        #ssl_protocols xxxxxxxxxxxxxxxxxxxxxxxx;
        #ssl_prefer_server_ciphers on;
        #ssl_ciphers  xxxxxxxxxxxxxxxxxxxxxxxx;
        #ssl_session_timeout  xxxxxxxxxxxxxxxxxxxxxxxx;	
        #ssl_session_cache  xxxxxxxxxxxxxxxxxxxxxxxx;
        #ssl_stapling  xxxxxxxxxxxxxxxxxxxxxxxx;
        #ssl_stapling_verify  xxxxxxxxxxxxxxxxxxxxxxxx;
        #add_header Strict-Transport-Security  xxxxxxxxxxxxxxxxxxxxxxxx;
		
		location / {			
    		proxy_pass https://zimbra:443; 
					proxy_set_header X-Real-IP $remote_addr;

			
			#proxy_set_header X-Real-IP $remote_addr;
			#add_header  Feedback $host;
		}
		
    }				
	server { 
    	listen 7071 ssl; 
		server_name  mail.*;

		#ssl_certificate xxxxxxxxxxxxxxxxxxxxxxxx;
    	#ssl_certificate_key  xxxxxxxxxxxxxxxxxxxxxxxx;
		#ssl_dhparam  xxxxxxxxxxxxxxxxxxxxxxxx;
        #ssl_protocols xxxxxxxxxxxxxxxxxxxxxxxx;
        #ssl_prefer_server_ciphers on;
        #ssl_ciphers  xxxxxxxxxxxxxxxxxxxxxxxx;
        #ssl_session_timeout  xxxxxxxxxxxxxxxxxxxxxxxx;	
        #ssl_session_cache  xxxxxxxxxxxxxxxxxxxxxxxx;
        #ssl_stapling  xxxxxxxxxxxxxxxxxxxxxxxx;
        #ssl_stapling_verify  xxxxxxxxxxxxxxxxxxxxxxxx;
        #add_header Strict-Transport-Security  xxxxxxxxxxxxxxxxxxxxxxxx;
		
		location / {			
    		proxy_pass https://zimbra:7071; 
					proxy_set_header X-Real-IP $remote_addr;

			
			#proxy_set_header X-Real-IP $remote_addr;
			#add_header  Feedback $host;
		}
		
    }				
	


	#############################################################
    #
    #	DEFAULT	
    #
    #############################################################

    server {
        listen       80;
        server_name  xxx;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            root   html;
            index  index.html index.htm;
        }
        location /nginx_status {
          stub_status;
          #access_log   off;
          #allow 1.1.1.1;
          #deny all;
        }

        #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;
        }

    }


}

Trabalhando com Metadados:

No dia-a-dia você acaba por não ter de dar manutenção nestes arquivos com freqüência, e por isso você acaba por não lembrar de todos os passos necessários ou todas as features possíveis, assim automatizar gera o conforto e reduz o atrito na hora de fazer uma nova configuração no NGINX, a cada vez que você precise adicionar um novo endereço, ou aplicação no stack.

Embora o arquivo do exemplo 1 seja extenso, no dia-a-dia eu não preciso gerenciá-lo diretamente. Usando um script de automação que criei, eu modifico um arquivo json de metadados, executo um comando e pronto: arquivo de configuração gerado e script de recriação do container pronto para ser executado. Abaixo temos o exemplo de metadados, ele está disponível no repositório do projeto, no github.

{
    "NginxContainerName": "EntryPoint",
    "Workers": {
        "Count": 1,
        "Connections" : 4096        
    },
    "Services" :[
		{             
            "ContainerName" : "blog",
            "Enabled" : true,
            "Binds":[
                { "Type": "http", "Port" : 80, "HostHeaderPattern": "endereco.com.br www.endereco.com.br docker.endereco.com.br" },
                { "Type": "https", "HostPort" : 443, "ContainerPort": 80, "HostHeaderPattern": "endereco.com.br www.endereco.com.br docker.endereco.com.br" }
            ]
        },{             
            "ContainerName" : "wso2esb",
            "Enabled" : false,
            "Binds":[
          		{ "Type": "http", "HostPort" : 80, "ContainerPort": 8020, "HostHeaderPattern": "esb.endereco.com.br api.endereco.com.br", "LocationExtensions": ["proxy_set_header X-Real-IP $remote_addr"] }
            ]
        },{             
            "ContainerName" : "javaApp",
            "Enabled" : true,
            "Binds":[
                { "Type": "http", "HostPort" : 80, "ContainerPort": 8080, "HostHeaderPattern": "jira.endereco.com.br" }                
            ]
        },{             
            "ContainerName" : "zimbra",
            "Enabled" : true,
            "Binds":[
                { "Type": "stream", "HostPort" : 23, "ContainerPort": 22 },
                { "Type": "stream", "Port" : 53 },
                { "Type": "stream", "Port" : 110 },
                { "Type": "stream", "Port" : 143 },
                { "Type": "stream", "Port" : 389 },
                { "Type": "stream", "Port" : 465 },
                { "Type": "stream", "Port" : 587 },
                { "Type": "stream", "Port" : 993 },
                { "Type": "stream", "Port" : 995 },
                { "Type": "stream", "Port" : 3443 },
                { "Type": "stream", "Port" : 9071 },
                { "Type": "stream", "HostPort" : 444, "ContainerPort": 443 },
                { "Type": "http", "Port" : 80, "HostHeaderPattern": "mail.*" },
                { "Type": "https", "Port" : 443, "HostHeaderPattern": "mail.*", "LocationExtensions": ["proxy_set_header X-Real-IP $remote_addr"] },                
                { "Type": "https", "Port" : 7071, "HostHeaderPattern": "mail.*", "LocationExtensions": ["proxy_set_header X-Real-IP $remote_addr"] }
            ]
        },{             
            "ContainerName" : "MySQL",
            "Enabled" : true,
            "Binds":[
                { "Type": "stream", "Port" : 3306 }
            ]
        },{             
            "ContainerName" : "Redis",
            "Enabled" : true,
            "Binds":[
                { "Type": "stream", "Port" : 6379 }
            ]
        },{             
            "ContainerName" : "MongoDB",
            "Enabled" : true,
            "Binds":[
                { "Type": "stream", "Port" : 27017 }
            ]
        },{             
            "ContainerName" : "RabbitMQ",
            "Enabled" : true,
            "Binds":[
                { "Type": "stream", "Port" : 4369 },
                { "Type": "stream", "Port" : 25672 },
                { "Type": "stream", "Port" : 5671 },
                { "Type": "stream", "Port" : 5672 },
                { "Type": "stream", "Port" : 15672 },
                { "Type": "stream", "Port" : 61613 },
                { "Type": "stream", "Port" : 61614 },
                { "Type": "stream", "Port" : 1883 },
                { "Type": "stream", "Port" : 8883 }
            ]
        }
    ],
    "templates": [
        { "Name": "Nginx Configuration", "Template": "./templates/nginx.config.template.asp", "Output": "./output/nginx.conf"  },
        { "Name": "Container Recreation", "Template": "./templates/nginx-recreate.sh.template.asp", "Output": "./output/rebuild-container.generated.sh"  }
    ]
}

Sem sombra de dúvidas, o arquivo JSON, acima, apresenta um modelo mais simples e melhor estruturado, tornando fácil criar novas configurações. Mas o NGINX não reconhece esse formato, para isso um script que transforma esse arquivo em 2 outros, o de configuração e o de recriação do container.

O repositório já conta com um arquivo de configuração parecido com esse que está aqui no post, além disso, o resultado do script é o arquivo de configuração “` ./output/nginx.conf “` e um arquivo sh “` ./output/reebuild-container.generated.sh “` , destinado à recriação do container.

O arquivo de configuração segue o exemplo 1, enquanto o script de recriação do container está abaixo no exemplo 3.

#!/bin/bash
docker rm -f EntryPoint

docker run \
-d \
--name EntryPoint \
--hostname EntryPoint \
--network=front \
-p 80:80 -p 443:80 \
 \
-p 23:22 -p 53:53 -p 110:110 -p 143:143 -p 389:389 -p 465:465 -p 587:587 -p 993:993 -p 995:995 -p 3443:3443 -p 9071:9071 -p 444:443   -p 7071:7071 \
-p 3306:3306 \
-p 6379:6379 \
-p 27017:27017 \
-p 4369:4369 -p 25672:25672 -p 5671:5671 -p 5672:5672 -p 15672:15672 -p 61613:61613 -p 61614:61614 -p 1883:1883 -p 8883:8883 \
-v /docker/EntryPoint/config:/etc/nginx/ \
-v /docker/EntryPoint/PageSpeed:/PageSpeed/ \
-v /docker/EntryPoint/logs:/var/log/nginx/ \
-v /docker/Certificados/:/cert/ \
luizcarlosfaria/nginx-pagespeed

sleep 2

docker ps -a --filter "name=EntryPoint"
docker logs EntryPoint

Este repositório é uma base para você criar sua própria automação. Se você usar a imagem luizcarlosfaria/nginx-pagespeed/ você ganha features como:

  • Stream – Possibilidade de usar o NGINX como Reverse Proxy de serviços não HTTP, como Banco de Dados, Redis. Mongo etc. Isso torna mais segura sua infra, na medida que você não precisa criar seus containers com binding para as portas. Habilita somente nos períodos em que precisar.
  • ngx_http_substitutions_filter_module – possibilita a substituição do conteúdo HTTP de uma requisição.
  • Google Page Speed – uma verdadeira bruxaria! O Google Page Speed realiza otimizações no HTML, Javascript de forma coordenada e unificada, permitindo que você melhore aplicações das quais você não tem controle sobre o desenvolvimento.
  • nginx-rtmp-module – NGINX-based Media Streaming Server, desenhado para enconding em realtime.

Os templates de criação estão na pasta ./Templates e devem ser customizados a gosto!

Get Started

Download

Baixe o repositório:

git clone https://github.com/docker-gallery/nginx-pagespeed-automation.git
cd ./nginx-pagespeed-automation
npm install

Customizando os metadados

No arquivo ./data.json substitua ./output/nginx.conf pelo path real onde deve ficar o arquivo de configuração do NGINX.

    "templates": [
        { "Name": "Nginx Configuration", "Template": "./templates/nginx.config.template.asp", "Output": "./output/nginx.conf"  },
        { "Name": "Container Recreation", "Template": "./templates/nginx-recreate.sh.template.asp", "Output": "./output/rebuild-container.generated.sh"  }
    ]

Customizando script de recriação do container

No arquivo ./templates/nginx-recreate.sh.template.asp fique atento às linhas 8 e 26:

#!/bin/bash
docker rm -f <%= data.NginxContainerName %>

docker run \
-d \
--name <%= data.NginxContainerName %> \
--hostname <%= data.NginxContainerName %> \
--network=front \<% 
	var portsInUse = [];
Enumerable.from(data.Services).where("$.Enabled").toArray().forEach(function(service){ 
	var result = Enumerable.from(service.Binds).select(function(bind){
		var bindPort = is.existy(bind.Port)?bind.Port : bind.HostPort;
		var containerPort = is.existy(bind.Port)?bind.Port : bind.ContainerPort;
		var returnValue = "";
		if(Enumerable.from(portsInUse).any(function(it){ return it == bindPort}) == false)
		{
        	portsInUse.push(bindPort);
			return "-p " + bindPort + ":" + containerPort;
		}
		return returnValue;
	}).toArray().join(" "); 
%>
<%= result %> \<%
});
%>
-v /docker/EntryPoint/config:/etc/nginx/ \
-v /docker/EntryPoint/PageSpeed:/PageSpeed/ \
-v /docker/EntryPoint/logs:/var/log/nginx/ \
-v /docker/Certificados/:/cert/ \
luizcarlosfaria/nginx-pagespeed

sleep 2

docker ps -a --filter "name=<%= data.NginxContainerName %>"
docker logs <%= data.NginxContainerName %>

Na linha 8 temos a configuração da rede docker usada pelo container, enquanto na linha 26 o bind com o path do volume destinado às configurações do NGINX. Este último precisa ser coerente com path do arquivo gerado.

Executando

Acertando esses pontos para o teu cenário, basta rodar:

node ./index.js

Os arquivos são regerados e você pode executar o script “` ./reebuild-container.generated.sh“` localizado na pasta output. Este script removerá o container chamado EntryPoint, e o recriará, com base nas configurações de portas determinados no seu data.json.

Outras possibilidades

Esse procedimento pode ser automatizado no WebMin, mas isso é um assunto para um segundo post.

Você ainda pode customizar todo o fluxo para gerar outros artefatos, como um docker-compose.yml, por exemplo, e enfim, muito mais.

Forke o projeto no github.

0 comentários

Enviar um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.

[docker de a a z]

Lives

Fique de olho nas lives

Fique de olho nas lives no meu canal do Youtube, no Canal .NET e nos Grupos do Facebook e Instagram.

Aceleradores

Existem diversas formas de viabilizar o suporte ao teu projeto. Seja com os treinamentos, consultoria, mentorias em grupo.