Introdução
Docker é uma plataforma de contêineres que consiste em um ambiente padronizado, leve, virtualizado, portátil e definido por software. Ele permite que o software seja executado de forma isolada de outros softwares em execução na máquina host física. O Docker é um componente definidor do aspecto de Desenvolvimento e Integração Contínuos do Desenvolvimento de Software. Ele oferece uma alternativa leve às máquinas virtuais e permite que os desenvolvedores desfrutem de arquiteturas de aplicativos distribuídos. Para uma visão geral detalhada do ecossistema Docker, confira este artigo.
O processo de construção de uma aplicação com o Docker começa com o desenvolvedor criando uma imagem para a sua aplicação. Em seguida, a imagem será implantada dentro de um contêiner. A imagem contém componentes definidores de uma aplicação, tais como o código da aplicação, bibliotecas, arquivos de configuração, variáveis de ambiente e o ambiente de execução. A imagem padroniza o ambiente dentro de um contêiner, conferindo à conteinerização as características de portabilidade. Node.js é um ambiente de execução JavaScript backend de código aberto e multiplataforma que pode executar código JavaScript fora de um navegador web. Ele é construído sobre o V8 JavaScript Engine. Express.js é um framework JavaScript de backend minimalista que roda sobre o Node.js.
Neste tutorial, criaremos uma imagem para um site que roda no framework Express. Usaremos o Bootstrap, que é uma biblioteca frontend, para melhorar a aparência do frontend. Depois de criar a imagem, construiremos um contêiner e o enviaremos para o Docker Hub. O Docker Hub permite que os desenvolvedores hospedem aplicações conteinerizadas para facilitar a implantação em qualquer ambiente Docker. Assim que seu contêiner estiver hospedado no Docker Hub, nós o baixaremos (pull) e construiremos outra imagem que realmente servirá nosso site.
Pré-requisitos
Este será um tutorial prático. Você deve criar um ambiente que permita acompanhar os passos.
- Você deve ter uma instalação do Ubuntu 20.04 como seu ambiente operacional inicial e criar um usuário não-root com privilégios sudo. Faça login com o usuário não-root e prossiga com as etapas a seguir.
- Você precisa instalar o Docker. Siga as etapas 1, 2, 3 e 4 do nosso tutorial sobre como instalar e operar o Docker no Ubuntu. Isso deve funcionar para qualquer distribuição Ubuntu.
- Crie uma conta no Docker Hub se ainda não tiver uma. Você pode seguir este link para o Guia de início rápido do Docker Hub.
- Instale o Node.js e o NPM. O NPM é um gerenciador de pacotes JavaScript. Você pode seguir estas instruções sobre como instalar o Node e o npm.
Passo 1: Configurar as Dependências da Aplicação
Você precisa criar o código-fonte da sua aplicação antes de poder criar a imagem. O código-fonte da aplicação inclui código, conteúdo estático e dependências que serão copiados para o contêiner. Comece criando um diretório para o seu projeto no diretório home do usuário não-root. Nós o chamaremos de node_express, mas você está livre para usar o nome de diretório que preferir:
|
1 |
mkdir node_express |
Em seguida, entre neste diretório:
|
1 |
cd node_express |
Este será o diretório raiz da sua aplicação. Uma aplicação node.js espera um arquivo package.json na pasta raiz. O Npm usa este arquivo para determinar de quais dependências sua aplicação precisa. Insira o seguinte comando para criar este arquivo:
|
1 |
nano package.json |
Depois disso, adicione o seguinte trecho de código ao arquivo. Você pode atualizar o nome, autor, descrição e arquivo de ponto de entrada como desejar:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
{ "name": "node-express-docker-image", "version": "1.0.0", "description": "Nodejs Express Docker Image Example", "author": "hackins", "main": "index.js", "license": "ISC", "keywords": [ "nodejs", "express", "bootstrap" ], "dependencies": { "express": "^4.17.1" } } |
Como você pode ver, este arquivo especifica o nome do projeto, versão, autor e licença sob a qual o código da aplicação será compartilhado. É recomendável usar um nome curto e descritivo para o seu projeto para evitar duplicatas no registro npm. Nós especificamos a licença ISC para o projeto, que permite a cópia, modificação ou distribuição gratuita do código da aplicação.
Mais importante ainda, você deve observar as seguintes diretivas no arquivo:
- “
main”: esta diretiva especifica o ponto de entrada da aplicação, que definimos como index.js. Criaremos este arquivo em breve. - “
dependencies”: esta diretiva especifica as dependências da aplicação que serão obtidas do registro npm quando executarmos o comandonpm, no nosso caso, queremos o Express versão 4.17.1 e superior.
Agora você pode salvar o arquivo pressionando Ctrl + O. Em seguida, feche o arquivo pressionando Ctrl + X. Depois, instalaremos as dependências executando o seguinte comando:
|
1 |
npm install |
O comando instala as dependências da aplicação especificadas no arquivo package.json dentro dos diretórios node_modules. Eles foram criados automaticamente quando você executou o comando pela primeira vez. Com as dependências da nossa aplicação instaladas, agora você pode começar a adicionar o código da aplicação.
Passo 2: Adicionando os Arquivos de Código da Sua Aplicação
Criaremos um site de receitas básico, cortesia do allrecipes. O ponto de entrada principal para a aplicação é o arquivo index.js. Adicionaremos um diretório views que conterá as várias páginas e recursos estáticos do projeto. O site terá uma página de destino que conterá informações introdutórias e links para algumas receitas.
O código da nossa página de destino será colocado no arquivo home.html. Primeiro, crie o arquivo index.js inserindo o seguinte comando:
|
1 |
nano index.js |
Adicione o seguinte código, que importa e cria uma aplicação Express. Ele também especifica o objeto Router, o diretório base e a porta na qual este aplicativo será servido:
|
1 2 3 4 5 6 |
const express = require('express'); const app = express(); const router = express.Router(); const path = __dirname + '/views/'; const port = 8090; |
require é uma função JavaScript que carrega um módulo. Neste caso, estamos carregando o módulo express. Em seguida, usaremos o módulo importado para criar os objetos express e router. O objeto router executa as funções de roteamento do aplicativo respondendo a chamadas de métodos HTTP que serão adicionadas a este objeto ao longo do tutorial.
Também definimos path e port. A constante path define o diretório base para o código. No nosso caso, é o subdiretório views dentro do diretório raiz do projeto. O port especifica a porta na qual o aplicativo express deve escutar, no nosso exemplo, definimos como 8090.
Assim que tivermos as constantes, podemos especificar algumas rotas para a aplicação usando o objeto router. Adicione o seguinte código ao arquivo index.js para especificar as rotas:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
router.use(function (req,res,next) { console.log('/' + req.method); next(); }); router.get('/', function(req,res){ res.sendFile(path + 'home.html'); }); router.get('/lasagna', function(req,res){ res.sendFile(path + 'lasagna.html'); }); router.get('/guacamole', function(req,res){ res.sendFile(path + 'guacamole.html'); }); router.get('/banana-bread', function(req,res){ res.sendFile(path + 'banana_bread.html'); }); |
Você pode adicionar middleware às rotas usando a função router.use. Neste caso, adicionamos uma função que registra as requisições do roteador antes de passá-las para as rotas da aplicação. Uma requisição GET para a base da aplicação retornará um home.html página. Em seguida, adicionamos páginas para três receitas que também serão recuperadas usando a requisição GET para a página de receita específica.
Finalmente, adicione o seguinte código para montar o router middleware e os ativos estáticos da aplicação. Além disso, diga à aplicação express para escutar na porta 8090:
|
1 2 3 4 5 6 |
app.use(express.static(path)); app.use('/', router); app.listen(port, function () { console.log('Nodejs Express Example App listening on port ' + port) }) |
Seu arquivo index.js completo deve se parecer com isto:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
const express = require('express'); const app = express(); const router = express.Router(); const path = __dirname + '/views/'; const port = 8090; router.use(function (req,res,next) { console.log('/' + req.method); next(); }); router.get('/', function(req,res){ res.sendFile(path + 'home.html'); }); router.get('/lasagna', function(req,res){ res.sendFile(path + 'lasagna.html'); }); router.get('/guacamole', function(req,res){ res.sendFile(path + 'guacamole.html'); }); router.get('/banana-bread', function(req,res){ res.sendFile(path + 'banana_bread.html'); }); app.use(express.static(path)); app.use('/', router); app.listen(port, function () { console.log('Nodejs Express Example App listening on port ' + port) }) |
Você pode salvar e fechar o arquivo agora. O próximo passo é adicionar as páginas web estáticas ao diretório views. Comece inserindo o seguinte comando para criar o diretório:
|
1 |
mkdir views |
Insira o seguinte comando para abrir o arquivo de página de destino home.html:
|
1 |
nano views/home.html |
Adicione o seguinte código ao arquivo. O código importa o Bootstrap e oferece aos visitantes do site algumas informações sobre o que é o site:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
<!DOCTYPE html> <html lang="pt"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="description" content=""> <meta name="author" content="Mark Otto, Jacob Thornton e colaboradores do Bootstrap"> <meta name="generator" content="Hugo 0.80.0"> <title>Receitas Incríveis</title> <!-- CSS básico do Bootstrap --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"> <link href="css/custom.css" rel="stylesheet"> </head> <body> <nav class="navbar navbar-dark bg-dark navbar-static-top navbar-expand-md"> <div class="container"> <button type="button" class="navbar-toggler collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Alternar navegação</span> </button> <a class="navbar-brand" href="#">Receitas Incríveis</a> <div class="collapse navbar-collapse justify-content-center" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav justify-content-center"> <li class="active nav-item"> <a href="/" class="nav-link">Início</a> </li> <li class="nav-item"> <a href="/lasagna" class="nav-link">Lasanha</a> </li> <li class="nav-item"> <a href="/guacamole" class="nav-link">Guacamole</a> </li> <li class="nav-item"> <a href="/banana-bread" class="nav-link">Pão de Banana</a> </li> </ul> </div> </div> </nav> <main> <section class="py-5 text-center container"> <div class="row py-lg-5"> <div class="col-lg-6 col-md-8 mx-auto"> <h1 class="fw-light">Receita Incrível</h1> <p class="lead text-muted"> Encontre e compartilhe inspiração culinária para o dia a dia a partir destas receitas incríveis. Descubra receitas, cozinheiros, vídeos e tutoriais com base na comida que você ama e nos amigos que você segue. <br /> <em>(Nada sério, isso é apenas para o nosso aplicativo de demonstração de imagem node-express-docker)</em> </p> </div> </div> </section> </main> </body> </html> |
Além de importar o Bootstrap, a página também adiciona um menu de navegação básico para nos ajudar a navegar pelas páginas e voltar para a página inicial. Também adicionamos uma linha para importar nosso arquivo CSS personalizado:
|
1 |
<link href="css/custom.css" rel="stylesheet"> |
Usaremos esse arquivo para adicionar estilos personalizados ao aplicativo mais tarde. Agora, vamos criar as três páginas para as receitas. Começamos primeiro criando a página da lasanha. Abra o arquivo com o editor nano usando o seguinte comando:
|
1 |
nano views/lasagna.html |
No arquivo aberto, adicione o seguinte código. Este arquivo importará o Bootstrap, o arquivo custom.css, especificará um menu de navegação e oferecerá algumas informações sobre a receita de Lasanha:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
<!DOCTYPE html> <html lang="pt"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="description" content=""> <meta name="author" content="Mark Otto, Jacob Thornton e colaboradores do Bootstrap"> <meta name="generator" content="Hugo 0.80.0"> <title>Receita de Lasanha</title> <!-- CSS principal do Bootstrap --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"> <link href="css/custom.css" rel="stylesheet"> </head> <body> <nav class="navbar navbar-dark bg-dark navbar-static-top navbar-expand-md"> <div class="container"> <button type="button" class="navbar-toggler collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Alternar navegação</span> </button> <a class="navbar-brand" href="#">Receitas Incríveis</a> <div class="collapse navbar-collapse justify-content-center" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav justify-content-center"> <li class="active nav-item"> <a href="/" class="nav-link">Início</a> </li> <li class="nav-item"> <a href="/lasagna" class="nav-link">Lasanha</a> </li> <li class="nav-item"> <a href="/guacamole" class="nav-link">Guacamole</a> </li> <li class="nav-item"> <a href="/banana-bread" class="nav-link">Pão de Banana</a> </li> </ul> </div> </div> </nav> <main> <section class="py-5 text-center container bg-light"> <div class="row py-lg-5"> <div class="col-lg-6 col-md-8 mx-auto"> <h1 class="fw-light">A Melhor Receita de Lasanha</h1> <p class="lead text-muted"> Esta é a melhor lasanha que você vai fazer. <br /> <em>(Nada sério, isso é apenas para o nosso aplicativo de demonstração de imagem node-express-docker)</em> </p> <h3>Ingredientes</h3> <ul class="list-group"> <li class="list-group-item">1 libra de linguiça italiana doce</li> <li class="list-group-item">¾ de libra de carne moída magra</li> <li class="list-group-item">½ xícara de cebola picada</li> <li class="list-group-item">2 dentes de alho amassados</li> <li class="list-group-item">1 lata (28 onças) de tomates esmagados</li> <li class="list-group-item">2 latas (6 onças) de extrato de tomate</li> <li class="list-group-item">2 latas (6,5 onças) de molho de tomate enlatado</li> <li class="list-group-item">½ xícara de água</li> <li class="list-group-item">2 colheres de sopa de açúcar branco</li> <li class="list-group-item">1 ½ colheres de chá de folhas de manjericão seco</li> <li class="list-group-item">½ colher de chá de sementes de erva-doce</li> <li class="list-group-item">1 colher de chá de tempero italiano</li> <li class="list-group-item">1 ½ colheres de chá de sal, dividido, ou a gosto</li> <li class="list-group-item">¼ colher de chá de pimenta-do-reino moída</li> <li class="list-group-item">4 colheres de sopa de salsa fresca picada</li> <li class="list-group-item">12 folhas de massa para lasanha</li> <li class="list-group-item">16 onças de queijo ricota</li> <li class="list-group-item">1 ovo</li> <li class="list-group-item">¾ de libra de queijo muçarela fatiado</li> <li class="list-group-item">¾ xícara de queijo parmesão ralado</li> </ul> </div> </div> </section> </main> </body> </html> |
Vamos seguir o mesmo processo para criar um arquivo para a página da receita de guacamole. Abra o arquivo com o nano executando o seguinte comando:
|
1 |
nano views/guacamole.html |
Em seguida, adicione este código ao arquivo:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
<!DOCTYPE html> <html lang="pt"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="description" content=""> <meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors"> <meta name="generator" content="Hugo 0.80.0"> <title>Receita de Guacamole</title> <!-- CSS principal do Bootstrap --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"> <link href="css/custom.css" rel="stylesheet"> </head> <body> <nav class="navbar navbar-dark bg-dark navbar-static-top navbar-expand-md"> <div class="container"> <button type="button" class="navbar-toggler collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Alternar navegação</span> </button> <a class="navbar-brand" href="#">Receitas Incríveis</a> <div class="collapse navbar-collapse justify-content-center" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav justify-content-center"> <li class="active nav-item"> <a href="/" class="nav-link">Início</a> </li> <li class="nav-item"> <a href="/lasagna" class="nav-link">Lasanha</a> </li> <li class="nav-item"> <a href="/guacamole" class="nav-link">Guacamole</a> </li> <li class="nav-item"> <a href="/banana-bread" class="nav-link">Bolo de Banana</a> </li> </ul> </div> </div> </nav> <main> <section class="py-5 text-center container bg-light"> <div class="row py-lg-5"> <div class="col-lg-6 col-md-8 mx-auto"> <h1 class="fw-light">A Melhor Receita de Guacamole</h1> <p class="lead text-muted"> Você pode fazer esta salada de abacate cremosa ou com pedaços, dependendo do seu gosto. <br /> <em>(Nada sério, isso é apenas para o nosso aplicativo de demonstração de imagem node-express-docker)</em> </p> <h3>Ingredientes</h3> <ul class="list-group"> <li class="list-group-item">3 abacates - descascados, sem caroço e amassados</li> <li class="list-group-item">1 limão, espremido</li> <li class="list-group-item">1 colher de chá de sal</li> <li class="list-group-item">½ xícara de cebola picada</li> <li class="list-group-item">3 colheres de sopa de coentro fresco picado</li> <li class="list-group-item">2 tomates roma (italiano), picados</li> <li class="list-group-item">1 colher de chá de alho picado</li> <li class="list-group-item">1 pitada de pimenta caiena em pó (Opcional)</li> </ul> </div> </div> </section> </main> </body> </html> |
Finalmente, vamos criar o arquivo banana_bread.html inserindo o comando:
|
1 |
nano views/banana_bread.html |
Em seguida, adicione o seguinte código HTML ao arquivo:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
<!DOCTYPE html> <html lang="pt"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="description" content=""> <meta name="author" content="Mark Otto, Jacob Thornton e colaboradores do Bootstrap"> <meta name="generator" content="Hugo 0.80.0"> <title>Receita de Pão de Banana</title> <!-- CSS principal do Bootstrap --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"> <link href="css/custom.css" rel="stylesheet"> </head> <body> <nav class="navbar navbar-dark bg-dark navbar-static-top navbar-expand-md"> <div class="container"> <button type="button" class="navbar-toggler collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Alternar navegação</span> </button> <a class="navbar-brand" href="#">Receitas Incríveis</a> <div class="collapse navbar-collapse justify-content-center" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav justify-content-center"> <li class="active nav-item"> <a href="/" class="nav-link">Início</a> </li> <li class="nav-item"> <a href="/lasagna" class="nav-link">Lasanha</a> </li> <li class="nav-item"> <a href="/guacamole" class="nav-link">Guacamole</a> </li> <li class="nav-item"> <a href="/banana-bread" class="nav-link">Pão de Banana</a> </li> </ul> </div> </div> </nav> <main> <section class="py-5 text-center container bg-light"> <div class="row py-lg-5"> <div class="col-lg-6 col-md-8 mx-auto"> <h1 class="fw-light">A Melhor Receita de Pão de Banana</h1> <p class="lead text-muted"> Por que comprometer o sabor da banana? Este pão de banana é úmido e delicioso, com muito sabor de banana! Amigos e familiares adoram a minha receita e dizem que é de longe a melhor! Fica maravilhoso torrado!! Aproveite! <br /> <em>(Nada sério, isto é apenas para a nossa aplicação de demonstração de imagem node-express-docker)</em> </p> <h3>Ingredientes</h3> <ul class="list-group"> <li class="list-group-item">2 xícaras de farinha de trigo</li> <li class="list-group-item">1 colher de chá de bicarbonato de sódio</li> <li class="list-group-item">¼ de colher de chá de sal</li> <li class="list-group-item">½ xícara de manteiga</li> <li class="list-group-item">¾ de xícara de açúcar mascavo</li> <li class="list-group-item">2 ovos batidos</li> <li class="list-group-item">2⅓ xícaras de bananas maduras amassadas</li> </ul> </div> </div> </section> </main> </body> </html> |
Agora, criamos todas as páginas. Se você se lembra, devemos adicionar o arquivo css/custom.css. Insira o seguinte comando para criar o diretório:
|
1 |
mkdir views/css |
Em seguida, crie e abra o arquivo no editor nano com o comando:
|
1 |
nano views/css/custom.css |
Você pode adicionar mais códigos CSS para estilizar seu site como desejar. Por brevidade, vamos adicionar o seguinte trecho de código ao arquivo:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
.bd-placeholder-img { font-size: 1.125rem; text-anchor: middle; -webkit-user-select: none; -moz-user-select: none; user-select: none; } @media (min-width: 768px) { .bd-placeholder-img-lg { font-size: 3.5rem; } } |
Salve e feche o arquivo quando terminar.
Você pode iniciar a aplicação, pois agora temos o código-fonte da aplicação e as dependências do projeto instaladas.
We had set the app to listen on a port 8090, execute o seguinte comando para instruir o firewall a permitir o tráfego por esta porta. Se você especificou uma porta diferente, substitua o número da porta no comando:
|
1 |
sudo ufw allow 8090 |
Agora, você pode iniciar a aplicação. Mas primeiro, certifique-se de que está no diretório raiz do projeto executando o seguinte comando:
|
1 |
cd ~/node_express |
Inicie a aplicação com node index.js. Se você especificou um ponto de entrada diferente, substitua-o pelo seu ponto de entrada:
|
1 |
node index.js |
Se você navegar no seu navegador para http://your_public_server_ip:8090, você verá a página inicial de Receitas conforme definido:
Você pode ver os links para as várias receitas na navegação. Vamos clicar em alguns. Abaixo temos a página da receita de Lasanha:
E aqui temos a página da receita de Guacamole:
Até este ponto, você criou sua aplicação e testou que ela está funcionando como esperado. Você pode sair do servidor pressionando Ctrl + C e prosseguir para a criação do Dockerfile. Os Dockerfiles ajudam na escalabilidade, tornando possível recriar a instância de uma aplicação quando necessário.
Step 3: Creating the Dockerfile
O Docker lê as instruções especificadas em um Dockerfile ao construir imagens. Ele especifica o ambiente de execução de uma aplicação. Assim, ajuda os desenvolvedores a evitar discrepâncias com dependências ou alteração de versões de tempo de execução. Insira o seguinte comando para criar o Dockerfile:
|
1 |
nano Dockerfile |
Uma imagem Docker é criada usando várias camadas de imagens que se baseiam umas nas outras. Você começa adicionando uma imagem base para formar o ponto de partida para o aplicativo.
Como a aplicação espera ser executada em um ambiente node.js, começaremos adicionando a imagem node:10-alpine para o node.js. Atualmente, enquanto escrevemos este tutorial, esta é a versão LTS recomendada do Node.js. Escolhemos esta imagem específica porque ela é derivada do projeto Alpine Linux. Assim, ela ajudará a manter o tamanho da nossa imagem no mínimo. Existem várias variantes de imagem na página de imagens Node do Docker Hub que você pode escolher dependendo das suas necessidades.
Adicione o seguinte código para definir a imagem base da aplicação usando a diretiva FROM:
|
1 |
FROM node:10-alpine |
Esta imagem inclui o Node.js e o npm. Todo Dockerfile deve começar com uma diretiva FROM. A imagem Docker node vem com um usuário node não-root por padrão que você pode usar para executar o container da sua aplicação como root. A segurança do Docker recomenda não executar os containers como root e restringir os privilégios apenas àqueles necessários para executar seus recursos.
Nesse caso, usaremos o diretório home do usuário node como o diretório de trabalho para a aplicação, bem como o usuário dentro do container. Você pode verificar este guia de melhores práticas de imagem Node do Docker para mais informações.
Criaremos o subdiretório node_modules dentro de /home/node junto com o diretório app para ajudar a simplificar as permissões do código da aplicação. A criação desses diretórios garante que eles tenham as permissões corretas quando executarmos o comando npm install localmente dentro dos containers. Depois de criar os diretórios, você deve definir a propriedade deles para o usuário node . Faremos isso dentro do Dockerfile adicionando a seguinte linha:
|
1 |
mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app |
Em seguida, você definirá o diretório de trabalho adicionando a seguinte linha:
|
1 |
WORKDIR /home/node/app |
É uma boa ideia sempre definir o WORKDIR para que o Docker não precise criar um por padrão.
Adicione a seguinte linha para copiar os arquivos package.json e package-lock.json:
|
1 |
COPY package*.json ./ |
Recomenda-se adicionar a instrução COPY antes de executar npm install ou de copiar o código-fonte da aplicação. Isso permite que você aproveite o mecanismo de cache do Docker. Durante o processo de build, o Docker verifica se possui uma camada em cache para cada instrução. Isso significa que, se você não alterou o arquivo package.json, o Docker usará a camada de imagem existente e evitará reinstalar os módulos node, resultando em processos de build mais rápidos.
Antes de executar npm install, adicione a seguinte linha para alternar o usuário para node para garantir que todos os arquivos da aplicação e o diretório node_modules sejam de propriedade do usuário node não-root:
|
1 |
USER node |
Nosso container agora está pronto para executar o comando npm install. Adicione a seguinte linha ao Dockerfile:
|
1 |
RUN npm install |
Depois que o node_modules tiver sido instalado, adicione a seguinte linha que dirá ao Docker para copiar o código da aplicação para o diretório da aplicação no container com as permissões e propriedade corretas, ou seja, o usuário node não-root:
|
1 |
COPY --chown=node:node . . |
O último passo é expor a porta 8090 no container, como definimos em nosso arquivo de entrada index.js:
|
1 2 |
EXPOSE 8090 CMD [ "node", "index.js" ] |
EXPOSE define quais portas no container estarão abertas em tempo de execução. CMD executa o comando para iniciar a aplicação, neste caso, node index.js.
Você deve ter apenas um comando CMD no Dockerfile, pois apenas o último entra em vigor. Por favor, verifique a documentação de referência do Dockerfile para obter uma lista de coisas que você pode fazer com o Dockerfile.
Seu Dockerfile completo deve se parecer com isto:
|
1 2 3 4 5 6 7 8 9 |
FROM node:10-alpine RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app WORKDIR /home/node/app COPY package*.json ./ USER node RUN npm install COPY --chown=node:node . . EXPOSE 8090 CMD [ "node", "index.js" ] |
Agora você pode salvar e fechar o arquivo.
A próxima coisa que você fará é adicionar o arquivo .dockerignore. Assim como o arquivo .gitignore, o .dockerignore especifica quais arquivos e diretórios dentro do diretório do projeto não devem ser copiados para o contêiner.
Abra o arquivo com o editor nano:
|
1 |
nano .dockerignore |
Adicione as seguintes linhas dentro do arquivo:
|
1 2 3 4 |
node_modules npm-debug.log Dockerfile .dockerignore |
Se estiver trabalhando com um repositório git, você também deve adicionar o diretório .git e o arquivo .gitignore. Salve e feche o arquivo.
Se tudo correu bem, é hora de compilar a imagem da aplicação usando o comando docker build. Você pode adicionar a flag –t ao comando docker build para marcar a imagem com um nome fácil de lembrar, em vez da string aleatória que o docker define por padrão. Também enviaremos a imagem para o Docker Hub, por isso é melhor incluir seu nome de usuário do Docker Hub na tag.
Usaremos nodejs-express-image como o nome da tag. Você é livre para escolher o nome de tag que preferir. Aqui está o comando para compilar a imagem:
|
1 |
sudo docker build -t your_dockerhub_username/nodejs-express-image . |
Lembre-se de substituir your_dockerhub_username pelo seu nome de usuário real do Docker Hub. O . (ponto) no final especifica que o contexto de compilação é o diretório atual.
O processo de compilação leva um ou dois minutos. Assim que terminar, insira o comando para verificar suas imagens:
|
1 |
sudo docker images |
Você deve ver algo como isto:
Lembre-se de que substituímos your_dockerhub_username por um nome de usuário real.
Após confirmar que sua imagem foi compilada, você pode criar um contêiner com a imagem usando docker run. As seguintes flags serão incluídas:
-p: publica a porta no contêiner e a mapeia para uma porta no sistema host. Usaremos a porta 80 no sistema host para fins de demonstração. No entanto, se você tiver outro processo em execução nessa porta, sinta-se à vontade para modificar isso conforme necessário. Saiba mais sobre vinculação de portas na documentação do Docker.-d: para o modo em segundo plano (detached). Permite que o contêiner continue em execução em segundo plano.--name: você pode usar isso para definir um nome fácil de lembrar em vez de deixar o Docker atribuir uma string aleatória.
O comando para criar o contêiner é o seguinte. Substitua seu nome de usuário do Docker Hub adequadamente:
|
1 |
sudo docker run --name nodejs-express-image -p 80:8090 -d your_dockerhub_username/nodejs-express-image |
Aguarde o contêiner ser criado e começar a rodar. Você pode usar este comando para inspecionar todos os contêineres em execução:
|
1 |
sudo docker ps |
Você deve ver uma saída semelhante à seguinte:
Como visto na saída, o contêiner agora está em execução. Você pode visualizá-lo no navegador se visitar o endereço IP público do seu servidor sem a porta no navegador. Sua página inicial será carregada:
Você implantou com sucesso um site estático Node Express com o Docker. Vamos ver como podemos enviar essa imagem para o Docker Hub para uso futuro e fins de dimensionamento.
Passo 4: Trabalhando com Repositórios de Imagens Docker
Você pode enviar suas imagens para registros de imagens como o Docker Hub e salvá-las para uso futuro, compartilhá-las com outros desenvolvedores ou permitir o dimensionamento de seus contêineres. Podemos enviar a imagem que criamos para o Docker Hub e usá-la para recriar um contêiner.
Use o seguinte comando para fazer login na sua conta do Docker Hub. Substitua-o pelo seu nome de usuário real do Docker Hub:
|
1 |
sudo docker login -u your_dockerhub_username |
Insira sua senha quando solicitado. Uma vez logado, um ~/.docker/config.json arquivo é criado no diretório home do seu usuário contendo suas credenciais do Docker Hub.
Com isso configurado, insira o seguinte comando para enviar a imagem para o Docker Hub, especificando a tag que você definiu ao compilar a imagem anteriormente:
|
1 |
sudo docker push your_dockerhub_username/nodejs-express-image |
Este comando envia a imagem docker para a sua conta do Docker Hub. Se você visitar sua conta, poderá ver sua imagem enviada recentemente:
Podemos testar a utilidade do repositório de imagens destruindo o contêiner de aplicativo atual e reconstruindo-o usando a imagem no repositório.
Liste seus contêineres atuais inserindo o comando:
|
1 |
sudo docker ps |
Você deve ver uma saída semelhante a esta:
Observe o CONTAINER ID listado em sua saída, copie-o e use-o para parar seu contêiner com o comando, substituindo o ID pelo seu:
|
1 |
sudo docker stop 1bb2d65279bb |
Insira o seguinte comando para listar todas as imagens docker disponíveis em seu sistema:
|
1 |
sudo docker images –a |
A saída mostrará o nome da sua imagem, a imagem do node.js e outras imagens do processo de compilação.
Insira o seguinte comando para remover as imagens, incluindo imagens não utilizadas ou pendentes:
|
1 |
sudo docker system prune |
Digite y para confirmar. Isso remove os contêineres e imagens parados. Se você listá-los, verá uma lista vazia na saída:
Agora, você removeu tanto o contêiner que executa o aplicativo quanto a própria imagem. Saiba mais sobre como remover contêineres, imagens e volumes do Docker seguindo nosso tutorial.
Podemos agora recriar todo o processo primeiro baixando a imagem do Docker Hub com o seguinte comando. Substitua seu nome de usuário do Docker Hub adequadamente:
|
1 |
sudo docker pull your_dockerhub_username/nodejs-express-image |
Liste suas imagens do Docker novamente com o comando:
|
1 |
sudo docker images |
Você deve ver a imagem na saída:
Agora você pode reconstruir seu contêiner usando o comando do Passo 3. Claro, substitua seu nome de usuário do Docker Hub onde apropriado:
|
1 |
sudo docker run --name nodejs-express-image -p 80:8090 -d your_dockerhub_username/nodejs-express-image |
Liste seus contêineres para confirmar que ele foi reconstruído:
|
1 |
sudo docker ps |
Você deve ver uma saída semelhante:
No seu navegador, navegue até o endereço IP público do seu servidor e você deverá ver seu aplicativo em execução.
Conclusão
Se você acompanhou o tutorial até este ponto, agora você tem um site estático feito com Express e Bootstrap, e implantado com o Docker. Você usou os arquivos do site estático para compilar uma imagem Docker e usou a imagem para criar um contêiner. Em seguida, você enviou a imagem para um registro de imagens Docker, Docker Hub, tornando-a disponível para uso futuro ou dimensionamento. Para testar o uso do registro de imagens, você destruiu as imagens e contêineres, baixou as imagens do registro e reconstruiu os contêineres.
Este tutorial explicou como implantar um aplicativo Node.js. Se você quiser aprender como usar uma pilha de desenvolvimento web diferente, temos um tutorial sobre Como implantar um aplicativo Laravel com o Docker Compose no Nginx.
Para mais recursos sobre a utilização do Docker, confira os seguintes tutoriais:
- Como instalar e configurar o Docker Compose no Ubuntu 20.04
- Como compartilhar dados entre um contêiner Docker e um host
- Instalando e Configurando o Docker no CentOS 7
Feliz Computação!











Comentários
Nenhum comentário ainda. Seja o primeiro.