Automatizando o deploy com GitHub Actions e Azure AppService

Como configurar e automatizar o deploy de uma aplicação Node.js na Azure App Service utilizando o GitHub Actions.

Ilustração da sequência de etapas: código fonte, push, GitHub, deploy, App Services da Azure
Configurando o deploy automático do GitHub para a Azure

Para alguns desenvolvedores, talvez com maior tempo de atuação, o processo de deploy foi algo que fazia parte da lista de tarefas. Em algum momento os desenvolvedores tinham que fechar o pacote e utilizar por exemplo, um FTP para efetuar o upload dos arquivos para o servidor.

Efetuar deploys manualmente pode não ser um bom processo, já que é suscetível a erros operacionais, falhas humanas. Por sorte, atualmente as ferramentas DevOps estão bem evoluídas e repletas de integrações que podem facilitar nossa vida, incluíndo a automação no processo de deploy.

Neste texto será abordado como configurar o deploy automático no Azure App Service a partir de um repositório no GitHub.

Criando o repositório

O primeiro passo para a construção do cenário de estudo é criar o repositório no GitHub, o processo é bem simples e direto:

Formulário para criar um novo repositório no GitHub
Criando um novo repositório no GitHub

Clonando o repositório

Com o repositório criado, efetuaremos o clone através do seguinte comando:

$ git clone https://github.com/marcelovismari/consolelog-github-actions-appservice-azure.git

Cloning into 'consolelog-github-actions-appservice-azure'...
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 4 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (4/4), done.

Criando Hello World em Node.js

Abaixo acessamos o diretório onde o repositório foi clonado, verificamos a versão do Node.js e finalmente criamos um novo projeto com o npm init:

$ cd consolelog-github-actions-appservice-azure/

$ node --version
v18.12.1

$ npm init -y

Abrindo o projeto no VSCode, adicionei uma nova linha no arquivo package.json e alterei o nó scripts, ficando da seguinte forma:

{
  "name": "consolelog-github-actions-appservice-azure",
  "version": "1.0.0",
  "description": "Projeto exemplificando o deploy automático de uma API em Node.js no Azure AppServices",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/marcelovismari/consolelog-github-actions-appservice-azure.git"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/marcelovismari/consolelog-github-actions-appservice-azure/issues"
  },
  "homepage": "https://github.com/marcelovismari/consolelog-github-actions-appservice-azure#readme",
  "type": "module"
}
package.json

Criando o arquivo index.js:

import { createServer } from "http";

const PORT = process.env.PORT || 8080;
const server = createServer((_, response) => {
  response.writeHead(200, {
    "Content-Type": "application/json",
  });

  response.write(
    JSON.stringify({ mensagem: "Hello world" })
  );

  response.end();
});

server.listen(PORT, () => console.log(`Listening ${PORT}`));
index.js

Para executar o projeto basta executar o comando npm start. Na sequência efetuei um simples GET através do CURL:

$ curl localhost:8080
{"mensagem":"Hello world"}%

Agora que o projeto está funcionando é só efetuar o commit e push. Na sequência vamos configurar o serviço na Azure.


Observação: na linha const PORT = process.env.PORT || 8080; primeiro tentamos pegar o número da porta da variável de ambiente PORT, caso não tenha um valor o número 8080 será adotado como valor. O motivo é que por padrão o App Service configura o valor da porta nesta variável de ambiente.

App Service sets the environment variable PORT in the Node.js container, and forwards the incoming requests to your container at that port number. To receive the requests, your app should listen to that port using process.env.PORT

https://learn.microsoft.com/en-us/azure/app-service/configure-language-nodejs?pivots=platform-linux#get-port-number

Azure AppService

A Azure fornece uma série de serviços, mas para este cenário de estudo vamos utilizar o App Service, que é categorizado como um serviço como plataforma (PaaS - Plataform as a Service). Neste tipo de serviço o desenvolvedor deve se preocupar  com a aplicação e seus dados, deixando a responsabilidade de gerenciar a rede, sistema operacional, virtualização e outros pontos para a Azure.

Overview - Azure App Service
Learn how Azure App Service helps you develop and host web applications
Lista das suas responsabilidade e da empresa que gerencia os serviços na cloud por categoria: On-Premises, IaaS, PaaS e SaaS
Responsabilidades por categoria: On-Premises, IaaS, PaaS e SaaS

Configurando o AppService na Azure

Acessando o portal da Azure podemos utilizar a barra de busca superior e procurar pelo App Services como a imagem abaixo ilustra:

Pesquisando por App Services na barra de busca do porta da Azure
Portal da Azure - Barra de busca

Observação: é necessário ter uma subscrição (subscription) configurada.

Na sequência será carregado um formulário, que está preenchido na imagem abaixo:

Formulário da Azure para criar um App Service
Criando um App Service na Azure

Do formulário acima:

  • Subscription - refere-se a sua subscrição, é uma espécie assinatura onde a Azure irá cobrar pelos serviços.
  • Resource Group - é um agrupador lógico de recursos. Ajuda bastante na organização. Criei um novo agrupador, consolelog-temporario, para facilitar na hora da exclusão, ou seja, quando finalizar os testes basta remover este resource group e todos os serviços vinculados à ele também serão removidos.
  • Runtime stack - como vamos trabalhar com uma aplicação Node.js, basta selecionar a versão desejada.
  • Pricing plan - como vamos efetuar apenas alguns testes selecionei o Free F1 (Shared infrastructure)
The Free and Shared (preview) service plans are base tiers that run on the same Azure VMs as other apps. Some apps may belong to other customers. These tiers are intended to be used only for development and testing purposes. There is no SLA provided for Free and Shared service plans. Free and Shared plans are metered on a per App basis.

https://azure.microsoft.com/en-us/pricing/details/app-service/windows/

Para deixar o processo bem simples, pulei as opções e fui direto para o Review + create para ser direcionado para a tela de revisão:

Tela de revisão de dados para a criação do App Service
Tela de revisão de dados para a criação do App Service

Após clicar no botão Create é necessário aguardar alguns instantes.

Duas telas, uma mostrando que o App Service está sendo criado e a outra indicando que o App Service foi criado
Aguardando a criação do App Service

Após a conclusão da criação do recurso, podemos clicar no botão Go to resource que aparece na tela para visualizar os detalhes do recém-criado App Service.

Na página do recurso podemos observar algumas informações interessantes, como a URL de acesso:

Trecho da tela contendo informações do o App Service criado
Trecho da tela contendo informações do o App Service criado

Copiando a URL e colando no navegador, podemos ver uma página padrão, já que ainda não subimos nenhum código.

App Service aberto no navegador com uma página padrão da Azure
Página padrão do App Service

Configurando o deploy

Agora vamos para a parte interessante! O GitHub possui um recurso chamado Actions, que permite a automação de tarefas, dentre elas, o deploy. Para nossa sorte, a Azure já faz essa configuração no GitHub Actions de forma simples. Então vamos configurar o deploy da forma padrão (sem customizações) através do menu Deployment Center, como a imagem abaixo mostra:

Formulário para configuração de deploy
Formulário para configuração de deploy

Analisando o formulário temos o seguinte:

  • Source - onde o código fonte está hospedado
  • Signed in as - conta no GitHub que está vinculada à Azure
  • Repository - nome do repositório onde está o código fonte
  • Branch - branch onde está o código fonte

Após preencher o formulário é só clicar no botão Save na parte superior esquerda do formulário. Na sequência podemos visualizar os detalhes na aba Logs:

Mensagens de log do processo de deploy do App Service no portal da Azure
Logs de deploy
Mensagens mostrando que o deploy foi concluído com sucesso
Deploy concluído

Após a conclusão do deploy podemos consultar novamente a URL da nossa aplicação:

App Service aberto no navegador mostrando a resposta da API publicada: Hello world
Chamando a aplicação através do navegador

Testando o deploy automático

Voltando ao código fonte, se você efetuar o git pull perceberá a presença de uma nova pasta chamada .github, com a subspasta workflows e um arquivo chamado main_consolelog-webapp-1.yml. Dentro deste arquivo há uma configuração para que a cada push efetuado na branch main, seja feito um novo deploy:

# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
# More GitHub Actions for Azure: https://github.com/Azure/actions

name: Build and deploy Node.js app to Azure Web App - consolelog-webapp-1

on:
  push:
    branches:
      - main
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2

      - name: Set up Node.js version
        uses: actions/setup-node@v1
        with:
          node-version: '18.x'

      - name: npm install, build, and test
        run: |
          npm install
          npm run build --if-present
          npm run test --if-present

      - name: Upload artifact for deployment job
        uses: actions/upload-artifact@v2
        with:
          name: node-app
          path: .

  deploy:
    runs-on: ubuntu-latest
    needs: build
    environment:
      name: 'Production'
      url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}

    steps:
      - name: Download artifact from build job
        uses: actions/download-artifact@v2
        with:
          name: node-app

      - name: 'Deploy to Azure Web App'
        id: deploy-to-webapp
        uses: azure/webapps-deploy@v2
        with:
          app-name: 'consolelog-webapp-1'
          slot-name: 'Production'
          publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_1231212312312312 }}
          package: .
main_consolelog-webapp-1.yml

Este arquivo pode ser criado manualmente para realizar outras tarefas como builds, testes, etc. Neste caso o arquivo foi criado no momento em que fizemos a configuração de deploy na Azure.

Para testar a configuração de deploy é bem simples, basta efetuar alguma alteração no código fonte e efetuar o push para o repositório. Neste caso alterei a resposta de Hello world para Hello world 123:

import { createServer } from "http";

const PORT = process.env.PORT || 8080;
const server = createServer((_, response) => {
  response.writeHead(200, {
    "Content-Type": "application/json",
  });

  response.write(
    JSON.stringify({ mensagem: "Hello world 123" })
  );

  response.end();
});

server.listen(PORT, () => console.log(`Listening ${PORT}`));
index.js

Após o push, é possível acompanhar no GitHub o workflow de deploy na aba Actions:

Status do deploy no menu Actions do repositório no GitHub
Acompanhando o deploy na aba Actions do GitHub

Quando o workflow for finalizado com sucesso, significa que o deploy foi efetuado. Testando no navegador:

App Service aberto no navegador mostrando a resposta da API recém-modificada e publicada: Hello world 123
Resultado do deploy

Considerações

O objetivo era mostrar uma forma simples de como configurar um deploy de um repositório no GitHub para o serviço App Service da Azure.

Deixo como sugestão explorar um pouco mais o GitHub Actions e os serviços da Azure. Inclusive para novos usuários a Azure concede 200 USD para testar os serviços:

Create Your Azure Free Account Today | Microsoft Azure
Get started with 12 months of free services, 40+ services that are always free, and USD200 in credit. Create your free account today with Microsoft Azure.