Criando esteiras de CI/CD performáticas e seguras

Vinícius Campitelli

Sobre

Sobre

Vinícius Campitelli
  • Membro do PHPSP
  • Desenvolvedor há mais de 15 anos
  • Entusiasta em cibersegurança
  • Consultor de TI e instrutor de treinamentos
Vinícius
Campitelli

Slides

CI/CD

CI/CD

Conceito

CI/CD, continuous integration/continuous delivery, é um método para entregar aplicações com frequência aos clientes. Para isso, é aplicada a automação nas etapas do desenvolvimento de aplicações. Os principais conceitos atribuídos a esse método são a integração, entrega e implantação contínuas.
Red Hat

CI/CD

Conceito

Com o CI/CD, é possível solucionar os problemas que a integração de novos códigos pode causar para as equipes de operações e desenvolvimento (o famoso "inferno de integração").
Red Hat

CI/CD

Conceito

Especificamente, o CI/CD aplica monitoramento e automação contínuos em todo o ciclo de vida das aplicações, incluindo as etapas de teste e integração, além da entrega e implantação. Juntas, essas práticas relacionadas são muitas vezes chamadas de "pipeline de CI/CD" e são compatíveis com o trabalho conjunto das equipes de operações e desenvolvimento com métodos ágeis.
Red Hat

CI/CD

Conceito

O acrônimo CI/CD tem alguns significados. "CI" sempre se refere à integração contínua, que é um processo de automação para desenvolvedores. "CD" se refere à entrega contínua e/ou à implantação contínua, conceitos relacionados e usados alternadamente às vezes.
Red Hat

CI/CD

Conceito

Geralmente, a entrega contínua representa as mudanças feitas pelo desenvolvedor em uma aplicação, que são testadas contra bugs e carregadas em um repositório ou em um registro de container. Nesse repositório, a equipe de operações pode implantar essas mudanças em um ambiente de produção ativo.
Red Hat

CI/CD

Conceito

Isso resolve o problema de baixa visibilidade e comunicação entre as equipes de negócios e desenvolvimento. Para isso, a finalidade da entrega contínua é garantir o mínimo de esforço na implantação de novos códigos.
Red Hat

CI/CD

Conceito

A implantação contínua, outro significado para "CD", se refere ao lançamento automático das mudanças feitas por um desenvolvedor do repositório à produção, onde podem ser usadas pelos clientes. Isso evita a sobrecarga das equipes de operações por conta dos processos manuais que atrasam a entrega de aplicações.
Red Hat

CI/CD

Fluxo

CI/CD Red Hat

CI/CD

Integração contínua

Com a integração contínua (CI), os desenvolvedores consolidam as mudanças no código de volta a uma ramificação compartilhada ou "tronco" com mais frequência (às vezes, até diariamente). As mudanças são consolidadas e depois validadas através da criação automática da aplicação.
Red Hat

CI/CD

Integração contínua

Vários testes automatizados, geralmente de unidade e integração, são feitos para garantir que as mudanças não corrompam a aplicação. Basicamente, tudo é testado, incluindo classes, funções e diferentes módulos que formam toda a aplicação. Em caso de conflito entre os códigos novos e existentes, a CI facilita a correção desses bugs com rapidez e frequência.
Red Hat

CI/CD

Entrega contínua

Depois de realizar a automação de compilações e da unidade e os testes de integração na CI, a entrega contínua automatiza o lançamento desse código validado em um repositório.
Red Hat

CI/CD

Entrega contínua

Portanto, para um processo eficaz de entrega contínua, é importante que a CI já esteja integrada ao pipeline de desenvolvimento. O objetivo da entrega contínua é garantir uma base de códigos que esteja sempre pronta para implantação em um ambiente de produção.
Red Hat

CI/CD

Entrega contínua

Cada etapa da entrega contínua, da consolidação das mudanças de código à entrega de compilações prontas para produção, inclui a automação do lançamento de códigos e do teste. No final desse processo, a equipe de operações pode implantar uma aplicação na produção com rapidez e facilidade.
Red Hat

CI/CD

Implantação contínua

A etapa final de um pipeline de CI/CD sólido é a implantação contínua. Ela é um complemento da entrega contínua, que automatiza o lançamento de compilações prontas para produção em um repositório de códigos.
Red Hat

CI/CD

Implantação contínua

A implantação contínua automatiza o lançamento de uma aplicação para a produção. Como não há um canal manual na etapa do pipeline antes da produção, a implantação contínua depende muito da automação otimizada dos testes.
Red Hat

CI/CD

Implantação contínua

Na prática, a implantação contínua significa que a mudança do desenvolvedor em uma aplicação será habilitada depois de alguns minutos após a gravação (supondo que ela seja aprovada no teste automatizado). Isso facilita muito mais o recebimento do feedback dos usuários e a incorporação dele.
Red Hat

CI/CD

Implantação contínua

Juntas, todas essas práticas de CI/CD relacionadas diminuem o risco da implantação de aplicações, facilitando o lançamento das mudanças em pequenas partes, e não de uma só vez. No entanto, há também a necessidade de muitos investimentos iniciais, já que os testes automatizados precisam ser gravados para acomodar várias etapas de teste e lançamento no pipeline de CI/CD.
Red Hat

CI/CD

Ferramentas de CI

GitHub Actions

GitHub Actions

Conceito

Automatize, personalize e execute seus fluxos de trabalho de desenvolvimento do software diretamente no seu repositório com o GitHub Actions. Você pode descobrir, criar e compartilhar ações para realizar qualquer trabalho que desejar, incluindo CI/CD, bem como combinar ações em um fluxo de trabalho completamente personalizado.
Documentação do GitHub Actions

GitHub Actions

Sintaxe

Um Fluxo de trabalho ("workflow") é configurado através de um arquivo YAML na pasta .github/workflows do seu repositório

Documentação do GitHub Actions

GitHub Actions

Sintaxe

Cada Fluxo é composto por:

  • Gatilhos ("triggers") responsáveis por dispará-lo, como push em alguma branch, pull request aberta, issue criada, sistema de agendamento (como o "cron"), entre outros
  • Jobs que podem ser executados de maneira sequencial ou em paralelo entre si, que serão executados em runners (geralmente um container)
  • Os Jobs são compostos de etapas ("steps") para executar scripts definidos por você ou ações de terceiros (que são disponibilizadas como pacotes)
Documentação do GitHub Actions

GitHub Actions

Sintaxe


                            name: Backend

                            on:
                              push:
                                branches: [ "main" ]
                              pull_request:
                                branches: [ "main" ]

                            jobs:
                              job1:
                                name: Nome do primeiro job
                                runs-on: ubuntu-latest
                                steps:
                                  - name: "Passo que usa um pacote pronto"
                                    uses: autor/pacote@versao
                                    with:
                                      configuracao1: valor1
                                      configuracao2: valor2
                                      configuracao3: valor3
                                      configuracao4: ${{ secrets.VARIAVEL_DO_SECRETS }}

                                  - name: "Passo que executa comandos no shell"
                                    run: |
                                      comando1 args...
                                      comando2 args...
                                      comando3 args...

                              job2:
                                name: Nome do segundo job
                                runs-on: ubuntu-latest
                                needs: job1
                                if: github.ref == 'refs/heads/main'
                                steps:
                                  - name: Login to Container Registry
                                    uses: docker/login-action@v3
                                    with:
                                      registry: ${{ env.REGISTRY }}
                                      username: ${{ github.actor }}
                                      password: ${{ secrets.GITHUB_TOKEN }}
                        
Sintaxe de fluxo de trabalho para o GitHub Actions

GitHub Actions

Dica

Ao invés de ficar fazendo push ou outras ações reais no seu repositório para rodar um workflow, recomendo usar o nektos/act , ferramenta que permite executá-lo localmente em sua máquina, simulando os eventos de gatilho

Dicas de Performance

Dicas de Performance

Falhar Rápido

Organize os jobs e steps para que as ações mais rápidas sejam executadas primeiro — assim, caso alguma etapa falhe, economizaremos tempo (e dinheiro)

Dicas de Performance

Falhar Rápido

Exemplo de pipeline:


                                    jobs:
                                      job1:
                                        name: Nome do primeiro job
                                        runs-on: ubuntu-latest
                                        steps:
                                          - name: Checkout do código
                                            uses: actions/checkout@v4

                                          - name: Configurando ambiente
                                            run: apt-get update && apt-get install jq

                                          - name: Instalando dependências de desenvolvimento
                                            run: npm install --production=false

                                          - name: Análise estática do código
                                            run: |
                                              npm run lint
                                              sonarscanner src/

                                          - name: Build de desenvolvimento
                                            run: npm run build:dev

                                          - name: Testes
                                            run: npm test

                                          - name: Instalando dependências de produção
                                            run: npm install --production=true

                                          - name: Build de produção
                                            run: npm run build

                                          - name: Deploy
                                            run: ./deploy-to-production build/
                                
  1. Checkout do código
  2. Configuração do ambiente
  3. Instalação de dependências de desenvolvimento
  4. Análise estática do código
    • Linting
    • Análise de segurança
  5. Build de desenvolvimento
  6. Testes
    • Testes de unidade
    • Testes de integração
    • Testes End-to-end
  7. Instalação de dependências de produção
  8. Build de produção
  9. Deploy

Dicas de Performance

Caching

Faça cache de dependências, layers de containers e outros arquivos para diminuir o tempo de execução do workflow


                                    jobs:
                                      build:
                                        runs-on: ubuntu-latest
                                        steps:
                                          - name: Checkout do código
                                            uses: actions/checkout@v4

                                          - name: Configurando Node
                                            uses: actions/setup-node@v4
                                            with:
                                              node-version: 22
                                              cache: 'npm'

                                          - name: Instalando dependências
                                            run: npm ci
                                
Usando cache no GitHub Actions

Dicas de Performance

Paralelização

Execute em paralelo jobs que não possuem dependência entre si


                                    jobs:
                                      testFirefox:
                                        runs-on: ubuntu-latest
                                        steps:
                                          - uses: actions/checkout@v4
                                          - name: "Testando no Firefox"
                                            run: npm run e2e:test-firefox

                                      testChrome:
                                        runs-on: ubuntu-latest
                                        steps:
                                          - uses: actions/checkout@v4
                                          - name: "Testando no Chrome"
                                            run: npm run e2e:test-chrome

                                      testWebkit:
                                        runs-on: ubuntu-latest
                                        steps:
                                          - uses: actions/checkout@v4
                                          - name: "Testando no Webkit"
                                            run: npm run e2e:test-webkit
                                
Paralelização de jobs

Dicas de Performance

Matrizes

Para facilitar a escrita de jobs em paralelo, é possível usar matrizes para criar variações de versões, sistemas operacionais, configurações e mais


                                    jobs:
                                      build:
                                        strategy:
                                          matrix:
                                            version: [20, 21, 22]
                                            os: [ubuntu-latest, windows-latest]
                                        runs-on: ${{ matrix.os }}
                                        steps:
                                          - name: "Node ${{ matrix.version }} em ${{ matrix.os }}"
                                            run: ...
                                
Usando matrizes para paralelização
Using a matrix for your jobs - GitHub Docs

Dicas de Performance

Imagens customizadas

Se seu workflow possuir grandes etapas de configuração de ambiente, você pode criar uma imagem de container em algum Registry (como o Docker Hub) e especificá-la


                            jobs:
                              build:
                                container:
                                  image: organizacao/imagem
                                  credentials:
                                     username: ${{ secrets.RUNNER_REGISTRY_USERNAME }}
                                     password: ${{ secrets.RUNNER_REGISTRY_PASSWORD }}
                        
Running jobs in a container - GitHub Docs

Dicas de Performance

Runners melhores

Se ainda assim sentir que seu workflow está lento, você pode pagar pelos planos GitHub Team ou GitHub Enterprise Cloud para ter runners maiores ou rodá-los em sua própria infraestrutura

Caso tenha planos pagos:


                                    jobs:
                                      build:
                                        runs-on:
                                          group: my-large-runners-group
                                        steps:
                                          - ...
                                

Caso tenha configurado em sua infra:


                                    jobs:
                                      build:
                                        runs-on: self-hosted
                                        steps:
                                          - ...
                                
Choosing the runner for a job - GitHub Docs

Dicas de Segurança

Dicas de Segurança

Não deixar credenciais

Nunca deixe credenciais (como senhas e chaves de API) nos workflows

Utilize os secrets para guardar essas informações, que não vazarão em logs e nem poderão ser lidas por outros colaboradores do repositório


                                    jobs:
                                      build:
                                        runs-on: ubuntu-latest
                                        steps:
                                          - name: Requisição da API
                                            env:
                                              access_token: ${{ secrets.API_ACCESS_TOKEN }}
                                            run: |
                                              curl -X POST \
                                                -H "Authorization: Bearer $access_token" \
                                                https://api.com/endpoint
                                
Usando Secrets
Using secrets in GitHub Actions - GitHub Docs

Dicas de Segurança

Marcar versões

Para evitar que seu workflow pare de funcionar caso alguma ação ou pacote tenha uma quebra de compatibilidade reversa, marque exatamente as versões das ações e pacotes que irá precisar

Antes:


                                    steps:
                                      - name: Checkout do código
                                        uses: action/checkout

                                      - name: Configurando Node
                                        uses: actions/setup-node

                                      - name: Baixando e executando algum cliente
                                        run: |
                                          wget https://download.com/latest.zip
                                          unzip latest.zip
                                          ./client ...
                                

Depois:


                                    steps:
                                      - name: Checkout do código
                                        uses: action/checkout@v4

                                      - name: Configurando Node
                                        uses: actions/setup-node@v4

                                      - name: Baixando e executando algum cliente
                                        run: |
                                          wget https://download.com/v1.5.7.zip
                                          unzip v1.5.7.zip
                                          ./client ...
                                

Dicas de Segurança

Marcar versões

Para evitar que seu workflow pare de funcionar caso alguma ação ou pacote tenha uma quebra de compatibilidade reversa, marque exatamente as versões das ações e pacotes que irá precisar

Antes:


                                    steps:
                                      - name: Checkout do código
                                        uses: action/checkout

                                      - name: Configurando Node
                                        uses: actions/setup-node

                                      - name: Baixando e executando algum cliente
                                        run: |
                                          wget https://download.com/latest.zip
                                          unzip latest.zip
                                          ./client ...
                                

Ou até mesmo:


                                    steps:
                                      - name: Checkout do código
                                        uses: action/checkout@a5ac7e51b41094c92402da3b24376905380afc29

                                      - name: Configurando Node
                                        uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8

                                      - name: Baixando e executando algum cliente
                                        run: |
                                          wget https://download.com/v1.5.7.zip
                                          unzip v1.5.7.zip
                                          ./client ...
                                

Dicas de Segurança

Protegendo workflows

Crie um arquivo .github/CODEOWNERS com a sintaxe a seguir para determinar um time para aprovar pull requests que contenham modificações nos seus workflows


                            /.github/workflows @sua-organizacao/nome-do-time
                        
About code owners - GitHub Docs

Dicas de Segurança

Permissões do token

Todo workflow é executado com um access token para autenticação no repositório

Dependendo das ações do seu workflow, será preciso permitir alguns acessos através da configuração permissions:

Acesso geral (não recomendado):


                                    name: "Meu workflow"
                                    on:
                                      push:
                                        branches: [ "main" ]
                                    permissions:
                                      contents: write
                                    jobs:
                                      build:
                                        steps:
                                          - ...
                                

Acesso em um único job:


                                    name: "Meu workflow"
                                    on:
                                      push:
                                        branches: [ "main" ]
                                    jobs:
                                      build:
                                        permissions:
                                          contents: write
                                        steps:
                                          - ...
                                
Dica: utilize a ação GitHubSecurityLab/actions-permissions para monitorar e aplicar o privilégio mínimo do token
Automatic token authentication - GitHub Docs

Dicas de Segurança

Usando OIDC

Vários serviços (como os provedores de cloud) possuem integrações via OpenID Connect (OIDC), protocolo de autenticação baseado no OAuth 2, removendo a necessidade de usar credenciais como access tokens e garantindo maior segurança e praticidade

About security hardening with OpenID Connect - GitHub Docs

Dicas de Segurança

Usando OIDC

Para isso, configure no serviço uma relação de confiança, dizendo que determinados tokens emitidos pelo GitHub Actions podem ter acesso à sua conta, e configure a troca do token OIDC em seu workflow

Exemplo com a Amazon Web Services:


                            jobs:
                              build:
                                runs-on: ubuntu-latest
                                steps:
                                  - name: "Checkout do código"
                                    uses: actions/checkout@v4

                                  - name: "Configurando AWS"
                                    uses: aws-actions/configure-aws-credentials@v4
                                    with:
                                      role-to-assume: ${{ secrets.AWS_ROLE }}
                                      aws-region: ${{ env.AWS_REGION }}

                                  - name: "Subindo no S3"
                                    run: aws s3 sync ./dist/ s3://${{ env.AWS_BUCKET_NAME }}/ --delete
                        
Configuring OpenID Connect in Amazon Web Services - GitHub Docs

Referências

Referências

Treinamentos in company

Workshops

Obrigado!