DevOps é Essencial

O que é DevOps?

DevOps é uma cultura de princípios desenvolvidos baseado no Manifesto Ágil para aproximar o desenvolvimento do deployment do software. O próprio nome DevOps significa Dev de software development e Ops de IT Operations. Um dos problemas que software passou na sua jornada foi o processo como o deployment (implantação) era feito.

DevOps começou mais ou menos em 2009 com Patrick Debois, considerado o pai do DevOps. Ele sendo um desenvolvedor e parte do operacional, queria aproximar o dois times. O que faz sentido porque imagina a dificuldade que é desenvolvedor sem ter noção de como ser implantado ou implantar o software sem saber detalhes de como o código foi desenvolvido. E isso gerava problemas, e para evitar erros de produção a implantação não era frequente, com meses sem implantação de software.

Como as implantações eram feitas de formas manuais, a chance de erros acontecerem eram significativas. Já que onde há a ação humana, há a chance de erros. Assim o DevOps vem com a missão de minimizar os erros humanos, minimizando a ação humana o máximo possível. Como isso poderia ser feito? Software. Gerando um software que automatizasse tudo que o humano fizesse e transformasse isso numa receita de bolo automatizada independente, e problema resolvido.

Com o operacional muito mais próximo do software, isso aproxima os dois lados do DevOps ou até mesmo unifica. Com isso conseguimos várias implantações por dia, funcionalidades e resultados mais rápidas para os clientes, ambientes de testes similares ao de produção para QA, menos problemas causados por erro na implantação.

Github Actions

Github Actions é uma ferramenta do Github para automatização de scripts em repositórios do Github. Mas para demonstrar os conceitos de DevOps, não precisa especificamente dele. Há outras ferramentas similares como Jenkins, CircleCI ou Gitlab CI (solução do Gitlab). Então os conceitos apresentados pelo Github Actions é totalmente transferível. Então a tecnologia não importa.

Recentemente criei um projeto para colocar em prática conceitos de DevOps com uma API Rest bem simples e básica. Minha inteção mais era testar Github Actions e configuração do Nginx com HTTPS.

Nesse projeto criei algumas pipelines. Pipeline é um conjunto de scripts para se obter um resultado ou produto do software. Por exemplo criei uma pipeline chamada Unit tests para rodar testes unitários.

name: 'Unit tests'
on: [push, workflow_call]
jobs:
  test:
    runs-on: ubuntu-22.04
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      - name: Generate package-lock.json
        run: npm i --package-lock-only
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: 18
          cache: 'npm'
      - name: Install deps
        run: npm ci
      - name: 'Run unit tests'
        run: npm run test

O arquivo acima é um arquivo .yml no path .github/workflows. Aqui é onde ficará toda a configuração para essa pipeline.

O que o arquivo está fazendo?

  • name: É o nome da pipeline, ou no Github Actions o nome dado é Workflow
  • on: Quando essa pipeline será acionada Aqui defini no push de commits no repositório e em workflow_call que permite que seja executado quando um outro workflow tentar executar esse workflow.
  • jobs: Job é uma serie de scripts que rodam pelo o mesmo runner. Nesse exemplo, tem somente um job chamado test.
  • runs-on: Definimos o runner, descrito em anteriormente, para o job. Aqui estou definindo o Ubuntu versão 22.04. Github actions executa os runners em Máquinas virtuais. Tem disponível os sistemas operacionais como runners, Ubnuntu Linux, WIndows ou MacOS. Para saber mais detalhes
  • steps: São os scripts para ser executado. Para step definimos name, e ou uses ou run para rodar os scripts. No primeiro usamos um script do próprio do Github para baixar o repositório de actions/checkout com a versão 3 (v3). E outro utilizado, é o step Run unit tests que executa um comando no runner npm run test descrito em run.

Essa é a configuração de uma pipeline de testes, mas pode ser criadas muitas outras. Como por exemplo um de release para ser acionado em quando der um push de commit para a branch principal.

name: Release
on:
  push:
    branches:
      - master
jobs:
  test:
    name: Test
    uses: ./.github/workflows/tests.yml
  docker:
    name: Build container
    runs-on: ubuntu-22.04
    steps:
      - name: "☁️ checkout repository"
        uses: actions/checkout@v3
      - name: Login to docker
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKERHUB_LOGIN }}
          password: ${{ secrets.DOCKERHUB_PASSWORD }}
      - name: "🔧 setup buildx"
        uses: docker/setup-buildx-action@v2.5.0
      - name: "🔧 cache docker layers"
        uses: actions/cache@v3
        with:
          path: /tmp/.buildx-cache
          key: ${{ runner.os }}-buildx-${{ github.sha }}
      - name: "🔧 docker meta"
        id: meta
        uses: docker/metadata-action@v4
        with:
          images: ${{ github.repository }}
          tags: latest
      - name: "📦 docker build"
        uses: docker/build-push-action@v4
        with:
          push: false
          load: true
          tags: ${{ github.run_id }}

      - name: Run CVEs scan (Trivy non-blocking)
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: ${{ github.run_id }}
          exit-code: 0
          format: table

      - name: Run Trivy for HIGH,CRITICAL CVEs and report (blocking)
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: ${{ github.run_id }}
          exit-code: 1
          ignore-unfixed: true
          vuln-type: "os,library"
          severity: "HIGH,CRITICAL"
          format: "sarif"
          output: "trivy-results.sarif"

  release:
    name: Release in deploy server
    needs:
      - test
      - docker
    runs-on: ubuntu-20.04
    steps:
      - name: "☁️ checkout repository"
        uses: actions/checkout@v3
        with:
          fetch-depth: 0
      - name: "🚀 release"
        run: |
          echo "DATABASE_URL=${{ secrets.DATABASE_URL }}" >> .env
          echo "POSTGRES_DB=${{ secrets.POSTGRES_DB }}" >> .env
          echo "POSTGRES_USER=${{ secrets.POSTGRES_USER }}" >> .env
          echo "POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }}" >> .env

          echo "${{ secrets.CERT_FILE }}" | tr -d '\r' > cert.pem
          chmod 400 cert.pem
          scp -i cert.pem -o "StrictHostKeyChecking=no" ./.env ${{ secrets.DEPLOY_SERVER }}:~/nodejs_cicd/.env
          scp -i cert.pem -o "StrictHostKeyChecking=no" ./docker-compose.yml ${{ secrets.DEPLOY_SERVER }}:~/nodejs_cicd/docker-compose.yml
          scp -i cert.pem -o "StrictHostKeyChecking=no" ./default.conf ${{ secrets.DEPLOY_SERVER }}:~/nodejs_cicd/
          ssh -i cert.pem -o "StrictHostKeyChecking=no" ${{ secrets.DEPLOY_SERVER }} << 'ENDSSH'
            cd ~/nodejs_cicd
            docker compose down
            docker compose pull
            docker compose up --build -d

            docker image prune -f
            rm ./docker-compose.yml
            rm ./default.conf
            rm ./.env
          ENDSSH

Aqui é uma pipeline de release utilizando uma máquina virtual como servidor. E como pode ser visto lá em cima no trecho:

on:
  push:
    branches:
      - master

A pipeline será somente acionada no push na branch master.

Assim como temos outras funcionalidades da ferramenta, como dependências entre jobs, reutilização de pipelines, utilização de Docker containers. Então para saber mais sempre conte com a documentação do Github Actions.

Referências