Desenvolvendo Aplicação Node.js & Express.js com Docker
Tags: docker, devops, beginners, tutorial
Published: 2019-08-26 14:46:53

---
title: Desenvolvendo Aplicação Node.js & Express.js com Docker
published: true
description: Série de artigos que falaremos a respeito de Docker e Conteiners.
tags: docker, devops, beginners, tutorial
canonical_url: https://dev.to/azure/docker-from-the-beginning---part-ii-5g8n
series: Aprenda Docker - Zero to Hero
---

[![docker.png](https://i.postimg.cc/7LZsn8Tc/docker.png)](https://postimg.cc/VJ2WzhgB)

_artigo originalmente escrito pelo Chris Noring - **[AQUI](https://dev.to/softchris/5-part-docker-series-beginner-to-master-3m1b)**_

No **[último artigo](https://dev.to/azure/introducao-a-docker-conceitos-basicos-4g19)** vimos e aprendemos conceitos básicos sobre Docker, Imagens e Contêineres. Agora é a hora de desenvolvermos uma aplicação e aprender em código como usar o Docker numa aplicação.

Vamos nessa?!

## Vamos por a mão na massa!

Agora que já entendemos o que é Docker e porque devemos usar, agora é aquele momento que precisamos praticar o uso do Docker em alguma aplicação!

Bom, vamos precisar de um arquivo que é bastante usado no Docker: _DockerFile_. Pois é no _DockerFile_ que especificaremos tudo o que precisaremos no que diz respeito ao sistema operacional, variáveis de ambiete e como executar o nosso aplicativo.

Vamos nos aprofundar agora nessa parte. Vamos desenvolver uma aplicação e _Dockerizar_ ele. Através dele iremos executar a nossa aplicação dentro de um contêiner isolado do mundo externo, porém disponível para demais portas.

Vamos seguir os seguintes passos:

* **Criar um aplicativo em Node.js**

Vamos desenvolver um aplicativo Node.js com Express. Ele funcionará como um REST API.

* **Criar o arquivo 'Dockerfile'**

Esse arquivo será responsável por informar ao Docker como construir nossa aplicação.

* **Criar uma imagem**

Para que a nossa aplicação funcione, devemos criar uma imagem chamada Docker.

* **Criar um contêiner**

E por último, veremos a nossa aplicação em funcionamento e criaremos um contêiner a partir de uma imagem Docker.

Vamos seguir esses passos agora?!

## Criando a Aplicação Node.js & Express com Docker

Abre agora o seu Visual Studio Code, crie uma pasta chamada: _exemplo-2_ e dentro dessa pasta digite o seguinte comando:

```bash
> npm init -y
```

Esse comando cria para nós um arquivo **package.json** padrão.

No final, o arquivo **package.json** ficará da seguinte forma:

```json
{
  "name": "exemplo-2",
  "version": "1.0.0",
  "description": "Código do exemplo do artigo: Introdução a Docker - Parte I",
  "main": "app.js",
  "scripts": {
    "dev": "nodemon",
    "lint": "eslint --ext .js,.html -f ./node_modules/eslint-friendly-formatter . --fix",
    "prepush": "npm run lint",
    "start": "node app.js"
  },
  "keywords": [
    "nodejs",
    "javascript",
    "docker",
    "azure",
    "express"
  ],
  "author": "Glaucia Lemos",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/glaucia86/docker-zero-to-hero-series/issues"
  },
  "homepage": "https://github.com/glaucia86/docker-zero-to-hero-series/blob/master/README.md",
  "devDependencies": {
    "eslint": "^5.16.0",
    "eslint-config-airbnb-base": "^13.1.0",
    "eslint-plugin-import": "^2.17.3",
    "husky": "^2.3.0",
    "nodemon": "^1.19.1"
  },
  "dependencies": {
    "eslint-friendly-formatter": "^4.0.1",
    "eslint-plugin-html": "^5.0.5",
    "express": "^4.17.1"
  }
}
```
Caso queiram ter o código fonte desenvolvido, criei um repositório no GitHub. Nesse caso, bastam fazer git clone ou download do projeto **[AQUI](https://github.com/glaucia86/docker-zero-to-hero-series)**

Bom, dando continuidade vamos criar dentro da pasta _exemplo-2_ o arquivo **app.js** e depois o arquivo **Dockerfile**

No final, a estrutura do projeto ficará da seguinte maneira:

[![Screen-Shot-07-05-19-at-03-40-PM.png](https://i.postimg.cc/76GrVVHy/Screen-Shot-07-05-19-at-03-40-PM.png)](https://postimg.cc/cgSz1ftD)

Agora, dentro da pasta _exemplo-2_ execute o comando abaixo:

```bash
> npm install
```

Esse comando instalará todas as dependências necessárias ao nosso projeto e criará uma pasta chamada _node_modules_

Ótimo! Agora vamos começar a desenvolver!

## Desenvolvendo o arquivo 'App.js'

Depois de termos criado a estrutura do projeto e instalado as dependências necessárias na nossa aplicação, vamos abrir o arquivo **app.js** e adicione o seguinte código:

```javascript
/**
 * Arquivo: app.js
 * Descrição: arquivo principal e responsável pela execução da aplicação.
 * Data: 05/07/2019
 * Author: Glaucia Lemos
 */

const express = require('express');

const app = express();

const port = process.env.PORT || 3000;

app.get('/', (req, res) => {
  res.status(200).send({
    success: 'true',
    message: 'Seja Bem-Vindo(a) ao mundo Docker!',
    version: '1.0.0',
  });
});

app.listen(port);
console.log('Aplicação executando na porta ', port);
```

Agora vão até o prompt de comando e execute o comando: **nodemon** e depois abram o postman e coloquem a url: _http://localhost:3000/_ e vejam se Api está funcionando:

[![Screen-Shot-07-05-19-at-04-30-PM.png](https://i.postimg.cc/cHq3mdw1/Screen-Shot-07-05-19-at-04-30-PM.png)](https://postimg.cc/Z9c0YtwX)

Se aparecer a mensagem como na imagem acima é porque está tudo funcionando perfeitamente! Vamos continuar com o nosso tutorial!

## Desenvolvendo o arquivo 'Dockerfile'

O próximo passo agora é criar o arquivo _Dockerfile_. Esse arquivo, como já dito antes, é como se fosse um manifesto, onde possui todas as instruções necessárias para compilar e executar a nossa aplicação. Mas, o que é necessário para executar a aplicação por meio do _Dockerfile_? Vamos precisar:

* **copiar** todos os arquivos da aplicação através do contêiner docker.

* **instalar** as dependências

* **abrir uma porta** no contêiner que será acessado, por assim dizer, no lado de fora.

* **instruir** o contêiner como iniciar a nossa aplicação

Numa aplicação mais complexa, precisamos informar mais coisas, tais como: configuração de variáveis de ambiente ou definição de credenciais para uma base de dados ou executar uma determinada base de dados entre outros pontos. Porém, como se trata de uma aplicação simples, vamos criar o _Dockerfile_ de uma maneira bem simples:

```docker
//==> Dockerfile

FROM node:latest

WORKDIR /app

COPY . .

RUN npm install

EXPOSE 3000

ENTRYPOINT ["node", "app.js"]
```

Vamos entender o que cada comando faz:

* **FROM** aqui estamos selecionando uma imagem do sistema operacional do Docker Hub. O Docker Hub é um repositório global que contém imagens que podemos extrair localmente. No nosso caso, estamos escolhendo uma imagem baseada no Ubuntu que possui o Node.js instalado, chamado de node. Também especificamos que queremos uma versão mais recente do Node.js, usando a seguinte tag: `latest`

* **WORKDIR** aqui significa simplesmente que definimos um diretório de trabalho. Esta é uma maneira de configurar o que vai acontecer mais tarde, no próximo comando abaixo.

* **COPY** aqui estamos copiando os arquivos do diretório em que estamos, especificando um diretório através desse comando.

* **RUN** aqui executaremos um comando no termina. No nosso caso, estaremos instalando todas as bibliotecas que precisamos para construir a nossa aplicação em Node.js com Express.

* **EXPOSE** aqui significa que estaremos abrindo uma determinada porta. E é através dessa porta que nos comunicaremos com o nosso contêiner.

* **ENTRYPOINTS** esse é o lugar onde devemos declarar como iniciaremos a nossa aplicação. Os comandos precisam ser especificados como um array, por exemplo: ["node", "app.js"]. É como se executássemos o comando no terminal: 'node app.js'

## Criando uma Imagem

Para criarmos uma imagem, precisaremos seguir 2 passos, são elas:

* **Criando uma imagem:** com ajuda do arquivo 'Dockerfile' e o comando `docker build` criaremos uma imagem

* **Iniciando o contêiner:** agora que temos uma imagem, precisaremos criar um contêiner.

Vamos realizar esses passos agora! Abre a pasta do projeto `exemplo-2` e execute o seguinte comando: (**não se esqueçam de executar o Docker Desktop!!!**)

```bash
> docker build -t exemplo-2-node-docker/node:latest .
```

Notem que tem um ponto `(.)` no final. Ele precisa ser incluso para criar a nossa imagem! Pois ele instrui o Docker e informa onde o Dockerfile está localizado, no nosso caso no diretório em que vocês estão. Se você não tiver a imagem do sistema operacional, pedimos no comando `FROM` e nos levará para que seja baixado pelo Docker Hub e enfim, criará a imagem especificada no Dockerfile. O prompt de comando de vocês deverá parecer como no gif abaixo (dependendo da conexão da internet esse comando pode levar alguns minutos para ser concluído):

[![gif-3d3a08bc2e12687ca.gif](https://s3.gifyu.com/images/gif-3d3a08bc2e12687ca.gif)](https://gifyu.com/image/EJJm)

O que vemos no gif acima é como a imagem do SO `node:latest` está sendo trazido e baixado pelo Docker Hub. E em seguida, cada um dos nossos comandos está sendo executado como `WORKDIR`, `RUN` e assim por diante. O que vemos aqui é o Docker executando de maneira muito inteligente e armazenando em cache todas as camadas de arquivos diferentes após cada comando, para que ele seja mais rápido. No final, vemos a mensagem: `successfully built` indicando que a nossa imagem foi construída com sucesso! Vamos dar uma olhada e ver como foi criada a nossa imagem. Abre o prompt e digite o seguinte comando:

```bash
> docker images
```

[![Screen-Shot-07-05-19-at-08-08-PM.png](https://i.postimg.cc/fRRytsMP/Screen-Shot-07-05-19-at-08-08-PM.png)](https://postimg.cc/dLgwpzkR)

Se aparecer como a figura acima é porque foi criado com sucesso a imagem! 

## Criando um Contêiner

O próximo passo é pegar a nossa imagem criada e construir um contêiner dela. Um contêiner é essa peça isolada que executa a nossa aplicação dentro dele. Nós criamos um contêiner executando o comando: `docker run`. Abre o prompt novamente e execute o seguinte comando abaixo:

```bash
> docker run -p 8000:3000 exemplo-2-node-docker/node
```

Lembra que no arquivo `Dockerfile` definimos uma porta? E que já explicamos no artigo anterior que precisamos de uma porta interna mapeando para uma porta externa, na máquina host. Lembrem-se que esta é uma aplicação que queremos executar no browser. Por isso devemos mapear da seguinte maneira:

```bash
-p [porta externa]:[porta interna]
```

Observem no gif a execução do comando acima:

[![gif-4.gif](https://s3.gifyu.com/images/gif-4.gif)](https://gifyu.com/image/EltG)

Notem que no browser, devemos colocar a porta externa que no nosso caso é a porta: `8000`.

Porém, para parar de executar esse container precisamos executar alguns comandos:

1. Primeiro devemos listar os contêineres que estão executando

```bash
> docker container ls 
```

2. Depois copiar o **Id** do container e executar o seguinte comando:

```bash
> docker kill <Id do Container>
```

Vejam no gif abaixo:

[![gif-5.gif](https://s3.gifyu.com/images/gif-5.gif)](https://gifyu.com/image/El5b)

## Melhorando a Configuração com variáveis de ambiente

Show de bola! Já aprendemos a criar nossa primeira imagem do Docker com Node.js & Express, aprendemos como devemos executar um contêiner e enfim executamos a nossa aplicação usando o arquivo `DockerFile`. Porém, podemos melhorar a variável **PORT** no arquivo `DockerFile`. Pois da maneira que está, estará propenso a erros.

Para resolver esse problema, podemos incluir uma variável de ambiente. Com isso, vamos fazer duas alteraçõe:

* adicionar uma variável de ambiente ao **Dockerfile**
* ler a partir da variável de ambiente no arquivo **app.js**

### Adicionnado uma variável de ambiente

Para isso, precisamos usar o comando **ENV**. Abram o arquivo `DockerFile` e alteram conforme abaixo:

```bash
FROM node:latest

WORKDIR /app

COPY . .

ENV PORT=3000

RUN npm install

EXPOSE 3000

ENTRYPOINT ["node", "app.js"]
```

Vamos fazer mais uma alteração! Vamos atualizar agora a variável **EXPOSE**, daí então nos livraremos de valores estáticos e confiaremos em variáveis, da seguinte forma:

```bash
FROM node:latest

WORKDIR /app

COPY . .

ENV PORT=3000

RUN npm install

EXPOSE $PORT

ENTRYPOINT ["node", "app.js"]
```

Observem que, mudamos o comando **EXPOSE** para o **$PORT**. Um ponto importante a ser falado aqui: qualquer variável que usamos, precisa ser prefixada com um caractere `$`

```bash
> EXPOSE $PORT
```

## Lendo o valor da variável de ambiente no arquivo App.js

Podemos ler valores de variáveis de ambiente em Node.js da seguinte forma:

```bash
> process.env.PORT
```

Então, vamos atualizar o arquivo `app.js`, conforme abaixo:

```javascript
/**
 * Arquivo: app.js
 * Descrição: arquivo principal e responsável pela execução da aplicação.
 * Data: 05/07/2019
 * Author: Glaucia Lemos
 */

const express = require('express');

const app = express();

const port = process.env.PORT;

app.get('/', (req, res) => {
  res.status(200).send({
    success: 'true',
    message: 'Seja Bem-Vindo(a) ao mundo Docker!',
    version: '1.0.0',
  });
});

app.listen(port);
console.log(`Aplicação executando na porta..: ${port}`);

```

Observem que, quando fizemos a alteração no arquivo `app.js` ou no arquivo `Dockerfile`, precisamos reconstruir nossa imagem. Isso significa que precisamos executar o comando de compilação do docker novamente e, antes disso, precisamos derrubar nosso contêiner com o comandos: 

```bash
> docker stop
```

```bash
> docker rm
```

Vamos aprender um pouco mais sobre esses dois comandos nas próximas seções! 😉

## Gerenciando o contêiner

Ótimo! Você acabou de iniciar o seu contêiner e percebe que não terminar/fechar a execução no terminal. É aí que bate aquele pânico! 😳😳 

Nesse caso, você pode fazer o seguinte, abre uma outra janela do terminal e execute o seguinte comando abaixo:

```bash
> docker ps
```

Esse comando listará todos os contêineres que estão em execução na sua máquina. Você poderá ver o nome dos contêineres e os seus respectivos Id's, conforme a imagem abaixo:

[![Screen-Shot-08-03-19-at-04-41-PM.png](https://i.postimg.cc/C1SScXVN/Screen-Shot-08-03-19-at-04-41-PM.png)](https://postimg.cc/Y4n59X0G)

Como você pode perceber acima, temos a coluna **CONTAINER_ID** e a coluna **NAMES**. Ambos esses valores funcionarão para interromper nosso contêiner, porque é isso que precisamos fazer, assim:

```bash
> docker stop 3da
``` 

Nós optamos por usar a coluna **CONTAINER_ID** e só precisamos colocar os 3 primeiros dígitos, sem necessidade de incluir todo o **ID**. Depois de executar esse comando, percebam que após digitar `docker ps` não aparecerá mais nenhum contêiner e parará de executar a nossa imagem!

## Modo Daemon

Podemos fazer como fizemos acima e abrir um novo terminal separado. Porém, executar no **Modo Daemon**, que nesse caso é a melhor opção. Isso significa que executaremos o contêiner em segundo plano e todas as saídas dele não estarão visíveis. Para que isso aconteça, basta adicionar a flag -d. Vamos tentar fazer isso?! 

```bash
> docker run -d -p 8000:3000 exemplo-2-node-docker/node
```
[![Screen-Shot-08-03-19-at-04-56-PM.png](https://i.postimg.cc/BbvgBS7G/Screen-Shot-08-03-19-at-04-56-PM.png)](https://postimg.cc/bDKkzhj6)

Observem que agora temos o Id do Contêiner. Agora fica mais fácil parar de executar! Basta digitarmos os 3 primeiros dígitos do ID acima. Exemplo:

```bash
> docker stop 014 
```

## Docker kill vs Docker stop

Até agora temos usado o comando `docker stop` como forma de parar o contêiner. Porém, existe uma outra maneira de pararmos o contêiner, ou seja, executando o comando `docker kill`. Mas, qual é a diferença?

* **docker stop**: esse comando envia o sinal **SIGTERM** seguido por **SIGKILL** após um período de carência. Resumindo, essa é uma maneira de reduzir o contêiner de uma maneira mais elegante, o que significa que ele libera recursos e salva o estado.

* **docker kill**: esse comando envia **SIGKILL** imediatamente. Isso significa que a liberação de recursos ou a salvaguarda de estado podem não funcionar como planejado. Em desenvolvimento, não importa qual dos dois comandos está sendo usado, mas, em um cenário de produção é mais prudente confiar no comando `docker stop`

## Limpando os Contêineres

Em algum momento, durante o percurso do desenvolvimento de alguma aplicação, você acabará criando inúmeros contêineres. Então, para garantir de remover/limpar esses contêineres, bastam digitar o comando abaixo:

```bash
> docker rm <id-do-contêiner>
```

Exemplo:

```bash
> docker rm -f ef8
```

```bash
> docker ps
```

## Palavras Finais

Agora sim...aprendemos nesse artigo como criar uma aplicação Node.js & Express.js usando Docker de maneira prática e passo a passo. Aprendemos alguns comandos importantes do Docker e isso não ficará por aqui. Pois ainda teremos muitas coisas importantes para aprender, como por exemplo: como trabalhar com bancos de dados, volumes, como vincular contêineres e por que e como gerenciar vários contêineres, também conhecidos como orquestração.

Mas esta é uma série de artigos! E temos que parar em algum lugar ou este artigo será muito longo. Fiquem ligados e atentos para a próxima parte, onde falaremos sobre **Volumes** e **Bancos de Dados**.

## Palavras Finais

Hoje aprendemos na prática como é fácil criar uma aplicação Node.js & Express.js usando o Docker.

Caso queiram aprender e aprofundar mais os estudos em Docker, recomendo os cursos de Docker abaixo:

**[Curso Grátis - Criando um aplicativo Web em contêiner com o Docker](https://docs.microsoft.com/learn/modules/intro-to-containers/?WT.mc_id=devto-blog-gllemos)**

**[Vários Cursos Grátis de Docker](https://docs.microsoft.com/learn/browse/?term=docker&WT.mc_id=devto-blog-gllemos)**

E, para ficarem por dentro das últimas atualizações não deixem de me seguir no Twitter!

[![Twitter](https://code4coders.files.wordpress.com/2019/05/image-12.png)](https://twitter.com/glaucia_lemos86)

E desde já, agradeço novamente ao **Chris Noring** por ceder essa excelente série de artigos sobre Docker. E sigam ao Chris Noring também no twitter:

[![Screen-Shot-08-03-19-at-05-14-PM.png](https://i.postimg.cc/kGDD7GgG/Screen-Shot-08-03-19-at-05-14-PM.png)](https://postimg.cc/4HkX5JtR)

Até a próxima pessoal!