Durante uma madrugada, comecei a receber alertas do Gitaly, o mesmo estava com picos de uso de memória que não eram liberados automaticamente após Backup diário.
Esse artigo se trata de um GitLab self hosted no EKS e o comportamento de alguns componentes do gitlab no Kubernetes.
Se você também roda GitLab em Kubernetes, vale entender o que realmente acontece — e por que o Cgroup v2 é a solução definitiva pra esse tipo de problema.
GITALY
O Gitaly é o componente do GitLab responsável por todas as operações Git, como clone, push, pull, merge, diff e blame. Ele isola o armazenamento dos repositórios da aplicação web e se comunica com os demais serviços via gRPC, otimizando a performance e o controle de concorrência.
┌─────────────────────┐
│ GitLab Webservice │
│ │
└──────────┬──────────┘
│ gRPC
↓
┌─────────────────────┐
│ Gitaly │
│ - Git operations │
│ - Repository access │
└──────────┬──────────┘
↓
┌─────────────────────┐
│ Persistent Volume │
│ /home/git/repos │
└─────────────────────┘
GITLAB TOOLBOX BACKUP
É um componente do Gitlab usado para realizar backups em ambientes Kubernetes (especificamente deployado usando helm charts). É um pod/container que contém ferramentas e scripts para executar operações de backup e restore do Gitlab. E quando ele aciona o Gitaly? durante o Backup dos repositórios.
- -Conecta ao Gitaly via gRPC
- -Solicita backup de cada repositório
- -Recebe bundles Git do Gitaly
- -Processa e comprime os dados
- -Envia para algum object storage (S3, GCS, etc)
Durante a execução do cronjob gitlab-toolbox-backup na madrugada, observei um uso elevado de memória no pod do Gitaly. Esse consumo é causado pelo comportamento padrão do kernel Linux, que utiliza a RAM como cache de arquivos lidos do disco (page cache).
Em ambientes Kubernetes, esse comportamento pode gerar problemas de alocação de recursos, pois o kernel é compartilhado entre todos os pods do node.
Sintomas:
Alto uso de memória
Implicações críticas:
- O kernel é único para todo o node
- O Page Cache é global e compartilhado
- CgroupsV1 apenas limitam quanto cada container pode usar
- ** O kernel não sabe o que é um "pod" ou "container" então se o Node tiver muita memória o Kernel vai considerar que tem memória disponível mesmo o pod estourando, pedindo pra morrer. **
┌─────────────────────────────────────────────────┐
│ NODE
│ │
│ ┌──────────────────────────────────────────┐ │
│ │ KERNEL LINUX (único) │ │
│ │ - Gerencia TODA a RAM do node │ │
│ │ - Page Cache é COMPARTILHADO │ │
│ │ - Não sabe o que é "pod" │ │
│ └──────────────────────────────────────────┘ │
│ │
│ ┌────────────┐ ┌────────────┐ ┌──────────┐ │
│ │ Pod A │ │ Pod B │ │ Pod C │ │
│ │ (Gitaly) │ │ (Redis) │ │ (Web) │ │
│ │ │ │ │ │ │ │
│ │ Vê: │ │ Vê: │ │ Vê: │ │
│ │ Limit:8GB │ │ Limit:4GB │ │ Limit:2GB│ │
│ └────────────┘ └────────────┘ └──────────┘ │
│ │
│ RAM Total: 32GB │
│ Cache Total: 20GB (visível para TODOS) │
└─────────────────────────────────────────────────┘
Obs: Page Cache é a RAM usada pelo kernel para cachear arquivos do disco.
Fluxo de Backup
O que acontece?
Durante o backup (1h):
- Gitaly lê centenas de repositórios Git
- Kernel cacheia tudo: "Vou guardar esses arquivos .git na RAM"
- Backup termina: Processo Gitaly volta ao normal (195MB)
- Kernel não limpa: Cache fica marcado como "active_file" = 35.6GB
- Kubernetes vê: Pod usando 37GB → perigo de OOM!
Por que não limpa automaticamente?
O cache está marcado como "active" (não "inactive"), então o kernel pensa:
"Esses arquivos foram usados recentemente"
"Provavelmente vão ser usados de novo em breve"
"Vou manter eles na RAM"
Mas como é um backup que roda 1x por dia, esses arquivos não vão ser usados de novo até amanhã!
Possíveis soluções estudadas:
| Opção | Esforço | Benefício | Recomendação |
|---|---|---|---|
| Migrar para cgroup v2 | Alto (reboot nodes) | Solução definitiva | A melhor opção |
| CronJob privilegiado | Baixo (15min) | Resolve o problema | se estiver com pressa |
| DaemonSet monitor | Médio (1h) | Automático | Opcional |
| Aumentar limite | Baixo | Paliativo | Só em emergência |
Como podemos ver existem paliativos, porém a melhor opção a longo prazo é Cgroup V2. Só tem um pouco de complexidade para aplicar, mas mesmo assim se destaca pelos benefícios.
Dados do Cgroup V1 atual:
cache: 38829035520 # 36.2 GB !!!!!
rss: 204779520 # 195 MB
inactive_file: 568246272 # 542 MB
active_file: 38260654080 # 35.6 GB !!!!!
**35.6GB de `active_file`** = arquivos em cache ativo (page cache)!
Breakdown:
- Processo Gitaly (RSS): 195 MB
- Cache de arquivos ativos: 35.6 GB ← AQUI!
- Cache de arquivos inativos: 542 MB
- Total cache: 36.2 GB
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Total do pod: ~37 GB
Cgroup V2
Cgroup V2 tem um recurso chamado PSI que detecta quando há "pressão" de memória:
# cgroup v2 expõe:
/sys/fs/cgroup/memory.pressure
# Conteúdo:
some avg10=0.00 avg60=0.00 avg300=0.00 total=0
full avg10=0.00 avg60=0.00 avg300=0.00 total=0
Quando há pressão, o kernel automaticamente libera cache mesmo que seja "active"!
Cgroup v2 é a segunda geração do sistema de control groups do kernel Linux, com melhorias significativas sobre o v1, atualmente o EKS do Gitlab está com Cgroup V1.
Cgroup v1 tem múltiplas hierarquias independentes (memory, cpu, io), causando inconsistências. Cgroup v2 usa uma única árvore:
Bom, é isso... tive a oportunidade de estudar sobre essa semana e quis compartilhar o conhecimento.
Docs: backup-restore
Kernel Tuning and Optimization for Kubernetes: A Guide
Linux Kernel Version Requirements


Top comments (0)