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óteses, Desenvolver na empresa ou comprar pronto, Fuja da otimização prematura, entre outros.
Sumário
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:
- WSLg – Windows Subsystem for Linux GUI
- Instalando o Docker no WSL2
- Usando um “Github” onpremise
- Sincronizando Gitea com Github
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.
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’.
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.
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.