Criando uma aplicação node com proxy reverso usando nginx
Neste artigo vamos criar um proxy reverso utilizando nginx e também vamos criar uma aplicação com o node diretamente de um container.
Criando a aplicação node
O primeiro passo é criar um diretório, vamos chamar esse diretório de nodeapp e acessar ele
mkdir nodeapp && cd nodeapp
Agora vamos abrir esse diretório em uma ide ou editor de texto, no meu caso abri no VS Code
code .
Agora crie um diretório chamado node para criarmos nossa aplicação node ali dentro.
Feito isso vamos executar uma imagem docker com o node na sua última versão, também vamos fazer com que o diretório /usr/src/app
aponte para o diretório que criamos e que estamos acessando no VS Code, pra isso vamos executar o seguinte comando:
docker run --rm -it -v $(pwd)/node:/usr/src/app -p 3000:3000 node:15 bash
Explicando o comando:
docker run
- executa uma imagem docker qualquer
--rm
- o cotainer será destruído ao parar de ser executado
--it
- ativa o modo interativo para que possamos acessar o shell bash e interagir com o container
-v $(pwd)/:/usr/src/app
- mapeamos um volume e dizer que o diretóio em que esamos agora
$(pwd)
vai ser mapeado para o diretório /usr/src/app
-p 3000:3000
- especifica que o container deverá expor a porta 3000 e redirecionar o tráfego da porta externa 3000 para a porta interna 3000
node:latest
- por fim estamos dizendo qual imagem queremos, nesse caso node na sua última versão disponível
bash
- estamos falando que o modo interativo deve executar o born again shell (bash)
Após fazer isso estaremos dentro do container, você deverá ver algo mais ou menos assim:
root@25e599b36ad1:/#
Vamos nos dirigir ao diretório mapeado para o nosso volume
cd /usr/src/app
E finalmente criar uma aplicação node para isso digite
npm init
Após preencher os campos você deverá notar que foi criado um arquivo chamado package.json
O próximo passo será instalar o express
npm install express --save
Após esse comando você verá que agora temos um diretório chamado node_modules
e os arquivos package.json
e package-lock.json
Agora crie um arquivo chamado index.js
Nesse arquivos vamos configurar uma aplicação node bem simples:
const express = require('express')
const app = express()
const port = 3000
app.get('/', (req, res) => {
res.send('<h1>It Works !</h1>')
})
app.listen(port, () => {
console.log('Running on ' + port);
})
E vamos executar nossa aplicação pra ver se está funcionando
node index.js
Após esse comando acesse http://localhost:3000/ e você verá uma página com o cabeçalho "It Works !"
OK nossa aplicação está pronta, pressione CTRL+C para parar a aplicação e vamos sair do container:
exit
Apesar de sairmos do container veja que os arquivos permaneceram no VS Code e temos uma estrutura mais ou menos assim:
nodeapp
└── node
├── node_modules
├── index.js
├── package-lock.json
└── package.json
Criando a imagem da aplicação
Vamos criar uma imagem para o nosso container de aplicação node, pra isso crie um Dockerfile
dentro do diretório node
Nesse Dockerfile vams querer utilizar a última versão do node e vamos utilizar multi stage building também.
FROM node:latest AS build
WORKDIR /usr/src/app
COPY . .
FROM node:15-alpine
WORKDIR /usr/src/app
COPY --from=build /usr/src/app .
RUN npm install
EXPOSE 3000
CMD ["node", "index.js"]
FROM node:latest AS build
- Especifica que vamos utilizar a última versão do node para fazer o build da imagem
WORKDIR /usr/src/app
- Definimos o diretório
/usr/src/app
como diretório de trabalho do nosso container
COPY . .
- Estamos copiado tudo que está no diretório atual para o workdir definido acima
FROM node:15-alpine
- A partir daqui começa o build da imagem final na qual vamos utilizar na imagem final a versão
node:15-alpine
WORKDIR /usr/src/app
- Definimos o diretório
/usr/src/app
como diretório de trabalho do nosso container
COPY --from=build /usr/src/app .
- Estamos copiando tudo que está no diretório /usr/src/app da build acima para o workdir atual
RUN npm install
- Executamos o comando
npm install
EXPOSE 3000
- Vamos deixar a porta 3000 exposta
CMD ["node", "index.js"]
- Por fim vamos executar o comando
node index.js
Veja que fizemos no Dockerfile praticamente tudo que fizemos no container para executar a nossa aplicação.
Agora basta gerar a imagem
docker build -t jorgerabello/nodeapp .
Criando um proxy reverso com nginx
Nesse temos a seguinte estrutura
nodeapp
└── node
├── node_modules
├── index.js
├── package-lock.json
└── package.json
Vamos criar um diretório chamado nginx de modo que nossa estrutura fique assim:
nodeapp
├── nginx
└── node
├── node/index.js
├── node/node_modules
├── node/package-lock.json
└── node/package.json
Dentro do diretório nginx vamos criar um arquivo de configuração chamado nginx.conf
com o seguinte conteúdo:
server {
listen 80;
server_name app;
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://app:3000/#;
}
}
server {:
- Define um bloco de configuração para um servidor.
listen 80;:
- Especifica que o servidor deve escutar na porta 80.
server_name app;:
- Define o nome do servidor como "app". Isso significa que o servidor responderá a solicitações para esse nome de domínio.
location / {:
- Define uma localização dentro do servidor onde as configurações específicas serão aplicadas. Neste caso, as configurações se aplicam a todas as solicitações (todas as rotas).
As próximas linhas dentro do bloco de localização são diretivas para configurar o proxy reverso:
proxy_set_header Host $host;:
- Define o cabeçalho Host da solicitação HTTP para o valor do cabeçalho Host recebido pelo Nginx.
proxy_set_header X-Real-IP $remote_addr;:
- Define o cabeçalho X-Real-IP para o endereço IP real do cliente.
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;:
- Adiciona o endereço IP do cliente ao cabeçalho X-Forwarded-For, preservando os endereços IP anteriores, se houver.
proxy_set_header X-Forwarded-Proto $scheme;:
- Define o cabeçalho X-Forwarded-Proto para o esquema (HTTP ou HTTPS) da solicitação original.
proxy_pass http://app:3000/;:
- Define para onde o Nginx deve encaminhar as solicitações. Neste caso, as solicitações serão encaminhadas para "http://app:3000/". Isso é comumente usado para encaminhar solicitações para um servidor de aplicativos em execução em outra porta ou máquina.
Agora temos a seguinte estrutura
nodeapp
├── nginx
│ └── nginx/nginx.conf
└── node
├── node/index.js
├── node/node_modules
├── node/package-lock.json
└── node/package.json
Vamos criar um Dockerfile para o container do nginx
FROM nginx:1.15.0-alpine
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/conf.d/
RUN mkdir /var/www/html -p && touch /var/www/html/index.html
Esse é um dockerfile bem simples onde:
FROM nginx:1.15.0-alpine:
- Vamos utilizar a imagem nginx:1.15.0-alpine
RUN rm /etc/nginx/conf.d/default.conf
- Estamos removendo o arquivo de configuração padrão chamado
default.conf
COPY nginx.conf /etc/nginx/conf.d/
- Estamos copiando o arquivo
nginx.conf
que acabamos de criar para o diretório onde ficam os arquivos de configuração do nginx
RUN mkdir /var/www/html -p && touch /var/www/html/index.html
- Por fim estamos criar um diretório
/var/www/html
e dentro dele criando o arquivoindex.html
que é necessário para o nginx executar.
Buildando a imagem do nginx
docker build -t jorgerabello/nginx:reverseproxy .
Podemos conferir se as imagens foram criadas
REPOSITORY TAG IMAGE ID CREATED SIZE
jorgerabello/nodeapp latest b7e295a5152d 44 seconds ago 116MB
jorgerabello/nginx reverseproxy 287ea2b92516 2 hours ago 18MB
Por fim temos a seguinte estrutura de diretórios:
nodeapp
├── nginx
│ ├── nginx/Dockerfile
│ └── nginx/nginx.conf
└── node
├── node/Dockerfile
├── node/index.js
├── node/node_modules
├── node/package-lock.json
└── node/package.json
Faça o upload das suas images para o docker hub
docker push jorgerabello/nodeapp:latest
docker push jorgerabello/nginx:reverseproxy
Na raiz vamos criar um docker-compose.yaml
para executar nossos containers
version: '3'
networks:
nodeappnet:
driver: bridge
services:
app:
build:
context: ./node
dockerfile: Dockerfile
networks:
- nodeappnet
volumes:
- ./node:/usr/src/app
- node_modules:/usr/src/app/node_modules
tty: true
container_name: nodeapp
image: jorgerabello/nodeapp
ports:
- "3000:3000"
nginx:
build:
context: ./nginx
dockerfile: Dockerfile
image: jorgerabello/nginx:reverseproxy
container_name: nginx
networks:
- nodeappnet
ports:
- "8080:80"
depends_on:
- app
volumes:
node_modules:
Esse é um docker-compose.yaml bem elementar onde executamos os containers pelas suas imagens, caso você tenha dúvidas sobre docker-compose pode consultar a documentação aqui ou ler esse artigo sobre docker compose.
Executando tudo
Por fim temos a seguinte estrutura de diretórios
nodeapp
├── docker-compose.yaml
├── nginx
│ ├── nginx/Dockerfile
│ └── nginx/nginx.conf
└── node
├── node/Dockerfile
├── node/index.js
├── node/node_modules
├── node/package-lock.json
└── node/package.json
No diretório node app vamos utilizar o nosso docker-compose
docker-compose up -d
E podemos verificar que os containers estão executando
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
af908e4147ad jorgerabello/nginx:reverseproxy "nginx -g 'daemon of…" 4 seconds ago Up 4 seconds 0.0.0.0:8080->80/tcp, :::8080->80/tcp nginx
f7d9b08df4a5 jorgerabello/nodeapp "docker-entrypoint.s…" 4 seconds ago Up 4 seconds 0.0.0.0:3000->3000/tcp, :::3000->3000/tcp nodeapp
Agora se acessarmos http://localhost:8080/ seremos redirecionado para a aplicação node.
Se você quiser provar a si mesmo que o redirecionamento está acontecendo pare o container da aplicação node e tente acessar novamente na porta 8080 e você receberá um belíssimo 503 Bad Gateway.
Para parar o container da aplicação utilize:
docker rm -f f7d9b08df4a5
Finalizando
Bom pessoal nesse artigo usamos algumas features bem legais do docker:
- Criamos uma aplicação node diretamente do container
- Criamos um proxy reverso com nginx
- Construímos imagens dos 2 containers e colocamos no docker hub
- Executamos os containers fazendo com que acessando o nginx sermos redirecionados para a aplicação node
Valeu e até a próxima !