Guia prático: como auto‑escalar runners self‑hosted no GitHub Actions para pipelines CI de projetos PHP
Por Kevin Marcondes · Publicado em · Atualizado em · 9 min de leitura
Guia prático: como auto‑escalar runners self‑hosted no GitHub Actions para pipelines CI de projetos PHP. Aqui você aprende quando faz sentido adotar, como desenhar labels, usar runners efêmeros, escolher padrões de autoscaling, configurar workflows e evitar armadilhas. O objetivo é reduzir custo e latência sem comprometer segurança e confiabilidade.
Quando faz sentido auto‑escalar runners self‑hosted
Nem todo pipeline precisa de runners self‑hosted. Eles brilham quando você busca menor latência, controle de custo e ambientes sob medida. Considere‑os quando a fila em hosted crescer e builds demorarem apenas para iniciar.
Casos típicos em PHP: Composer com extensões nativas, builds Docker da aplicação, testes que acessam recursos internos e pipelines que exigem rede privada.
Se precisa de uma visão geral de CI/CD em PHP antes de avançar, veja o nosso guia de CI/CD para PHP/Laravel e depois retorne ao tema de autoscaling.
- Controle de custo por minuto e tamanho de máquina.
- Menor latência perto do registry, banco e cache.
- Ambiente sob medida: versões de PHP, extensões e caches pré‑aquecidos.
- Acesso seguro a redes privadas e artefatos internos.
- Conformidade: isolamento, auditoria e retenção de logs.
Arquitetura: labels, runners efêmeros e isolamento
A base é simples: você etiqueta seus runners e seus jobs pedem essas labels. Use runners efêmeros para que cada job ganhe uma VM/contêiner limpo e, ao terminar, o runner se autodesregistre.
Defina uma taxonomia de labels que represente capacidades. O agendador do GitHub casa jobs e runners por labels, melhorando previsibilidade e paralelismo.
Para PHP, combine labels de versão, SO, Docker e tamanho. Isso facilita rotas específicas por stack e otimiza caches. Quando for integrar com deploy, alinhe as labels ao seu pipeline de deploy automático.
- Exemplos: self‑hosted, linux, php‑8‑2, php‑8‑3, docker, large, ssd.
- Mantenha labels estáveis e versionadas (php‑8‑3 em vez de latest).
- Prefira runners efêmeros a persistentes para reduzir deriva.
- Separe ambientes por sensibilidade: prod‑ci e sandbox‑ci.
Padrões de autoscaling: qual usar em cada cenário
Existem três padrões recorrentes. Escolha pelo nível de controle, complexidade e custo. Compare a seguir e selecione o que combina com sua operação e skills da equipe.
| Padrão | Como escala | Prós | Contras | Quando usar |
|---|---|---|---|---|
| Kubernetes + controlador de runners | Pods sob demanda; cada pod executa um runner efêmero | Isolamento forte; integração nativa; alta densidade | Requer k8s; curva de aprendizado maior | Times com k8s e alta variação de carga |
| VMs com cloud-init/terraform + systemd | Cria/destrói VMs conforme fila; cada VM roda 1 runner | Máximo isolamento; controle de tamanho e região | Provisionamento mais lento que pods | Workloads pesados, builds Docker e segurança rígida |
| VM única com múltiplos containers runner | Escala contêineres no host conforme métricas | Operação simples; custos menores | Menos isolamento; disputar I/O e rede | Times pequenos e pipelines moderados |
Setup mínimo de runner efêmero em VM (script base)
Este exemplo registra um runner efêmero em um repositório e executa um único job. Exige token com permissões adequadas e acesso root para instalar dependências. Adapte para sua nuvem e política de imagens douradas.
O runner efêmero remove o registro ao concluir. Em casos específicos, você pode chamar a remoção explicitamente após o término.
#!/usr/bin/env bash
set -euo pipefail
# Variáveis de ambiente esperadas:
# GH_REPO="owner/repo" # alvo do runner
# GH_PAT # token com escopo para criar registration tokens
# RUNNER_LABELS="self-hosted,linux,php-8-3,docker"
WORKDIR=/opt/actions-runner
mkdir -p "$WORKDIR" && cd "$WORKDIR"
# 1) Obter registration token de curta duração
REG_TOKEN=$(curl -s -X POST \
-H "Authorization: Bearer ${GH_PAT}" \
-H "Accept: application/vnd.github+json" \
https://api.github.com/repos/${GH_REPO}/actions/runners/registration-token \
| jq -r .token)
# 2) Baixar e preparar o runner (ajuste a versão conforme necessidade)
RUNNER_VERSION="2.317.0"
curl -L -o actions-runner.tar.gz \
https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz
tar xzf actions-runner.tar.gz
# 3) Configurar como efêmero e etiquetar
./config.sh \
--unattended \
--url https://github.com/${GH_REPO} \
--token "${REG_TOKEN}" \
--name "runner-${HOSTNAME}-$(date +%s)" \
--ephemeral \
--labels "${RUNNER_LABELS}"
# 4) Executar um único job; o runner encerra após concluir
./run.sh
# Opcional: remoção explícita (caso tenha política de limpeza extra)
# REMOVE_TOKEN=$(curl -s -X POST \
# -H "Authorization: Bearer ${GH_PAT}" \
# -H "Accept: application/vnd.github+json" \
# https://api.github.com/repos/${GH_REPO}/actions/runners/remove-token \
# | jq -r .token)
# ./config.sh remove --unattended --token "${REMOVE_TOKEN}"
Workflow YAML: labels, cache do Composer e concorrência
Aponte seus jobs para labels específicas. Use cache do Composer e controle concorrência para evitar filas desnecessárias. O exemplo roda testes em diferentes versões de PHP e limita execuções simultâneas por branch.
Ajuste as labels ao seu padrão. Se usar Docker no runner, garanta que o usuário tenha permissão sem expor o socket a jobs inseguros.
name: ci-php
on:
push:
branches: [ main ]
pull_request:
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
name: PHPUnit (PHP ${{ matrix.php }})
runs-on: [self-hosted, linux, docker, php-${{ matrix.php }}]
strategy:
fail-fast: false
matrix:
php: [ '8-1', '8-2', '8-3' ]
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: mbstring, intl, pdo_mysql, redis
coverage: none
- name: Cache Composer
uses: actions/cache@v4
with:
path: |
~/.composer/cache
key: composer-${{ runner.os }}-${{ matrix.php }}-${{ hashFiles('**/composer.lock') }}
restore-keys: |
composer-${{ runner.os }}-${{ matrix.php }}-
- name: Install deps
run: composer install --no-interaction --prefer-dist --no-progress
- name: Run tests
run: vendor/bin/phpunit --colors=always
timeout-minutes: 30
Orquestrando o autoscaling: sinais, limites e gatilhos
Escalar bem é reagir à fila e evitar superprovisionamento. Meça o tempo em fila dos jobs e o tempo de bootstrap do runner, e ajuste o alvo de instâncias.
Crie gatilhos de scale‑out por filas acima de um limiar e scale‑in quando a fila zera por um período. Respeite limites de cota e faça backoff exponencial ao chamar APIs de registro para não estourar rate limits.
Tokens de registro expiram rápido. Gere‑os sob demanda no momento do bootstrap e evite armazená‑los em disco. Pré‑aqueça imagens para reduzir cold start.
- Priorize PRs recentes para reduzir ciclo de feedback.
- Use janelas de resfriamento entre eventos de escala.
- Pré‑aqueça imagens com PHP, Composer e extensões.
- Defina limites máximos para impedir explosão de custos.
- Quotas por repositório e organização, com auditoria.
Observabilidade, custo e performance em pipelines PHP
Colete métricas de fila, tempo de provisionamento e duração do job. Relacione com tamanho de máquina e tipo de storage. Ajuste o autoscaling com dados, não apenas intuição.
Reduza o custo acelerando os builds. Imagens menores e cache eficiente impactam diretamente. Se você constrói imagens Docker no CI, o cache remoto do BuildKit ajuda muito. Combine com boas práticas de multistage e, quando fizer sentido, com imagens multi‑arquitetura.
- Métricas chave: queue_time, runner_boot, job_duration, success_rate.
- Acompanhe consumo de CPU, memória, disco e egress.
- Bake de imagem: PHP, Composer, extensões e ferramentas de teste.
- Reaproveite camadas Docker entre branches com cache remoto.
- Evite instalar toolchains em tempo de job sempre que possível.
Segurança dos runners self‑hosted: práticas essenciais
Runners processam código não confiável em PRs. Isolar é obrigatório. Use runners efêmeros, usuários sem privilégios e segmentação de rede. Evite montar segredos desnecessários.
Restrinja quando PRs de forks podem usar self‑hosted. Prefira executar apenas validações seguras ou use ambientes dedicados com privilégios mínimos.
Mantenha imagens atualizadas. Faça rotação de tokens e limpe caches entre execuções. Registre eventos de escala e acesso aos runners para auditoria.
- Menor privilégio: sem root no job e sem docker.sock em PRs.
- Segredos por ambiente e por repositório, com escopo mínimo.
- Bloqueie portas desnecessárias e limite egress a destinos requeridos.
- Atualize o runner binário e o SO periodicamente.
- Limpeza agressiva de caches entre execuções sensíveis.
Perguntas frequentes
Quando vale a pena migrar para self‑hosted runners?
Quando a fila de jobs cresce, o boot dos hosted adiciona latência, você precisa de extensões nativas específicas, acesso a rede privada ou deseja controlar custos e tamanhos de máquina. Comece com um piloto nos pipelines mais pesados.
Runners efêmeros removem o registro automaticamente?
Sim, o modo efêmero encerra o runner após um job e remove o registro. Ainda assim, tenha rotina de limpeza defensiva para falhas no bootstrap ou timeouts que deixem registros órfãos temporariamente.
Como evitar que PRs de forks usem meus runners?
Separe workflows por evento. Evite acionar self‑hosted em pull_request de forks. Reserve self‑hosted para branches internas ou use ambientes isolados com privilégios mínimos e políticas de segredos restritas.
Como acelerar Composer no CI sem comprometer segurança?
Use cache por versão de PHP e sistema, invalidado por composer.lock. Armazene vendor apenas quando necessário. Rode install com flags de produção quando aplicável e limpe credenciais após o job. Evite reusar caches entre projetos.
Quais métricas devo observar no autoscaling?
Monitore queue_time, tempo de bootstrap do runner, duração do job, taxa de sucesso e custos por job. Acompanhe também CPU, memória, disco e rede para detectar gargalos. Ajuste limites de escala com base nessas séries históricas.
Quer evoluir o autoscaling dos seus runners no GitHub Actions?
Se você precisa revisar labels, isolar segurança e reduzir latência em pipelines PHP, descreva seu contexto e objetivos para traçar próximos passos.
Conversar sobre seu pipeline CIVeja também
- Guia: checklist de pré‑deploy e plano de rollback rápido para aplicações PHP em produção
- Checklist de segurança para pipelines CI/CD com runners self‑hosted e secrets management
- Guia: centralização de logs com Fluentd e Elasticsearch para aplicações PHP em VPS
- Guia: organizar migrations e rollbacks em PostgreSQL para equipes pequenas