Um dos princípios para ter um projeto bem estruturado é tornar a configuração inicial tão simples quanto possível. Com menos impedimento para de fato "rodar" o projeto, é possível inserir mais desenvolvedores no fluxo de trabalho de forma acelerada.
Um dos grandes gargalos, sem dúvidas, é montar a infraestrutura necessária, otimizada para o ambiente de desenvolvimento. As práticas e conceitos do mundo DevOps entram para auxiliar e, nesse artigo, vamos abordar docker e conteinerização de um backend feito com Nodejs e mongodb. Além disso, no fim vamos ver uma dica para visualizar melhor os dados.
Primeiro, vamos criar uma aplicação com node. Você pode utilizar algum projeto que já esteja configurado (e, caso o faça, pule pro próximo tópico). Verifique que ele possui um script "start" que possa ser utilizado.
Iniciando o projeto
Utilizando o yarn:
$yarn inityarn init v1.22.4question name(example_docker): question version(1.0.0): question description: A simple backendquestion entry point(index.js): question repository url: question author: jrmmendes <jrmmendes@outlook.com>question license(MIT): question private: success Saved package.jsonDonein22.54s.
Instalando pacotes necessários
Vamos instalar oexpress.js
(para construir a aplicação) e odotenv
(para carregar variáveis de ambiente mais facilmente):
$yarn add express dotenv
Numa aplicação real, outros pacotes são de fato importantes, sobre tudo para questões ligadas à segurança de dados. Esse exemplo é apenas ilustrativo.
Além disso, para conexão com o banco de dados, vamos instalar omongoose
:
$yarn add mongoose
Escrevendo os arquivos da aplicação
Vamos criar oindex.js
com o seguinte conteúdo:
constexpress=require('express');constdotenv=require('dotenv');constmongoose=require('mongoose');// Definição da aplicaçãoconstapp=express();dotenv.config({path:'.env'});app.use(express.json());// Configuração do acesso ao banco de dadosmongoose.connect(process.env.MONGO_URI,{useCreateIndex:true,useNewUrlParser:true,useUnifiedTopology:true,});mongoose.connection.once('open',()=>{console.log('Conectado ao banco de dados');});mongoose.connection.on('error',(e)=>{console.log('Error ao tentar conectar-se ao banco de dados');console.error(e);});// Rotas de testeapp.route('/ping').all((req,res)=>{res.status(200).json({data:'PONG!'});});// Inicialização do servidorapp.listen(process.env.PORT||3000,()=>{console.log('Servidor Iniciado');});
Vamos também criar o arquivo.env
, com as variáveis de ambientePORT
eMONGO_URI
:
MONGO_URI="mongodb://root:toor@mongo:27017/development-db?authSource=admin"
Por fim, vamos adicionar ao arquivopackage.json
um scriptstart
, para iniciar o projeto. Ele deve estar assim:
{"name":"example_docker","version":"1.0.0","description":"A simple backend","main":"index.js","author":"jrmmendes <jrmmendes@outlook.com>","license":"MIT","dependencies":{"dotenv":"^8.2.0","express":"^4.17.1"}}
De modo que vamos editá-lo, adicionando uma chave "scripts":
{"name":"example_docker","version":"1.0.0","description":"A simple backend","scripts":{"start":"node index.js"},"main":"index.js","author":"jrmmendes <jrmmendes@outlook.com>","license":"MIT","dependencies":{"dotenv":"^8.2.0","express":"^4.17.1","mongoose":"^5.9.7"}}
Essa é a estrutura que o projeto deve ter no fim:
example_docker├── index.js├── node_modules├── package.json└── yarn.lock
Docker
O ponto de partida será criar um arquivo chamadoDockerfile
. É nele onde vamos especificar como ocorre o setup da aplicação.
Caso não esteja familiarizado com os conceitos do Docker, imagine que é uma forma de documentar os passos iniciais para instalar dependências, similar a uma script, mas que é executado de forma mais segura e independente do ambiente do desenvolvedor (desde que seja Linux, nesse caso).
Após isso, iremos configurar os outros serviços relacionados à nossa aplicação (como o banco de dados) e a interação entre eles com o Docker Compose. Aqui já podemos notar um benefício bem clássico dessa abordagem: não será necessário instalar nenhum SGBD no sistema operacional, removendo uma possível fonte de problemas de compatibilidade/configuração.
Definição da Aplicação
Vamos criar o arquivoDockerfile
. Ele terá a seguinte estrutura:
# Imagem baseFROM node:12.16# Configuração do usuário/permissõesUSER nodeWORKDIR /home/node/# Instalação das dependênciasCOPY package.json .COPY yarn.lock .RUNyarninstall# Copia dos arquivos do projetoCOPY . .# ExecuçãoCMD ["yarn", "start"]
Vamos estudar mais a fundo cada parte.
Base
FROM node:12.16
No mundo do Docker, existe oDockerHub, que funciona de maneira análoga ao Github, nos fornecendo um lugar para enviar e utilizar partes reutilizáveis. Nesse caso, vamos tirar proveito da existência de imagens já configuradas do node, em específico das versões12.16.x
, nos livrando da necessidade de instalar o próprio node e suas ferramentas, como o yarn.
Configuração do usuário/permissões
USER nodeWORKDIR /home/node/
Nessa parte, estamos definindo qual usuário será utilizado dentro do contêiner da aplicação. Essa parte é importante para evitar que todos os comandos sejam executados como superusuário (o que, dentre outros impactos, causa um problema de permissões em alguns arquivos, sendo no mínimo inconveniente).
Também alteramos a pasta onde estaremos copiando e executando instruçõesRUN
,COPY
,ADD
,CMD
eENTRYPOINT
.
Instalação das dependências
COPY package.json .COPY yarn.lock .RUNyarninstall
Aqui instalamos os pacotes que a aplicação necessita. É possível substituir essa fase por algo mais complexo como ummultistage build
, mas isso é algo que não vamos ver nesse artigo.
Copia dos arquivos do projeto
COPY . .
Nessa fase os arquivos que escrevemos (.env
,index.js
) são copiados para dentro do contêiner. Apenas para ficar claro, estamos copiando da mesma pasta em que o arquivo Dockerfile se encontra para a que definimos com o comandoWORKDIR
(/home/node
). Vale também lembrar que a segunda se refere ao contêiner, não ao nosso sistema de arquivos normal.
Execução
CMD ["yarn", "start"]
Aqui, iniciamos o projeto. Indicamos qual comando deve ser executado após o setup da aplicação.
Serviços e integração
Para definir os outros serviços e conectar todos os contêineres, além de facilitar a execução do projeto, vamos criar o arquivodocker-compose.yml
, com o seguinte conteúdo:
version: '3'services: api: build: dockerfile: ./Dockerfile context: . volumes: - .:/home/node - /home/node/node_modules ports: - 3000:3000 command: yarn start depends_on: - mongo mongo-express: image: mongo-express ports: - 8081:8081 environment: ME_CONFIG_BASICAUTH_USERNAME: mendes ME_CONFIG_BASICAUTH_PASSWORD: dotmendes ME_CONFIG_MONGODB_PORT: 27017 ME_CONFIG_MONGODB_ADMINUSERNAME: root ME_CONFIG_MONGODB_ADMINPASSWORD: toor depends_on: - mongo mongo: image: mongo command: [--auth] environment: MONGO_INITDB_ROOT_USERNAME: root MONGO_INITDB_ROOT_PASSWORD: toor ports: - 27017:27017 volumes: - ./volumes/db:/data/db
Explicando de maneira rápida, estamos definindo três serviços: api, mongo e mongo-express. O primeiro é construído a partir do Dockerfile que definimos anteriormente; o seguinte, é criado diretamente da imagem do mongo no Dockerhub (similar ao que fizemos com a imagem do node, contudo não modificamos nada).
O terceiro serviço é uma interface que nos permite visualizar o banco de dados e manusear documentos e coleções.
Existe, por fim, a criação de alguns volumes, que serão utilizados para sincronizar modificações entre os arquivos e o que está dentro do contêiner. Isso é especialmente útil durante o desenvolvimento, para que possamos adicionar novas funcionalidades e testá-las sem que precise ser realizado outro processo de build da aplicação.
Conclusão
Após criar todos os arquivos, podemos instalar e executar a aplicação com um simples comando:
$docker-compose up
De modo que teremos como acessar a aplicação emhttp://localhost:3000/ping
e a interface do mongo-express emhttp://localhost:8081
.
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse