Portas e Redes

Na nossa última aula, aprendemos a conectar o nosso ambiente de desenvolvimento local com os arquivos do container usando o bind mount. Porém, ficou um pequeno problema em aberto: ao tentar abrir a URL no navegador, o projeto simplesmente não carrega e recebemos um erro de conexão.

Hoje vamos resolver essa situação entendendo como funcionam as portas no Docker, e depois daremos os toques finais no nosso projeto adicionando o suporte a variáveis de ambiente usando um arquivo .env.

Por que o endereço não funciona?

Para entender a origem do problema, precisamos lembrar de um conceito essencial: o container é um ambiente isolado rodando dentro do nosso computador.

Quando o nosso código Node executa dentro do container e passa a escutar na porta 3000, essa porta está aberta apenas dentro do container.

A rede do container é isolada da nossa máquina, então quando digitamos http://localhost:3000/ no navegador, o nosso computador tenta procurar um serviço rodando na porta 3000 dele próprio e não encontra nada.

Para conseguir acessar a nossa aplicação a partir do navegador, precisamos criar uma ponte ligando uma porta do nosso computador com a porta interna do container.

Mapeando portas com o Docker

O mapeamento de portas serve para avisar ao Docker: "Toda requisição que chegar na porta X do nosso computador deve ser redirecionada para a porta Y do container".

Para fazer essa conexão, adicionamos a flag -p ao comando docker run:

docker run -p 3000:3000 -v ./:/app -v /app/node_modules -d --rm --name api my-node-api

O -p é a flag que faz o mapeamento das portas (abreviação de publish).

O primeiro 3000 (antes dos dois pontos :) é a porta no nosso computador.

E o segundo 3000 (depois dos dois pontos :) é a porta lá no container.

Mapeando portas diferentes

Não precisamos obrigatoriamente usar o mesmo número nos dois lados. Se por acaso a porta 3000 do nosso computador já estiver ocupada por outro projeto, podemos mapear a porta 8080 da nossa máquina para a 3000 do container:

docker run -p 8080:3000 -v ./:/app -v /app/node_modules -d --rm --name api my-node-api

Assim, ao acessar http://localhost:8080/ no navegador, o Docker redireciona o acesso para a porta 3000 interna do container e tudo funciona normalmente.

E o comando EXPOSE no Dockerfile?

É comum encontrarmos a instrução EXPOSE 3000 dentro de arquivos Dockerfile. Vale destacar que ela serve apenas como uma documentação indicando qual porta a imagem costuma utilizar. Ela não libera a porta para o nosso computador sozinha, sendo necessário usar a flag -p no docker run.

Variáveis de ambiente

Agora que a nossa aplicação já responde no navegador, podemos passar para o último ponto importante: a gestão de variáveis de ambiente.

Evitamos deixar portas, dados de conexão ou chaves secretas fixas direto no código. Em vez disso, lemos esses valores a partir do ambiente.

Podemos atualizar o nosso src/index.js para ler a porta através de uma variável de ambiente:

const express = require('express');

const app = express();
const port = process.env.PORT || 3000;

app.get('/', (req, res) => {
  res.json({
    message: "Hello Express"
  });
});

app.listen(
  port,
  () => console.log(`Server running at http://localhost:${port}/`)
);

Passando variáveis via -e

Podemos injetar uma variável de ambiente no momento em que subimos o container usando a flag -e:

docker run -p 5000:5000 -e PORT=5000 -v ./:/app -v /app/node_modules -d --rm --name api my-node-api

O -e é a flag para definir uma variável de ambiente (abreviação de env).

O PORT=5000 é a chave e o valor da variável que queremos passar para dentro do container.

Executando o docker logs api, podemos confirmar que a aplicação subiu na nova porta:

Server running at http://localhost:5000/

Usando um arquivo .env

Quando o projeto começa a ter muitas variáveis, passar cada uma manualmente no terminal com -e fica pouco prático. Para resolver isso, podemos centralizar as variáveis em um arquivo .env.

Podemos criar o arquivo .env na raiz da nossa pasta:

my-node-api/
├── src/
│   └── index.js
├── .env
├── package.json
├── package-lock.json
└── Dockerfile

Dentro dele, colocamos a nossa variável:

PORT=4000

Agora, em vez de passar variável por variável, usamos a flag --env-file no comando:

docker run -p 4000:4000 --env-file .env -v ./:/app -v /app/node_modules -d --rm --name api my-node-api

O --env-file indica o caminho do arquivo de onde o Docker deve ler as variáveis de ambiente.

O .env é o arquivo na nossa máquina que contém as definições que serão enviadas para o container.

Com isso, o Docker carrega automaticamente todas as variáveis listadas no arquivo .env e disponibiliza para o nosso container.

Considerações finais

Nessa aula aprendemos a expor as portas do container para o nosso computador e vimos como configurar variáveis de ambiente usando a flag -e ou carregando direto de um arquivo .env.

Com isso fechamos o pacote de ferramentas básicas para rodar e desenvolver nossas aplicações dentro do Docker no dia-a-dia.

Fica ligado, assista a versão em vídeo no Youtube, curta, comente se tiver alguma dúvida, e compartilhe para aquele seu amigo que também está interessado no Docker.

Até a próxima.