Voltar ao blog

Como implantar um aplicativo Node.js (Express.js) com Docker no Ubuntu 20.04

Como implantar um aplicativo Node.js (Express.js) com Docker no Ubuntu 20.04

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.

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:

Em seguida, entre neste diretório:

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:

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:

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 comando npm, 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:

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:

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:

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:

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:

Seu arquivo index.js completo deve se parecer com isto:

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:

Insira o seguinte comando para abrir o arquivo de página de destino 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:

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:

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:

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:

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:

Em seguida, adicione este código ao arquivo:

Finalmente, vamos criar o arquivo banana_bread.html inserindo o comando:

Em seguida, adicione o seguinte código HTML ao arquivo:

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:

Em seguida, crie e abra o arquivo no editor nano com o comando:

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:

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:

Agora, você pode iniciar a aplicação. Mas primeiro, certifique-se de que está no diretório raiz do projeto executando o seguinte comando:

Inicie a aplicação com node index.js. Se você especificou um ponto de entrada diferente, substitua-o pelo seu ponto de entrada:

Se você navegar no seu navegador para http://your_public_server_ip:8090, você verá a página inicial de Receitas conforme definido:

Recipes

 

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:

Node.js app install on Ubuntu 1

E aqui temos a página da receita de Guacamole:

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:

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:

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:

Em seguida, você definirá o diretório de trabalho adicionando a seguinte linha:

É 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:

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:

Nosso container agora está pronto para executar o comando npm install. Adicione a seguinte linha ao Dockerfile:

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:

O último passo é expor a porta 8090 no container, como definimos em nosso arquivo de entrada 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:

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:

Adicione as seguintes linhas dentro do arquivo:

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:

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:

Você deve ver algo como isto:

sudo docker images

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:

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:

Você deve ver uma saída semelhante à seguinte:

Node.js app install on Ubuntu 3

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:

awesome recipe

 

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:

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:

Este comando envia a imagem docker para a sua conta do Docker Hub. Se você visitar sua conta, poderá ver sua imagem enviada recentemente:

Docker Hub

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:

Você deve ver uma saída semelhante a esta:

Docker Hub

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:

Insira o seguinte comando para listar todas as imagens docker disponíveis em seu sistema:

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:

Digite y para confirmar. Isso remove os contêineres e imagens parados. Se você listá-los, verá uma lista vazia na saída:

output

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:

Liste suas imagens do Docker novamente com o comando:

Você deve ver a imagem na saída:

sudo docker

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:

Liste seus contêineres para confirmar que ele foi reconstruído:

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:

Feliz Computação!

author

Hark Labs

Autor · CloudSigma

Preslav Dobrev é um designer criativo na CloudSigma, focado na construção de uma identidade empresarial consistente por meio de canais de marketing tradicionais e inovadores. Ele é hábil em combinar a visão artística com o marketing estratégico para criar narrativas de marca impactantes.

Comentários

Nenhum comentário ainda. Seja o primeiro.