Integração Contínua Cloudless

a Integração Contínua CloudLess representa uma abordagem eficaz para equipes que buscam manter controle total sobre seu ambiente de desenvolvimento, eliminando a dependência de serviços em nuvem. Através da utilização do Gitea e a configuração de runners, é possível estabelecer um fluxo de trabalho robusto que garante a automação de builds e testes, promovendo uma cultura de qualidade e eficiência no desenvolvimento de software. Ao implementar essas práticas em um ambiente local, as equipes podem não apenas aumentar sua produtividade, mas também assegurar a segurança e a integridade de seu código, colocando-se em uma posição favorável para enfrentar os desafios do mercado atual.

O artigo Integração Contínua CloudLess surge como uma alternativa viável para equipes que desejam implementar um ambiente de CI localmente, sem depender de serviços em nuvem. Então, nele, exploraremos a configuração de uma solução de Integração Contínua utilizando o Gitea, uma plataforma de controle de versões, e detalharemos as etapas necessárias para configurar runners e workflows que permitam realizar builds, testes e integrações de maneira eficiente e segura em um ambiente totalmente local.

Bom, além desse artigo, aqui no blog também temos diversos outros artigos sobre kubernetes, desenvolvimento, gestão, devops, etc. Veja alguns exemplos: Diferenças entre Paradigmas, Axiomas e HipótesesDesenvolver na empresa ou comprar prontoFuja da otimização prematura, entre outros.

A História da Integração Contínua

Embora esse conceito seja tratado como algo relativamente recente, ele surge em 1968 com a chamada “Crise do Software”. Veja que o desenvolvimento de software crescia ao mesmo tempo que sua complexidade não gerenciada. Assim, o custo de manutenção cresceu absurdamente, riscos de morte por falhas de software e a inexistência de profissionais gerou um alerta. Então, a Conferência de Engenharia de Software da OTAN em 1968 trouxe luz ao problema. Lá algumas recomendações foram geradas, tais como:

  • Modularidade e Design Estruturado
  • Documentação e Padrões de Qualidade
  • Gerenciamento de Projetos e Estimativas Realistas
  • Ferramentas de Suporte ao Desenvolvimento
  • Práticas de Teste e Verificação
  • Integração e Comunicação entre Equipes

Já em 1975 Frederick Brooks escreve um livro tão memorável que hoje, em outubro de 2024 ele se mostra ainda bastante atual e válido para os problemas de software que temos: The Mythical Man-Month (O mítico homem-mês, em português). Nesse livro ele discute as bases do software, da problemática acerca da engenharia e arquitetura de software, bem como a gestão de projetos, ainda que esses termos não estejam estruturados como hoje.

Já na década de 80 os sistemas como SCCS (Source Code Control System) e RCS (Revision Control System) se popularizaram, com a possibilidade de gerenciamento de código fonte, controle de revisões, auditoria e rastreio, base para os sistemas que conhecemos hoje em dia.

Nasce o conceito formal de CI – Continous Integration (Integração Contínua)

Pois na década de 90 surge o livro clássico escrito por Kent Beck onde ele estrutura uma metodologia que tem características particulares do desenvolvimento de software, ao invés de outras que eram paralelos das engenharias tradicionais. No livro ele conceitua Integração contínua como “o processo de integrar código de forma frequente, idealmente várias vezes ao dia, com o objetivo de detectar rapidamente conflitos e problemas de integração” (Extreme Programming Explained: Embrace Change, Beck, 1999).

Seguindo, na década de 2000 softwares como Jenkins ganharam corpo e o tudo isso ganhou grande potência. De 2010 até hoje, primeiro o conceito de ALM (Application Lifecycle Management) e depois substituído por DevOps (Development + Operations) organizou um ecossistema de conceitos, hábitos, cultura, ferramentas e boas práticas, que viabilizam a melhoria contínua constante por meio do CI.

Mas o que é Integração Contínua na prática?

Vamos ser sucintos: toda vez que uma alteração de código é inserida no servidor de GIT, várias ações são feitas para garantir que o código, de fato, segue as práticas desejadas pela companhia. Ao mesmo tempo faz-se o download das dependências, a compilação, a inserção de variáveis secretas (senhas de banco, por exemplo), validações de segurança, validações de usabilidade, testes de regressão entre outros.

Tudo isso é feito por meio de ferramentas como o GitHub Actions, CircleCI, Gitea Actions, Drone CI, entre outros. Na prática arquivos YAML ficam no software indicando como será a integração contínua quando tocar no servidor de GIT.

Criando nosso CI com o Gitea – Integração Contínua Cloudless

Para quem não conhece, o Gitea (algo como GIT + TEA [chá em inglês]), é uma ferramenta OpenSource muito robusta que funciona como um servidor de git para uma infraestrutura onpremise. Ele é muito, mas muito (!!) semelhante ao Github, ou seja, será fácil para novos desenvolvedores.

Essa ferramenta possui repositórios, issues, pull requests, forks, secrets, etc. Tanto as características mais básicas quanto as avançadas do github. Aqui no blog escrevemos alguns outros artigos relacionados direta ou indiretamente ao Gitea: caso queira conhecer mais, veja:

Como funciona o CI no Gitea – Integração Contínua Cloudless

Compreenda que entender CI com o gitea é entender 99% do CI do Github, e talvez 80% de qualquer outra ferramenta. Considerando isso, veja um arquivo YAML muito básico onde considero que sempre que há um push para a branch main, uma máquina ubuntu é criada e o bash roda: echo “Alo Mundo!”.

name: Anselme - Exemplo de Yaml de CI
on:
  push:
    branches:
      - main

jobs:
  build-and-test:
    runs-on: ubuntu-latest

    steps:
      - name: Apenas um Hello World
        run: echo "Alo Mundo!"

Claro que isso é muito básico, mas é apenas para entender qual é sua dinâmica. Veja a seguir isso rodando dentro do gitea.

Tela de uma integração contínua ocorrendo cloudless no gitea (ferramenta semelhante ao github actions)

Compreendendo os Runners

Um runner é uma aplicação responsável por iniciar containers para que os jobs indicados no arquivo YAML sejam criados. O ambiente Gitea que tenho em casa é todo baseado em Docker no Windows com WSL2. Portanto, há um Docker Desktop instalado, integrado com o WSL2. Nele executo 2 containers: um para o Gitea e um para o runner. O Runner, quando executado cria outros container a depender do que o arquivo YAML requerer.

Como faço para subir meu Gitea e meu Gitea Runner? Apanhei bastante até encontrar a configuração correta, mas eis que ela surge em um docker-compose:

version: "3"

networks:
  gitea:
    driver: bridge
    
services:
  gitea_app:
    image: gitea/gitea:1.22.3
    container_name: gitea_app
    environment:
      - USER_UID=1000
      - USER_GID=1000
    restart: always
    networks:
      - gitea
    volumes:
      - ./gitea:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "3000:3000"
      - "222:22"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://gitea_app:3000/"]
      interval: 5s
      timeout: 10s
      start_period: 30s
      retries: 5

  runner:
    image: gitea/act_runner:nightly
    container_name: gitea-runner
    restart: always    
    environment:
      CONFIG_FILE: /config.yaml
      
    volumes:
      - ./config.yaml:/config.yaml
      - ./data:/data
      - /var/run/docker.sock:/var/run/docker.sock
    depends_on:
      gitea_app:
        condition: service_healthy
        restart: true
    networks:
      - gitea
        

Note que ambas as maquinas estão ligadas por uma rede chamada gitea, e há um volume que compartilha o docker.sock, isso por que o runner deve ter acesso ao docker para criar novos containers. Mas note também que há um arquivo config.yaml, que é a configuração de base para o funcionamento do runner: ele é fundamental para o seu funcionamento.

O arquivo config.yaml

Os problemas que tive (ignore se preferir)

Vasculhei boa parte do google e do chatGPT e não encontrei um arquivo de configuração descente para meus testes. Mas num lampejo pela madrugada consegui entender o que estava faltando e tudo funcionou.

O problema que eu estava tendo é que os containers gerados pelo runner não conhecem a rede gitea e, portanto, não conseguiam baixar o código fonte para compilar, testar, etc. Há variáveis de ambiente que posso passar diretamente no arquivo docker-compose, mas que simplesmente não funcionam para essa questão da rede. Nesse caso só consigo através do arquivo config.yaml.

Descobri a solução testando o seguinte: gerei um push do meu sistema que iniciou o checkout do meu código, e enquanto isso, procurei pelo container no docker gerado e injetei a network que estava faltando e tudo funcionou. A dúvida era como injetar isso diretamente na configuração para todos os casos. Bom, a linha network: gitea_gitea resolve tudo.

Há outro ponto interessante que apanhei nesse processo. O runner cria uma pasta ./data e um arquivo oculto chamado .runner. Se você deletar o runner no gitea, você simplesmente não consegue subi-lo novamente enquanto não apagar esse arquivo.

Ignorando esses problemas

Agora, desconsiderando os problema, note que é importante passar as variáveis de ambiente GITEA_INSTANCE_URL, GITEA_RUNNER_REGISTRATION_TOKEN e GITEA_RUNNER_NAME. A URL é algo bastante simples, a depender da configuração do docker-compose. O Token deve ser obtido da tela dos runners no próprio gitea. Já o nome é algo arbitrário a ser escolhido.

Outro ponto que dou um destaque são os labels. Se você observar o arquivo YAML de CI, ele tem dentro de jobs uma linha runs-on: ubuntu-latest. Isso signifiqua que ele procurará esse label para saber qual imagem docker baixar. Portanto, pode ser necessário incluir várias linhas para que o CI rode de maneira coerente.

log:
  level: info

runner:
  file: .runner
  capacity: 10
  envs:
    GITEA_INSTANCE_URL: http://gitea_app:3000/
    GITEA_RUNNER_REGISTRATION_TOKEN: iObVsHrpJpyjZCZAH2ug3CjRDkDOSyUsSE712AaU
    GITEA_RUNNER_NAME: DefaultRunner
  timeout: 60m
  shutdown_timeout: 0s
  insecure: false
  fetch_timeout: 5s
  fetch_interval: 2s
  labels:
  # Labels baseadas nas versões do Ubuntu
  - "ubuntu-20.04:docker://gitea/runner-images:ubuntu-20.04"
  - "ubuntu-22.04:docker://gitea/runner-images:ubuntu-22.04"
  - "ubuntu-latest:docker://gitea/runner-images:ubuntu-22.04"
  
cache:
  enabled: true
  dir: "/data/cache"

container:
  network: gitea_gitea
  privileged: true
  workdir_parent: /data/workspace
  valid_volumes:
    - "**"
  docker_host: unix:///var/run/docker.sock

host:
  workdir_parent: /data/workspace

Como obter o Token de registro do Runner

Muito simples, na tela de configuração procure por Ações > Runners e vá em ‘Criar novo runner’. A imagem a seguir exibe um runner que criei com vários labels chamado ‘Default Runner’.

Tela com um runner instalado no gitea cloudless (ferramenta semelhante ao github)

Note também que há três níveis diferentes de configuração no Gitea: Administração Geral (vale para todo o gitea), Configuração do usuário (vale apenas para o usuário), Configuração do repositório (vale apenas para o repositório em questão). Essas configurações funcionam em cascata, portanto se você configurar um runner no escopo da administração geral, você o verá em todos os repositórios específicos.

Conclusão de Integração Contínua CloudLess

Em conclusão, a Integração Contínua CloudLess representa uma abordagem eficaz para equipes que buscam manter controle total sobre seu ambiente de desenvolvimento, eliminando a dependência de serviços em nuvem. Assim, através da utilização do Gitea e a configuração de runners, é possível estabelecer um fluxo de trabalho robusto que garante a automação de builds e testes. Ao implementar essas práticas em um ambiente local, as equipes podem não apenas aumentar sua produtividade, mas também garantir a segurança e a integridade de seu código, colocando-se em uma posição favorável para enfrentar os desafios do mercado atual.


Thiago Anselme
Thiago Anselme - Gerente de TI - Arquiteto de Soluções

Ele atua/atuou como Dev Full Stack C# .NET / Angular / Kubernetes e afins. Ele possui certificações Microsoft MCTS (6x), MCPD em Web, ITIL v3 e CKAD (Kubernetes) . Thiago é apaixonado por tecnologia, entusiasta de TI desde a infância bem como amante de aprendizado contínuo.