Docker é uma ferramenta sensacional e a cada dia que me aprofundo um pouco mais venho me supreendendo, e baseado nisso gostaria de compartilhar o conhecimento que venho adquirindo. Com esse artigo espero ajudar você colocar sua aplicações em contêineres e a gerenciar vários deles utilizando o Docker Compose.
Por que utilizar Docker?
Sempre me perguntava isso. Achava tão fácil instalar todas as dependências do projeto em minha máquina que nunca havia pensado nas vantagens de utiliza-lô, porém um script Docker resolve isso de forma muito mais rápida.
Imagina você construindo uma aplicação utilizando vários bancos de dados diferentes. Você teria que instalar cada um deles em sua máquina para poder desenvolver o projeto, até ai parece tudo bem, mas se este projeto fosse desenvolvido por um time? Cada nova dependência fará com que todos os membros do time tenham que instalá-las, se o time possuir SOs (Sistemas Operacionais) diferentes é bem provável que a forma de instalação seja diferente, isso vai dar trabalho, não? E ai vem o Docker, ele cria contêineres para você de forma que sua aplicação será executada em um ambiente isolado, como se estivesses em “outra máquina” ou em um servidor.
Outra vantagem é que você não vai precisar instalar nada em sua máquina, apenas o Docker, isso evita poluir o seu ambiente com as instalações de diversas aplicações e caso instale alguma imagem que não queira você pode exclui-la via interface.
Tanto o NodeJS, quanto o MongoDB, possuem imagens no Docker, assim como diversas outras tecnologias, você pode encontrar imagens no Docker Hub, lá existem centenas de milhares delas, mas vamos ao que interessa, como utilizar tudo isso?
Exemplo prático
Para entendermos como utilizá-lo criei vamos usar como exemplo uma pequena API em NodeJS. A Aplicação consiste em um CRUD, e nossa plicação fará uso do MongoDB para armazenar nossos dados. Para esse tutorial você vai precisar ter apenas o Docker e o Docker Compose instalados. (Sugiro que confira o modo de instalação do Docker para Ubuntu, Windows ou Mac e para instalar o Docker Compose em qualquer versão)
Vamos utilizar o Docker Compose para gerenciar nossos Contêineres.
Dockerizando
Vamos começar configurando o contêiner de nossa aplicação Node.js, para isso vamos criar um arquivo com o nome Dockerfile
na raiz da aplicação com o seguinte conteúdo:
FROM node:latest
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY package.json /usr/src/app/
RUN npm install
COPY . /usr/src/app
Vamos entender o que esta acontecendo:
- FROM serve para dizermos qual imagem vamos utilizar no nosso contêiner, nessa caso vamos utilizar do próprio NodeJS contendo a sua ultima versão.
- RUN é utilizado sempre que queremos executar um comando dentro do contêiner, criar uma pasta, baixar as dependências do NPM, etc…
- WORKDIR define o diretório de trabalho onde vamos manter nossa aplicação, a partir da declaração dele os comandos RUN e CMD serão executados no caminho definido através deste comando.
- COPY serve para copiarmos nossas arquivos, dessa forma copiamos o nosso código para o nosso WORKDIR.
Temos o nosso script para montar um contêiner Docker, mas nossa aplicação ainda não funciona, falta nosso banco de dados. Para ele vamos criar um novo contêiner, criaremos um arquivo chamado docker-compose.yml
com o seguinte conteúdo:
version: "2"
services:
app:
container_name: "app"
restart: always
build: .
environment:
- MONGO_URI=mongodb://mongo/catstore
- PORT=3000
- NODE_ENV=production
ports:
- "3000:3000"
links:
- mongo
depends_on:
- mongo
command: npm start
mongo:
container_name: "mongo"
image: mongo
ports:
- "27017:27017"
command: mongod --smallfiles --logpath=/dev/null # --quiet
Funciona da seguinte forma:
- version serve apenas para dizermos qual a versão do Docker Compose estamos utilizando.
- services são os nossos contêineres, app é nossa aplicação em NodeJS e temos mais um chamado mongo que é o nosso banco de dados.
- container_name serve para darmos um nome ao nosso contêiner, um alias.
- restart dizemos quando queremos reiniciar nossa aplicação, com o always dizemos que sempre, o padrão é no que faz com que o contêiner não reinicie em nenhuma circunstância.
- build definimos onde se encontra o Dockerfile do contêiner*.*
- enviroment é onde listamos as variáveis de ambiente do contêiner, por exemplo a URI de acesso ao nosso banco, aqui temos um detalhe importante, a URI deve conter o nome do contêiner onde esta o banco, para o nosso script é mongo, fica assim: mongodb://mongo/catstore.
- ports colocamos as portas que queremos expor do nosso contêiner, primeiro vem a porta do nosso contêiner e depois a do host, nesse caso nossa própria máquina, dessa forma a nossa app sera acessível na porta 3000 de nossa máquina.
- links definimos a quais serviços nosso contêiner estará ligado.
- depends_on dizemos que o nosso contêiner depende de outro, assim quando subirmos ele suas dependências serão levantadas primeiro.
- image podemos definir qual imagem vamos utilizar no serviço, para o nosso serviço mongo vamos utilizar a imagem também chama mongo.
- command dizemos qual comando vamos executar ao subir aquele serviço.
Agora que entendemos cada linha vamos botar tudo isso para funcionar, com o comando docker-compose build app vamos construir nosso serviço app* e como ele depende do serviço mongo* esse sera priorizado e executando primeiro, é nessa hora que sera feito o download das imagens e a execução nosso script.
Quando os serviços estiverem prontos utilizamos o comando docker-compose up app para inicializar nossa aplicação, é nesse momento que a instrução CMD contendo o npm start
é executada.
Assim que terminar de utilizar os contêineres o comando docker-compose down vai parar e remover todos, caso deseja apenas parar-los use o docker-compose stop.
Ambiente de desenvolvimento
Da forma que criamos nosso contêiner, sempre que alterarmos algum código vamos executar o build O nodemon pode nos ajudar, ele é uma ferramente que sobe o nossa aplicação em Node e a qualquer alteração em algum arquivo reinicia o servidor para nós.
Isso pode dar um pouco de dor de cabeça, não basta apenas executar a aplicação com nodemon, existem alguns detalhes que vamos ver abaixo, vamos adicionar então o nosso novo serviço app-dev:
app-dev:
container_name: "app-dev"
restart: always
build: .
environment:
- MONGO_URI=mongodb://mongo/catstore
- PORT=3001
- NODE_ENV=developer
ports:
- "3001:3001"
- "5555:5555"
links:
- mongo
depends_on:
- mongo
command: node_modules/.bin/nodemon -L --inspect=5555 index.js
volumes:
- ./:/usr/src/app
Nessa configuração adicionamos o campo volumes, ele é necessário para dizermos onde o nosso source se encontra no HOST, funciona assim primeiro o caminho do HOST, no caso . e depois o caminho no contêiner que é /usr/src/app separados por :, outro detalhe é o uso da flag -L para habilitar o modo legacy do nodemon, dessa forma a pesquisa por mudanças de arquivos sera feita outra forma.
Agora basta executar docker-compose build e depois docker-compose up app-dev para levantarmos nossa aplicação para desenvolvimento, tente mudar algo no código e veja sua aplicação reiniciar.
Considerações
Assim temos nossa aplicação em NodeJS dockerizada, criamos um contêiner para o nosso ambiente de desenvolvimento*, isolamos o que é comum nos contêineres em nosso Dockerfile e o que é diferente deixamos em nosso docker-compose.yml.*
Aqui utilizamos uma stack NodeJS + MongoDB mais isso não impede você de utilizar outras tecnologias, fato é que o Docker é uma ferramenta incrível onde facilita bastante o desenvolvimento e cria uma certa camada de segurança, por exemplo pense que está aprendendo sobre Postgres você vai precisar somente baixar a imagem oficial e inicia-lá que vai estar disponivel no seu localhost para configurar no seu SGBD favorito preparado em minutos (ou até segundos) e caso não queria utilizar a imagem mais você pode para-lá ou simplesmente excluir. Sugiro muito que o Docker esteja nos seus planos de estudos e para isso a documentação dele te direciona de uma forma incrível.
Fontes onde pesquisei esse conteúdo:
Top comments (0)