Dockerizando sua Aplicação
Tags: docker, beginners, programming, tutorial
Published: 2022-04-10 19:20:27

![Container Docker](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ne7fs2xl7b4is861oqnj.png)

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](https://hub.docker.com/), 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](https://docs.docker.com/install/linux/docker-ce/ubuntu/), [Windows](https://docs.docker.com/docker-for-windows/install/) ou [Mac](https://docs.docker.com/docker-for-mac/install/) e para instalar o [Docker Compose](https://docs.docker.com/compose/install/#install-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:

```YAML
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:

```YAML
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**](https://www.npmjs.com/package/nodemon), existem alguns detalhes que vamos ver abaixo, vamos adicionar então o nosso novo serviço **app-dev**:

```YAML
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](https://github.com/remy/nodemon#application-isnt-restarting)* 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](https://docs.docker.com/) te direciona de uma forma incrível.


Fontes onde pesquisei esse conteúdo:

- https://hub.docker.com/
- https://docs.docker.com/install/linux/docker-ce/ubuntu/
- https://docs.docker.com/docker-for-windows/install/
- https://docs.docker.com/docker-for-mac/install/
- https://docs.docker.com/compose/install/#install-compose
- https://www.npmjs.com/package/nodemon
- https://docs.docker.com/