Vou mostrar aqui apenas como se inicia um projeto em Node e Typescript, com Docker e banco de dados Postgres. Espero que o que tem aqui os inspire a ir em busca de mais conhecimento sobre o assunto.
Sumário
- Iniciando o projeto
- Arquivos iniciais
- Criando o arquivo Dockerfile
- Docker Compose
- Usando variáveis de ambiente no docker compose
- Conclusão
- Links úteis
Iniciando o projeto
Para iniciar o projeto vou rodar o comando yarn init -y
, caso esteja usando o npm é só trocar por npm init -y
. Com isso vai ser criado o arquivo package.json.
Logo em seguida vamos instalar todas as dependências do projeto:
- yarn add express
- yarn add -D @types/express
- yarn add -D typescript ts-node nodemon
- yarn tsc —init (Para criar o arquivo
tsconfig.json
)
Com todas as dependências instaladas, agora vamos começar a codar.
Arquivos iniciais
Na raiz do seu projeto crie uma pasta chamada src
e dentro dela crie dois arquivos, index.ts
e routes.ts
. No arquivo index.ts
vamos ter o seguinte código:
// 1
import express from 'express';
// 2
import routes from './routes';
// 3
const app = express();
// 4
app.use(express.json());
// 5
app.use(routes);
// 6
app.listen(3000, () => console.log('🔥 Server started at http://localhost:3000'));
- Importamos o express.
- Importamos o arquivo de rotas.
- Criamos uma variável chamada
app
e atribuímos a ela o módulo com funções do express. - Configuramos para que o
app
faça o parse do JSON. - Dizemos para o
app
usar o arquivo de rotas. - Dizemos para o
app
subir o servidor na porta3000
.
Agora vamos para o arquivo de rotas. No arquivo routes.ts
coloque o seguinte código:
import { Router } from 'express';
const routes = Router();
routes.get('/', (req, res) => {
res.send('Olá Mundo!');
});
export default routes;
Apenas criamos uma rota do tipo GET que devolve uma resposta "Olá Mundo!", sem muita complicação, aqui é moleza!
Por último, mas não menos importante, no arquivo package.json
temos que inserir um script para subir a aplicação, então, coloque o seguinte código logo antes das declarações das dependências do projeto:
"scripts": {
"dev": "npx nodemon --exec ts-node ./src/index.ts --ignore-watch node_modules"
},
Aqui estamos dizendo pro nodemon
executar o ts-node
partindo do arquivo index.ts
ignorando a pasta node_modules
. Nada de outro mundo aqui.
E para testar tudo no seu terminal, rode o comando yarn dev
ou npm run dev
, o resultado deve ser mais ou menos esse:
Criando o arquivo Dockerfile
Depois de ter criado a aplicação e testado, vamos criar o arquivo Dockerfile. Nesse arquivo vai conter apenas as configurações iniciais do projeto para criar nossa imagem, como por exemplo, versão do node.
Mas antes disso você sabe o que é o Dockerfile? Para que serve?
Dockerfile é o arquivo onde definimos as instruções para criar as nossas próprias imagens. Ele tem sua própria sintaxe com os seus respectivos comandos. Nele é como se tivesse uma receita de bolo, só que no nosso caso o bolo é a aplicação, é uma receita para criar nossa imagem da aplicação.
Para este exemplo vamos colocar o seguinte conteúdo no nosso arquivo:
FROM node:alpine
WORKDIR /usr/src/app
COPY package*.json ./
RUN yarn
COPY . .
EXPOSE 3000
CMD ["yarn", "dev"]
Vamos saber pra que serve cada instrução dessa.
FROM
→ De onde vai baixar a imagem que vamos utilizar, no caso vamos usar a ver alpine do node, que é uma versão mais simplificada.
WORKDIR
→ Define o diretório onde a aplicação vai ficar no disco do container, aqui você pode usar o diretório que preferir.
COPY
→ Copia tudo que começa com package e termina com .json para dentro da pasta /usr/src/app.
RUN
→ Executa o yarn ou npm install para adicionar as dependências do projeto e criar a pasta node_modules.
COPY
→ Copia tudo que está no diretório onde o arquivo Dockerfile está para dentro da pasta que definimos no WORKDIR.
EXPOSE
→ Expomos uma porta para o container ficar ouvindo os acessos.
CMD
→ Executa o comando yarn dev que está nos scripts do package.json para iniciar a aplicação. Aqui separamos todas as palavras por vírgula dentro de um array.
Crie um arquivo .dockerignore para ignorar algumas coisas, neste exemplo vamos adicionar a pasta node_modules para ser ignorada.
Agora para verificar se está tudo certo vamos rodar o comando:
docker build -t dockernode .
-
docker build
cria uma imagem a partir do Dockerfile -
-t
é o nome da imagem -
dockernode
é o nome que escolhi para essa imagem -
.
é onde o Dockerfile está, o comando será executado no mesmo diretório que o Dockerfile se encontra.
Se a saída no terminal for algo semelhante a isso, deu tudo certo na criação da imagem:
Se você chegou até aqui sem nenhum erro, ótimo, mas ainda faltam algumas coisinhas. Até agora só criamos a imagem, falta criar o container. E para isso temos e executar o comando abaixo:
docker run -p 3000:3000 -d dockernode
-
docker run
cria um container. -
-p 3000:3000
libera a porta 3000 do container para que ele possa ouvir as requisições de fora acessando a porta 3000. -
-d
detach, o terminal fica livre e o processo roda em segundo plano. (Se você não passar essa tag você não conseguirá mais usar a aba do terminal, ficará travada mostrando o processo.) -
dockernode
o nome da imagem que estou usando para criar o container.
Rodando o comando será exibido o ID do container e executando no terminal docker ps
será listado o processo em execução no Docker.
É interessante observar, a aplicação está rodando dentro do container do Docker, e não na nossa máquina local. Para acessar basta colocar no navegador [http://localhost:3000](http://localhost:3000)
que será exibida a mensagem "Olá Mundo!"
O comando docker run só precisa ser executado uma vez para criar o container, para outras operações usamos: docker start <id do container>
para iniciar, docker stop <id do container>
para parar e docker logs <id do container>
para ver os logs.
Docker Compose
Estamos chegando a última parte do nosso exemplo de uso de Dockerfile e Docker Compose, agora vamos ver o que é e como funciona o Docker Compose.
Basicamente o Docker compose é um orquestrador de containers no Docker. Ele vai definir como o container deve se comportar. Anteriormente no dockerfile definimos como a aplicação vai funcionar, o Docker compose vai fazer o banco de dados subir, a aplicação ficar no ar e se conectar com o banco de dados, neste exemplo, mas ele pode fazer muito mais.
Vou mostrar também um recurso muito legal que são os volumes, usamos eles para espelhar os arquivos do projeto na máquina local com o volume do container. Dessa forma toda vez que alterarmos qualquer arquivo na máquina local ele enviará para o container do Docker. (Para isso que instalamos o nodemon).
Mão na massa...
Na raiz do projeto crie o arquivo docker-compose.yml
e dentro dele coloque o seguinte código:
version: "3"
services:
api:
image: dockernode
container_name: "app"
ports:
- "3000:3000"
links:
- link-db
volumes:
- ./:/usr/src/app
link-db:
image: postgres
container_name: "postgres"
volumes:
- ./postgres:/var/lib/postgres
ports:
- "5432:5432"
environment:
- POSTGRES_USER=your_user
- POSTGRES_DB=your_db
- POSTGRES_PASSWORD=your_pass
-
version
→ Especifica a versão do docker-compose file. -
services
→ Define um serviço. -
api
→ Nome do serviço, aqui você pode colocar o nome que preferir. -
image
→ imagem que o serviço vai usar. -
container_name
→ Como o próprio nome diz, é o nome do container. -
ports
→ Portas que serão usadas no host e no container. -
links
→ Link para containers em outro serviço. -
volumes
→ Diretório que usamos para espelhar, antes dos dois pontos é o diretório que vamos pegar os arquivos e depois dos dois pontos o diretório de destino, que vai ser o do container. -
environment
→ Contém as variáveis de ambiente do banco de dados, aqui definimos o usuário, senha e banco de dados que a aplicação vai usar para se conectar com o banco de dados.
Aqui coloquei a pasta para os arquivos do banco de dados na mesma pasta do projeto, mas só como exemplo, você deve definir uma outra pasta para poder guardar esses arquivos do banco de dados. (Volumes do service link-db)
Antes de executar o comando do docker-compose
vamos parar o container e excluí-lo.
Execute no terminal docker ps
para conferir se o container está executando, pegue o ID do container e para parar o container rode o comando docker stop <id>
e depois execute docker rm <id>
para remover o container, por fim execute o comando abaixo para criar o container e subir o serviço:
docker-compose up
Pronto, ele vai iniciar o serviço fazendo build do projeto conforme o Dockerfile, liberar a porta 3000 e ficará monitorando a pasta do projeto a partir da rootDir
e enviar para /usr/src/app
.
Para parar o serviço tecle CTRL+C
. Pode rodar com docker-compose up -d
para rodar em background e liberar o terminal.
Agora sim tudo pronto, já temos o serviço sendo executado, e acessando http://localhost:3000 teremos "Olá Mundo" como retorno.
Usando variáveis de ambiente no docker compose
Um arquivo que nos ajuda e poupa um bom trabalho nos projetos é o arquivo .env
, (eu espero que todos usem 😄), com ele definimos todas as variáveis de ambiente que nosso projeto usar, usuário de banco de dados, host, senha, por exemplo.
E como vimos anteriormente, no arquivo docker-compose.yml
existem algumas dessas variáveis, mas setamos elas manualmente, isso pode expor dados sensíveis da nossa aplicação, já que esse arquivo fica exposto para todos. Na sessão de environment
no banco de dados, vamos substituir algumas coisas. Então vamos lá.
Na raiz do projeto crie um arquivo chamado .env
e nele coloque o seguinte código:
# Database
DB_USER=your_user
DB_NAME=your_db
DB_PASSWORD=your_password
DB_PORT=5432
São as mesmas variáveis que estão no arquivo docker-compose.yml, substitua os valores delas pelos seus dados.
No arquivo docker-compose.yml faça as seguinte alterações:
ports:
- "${DB_PORT}:5432"
environment:
- POSTGRES_USER=${DB_USER}
- POSTGRES_DB=${DB_NAME}
- POSTGRES_PASSWORD=${DB_PASSWORD}
Na parte do serviço do banco de dados, coloque os mesmos nomes das variáveis de ambiente que você definiu anteriormente.
Certo, mas como o arquivo do docker-compose.yml
vai entender essas variáveis de ambiente se eu não tenho nenhuma configurada na minha máquina?
Para isso temos que criar um arquivo makefile
, que vai criar essas variáveis pra gente e fazer com que o arquivo do docker entenda essas variáveis. E para subir a aplicação, ao invés de usar docker-compose up
, vamos usar make up
.
Então na raiz do seu projeto crie um arquivo chamado Makefile
. Esse arquivo é bastante simples, não tem nada demais, apenas algumas instruções:
include .env
.PHONY: up
up:
docker-compose up -d
.PHONY: down
down:
docker-compose down
.PHONY: logs
logs:
docker-compose logs -f
-
include
vai incluir o arquivo.env
e no escopo da execução o arquivo vai entender as variáveis de ambiente como se todas tivessem exportadas. -
.PHONY
força a criação de um rótulo.
Agora você pode executar o comando make up
no terminal.
Pronto, acessando http://localhost:3000 no navegador você verá que a aplicação está no ar. E acessando o banco de dados com um aplicativo de sua preferência você verá que o banco de dados foi criado e também já está no ar.
Conclusão
Apesar desse app ter sido bem simples, as vantagens de usar o Docker são grandes, ainda mais quando começamos a usar mais de um banco de dados, vários serviços, e temos que trabalhar em equipe, todos com as mesmas versões e configurações do projeto.
Outra coisa que agrada bastante é que se formos apagar os containers e as imagens, não fica nenhum arquivo no nosso computador, aquele lixo na máquina.
Top comments (1)
Um complemento: dev.to/devbaraus/postgresql-docker...