Pendências¶
TLDR: bypass de CI pra desfazer depois, débito técnico real (não gambiarra descartável), rotação de senha planejada, a rotina de operação contínua ainda por criar, e o trabalho maior da organização (arquivar repositório, consolidar serviço, padronizar GitOps) ainda não retomado. RabbitMQ foi desligado em 2026-08-24. Cada item tem uma condição clara de quando resolver, não fica em aberto pra sempre sem critério.
Esta página existe pelo mesmo motivo de Estado fora do git: decisão deliberada de adiar algo é diferente de esquecer que ela existe. Toda vez que um bypass, gate desligado, ou solução temporária entrar no repositório, uma linha entra aqui antes de considerar o trabalho terminado.
Bypasses de CI a desfazer¶
Ordem deliberada, decisão do usuário em 2026-08-21: corrigir os bypasses de CI desta seção só depois de todo o resto do backlog deste documento estar resolvido, não antes e não em paralelo. Motivo prático: promover um check de continue-on-error: true pra bloqueante quebra merge de qualquer PR (inclusive da sessão paralela que também trabalha neste repositório) até o baseline estar limpo, então mexer aqui cedo demais é fonte de atrito sem necessidade. Melhor deixar por último, quando o resto já estiver estável.
-
misconfig(trivy) emsecurity.ymlestácontinue-on-error: truedesde 2026-08-21. Motivo: a primeira varredura real achousecurityContextausente empostgres,mariadb,minio,adminer(namespacedados),rabbitmqeredis-server, achado real, mas cada imagem tem UID/GID próprio, corrigir errado quebra o container. Condição pra reverter pra bloqueante: cada Deployment listado abaixo, em "Débito técnico:securityContext", corrigido e validado contra o serviço real. - Todos os checks de
codigo-e-infraemquality.yml(yamllint,ansible-lint,jscpd,kubeconform,shellcheck,actionlint,zizmor,cspell,ast-grep,kubescape) sãocontinue-on-error: truedesde que o job foi criado, nunca foram bloqueantes. Condição pra promover cada um: revisar o baseline de achados daquele check especificamente, zerar o que for real, só então tirar ocontinue-on-error(ver Qualidade de código e infraestrutura). -
argocd/apps/operators/cert-managernão existe mais, exclusões correspondentes removidas: resolvido em 2026-08-21, efeito colateral da migração do cert-manager pro chart oficial (ver auditoria de chart oficial abaixo).cert-managertirado deskip-dirs(trivy,security.yml),--exclude-namespaces(kubescape,quality.yml; o chart oficial já definesecurityContextpor padrão, resolvendo também o débito desecurityContextausente pra esse componente específico),--ignore(jscpd),ignore(.yamllint),ignorePaths(.cspell.config.yaml),ignoresda regrano-comments-yamldo ast-grep (bloco inteiro removido, já que não sobrou nenhuma exceção;cnpgnunca precisou dessa exclusão específica).argocd/apps/operators/cnpgcontinua excluído dos mesmos checks (exceto kubescape, que ver abaixo), permanente por natureza (RBAC amplo e conteúdo exigido pelo próprio operador vendorizado, ver Foundation) até esse operador também migrar pro chart oficial.find -not -pathna geração manual dokube-diagrams(não é CI, ver Topologia declarada do cluster) ainda precisa de conferência/ajuste separado (não coberto por este item).
Licença do repositório¶
- Este repositório não tinha
LICENSE: resolvido em 2026-08-21, criadoLICENSE(MIT, mesmo texto domanagement-service,Copyright (c) 2024-present LADESA e Colaboradores) e linkado noREADME.md.
Segurança do próprio Argo CD¶
Achados da auditoria de 2026-08-21 (ver Aprender: Argo CD). O detalhe dos que seguem em aberto fica no inventário privado apontado por Estado fora do git.
- TLS real na UI/API do Argo CD: resolvido. Estava em HTTP puro sem redirect (confirmado ao vivo com
curl,HTTP 200completo na porta 80) e, na 443, caía no certificado autoassinado padrão do Traefik. Corrigido comserver.ingress.tls: true+ annotationcert-manager.io/cluster-issuer: ladesa-ro-issuer-production; certificado real do Let's Encrypt confirmado (argocd-server-tls, válido até 2026-11-19). -
resources(requests/limits) em todos os componentes do Argo CD: resolvido, mas revelou um achado maior, registrado no inventário privado. Também desligadosdex/notifications(rodavam sem nenhuma configuração real) e zeradoapplicationSet.replicas(nenhumApplicationSetexiste no cluster). - TLS real em
infisical.ladesa.com.br,portainer.ladesa.com.breadminer.ladesa.com.br: resolvido em 2026-08-21, mesmo padrão do Argo CD (tls:+cert-manager.io/cluster-issuer: ladesa-ro-issuer-production), certificados reais do Let's Encrypt confirmados ao vivo pros três. OIngressdoadminernão existia no git (criado fora de banda pela UI do Portainer,io.portainer.kubernetes.ingress.owner: unknown), trazido pro git agora emargocd/foundation/dados/adminer.yaml. Pegadinha real descoberta no processo:portainer/infisicaltêm seuIngressgerado por chart Helm comvaluesembutido direto no manifest daApplication(argocd/applications/), e esse manifest só é aplicado no cluster quando aApplicationrooté sincronizada (ver "O padrão app-of-apps"); sincronizarfoundation-portainer/foundation-infisicaldireto, sem sincronizarrootprimeiro, aplicou os recursos usando ospec.source.helm.valuesantigo, ainda em cache no objetoApplication, produzindo um "sucesso" que não mudou nada. Só percebido conferindo oIngressao vivo depois, não confiando noargocd app diff --core(que também não renderirizou o chart Helm corretamente nesse modo, mais um motivo pra sempre verificar o recurso real, não só a saída do comando). - TLS real em
sso.ladesa.com.br: resolvido em 2026-08-21, na mesma mudança que migrou o provedor de identidade pro Argo CD. Servia em HTTP puro, e o 443 caía no certificado autoassinado padrão do Traefik. Agora serve certificado da Let's Encrypt emitido peloladesa-ro-issuer-production, o mesmo emissor dos outros nove certificados do cluster, declarado emgitops/apps/sso/values-production.yamldo repositório satélite. O HTTP continua respondendo, ver o item sobre oissuerdo OIDC no inventário privado. - Achados desta revisão que seguem em aberto: descritos no inventário privado apontado por Estado fora do git, e não aqui. Quem for trabalhar neles consulta lá. Conforme cada um for corrigido, a descrição completa volta pra esta página, porque problema resolvido pode ser documentado por inteiro sem servir de mapa pra ninguém.
Revisão de segurança: portas, Ingress e Service expostos¶
Levantamento completo feito em 2026-08-21, a pedido explícito do usuário ("revisão de segurança de tudo o que está liberado: portas no host, ingress, services"). Metodologia: nada assumido, cada achado testado ao vivo (nc/curl de fora do node, não só lendo configuração).
- Achados desta revisão que seguem em aberto: descritos no inventário privado apontado por Estado fora do git, e não aqui. Quem for trabalhar neles consulta lá. Conforme cada um for corrigido, a descrição completa volta pra esta página, porque problema resolvido pode ser documentado por inteiro sem servir de mapa pra ninguém.
- NodePorts e
LoadBalancerdo cluster, achado positivo: todos osServicedesse tipo foram testados ao vivo de fora do node e estão bloqueados de verdade, só 22, 80 e 443 respondem. Corrige uma suposição antiga em Foundation, que atribuía o bloqueio a um NAT que não existe. Sem ação necessária, achado só de correção de registro. O detalhe fica no inventário privado apontado por Estado fora do git.
Débito técnico: securityContext ausente¶
Acompanha o item acima. Um item por serviço, porque a correção de cada um é independente (UID diferente, risco de quebrar não é o mesmo):
-
argocd/foundation/dados/postgres.yaml: resolvido pela migração pro CloudNativePG em 2026-08-21 (o operador roda com usuário fixo não-root por padrão), arquivo removido do repositório, ver Foundation. -
argocd/foundation/dados/mariadb.yaml: resolvido em 2026-08-21, migrado promariadb-operator, arquivo removido do repositório, ver Foundation. -
argocd/foundation/dados/minio.yaml: resolvido em 2026-08-21. A imagem Silo não define nenhum usuário não-root noDockerfile(roda comouid=0por padrão, sem convenção própria, igual ao MinIO upstream), então foi escolhidouid=1000/gid=1000arbitrário. Backup dohostPathfeito antes (tar czf, ~19KB, só.minio.sysvazio, confirmado sem bucket real),chown -R 1000:1000 /root/dados/vols/miniodata/no node (autorizado explicitamente pelo usuário, ação bloqueada pelo classificador de modo automático), depoissecurityContextadicionado aoDeployment. Validado:idao vivo no pod confirmauid=1000, servidor Silo iniciou sem erro de permissão em/data. -
argocd/foundation/dados/adminer.yaml: resolvido em 2026-08-21.idao vivo confirmou que a imagem já roda comouid=100(adminer) gid=101(adminer)por padrão, sem hostPath nem estado nenhum, entãorunAsNonRoot: true/runAsUser: 100/runAsGroup: 101só formaliza o que já acontecia; validado com rollout limpo (1/1 Running, sem restart). -
argocd/foundation/rabbitmq/deployment.yaml: resolvido em 2026-08-21 pela migração pro RabbitMQ Cluster Operator, que já roda comrunAsNonRoot: true,runAsUser: 999,readOnlyRootFilesystem: true, capabilities dropadas por padrão. Odeployment.yamllegado foi removido em 2026-08-24 junto do desligamento completo do RabbitMQ (ver Desligar o RabbitMQ), fechando este item de verdade. -
argocd/foundation/redis/deployment.yaml: resolvido em 2026-08-21.idao vivo confirmou que a imagem Bitnami já roda comouid=1001/gid=0por padrão (convenção Bitnami de UID arbitrário compatível com OpenShift), entãorunAsNonRoot: true/runAsUser: 1001/fsGroup: 0só formaliza o que já acontecia. Validado depois do rollout:DBSIZE(1856 chaves) e as 23 conexões BullMQ do Infisical intactas, nenhum dado perdido no restart.
Rotação de senha planejada¶
Ver Estado fora do git, rotação preventiva pro procedimento de cada item. Itens específicos que valem rotação assim que a condição bater, não só "algum dia":
- As 4 roles do Postgres (
ladesa,infisical,api-dev,sso-production): depois que a migração pro CloudNativePG estiver concluída e confirmada (o processo de import descriptografa a senha de cada role em memória volátil no node, mesmo nunca gravando em disco ou git, defesa em profundidade justifica rotacionar depois, não só confiar que não vazou). - Senha do Ansible Vault: sem condição de gatilho específica ainda, cadência a definir pela equipe (ver rotação preventiva).
- As duas deploy keys (
infrastructure,infrastructure-vault): mesma cadência a definir, ou imediatamente se alguém que teve acesso ao node sair da equipe (ver ciclo de vida de acesso).
Rotina de operação contínua a criar¶
Hoje só existe documentação de bootstrap (rodar uma vez, do zero até o cluster de pé, ver Bootstrap mínimo na VM). Falta a contraparte de manutenção contínua: uma seção própria, separada do bootstrap, cobrindo o que se repete depois que o cluster já está rodando.
- Rotação de senha recorrente, cobrindo os dois universos de segredo deste repositório, não só um: os de bootstrap (Ansible Vault, ver acima) e os gerenciados pelo Infisical (
mariadb-env,minio-env,rabbitmq-config, os próprios segredos internos do Infisical). Definir cadência, quem executa, e o procedimento exato pra cada categoria (os dois não se resolvem do mesmo jeito: um éansible-vault+scripts/create-from-cluster, o outro é a UI/API do Infisical). - Acompanhamento de saúde do cluster (o que checar, com que frequência, o que fazer se algo estiver fora do esperado) ainda não tem escopo definido, precisa ser desenhado antes de virar documentação.
- Revisão dos bypasses e afrouxamentos de CI registrados nesta página (ver Bypass e afrouxamento de CI): reler a seção "Bypasses de CI a desfazer" acima e achar o que já tem condição de reversão satisfeita, não deixar acumular.
- Revisão de versão de cada ferramenta do stack de CI (ver Versões desatualizadas das ferramentas de bootstrap pro equivalente do lado Ansible/Helm/k3s): as ferramentas hermitizadas em
.config/hermit/hermit-packages/*.hcle as actions pinadas por hash nos workflows, conferindo se algum release novo já passou da janela de cooldown de 7 dias. - Revisão de licença de cada ferramenta do stack de CI (ver Licenças das ferramentas de CI): licença pode mudar numa atualização de versão, não só na adoção inicial, o caso real do
gitleaks-actionv2 (MIT pra licença própria) é o motivo desta linha existir. - Inventário do node, percorrendo três superfícies item por item: pacotes instalados, units habilitadas no
systemd, e sockets em escuta. Pra cada item, as mesmas três perguntas: quem é o dono, ainda serve, e a exposição de rede dele é compatível com quem alcança aquela interface. O desfecho é uma das três saídas de sempre, declarar, restringir, ou remover. Apoiado porscripts/inventariar-node.sh, previsto na próxima leva, que torna a passada repetível em vez de depender de quem lembra dos comandos. Achado com implicação de segurança vai pra quem opera o node, não pra esta página. - Decidir onde essa seção mora:
docs/operacao/ganha uma página nova (ex.:manutencao.md), ou vira parte de Checklist com uma segunda tabela, separada da de bootstrap.
Modernização de Applications do argocd/foundation¶
Levantamento feito em 2026-08-21, pesquisando estado atual de cada tecnologia antes de propor qualquer mudança (mesmo cuidado da migração do Postgres pro CloudNativePG, que já está concluída, ver Foundation). Cada item precisa do mesmo processo cuidadoso (pesquisa, plano, cluster descartável, dead man switch) antes de ir pra produção, nenhum aqui é "só trocar o YAML".
-
argocd/foundation/dados/minio.yaml: resolvido em 2026-08-21, imagem trocada pro Silo (fork comunitário mantido pelo projeto Pigsty, drop-in, mesmo CLI/env vars/formato de dado). Investigação achou que este MinIO nunca teve uso real (.minio.syssó, zero bucket, nenhum consumidor no cluster referenciando as credenciais), então a troca foi direta, sem migração de dado. De quebra, resolveu os 9652 restarts históricos do pod antigo (erro crônico "drive /data offline", não reproduzido no Silo). -
argocd/foundation/dados/mariadb.yaml: resolvido em 2026-08-21, migrado promariadb-operator(Helm, mesmo padrão de chart externo do Infisical). Sem import de dado (diferente do Postgres): investigação achou zero database de aplicação, só schemas de sistema, senha dorootpreservada viarootPasswordSecretKeyRef. Ver Foundation. -
argocd/foundation/rabbitmq/deployment.yaml: resolvido em 2026-08-21. Achado urgente que motivou a migração: a imagemdocker.io/bitnami/rabbitmq:3.12não existe mais (docker pullretornanot found, confirmado ao vivo), o repositóriodocker.io/bitnamifoi descontinuado e apagado em 2025. Mitigado de imediato trocando pradocker.io/bitnamilegacy/rabbitmq:3.12(pinada por digest), depois migrado por completo pro RabbitMQ Cluster Operator oficial (argocd/foundation/rabbitmq-operator/, release manifest vendorizado v2.22.4, mesmo padrão do cert-manager/CNPG, sem Helm).RabbitmqClusternovo (argocd/foundation/ladesa-ro-production-rabbitmq-cluster/) com import nativo de definitions (rabbitmqctl export_definitions, senhas preservadas comopassword_hash, nunca em texto puro, segredo cifrado eminfrastructure-vault). Achado não documentado em lugar nenhum: o startup probe padrão do operador (/api/health/checks/reached-target-cluster-size) só existe a partir do RabbitMQ 4.2.4, causando 404 permanente em qualquer versão anterior (inclusive 3.13); resolvido com a annotationrabbitmq.com/legacy-startup-probe: "true", documentada oficialmente pra esse cenário. Cutover de produção feito trocando o selector derabbitmq-amqp/rabbitmq-webpro novo pod (kubectl patch, sem Argo CD, mesma cautela do Postgres/MariaDB) e reconectando os consumidores reais (managements-service,timetable-generator-dev) comrabbitmqctl close_all_connectionsno broker antigo, verificado ao vivo (rabbitmqctl list_connections) antes e depois. Application do cluster promovida praautomated. Legado removido em 2026-08-24: o Deployment/PVC/Service antigos (argocd/apps/messaging/rabbitmq/) foram apagados junto do desligamento completo do RabbitMQ, depois de confirmar zero conexões e zero consumidores ao vivo (ver Desligar o RabbitMQ). -
argocd/foundation/cert-manager/cert-manager-v1.16.2.yaml: resolvido em 2026-08-21, atualizado pra v1.21.1 (release estável mais recente na data), ver Foundation. Sobrou lixo inofensivo: a v1.21 removeu oRole/RoleBindingcert-manager-tokenrequest(RBAC de um recurso que este cluster nunca usou, nenhumIssuer/ClusterIssuerreferenciaserviceAccountRef), Argo CD sinalizou comorequires pruningmas oargocd app sync --prunefoi bloqueado pelo classificador de modo automático desta sessão. Os dois objetos continuam no cluster, sem efeito nenhum (não concedem nada que algo use); remover é seguro quando alguém rodarargocd app sync foundation-cert-manager --prunemanualmente. -
argocd/foundation/redis/deployment.yaml: avaliado em 2026-08-21, decisão: manter single instance, sem operador. Investigação ao vivo (CLIENT LISTno broker, nomes de fila decodificados de base64) achou que este Redis não é só cache: é o backend de fila do BullMQ usado internamente pelo Infisical (23 conexões ativas, filas comosecret-rotation,dynamic-secret-revocation,audit-log,ca-crl-rotation,secret-full-repo-scan, entre outras, todas do mesmo cliente). Isso mudaria a decisão se não houvesse persistência, masappendonly yes(AOF) já está ativo e a PVC (redis-pvc, 5Gi,local-path) já sobrevive a um restart de pod sem perda (1856 chaves confirmadas ao vivo), então o requisito real de durabilidade já está atendido pela configuração atual, HA/Sentinel/Cluster não é necessário pra esse risco específico. Achado à parte, sem urgência: a imagem ébitnami/redissem tag (latestimplícito, ainda puxável e atualizado há poucos dias, diferente do caso do RabbitMQ), o que é reprodutibilidade ruim (atualização silenciosa a cada restart de pod) mas não indisponibilidade iminente; pinar numa versão+digest específicos fica como item de robustez, não como bloqueio. -
argocd/foundation/dados/adminer.yaml: ferramenta de debug simples, não precisava de operador.securityContextjá resolvido (ver acima),Ingresscom TLS real trazido pro git também (ver "Segurança do próprio Argo CD" acima).
Débito técnico: organização de pastas e arquivos em argocd/¶
Levantado em 2026-08-21, a pedido explícito do usuário ("analise outros débitos técnicos e registre, seja criativo e meticuloso"). Nada aqui quebra nada hoje, é dívida de legibilidade/manutenção, mas com o repositório crescendo (14 Application já em argocd/applications/) fica mais caro de corrigir quanto mais tempo passar.
- Repetição do prefixo
foundation-em todo arquivo deargocd/apps/: resolvido em 2026-08-21.argocd/apps/eargocd/foundation/reorganizados por categoria (operators/,data/,messaging/,platform/), prefixo removido, nome do arquivo virou só o nome do componente.metadata.namede todaApplicationnão mudou (só o arquivo/pasta), condição explícita do usuário pra não recriar nenhum recurso, ver Aprender: Argo CD sobre por que isso importa. - Separação por categoria, inexistente antes: resolvido junto do item acima, mesma reorganização, mesmas categorias da referência do
Abrl(operators/,data/,messaging/,platform/, adaptadas ao que este repositório realmente tem). - Inconsistência de nomenclatura entre o
Applicatione a pasta que ele aponta: resolvido em 2026-08-21.dados-mariadb/dados-postgres-clusteragora aninhados emargocd/apps/data/, junto dodados(MinIO/Adminer). O nome de arquivo do RabbitMQ de produção encurtou pramessaging/rabbitmq-cluster.yaml(ometadata.namedaApplication,foundation-ladesa-ro-production-rabbitmq-cluster, continua igual, só o arquivo). - Avaliação de Helm chart local, pedida explicitamente pelo usuário, comparando com o padrão do
Abrl: decisão seletiva, não wholesale (este cluster é single-environment, sem o ganho de reaproveitar template com valores diferentes que justifica oAbrlembrulhar quase tudo em chart). Adotado chart Helm local (Chart.yaml+templates/+values.yaml) só pros dois CRs com dado real (argocd/apps/data/dados-postgres-cluster/,argocd/apps/messaging/rabbitmq-cluster/), templatizando só os campos que fazem sentido variar (imagem, storage, resources/replicas), mantendo bootstrap/roles/Database/Serviceestáticos. Os Deployments simples (dados,rabbitmqlegado,redis) continuam manifesto cru: não há hoje ganho real de parametrização pra umDeploymentcom imagem e réplica fixas, embrulhar em chart só pra seguir padrão seria ritual sem função. Verificado antes de aplicar:helm templatede cada chart comparado documento a documento (não byte a byte, a saída dohelm templatereordena porkinde inclui comentário# Source:que nunca chega no cluster) contra o manifesto original, idêntico nos dois casos; depois do sync,specao vivo doCluster/RabbitmqClusterconfirmado byte-idêntico ao backup pré-mudança. Nenhum recurso recriado (AGEinalterado em ambos). - Nomenclatura dos dois diretórios de topo não batia com o
Abrl: resolvido em 2026-08-21.argocd/apps/(14 arquivos deApplication) renomeado praargocd/applications/, eargocd/foundation/(conteúdo/charts) renomeado praargocd/apps/, batendo exatamente comgitops/applications/+gitops/apps/doAbrl(git mv, histórico preservado).argocd/root/application.yaml(spec.source.path) e as 7Applicationcujosource.pathapontava praargocd/foundation/...atualizadas na mesma leva. Sequência de 3 commits (suspenderautomated, aplicar rename, restaurarautomated, mesma disciplina já usada nas migrações de chart) pra nenhumaApplicationcomselfHeal: truereagir a uma mudança desource.pathno meio do processo. Achado novo, mesma fronteira doproject.yaml:argocd/root/application.yamltambém não é sincronizado pelo próprioroot(é opathda própriaApplicationroot, não pode se autoatualizar via sync dela mesma), exigindokubectl apply -f argocd/root/application.yamldireto no node depois do pull, lição detalhada em Aprender: Argo CD. Confirmado depois: as 14Applicationcomautomatedrestaurado exatamente com oprunede antes da suspensão (11 comprune: true, edados-mariadb/cnpg/cert-managercomprune: false, mitigação doNamespacenão declarado pelo chart, ver abaixo),rootcontinua o único gateManual, zero pod reiniciado, zero recurso recriado. - Corrigir a causa raiz do
Namespacenão declarado (cert-manager e CNPG), em vez de só mitigar comprune: false: resolvido em 2026-08-21, mesmo padrão dodados-mariadb: wrapper chart local (argocd/apps/operators/cert-manager/eargocd/apps/operators/cnpg/,Chart.yamlcom o chart oficial comodependencies:,templates/namespace.yamldeclarando oNamespaceexplicitamente).helm templatedo wrapper comparado documento a documento contra o chart direto nos dois casos, diferença única e esperada: oNamespacea mais. Sequência de 3 commits por operador (suspenderautomated, trocarsourcepro path local, restaurarautomated, agora comprune: true), cert-manager primeiro (menor stakes), CNPG depois com odiffconferido três vezes antes de sincronizar, dado o incidente anterior. Nos dois casos odiffposterior ao sync veio totalmente vazio (sem exceção doNamespace, diferente de antes), confirmando a causa raiz resolvida, não só mitigada. Verificado depois:uid/creationTimestampdoNamespacee de todo recurso inalterados nos dois (zero recriação),Cluster postgresde produção continuouCluster in healthy stateo tempo todo, pod sem restart.automated.prunedos dois viroutrue(não precisa mais defalse).dados-mariadbcontinua comprune: falsepor um motivo diferente e não relacionado (mitigação de escopo mais grosso pra um CR de produção, não o caso doNamespacenão declarado, ver acima na auditoria de chart oficial), fora do escopo desta correção. - Auditoria completa de chart oficial pra cada tecnologia do repositório, pedido explícito do usuário ("que o chart interno tente usar o chart oficial, confiável, mantido, respeitável, maduro, atualizado, de cada dependência... e o stakater/application como fallback pra caso não tenha"), levantamento em 2026-08-21 componente por componente (
helm search repo/helm show valuescontra o repositório oficial de cada projeto, não suposição): - cert-manager (operador): resolvido em 2026-08-21, migrado pro chart oficial
jetstack/cert-manager(v1.21.1, mesma versão do manifesto vendorizado anterior). Plano próprio com dead man switch, executado com o mesmo rigor do item de maior risco desta lista:helm templatecomparado documento a documento contra o manifesto antigo (só diferença: labelshelm.sh/chart/app.kubernetes.io/managed-byaditivas, e oNamespaceque o chart não renderiza, protegido por construção já quecert-managercontinua semautomated/prune); backup completo de Deployments/Services/RBAC/webhooks/CRDs antes de tocar em qualquer coisa;AppProject(argocd/root/project.yaml) precisou dehttps://charts.jetstack.ioadicionado emsourceRepos(aplicado direto viakubectl apply, já queargocd/root/*.yamlnão é gerenciado pelo sync do próprioroot, é do bootstrap do Ansible); dead man switch (systemd-run --on-active=10min, reaplicando o manifesto original limpo) armado antes do sync, só cancelado depois de provar o webhook funcionando de verdade, não só os pods de pé, criando umCertificatede teste descartável contra oladesa-ro-issuer-productionreal e confirmando a cadeia completa (CertificateRequestaprovado,Order,Challengecriados) antes de limpar. Verificado depois:uid/creationTimestampidênticos nos 3 Deployments e nas 6 CRDs (zero recriação), todoCertificatereal do cluster continuaReady: Truecom idade inalterada, os 4 hosts públicos (argocd/infisical/portainer/adminer) respondendo200sobre TLS.syncPolicypromovido praautomated: {prune: false, selfHeal: true}em 2026-08-21 (revisitado depois do CNPG, que ensinou na prática o motivo deprune: falseaqui: o chart não renderizaNamespace, entãoprune: trueapagaria o namespace inteiro no próximo ciclo, mesmo incidente do CNPG documentado em Aprender: Argo CD). Confirmado depois: namespacecert-managerintacto, 3 pods sem restart. - CloudNativePG (operador): resolvido em 2026-08-21, migrado pro chart oficial
cnpg-op/cloudnative-pg(0.29.0,appVersion 1.30.0, mesma versão já rodando). Diferente do cert-manager: o chart usa convenção de nome diferente do manifesto vendorizado (Deployment/ServiceAccount/ClusterRole/ClusterRoleBindingviramcnpg-controller-manager/cnpg-manager, ambos renomeados pracnpg, sem jeito de preservar viavalues, confirmado no_helpers.tpldo chart), então a migração envolveu rename de verdade desses 4 recursos (CRDs, webhooks eServicemantiveram nome). Investigado antes de prosseguir, a pedido explícito do usuário ("investigar se isso pode causar perda de dados"): confirmado ao vivo que oClusterde produção não temownerReferences, e que o Pod/PVC reais são possuídos peloClusterCR, não peloDeployment/ServiceAccountdo operador, zero cadeia de cascata entre os dois.valuescorrigido pra restaurarresources(chart não define nenhum por padrão, crítico dado o node sem folga de memória) eimagePullPolicy: Always(chart usaIfNotPresentpor padrão). Perdidas 6ClusterRolegranulares de RBAC agregável (editor/viewer por tipo de CRD: database/publication/subscription) que o manifesto antigo tinha e o chart não replica. Confirmado que nenhumaRoleBindingas usa hoje, aceito como redução de escopo sem impacto real.- Incidente real durante o sync com
--prune(necessário pra completar o rename): oNamespace cnpg-systemtambém apareceu como "só ao vivo, não no alvo" (mesmo caso doNamespacedo cert-manager, o chart não o renderiza), e por estar na mesma leva de prune do rename, foi prunado junto, cascateando a exclusão de tudo que tinha acabado de ser criado no namespace (o operador ficou fora do ar por ~1 minuto). OClusterde produção (namespacedados, fora docnpg-system) nunca foi afetado, confirmado ao vivo durante o incidente (Cluster in healthy stateo tempo todo). Recuperado com um segundoargocd app syncsem--prune, que recriou oNamespace(viaCreateNamespace=true) e todos os recursos do operador de uma vez. Lição nova, registrada em Aprender: Argo CD:--pruneexplícito numa sincronização de rename afeta qualquer recurso "só ao vivo", não só os que a pessoa tinha em mente, inclusive oNamespace, se o chart não o declarar.automated.prunerestaurado comofalse(nãotrue) no final, mesma mitigação dodados-mariadb, justamente pra essa situação nunca se repetir automaticamente.
- Incidente real durante o sync com
- CloudNativePG,
ClusterCR: avaliado a fundo em 2026-08-21, decisão explícita: manter o chart local atual, não migrar. Tem chart oficial (cnpg/cluster, mesmo repositório do operador), e cobre boa parte do que oClusterde produção precisa (recovery.method: importtipomonolithproduz exatamente o mesmobootstrap.initdb.import+externalClustersque o chart local já declara na mão, confirmado renderizando o próprio fixture de teste do chart oficial;cluster.rolesreproduzmanaged.rolescompasswordSecretpor role igual;fullnameOverrideforça o nome doClusterprapostgres, batendo com o que já existe). Achado que motivou não migrar: o templatedatabases.yamldo chart oficial nomeia todaDatabaseCR como{{ fullname }}-{{ nome }}(ex.:postgres-api-dev-v1), sem nenhum campo de override disponível nosvalues, então adotar o chart renomearia as 5DatabaseCR de produção (api-dev-v1,api-dev-v2,infisical,sso-production,management-service-v3) sem alternativa, e o chart embute umServicegerenciado só pelo mecanismo nativo do operador (spec.managed.services, não um manifesto que o Argo CD aplica direto), o que faria o Argo CD parar de rastrear oService db-postgresque ele rastreia hoje, uma mudança de responsabilidade adicional.databaseReclaimPolicy: retainprotege o banco real do rename (o CNPG casaDatabaseporspec.name, não por identidade do objeto Kubernetes), mas o custo real (rename de 5 recursos de produção, handoff de posse doService) não trouxe ganho proporcional: o chart local hand-rolled já funciona, já temignoreDifferencescobrindo os campos que o CNPG default sozinho (ver "Auditoria desyncPolicy.automated" acima), e não é mais difícil de manter que o wrapper que a migração exigiria de qualquer jeito. Decisão do usuário, com a pesquisa completa registrada aqui pra não precisar refazer se a pergunta voltar. - mariadb-operator (operador): já usa chart oficial (
mariadb-operator/mariadb-operator+mariadb-operator-crds,https://helm.mariadb.com/mariadb-operator), nada a mudar. - mariadb-operator,
MariaDBCR: resolvido em 2026-08-21, migrado pro chart oficialmariadb-operator/mariadb-cluster(26.6.0, mesmo repositório do operador). O chart não expõe hook de annotation customizada nem gera oServicedb-mariadbque as aplicações já usam. Resolvido com wrapper chart local (Chart.yamlcom o chart de terceiro comodependencies:,templates/service.yamlpróprio proService), não multi-source (tentativa inicial, abandonada em favor do wrapper, ver Aprender: Argo CD), eautomated.prune: falseno lugar do antigoPrune=falsepor-recurso (mitigação de escopo mais grosso). Verificadohelm templateidêntico campo a campo ao CR antigo antes do commit, especao vivo sem recriação depois do sync (MariaDBCR mantevecreationTimestamp,Servicemanteve os 312 dias de idade). Decisão explícita: o wrapper chart não foi generalizado pra outro chart de terceiro do repositório (mariadb-operator,mariadb-operator-crds,portainer,infisical,secrets-operatorcontinuam com referência direta). Pesquisa confirmou que umbrella/wrapper chart só compensa quando há recurso extra acoplado, esse é o único caso aqui. - RabbitMQ Cluster Operator (operador): sem chart oficial (só Bitnami/comunidade, nenhum no próprio repositório
rabbitmq/cluster-operator). Manifesto vendorizado continua sendo a escolha certa. - RabbitMQ Cluster Operator,
RabbitmqClusterCR: sem chart oficial. Chart local escrito nesta sessão continua sendo a escolha certa, não é dívida. - Infisical (
secrets-operator, appinfisical-standalone) e Portainer: já usam chart oficial de cada projeto (repositórios próprios deles), nada a mudar. - MinIO/Silo: resolvido em 2026-08-21, mas não como a nota original desta linha assumia. Pesquisa mais funda (pedida explicitamente antes de mexer em qualquer coisa, ver Metodologia de mudança) revelou que não existe
helm/silo/standalone no repositório do Pigsty como um chart simples: o caminho realmente oficial e maduro é o MinIO Operator + Tenant chart (da própria MinIO Inc., comtenant.imagesobrescrito prapgsty/silo), arquitetura pesada (CRDTenant, Operator próprio, RBAC cluster-wide) desproporcional ao uso atual (zero bucket real). Existe também umsilo-operatorespecífico do Pigsty, mas é projeto comunitário ainda em avaliação pra nem entrar no README oficial, não confiável/maduro o bastante. Decisão, com o usuário: migrar pro fallback genéricostakater/application(argocd/applications/data/minio.yaml,foundation-minio,9.3.1), a mesma política de fallback já definida pra quando não existe chart oficial do tamanho certo.helm templatecomparado documento a documento contra o manifesto antigo antes do sync: idêntico, exceto a troca deliberada de volumehostPath(/root/dados/vols/miniodata, agora órfão no disco do node, sem dado real, não limpo automaticamente) por umaPersistentVolumeClaimgerenciada pelo chart (local-path, 1Gi). Sequência de 3 commits (suspenderautomateddefoundation-dados, prune doDeployment/Serviceantigos com autorização explícita do usuário pro--prune, criarfoundation-minioe sincronizar, por fim restaurarautomatednos dois, agorafoundation-miniotambém comprune: true, sem motivo pra ficar mais restrito). Verificado depois: podRunning, PVCBound,S3 APIe console respondendo200de verdade (curlcontra os dois NodePorts),diffvazio. - Adminer: ferramenta de debug de um container só, não existe (nem faz sentido existir) um "chart oficial" pra ela como tecnologia. Se algum dia virar chart, seria via o fallback genérico abaixo, não um chart específico do Adminer.
- Redis: resolvido em 2026-08-21, migrado pro fallback
stakater/application(argocd/apps/platform/redis/,foundation-redis,9.3.1), sem chart oficial de primeira linha pro caso de uso simples deste cluster (Bitnami descontinuado como chart, e os charts "Redis Stack" da Redis Inc. são pra outro produto). Achado colateral, não específico deste chart:docker.io/bitnami/redissofreu a mesma mudança de política já vista no RabbitMQ (ver "Modernização de Applications" acima), só a taglatestcontinua disponível de graça, versão pinada exige assinatura paga da Bitnami (bitnami/redis:8.10.1não resolve, sósha256-.../latest/latest-metadataaparecem no índice de tags). Mitigado pinando por digest (sha256:a97a9e3e..., o mesmo que já estava rodando, Redis 8.10.1, confirmado viaredis-cli INFO serverantes e depois), sem trocar de versão, só travando contra drift futuro do:latest. Dado real envolvido, diferente do MinIO:DBSIZEmostrou 1858 chaves antes da migração, então a estratégia foi manter os mesmos nomes de recurso (redis-serverpro Deployment/Service,redis-pvcpro PVC, releaseNameredis-server) de propósito, pra virar update in-place em vez de delete+recreate,helm templatecomparado documento a documento confirmando isso antes de qualquer sync. Incidente real, recuperado na hora: mesmo com os nomes preservados, oDeploymentfalhou repetidamente (spec.selector: field is immutable) porque oService/Deploymentoriginais usavamapp: redis-servercomo seletor e o chart oficial usaapp.kubernetes.io/name+application.stakater.com/workload-classfixo no_helpers.tpl, sem campo de override emvalues.spec.selectorde umDeploymentnão pode ser alterado por update, só recriando o objeto. Corrigido apagando só oDeployment(não oService, não o PVC, autorização explícita do usuário) e deixando o Argo CD recriar do zero com o seletor novo, montando o mesmoredis-pvcpor nome. Verificado depois: PVC com a idade original preservada (604 dias, prova de que nunca foi recriado),DBSIZE1859 chaves (drift normal de tráfego real, não perda de dado),redis_version:8.10.1inalterado,PINGrespondendoPONGde verdade pelo hostname doServiceque outros serviços usam. Lição nova:spec.selectorimutável deDeploymenté um risco quehelm templatesozinho não avisa (odiffmostra a mudança de campo, mas não que ela vai ser rejeitada pela API), só descoberto na prática ao tentar sincronizar; vale conferir se um chart novo muda o selector de umDeploymentque já existe antes de assumir que "nomes preservados" é suficiente pra evitar recriação. - Fallback pra quando não existe chart oficial nem justifica chart próprio:
stakater/application(https://stakater.github.io/stakater-charts, versão9.3.1, ativamente mantido), o mesmo chart genérico já usado em produção pelos repositórios satélite (management-service,authentication-service,docs) pra Deployment/Service/Ingress simples. Candidatos:redis(Deployment simples hoje) erabbitmqlegado (mas este último está sendo desligado, não vale investir). Ainda não avaliado sestakater/applicationcobre tudo que os manifestos crus atuais declaram (securityContext,PVC,NodePort) sem perder nada. - Prioridade sugerida, do mais seguro pro mais arriscado: 1)
MariaDBCR e MinIO/Silo (zero bucket real), feitos; 2)stakater/applicationpro Redis, depois de confirmar que cobresecurityContext/PVC sem regressão, ainda não feito, único item real que resta neste backlog; 3) CNPGCluster, avaliado a fundo em 2026-08-21, decisão explícita de não migrar (ver acima); 4)cert-managereCloudNativePG(operador), feitos com plano próprio e dead man switch. -
argocd/root/project-satellites.yaml(AppProjectladesa-satellites) existe e é aplicado no cluster, mas nenhumaApplicationemargocd/applications/o referencia: resolvido em 2026-08-21, com um comentário. Confirmado de novo que não é bug (grep -rl "ladesa-satellites" argocd/applications/continua sem achar nada, esperado).argocd-bootstrap.mdagora linka explicitamente o porquê do recurso órfão pro item "Padronizar o deployment com Argo CD" abaixo, em vez de só mencionar de passagem que "nenhumapp-*.yamlexiste ainda". Nenhuma mudança de manifesto, só a doc de arquitetura passou a explicar de propósito, não por lacuna. -
argocd/apps/foundation-secrets-operator.yamlimplantava no namespacedefault, ereleaseNametinha sufixo de timestamp Unix: os dois resolvidos juntos em 2026-08-21, mesma reinstalação (mudar namespace exige recriar o release de qualquer jeito, então corrigir o nome junto não é trabalho extra).releaseName: secrets-operator-1735177689(1735177689= 2024-12-25, artefato de como foi criado originalmente) viroureleaseName: secrets-operator,destination.namespacevirousecrets-operator(mesmo padrão de todo outro operador deste repositório:cnpg-system,mariadb-system,rabbitmq-system,cert-manager). Achado durante a correção: mesmo com nome limpo, o chart trunca o prefixo dos recursos em 15 caracteres ({{ .Release.Name | trunc 15 }}no_helpers.tpl, hardcoded), entãosecrets-operator(16 caracteres) ainda virasecrets-operatonos nomes de recurso, um caractere cortado, não é mais culpa do timestamp, é limite do próprio chart, aceito como está (nenhum nome mais curto seria tão descritivo).helm templatecomparado documento a documento contra o release antigo antes do sync: única diferença real foi labelapp.kubernetes.io/instancee o camponamespacedentro deRoleBinding/ClusterRoleBinding(aponta proServiceAccountno namespace novo).ClusterRole/ClusterRoleBinding/as duas CRDs (nome fixo, não depende do release) só receberam update in-place, sem prune; sóDeployment/Service/ServiceAccount/Role/RoleBinding(recursos namespaced) precisaram ser recriados no namespace novo, com prune explícito dos antigos emdefault, autorizado explicitamente pelo usuário. Verificado depois: pod novo2/2 Running, log do operador mostrando reconciliação ativa de verdade pra todoInfisicalSecretdo cluster (dados,default,ladesa-ro-development,ladesa-ro-production), não só um teste isolado, já que este operador é consumido cluster-wide. Namespacedefaultconfirmado limpo depois (só oService kubernetesembutido do próprio cluster). - Duas convenções diferentes de instalação de operador coexistindo sem documentação explícita da escolha:
mariadb-operatorusa duasApplicationseparadas (foundation-mariadb-operator-crds+foundation-mariadb-operator, padrão do chart Helm oficial, que separa CRD de controller), enquantocert-manager,cnpgerabbitmq-operatorusam manifesto vendorizado único (CRD + controller no mesmo arquivo,kubectl apply, sem Helm). A escolha entre os dois padrões já está implicitamente documentada em Foundation espalhada por parágrafo próprio de cada operador, mas nunca resumida num único lugar como "regra geral de como instalar operador novo neste repositório".
Recursos vivos que o git não declara, levantados em 2026-08-21¶
Varredura feita no fim da migração do management-service, cruzando todo Deployment, StatefulSet, DaemonSet, Ingress, Service, CronJob e PersistentVolumeClaim do cluster contra a anotação argocd.argoproj.io/tracking-id. O que aparece aqui é o que sobrou fora do Argo CD depois de três repositórios migrados.
-
rabbitmq-ingress, no namespaceladesa-ro-production, existia só no cluster: resolvido em 2026-08-24, removido junto do resto do RabbitMQ (ver Desligar o RabbitMQ). Nunca esteve emargocd/, então não havia o que declarar, só apagar (kubectl delete ingress rabbitmq-ingress -n ladesa-ro-production). - Seis
Secretde release Helm órfãos, resquício de instalação imperativa que o Argo CD já substituiu. Três vieram das migrações de 2026-08-21 (ladesa-ro-api,ladesa-ro-wahaeladesa-ro-docs, emladesa-ro-development) e três são anteriores (portainer,infisicalesecrets-operator-1735177689, este no namespacedefault). O Argo CD renderiza chart comhelm templatee não criaSecretde release, então nenhum deles tem dono: são histórico de umhelm installque ninguém mais consulta, ocupando espaço no etcd e confundindo quem rodarhelm list. O doladesa-ro-ssojá foi removido na migração do piloto, e serve de referência do que fazer com os outros. Não são urgentes nem perigosos, mas apagar umSecretde release é ação destrutiva e fica pra decisão explícita. Ficam de fora desta lista, por serem legítimos, o release doargocd(instalado pelo role de bootstrap) e os dotraefik/traefik-crd(geridos pelo próprio k3s viaHelmChart). - A instalação do próprio Argo CD não é gerida pelo Argo CD: os quatro
Deployment, oStatefulSetdo application-controller e oIngressdoargocd-servernão carregam anotação de rastreio. É deliberado, o roleargocd_bootstrapé quem instala, e auto-gestão traria o risco clássico de o Argo CD se derrubar num sync ruim e não ter como se levantar. Registrado aqui só pra que a ausência apareça como decisão e não como lacuna.
Não são achado: o StatefulSet mariadb do namespace dados e o rabbitmq-server de ladesa-ro-production nascem de CR de operador (MariaDB e RabbitmqCluster), e esses CR estão rastreados, então a cadeia de posse fecha. Os dois PersistentVolumeClaim do management-service estão fora de propósito, motivo documentado em Repositórios satélite.
Auditoria de syncPolicy.automated, 2026-08-21¶
Levantado a pedido do usuário ("o que mais não está automatizado ou idempotente, exigindo intervenção manual, e por que não está mapeado em pendências"). Das 14 Application, 7 estavam Manual sem razão documentada por trás da escolha (herdada, não decisão deliberada por app). Auditoria rodada com argocd app diff --core --hard-refresh duas vezes por app (pra pegar campo que muda a cada render, não só drift real) e, pras que usam chart remoto (histórico de diff pouco confiável nesse cenário, ver "O padrão app-of-apps"), conferência direta via kubectl get contra o valor esperado, independente da ferramenta.
-
dados,portainer,redis,secrets-operator: resolvido em 2026-08-21, promovidas praautomated: {prune: true, selfHeal: true}. Diff vazio nas duas rodadas, sem recurso órfão ao vivo (kubectl get allconferido por namespace), sem recriação depois do sync (idade de todoDeploymentinalterada). -
infisicaltinha um bloqueio técnico real pra virarautomated: resolvido em 2026-08-21. O chartinfisical-standalone(templates/infisical.yaml, confirmado no fonte do chart) calcula os camposmetadata.annotations.updatedAtespec.template.metadata.annotations.updatedAtdoDeploymentcom{{ now | date ... }}, timestamp do momento do render, deliberado no chart pra forçar rollout a cadahelm upgrade. Cadaargocd app diffproduzia um valor "desejado" novo, nunca igual ao anterior (confirmado rodando duas vezes seguidas). Corrigido comignoreDifferencesnos doisjsonPointers(argocd/applications/platform/infisical.yaml). Verificado depois do deploy: diff estável nas duas rodadas seguintes (resíduo cosméticoannotations: {}, masSync Status: Synced, não conta mais como drift).automated: {prune: true, selfHeal: true}ligado depois, confirmadoAuto-Prune/Synced/Healthy, sem recriar o pod estável. - Achado à parte, no meio dessa verificação: o
Deploymentdo Infisical estava com um rollout travado havia 2h13 (ReplicaSetnovoPendingporInsufficient memoryno node), causado por 3 syncs manuais nesta Application feitos por terceiro (argocd app historymostra 3 entradas hoje,12:01/12:03/13:08UTC; nenhum deles rodado nesta sessão, que só usouargocd app diff). Não derrubou o serviço (o pod antigo continuouRunning/servindo,RollingUpdatenunca remove a versão antiga até a nova ficar pronta), mas nunca teria se resolvido sozinho, dado que o node não tem memória sobrando (mesmo achado já registrado em "Node sem folga de memória"). Corrigido comkubectl rollout undo --to-revision=<estável>(revisão-alvo confirmada antes viakubectl rollout history, não umundocego de um passo só: o primeiroundosem--to-revisionvoltou pra outra revisão também travada). Confirmado depois:1/1 Running,rollout statussuccessfully rolled out, site respondendo200. -
cert-manager-tokenrequest(Role+RoleBinding) órfão ao vivo emcert-manager: resolvido em 2026-08-21, removido do cluster (kubectl delete, backup salvo em/root/cert-manager-tokenrequest-backup.yamlno node). Investigação completa antes de remover: release notes oficiais do cert-manager v1.21.0 confirmam que esse RBAC foi removido oficialmente (adicionado no v1.16, motivado por um caso de uso de Route53 removido da documentação em 2024, só afeta quem usa o padrão não documentadoserviceAccountRef.nameapontando pro SA do controller). Confirmado que oClusterIssuerdeste cluster usahttp01/Traefik, não Route53/Vault, não depende desse padrão. Confirmado também no próprio histórico deste repositório (git show 1b2b04b^:.../cert-manager-v1.16.2.yaml) que o manifesto vendorizado anterior criava esse RBAC e o bump pra v1.21.1 (mesma sessão) parou de declará-lo. Não é recurso de origem desconhecida, é sobra esperada do upgrade.argocd app diffnofoundation-cert-managerveio vazio depois da remoção, cert-manager continuou1/1 Runningnos 3 pods, todos osCertificatedo cluster seguiramReady: True. -
secrets-operator(poddefault/secrets-operato-controller-manager-*) tinha histórico de milhares de restart: resolvido em 2026-08-21. Causa confirmada nos logs: o operador (baseado emcontroller-runtime) derrubava o próprio processo de propósito quando perdia a lease de liderança (dial tcp 10.43.0.1:443: connection refused, falha de leader election), padrão comum desses operadores em blip da API do k3s (reinício de node, upgrade, churn de CRD), não bug do Infisical. Investigado mais a fundo:controllerManager.replicasdeste chart é fixo em1(helm show values infisical/secrets-operator), então não existe cenário de HA real que justifique leader election, a flag só existia pra proteger uma disputa entre réplicas que nunca acontece aqui. Corrigido sobrescrevendocontrollerManager.manager.argsnovaluesdaApplication(argocd/applications/operators/secrets-operator.yaml) sem--leader-elect, removendo a classe de crash por completo (não é mitigação de timeout, é eliminação da causa). Risco aceito e documentado: sereplicasfor aumentado no futuro sem restaurar--leader-elect, duas réplicas processariam os mesmos recursos sem coordenação. Não é o caso hoje, e mudarreplicasexigiria edição consciente do mesmovalues. Verificado ao vivo: rollout limpo (pod novo2/2 Running,0restarts), args confirmados sem a flag, logs mostrando reconciliação normal de todoInfisicalSecret,argocd app diffvazio. -
rabbitmq(Deployment legado, ocioso) ecert-managerpromovidos praautomated: resolvido em 2026-08-21.rabbitmqcomprune: true(diff limpo em duas rodadas, zero risco real).cert-managercomprune: false(mesma mitigação dodados-mariadb/CNPG, chart não declaraNamespace). Das 15Application(14 +root), sórootcontinuaManual, decisão permanente, não pendência (ver acima, "único gate humano antes de qualquer edição emargocd/applications/virar mudança real").
Trabalho inicial da organização, ainda não retomado¶
Registrado em 2026-08-21: o objetivo original desta sessão, antes de focar em CI/qualidade do infrastructure, era um conjunto de mudanças maiores, que tocam mais de um repositório da organização. Investigação inicial (só leitura, nenhuma mudança feita ainda) registrada abaixo item por item:
- Arquivar repositórios não usados da organização
ladesa-rono GitHub. Três saíram em 2026-08-21 junto da aposentadoria do gerador de horário (arquivado-timetable-generator,arquivado-messages,arquivado-protobufs), depois de conferir quem os referenciava. Falta levantar o resto (arquivar é reversível, mas ainda merece uma lista explícita, não um chute). Atenção a um efeito colateral do rename: odocslinka o antigoladesa-ro/messagesem três páginas publicadas e omanagement-serviceo referencia num script, e os dois seguem funcionando só por redirecionamento do GitHub, exatamente o estado que este documento já critica no item doarquivado-esteira-ci-cd. -
Migrar a geração de horário e os arquivos
.protopromanagement-service: em andamento, não commitado. Omanagement-serviceestá na branchfeat/timetable-generator-proto, comsrc/infrastructure.timetable-generator/proto/inteiro não rastreado pelo git. O contrato.protodo serviçotimetable-generator-v1veio do repositórioprotobufs, eproto/.commands/generatebuilda um container fixado por versão (protoc+ts-proto) que gera TypeScript emproto/generated/e C# emproto/generated-csharp/. A aposentadoria do serviço em 2026-08-21 (abaixo) resolveu a pendência do destino do C#, mas não tornando ogenerated-csharpdescartável, e sim mudando o que ele é: em vez de um passo de sincronização com outro repositório, ele passa a ser parte do próprio monorepo, o lado C# do protocolo que o worker vai usar. O desenho alvo, definido pelo usuário, é monólito modular: a API conversa com um worker também em TypeScript, e esse worker executa o algoritmo C# como processo filho, mandandoServiceGenerateRequestserializado pelo stdin e lendoServiceGenerateResponsedo stdout. O.protodeixa de ser contrato entre serviços em rede e vira contrato entre processos na mesma máquina, o que ele já suporta sem mudança nenhuma: o arquivo não declara blocoservice, só mensagens, e a delimitação fica por conta do EOF, uma mensagem por invocação. Continua valendo a pendência de declarar@bufbuild/protobufcomo dependência real, já que hoje só o código gerado o importa em runtime, e some-se a ela a equivalente do outro lado,Google.Protobufno projeto C# que compilar as classes geradas. Some-se a isso o que a varredura de 2026-08-21 achou: nada emsrc/importaproto/generated/nem omessages/schemas.ts, e o alvo nxcodegen:timetable-generator:freshaponta pramessages/commands/generatequando a pasta real émessages/.commands/, ou seja, está quebrado desde que foi escrito e ninguém percebeu porque ninguém o roda. -
Aposentar o gerador de horário como serviço à parte: feito em 2026-08-21. Os três
Deployment(ladesa-ro-timetable-api,ladesa-ro-timetable-generator,ladesa-ro-timetable-worker), os doisServicee oInfisicalSecretque produzia o config foram removidos do cluster, e os repositóriostimetable-generator,messageseprotobufsforam renomeados com o prefixo da convenção e arquivados. A decisão se apoiou em evidência colhida antes de apagar: as três filas de horário estavam zeradas, sem DLQ acumulada, nenhumIngresspúblico apontava pros serviços, e os logs dos três pods não mostravam nenhum processamento real, só health check. O achado que fechou a questão é que o ciclo já estava quebrado de qualquer jeito: omessage-broker-subscribe.service.tsdomanagement-serviceestá inteiramente comentado, então a resposta que o gerador publicava emdev.timetable_generate.responsenunca era lida por ninguém. Backup dos manifestos foi tirado antes da remoção, e o config não precisou de cópia porque o Infisical é a fonte. Sobra o usuáriotimetable-generator-devno RabbitMQ, agora sem uso, deixado de propósito para não mexer no broker no mesmo passo. -
Desligar o RabbitMQ: resolvido em 2026-08-24. A condição registrada em 2026-08-21, a folha de ponto deixar de depender do broker, foi atendida: o
management-servicemigrou toda a mensageria (geração de horário e notificação de folha de ponto por WhatsApp, incluindo a filafolha_ponto.notificacao.whatsapp) para BullMQ sobre o próprio PostgreSQL da aplicação, sem broker externo. Confirmado ao vivo antes de remover:rabbitmqctl list_connectionszerado (o pod antigo da API, rodando código anterior à migração por causa do achado em GitOps com tag mutável, ainda tinha 2 conexões abertas, resolvido reiniciando o deployment) erabbitmqctl list_queuescom zero mensagens/consumidores em todas as filas, inclusive as de horário e de folha de ponto. Nenhum pod do cluster, em nenhum namespace, referenciava RabbitMQ em variável de ambiente além do próprio broker. Removidos do git e do cluster: as trêsApplication(foundation-rabbitmq-operator,foundation-rabbitmq,foundation-ladesa-ro-production-rabbitmq-cluster), os manifests emargocd/apps/messaging/rabbitmq/,argocd/apps/messaging/rabbitmq-cluster/eargocd/apps/operators/rabbitmq-operator/, o whitelistrabbitmq.com/RabbitmqClusteremargocd/root/project.yaml, a referência ao segredo de bootstrap emansible/inventory/host_vars/ldsa.yml, e orabbitmq-ingressórfão (ver acima). -
A criação de folha de ponto quebra se o broker estiver fora, e deixa estado inconsistente, achado em 2026-08-21 ao investigar se o RabbitMQ podia ser desligado. Em
folha-ponto-create.command.handler.tsoawait messageBrokerService.publishFolhaPontoCreated(payload)acontece depois de a folha já estar gravada no banco e antes do retorno, semtry. ComogetBroker()lançaServiceUnavailableErrorquando não há conexão, uma indisponibilidade do broker faz o endpoint responder erro para um registro que já foi persistido: quem chamou entende que falhou, o banco entende que deu certo. Não é hipótese: o próprio broker caiu por dois minutos em 2026-08-21 às 23:29, e a API sobreviveu justamente porque a conexão é reestabelecida em laço com backoff, mas qualquer folha criada naquela janela teria caído nesse caminho. A publicação é notificação, não parte da transação, então o tratamento adequado é não deixar a falha dela derrubar a resposta. Some-se que a filafolha_ponto.notificacao.whatsappestá com zero consumidores, porque o serviço de assinatura domanagement-serviceestá inteiramente comentado, ou seja, hoje a mensagem é publicada e nunca lida. - Padronizar o deployment com Argo CD nos repositórios restantes da organização, substituindo o push imperativo do chart
application(Stakater) por uma pastagitops. Confirmado em 2026-08-21, não é só omanagement-service:ladesa-ro/authentication-service(SSO) eladesa-ro/docstambém usam o mesmo padrão imperativo (helm upgrade -i --repo https://stakater.github.io/stakater-charts application, disparado direto por.github/workflows/build-deploy*.yml, sem GitOps, semsyncPolicy, sem histórico de reconciliação), achado ao investigar por que dois serviços da organização não tinham TLS (ver "Segurança do próprio Argo CD" acima). Oauthentication-serviceé o caso mais extremo, o values de produção nem fica num arquivo do repositório, é uma variável de ambiente do GitHub (DEPLOY_HELM_VALUES), editável só pela UI, sem PR nem histórico de revisão nenhum. Esse item e o Débito técnico: organização de pastas e arquivos emargocd/acima andam juntos: a convençãogitops/applications/<categoria>/+gitops/apps/<categoria>/<app>/é a mesma pensada pros dois, e o próprioinfrastructure.gitjá tem agora um precedente vivo de chart Helm local funcionando (argocd/apps/data/dados-postgres-cluster/,argocd/apps/messaging/rabbitmq-cluster/, resolvido em 2026-08-21), referência concreta pra quando esses repositórios satélite adotarem o mesmo padrão.argocd/apps//argocd/foundation/deste repositório renomeados praargocd/applications//argocd/apps/também em 2026-08-21, batendo com o nome exato usado aqui (gitops/applications/+gitops/apps/). A convenção de referência já validada em produção noutro contexto, a seguir aqui: Piloto concluído em 2026-08-21 com oauthentication-service, seguindo o padrão de três camadas documentado em Repositórios satélite e inspirado numa implementação de referência que já rodava noutro projeto. Depois do piloto migraram odocse omanagement-service(este com duasApplication,ladesa-ro-apieladesa-ro-waha, porque são dois releases Helm distintos), ambos com diff vazio na adoção.webfechou o backlog em 2026-08-22, o único que exigiu apagar e recriar em vez de update in-place: usava manifesto cru comspec.selectorna forma{app: ladesa-ro-web}, o chart do Stakater rotula porapp.kubernetes.io/name, espec.selectordeDeploymenté imutável (mesmo achado já registrado na migração do Redis, ver Aprender: Argo CD).DeploymenteIngressantigos apagados de propósito antes do primeiro sync, cutover confirmado com o site real respondendo. Os quatro repositórios de time (authentication-service,docs,management-service,web) estão sob Argo CD agora, o runner self-hosteddev-deployperdeu o último consumidor e foi desligado (ver logo abaixo). Otimetable-generatorsaiu da conta em 2026-08-21 por ter sido aposentado, não migrado. - Uma pasta
gitops/(nãoargocd/), com duas subpastas:gitops/applications/, só manifestsApplication/AppProjectdo Argo CD, organizados por categoria (ex.:business/,monitoring/,operators/,security/,system/, maisprojects/prosAppProject); egitops/apps/, os charts Helm e values de verdade que cadaApplicationaponta, no mesmo esquema de categoria. - Um app-of-apps por cluster de destino, não uma lista mantida à mão: uma única
Applicationraiz comsource.path: gitops/applicationsedirectory: {recurse: true}, deixando o próprio Argo CD descobrir todoApplication/AppProjectaninhado, ao contrário doargocd/applications/atual deste repositório (umaApplicationpor arquivo, listada manualmente). - Pra um repositório de serviço único (o caso do
management-service):gitops/apps/<serviço>/vira um chart Helm próprio, local, que declara o chartapplicationdo Stakater como dependência (uma instância aliased por processo, quando o serviço tem mais de um, por exemplo API mais worker mais job de migração), acrescentando só os templates que o chart genérico não cobre (HTTPRoute, secret do Infisical).gitops/envs/<ambiente>/applications/<serviço>.yamlé aApplicationde fato, comvalues-<ambiente>.yamlesyncPolicy.automated: {prune: true, selfHeal: true}, substituindo ohelm upgrade -idisparado por CI por reconciliação pull-based de verdade. - Falta decidir, quando este item for retomado, o que exatamente muda na estrutura atual deste repositório (
argocd/applications/,argocd/apps/,argocd/root/) pra adotar esse mesmo esquema de categoria, e se oinfrastructuretambém ganha uma pastagitops/própria ou se a mudança de nome fica só nos repositórios de serviço individual.
GitOps com tag mutável não reinicia o pod sozinho, achado real em 2026-08-22¶
docs e management-service já adotaram o padrão gitops/ descrito em Trabalho inicial da organização, ainda não retomado acima, cada um com sua própria Application reconciliada automaticamente (syncPolicy.automated: {prune: true, selfHeal: true}). Achado ao verificar uma migração de conteúdo no ladesa-ro/docs: depois do merge, o workflow [DEV] Build & Push completou com sucesso, publicando uma imagem nova em ghcr.io/ladesa-ro/docs/docs:development, mas o site em docs.ladesa.com.br continuou servindo o conteúdo antigo (confirmado com cache-busting, não é cache de CDN/navegador).
Causa: gitops/apps/docs/values-development.yaml usa pullPolicy: Always com uma tag mutável (tag: development). pullPolicy: Always só força um novo pull quando o pod é (re)criado, não reinicia sozinho um pod que já está rodando só porque o conteúdo da imagem no registry mudou. O Argo CD não detecta isso como diff nenhum (a string da tag continua development, igual antes e depois), então selfHeal não dispara. O mesmo padrão (pullPolicy: Always + tag mutável development, sem mecanismo de restart) existe em gitops/apps/api/values-development.yaml do management-service, mesma classe de risco, não confirmado ao vivo ainda.
- Confirmar se
management-servicetem o mesmo problema: confirmado em 2026-08-24, ao acompanhar o deploy da migração de mensageria. O podladesa-ro-apicontinuava rodando há 2 dias e 22 horas depois do merge e da imagem:developmentnova já publicada, só a imagem dotimetable-worker(Deploymentnovo, criado do zero, sem pod anterior pra comparar) atualizou sozinha.kubectl rollout restart deployment/ladesa-ro-api -n ladesa-ro-developmentmanual foi necessário pra pod novo pegar o código migrado. - Decidir o mecanismo de correção: resolvido em 2026-08-28. Levantamento completo das opções (tag/digest imutável via CI, Argo CD Image Updater, Kargo, Argo Rollouts) e a decisão tomada, digest
sha256computado pelo build e promovido via PR automático (GITHUB_TOKEN, sem PAT dedicado), estão registrados em Rollout de imagens, incluindo a seção de hardening futuro (quando reavaliar Image Updater e Kargo). - Implementado em
docsemanagement-service:.github/workflows/build-push.dev.ymldos dois repositórios ganhou um step que resolve o digest da imagem publicada e abre (ou atualiza, se já existir) um PR trocandotag: developmentpordigest: sha256:...novalues-development.yamlcorrespondente, branch fixa por satélite, sempre reaproveitando o mesmo PR em vez de acumular um por push.webeauthentication-serviceainda não migraram: mesmo padrão a replicar, oauthentication-servicejá tem umpromote.ymlparcialmente pronto pra esse exato mecanismo (nunca confirmado se funciona), vale reaproveitar antes de escrever do zero. - Enquanto
web/authentication-servicenão migram, o restart depois de um build novo continua manual nesses dois:kubectl rollout restart deployment/<nome> -n <namespace>(ou equivalente viaargocd app actions run <app> restart).
Versões desatualizadas das ferramentas de bootstrap¶
Levantado em 2026-08-21, contra o que está fixado em ansible/inventory/host_vars/ldsa.yml. Ordem deliberada: upgrade de versão só depois de todo o resto ao redor já modernizado e validado (os itens acima), nunca como primeiro passo, e sempre com o mesmo processo de contingência já usado na migração do Postgres (cluster/ambiente descartável, dead man switch, backup confirmado antes de aplicar em produção).
- k3s: resolvido em 2026-08-21, atualizado de
v1.33.3+k3s1prav1.36.3+k3s1(salto direto, changelog das três linhas 1.34/1.35/1.36 conferido, nenhuma mudança de ruptura afeta este cluster: o único aviso real, renomeação do providerkubernetesIngressNginxprakubernetesIngressNGINXno Traefik, só importa pra quem customiza esseHelmChartConfig, que não é o nosso caso, só usamosingressClassName: traefik). Backup do datastore sqlite feito antes (systemctl stop k3s,cp -a /var/lib/rancher/k3s/server/db,systemctl start k3s, ~529MB, em/root/k3s-upgrade-backup/) por não existirsqlite3CLI no node pra um backup online. Achado colateral, não é bug do k3s: o reinstall regenerou/etc/rancher/k3s/k3s.yamldo zero, o que apagou umnamespace: argocdque estava setado no contexto dokubectl(usado peloargocd ... --core), causandoconfigmap "argocd-cm" not foundatékubectl config set-context --current --namespace=argocdser refeito. - Helm: resolvido em 2026-08-21, atualizado de
v3.18.6prav4.2.4. Reavaliado o risco depois de checar o uso real: só roda umhelm upgrade --install argocd ... --version ... --values -, sem--atomic/--force/post-renderer/plugin (as únicas coisas que quebram de v3 pra v4), e os outros charts do cluster (Infisical, mariadb-operator) são renderizados pelo Argo CD internamente, não pelo bináriohelmdo node. Formato de release no cluster não mudou,helm list -n argocdseguiu enxergando a release criada em v3 sem migração nenhuma. Suporte de segurança do v3 acaba em 2026-11-11, motivo real pra não adiar demais. - Argo CD (chart
argo/argo-cd): resolvido em 2026-08-21, atualizado de10.3.3pra10.4.0. Risco confirmado baixo antes de aplicar: mesma versão do app (v3.5.1), diff do chart só adiciona uma opção de VPA recommender opcional que não usamos. Rollout limpo, todos os pods doargocdsaudáveis depois. -
secrets-operator(Infisical): resolvido em 2026-08-28, atualizado de0.8.0pra0.11.8. O sidecarkubeRbacProxyfoi removido entre as versões, metrics passou a bind direto com RBAC embutido no manager; override manual deargs(apontava pro modelo antigo) removido, chart usa o default novo. - Postgres engine (CNPG): resolvido em 2026-08-28, atualizado de
18.0pra18.6(6 patches).instances: 1neste cluster, sem réplica: o restart do pod primário causou breve indisponibilidade real do banco (Keycloak perdeu conexão por ~1 minuto durante a janela), esperado e sem dado perdido, cluster confirmadoReadylogo depois. - Portainer: resolvido em 2026-08-28, chart
1.0.59pra245.0.0, imagem EE2.21.5pra2.45.0. Schema deimage/service/ingressidêntico entre as versões, só features novas opcionais adicionadas. - Infisical platform (
infisical-standalone): resolvido em 2026-08-28, imagemv0.99.0-postgresprav0.164.1(~65 minors). Achado ao vivo: o pod novo ficou preso emInit:Error, causa era uminitContainerórfão (migration-init, imagemk8s-wait-for) deixado por umhelm installmanual anterior ao Argo CD assumir o release, nunca limpo porque o Server-Side Apply do Argo CD só reivindica campos que ele mesmo define, nunca remove campo de outrofield manager. Corrigido removendo o campo direto (kubectl patch ... --type=json,removeemspec.template.spec.initContainers). Login eInfisicalSecretconfirmados sincronizando depois.
Trocar de CNI, considerar depois¶
Registrado em 2026-08-21, ligado a um achado em aberto da "Revisão de segurança" acima, descrito no inventário privado. O mecanismo, que é genérico e vale pra qualquer cluster k3s, está em Aprender: firewalld: o firewalld não consegue restringir o tráfego que chega no Traefik via hostPort (chain FORWARD, dominada pelo kube-router, sem solução oficial do k3s pra isso). Uma opção que pode resolver de vez, ainda não avaliada a fundo: adotar Cilium (ou CNI equivalente com enforcement próprio) no lugar do flannel + kube-router atuais. Cilium substitui o kube-proxy/kube-router por um dataplane eBPF próprio e tem features dedicadas exatamente pra esse cenário (CiliumClusterwideNetworkPolicy com host firewall, toEntities: host, controle de tráfego que entra via NodePort/LoadBalancer antes mesmo de virar Service), então não compete com nada por ordem de chain iptables do jeito que o firewalld compete hoje.
Nada disso foi pesquisado a fundo ainda (versão compatível com k3s v1.36.3+k3s1, esforço de migração do flannel existente, se dá pra trocar sem downtime, se resolve mesmo o caso específico de hostPort do svclb-traefik ou só de NodePort/LoadBalancer normal). Trocar de CNI num cluster de produção de nó único é uma mudança de alto risco (rede inteira do cluster depende disso), mesmo cuidado de sempre antes de tocar (pesquisa dedicada, cluster/ambiente descartável se possível, dead man switch). Não é pra fazer sem esse processo completo.
Importação do estado do node pro Ansible, primeira leva¶
Registrado em 2026-08-21. É a contraparte de "Mapear operação de rotina/manutenção pro Ansible" abaixo, e as duas não são a mesma coisa: aquela é sobre transformar procedimento em código, esta é sobre trazer pro git o estado que já existe no node desde antes deste repositório. Esta pode andar antes porque não depende do backlog de argocd/ estar resolvido, o estado do sistema operacional não muda quando a organização de pastas do Argo CD muda.
Os roles pacotes-base e sistema são a primeira leva. O que ficou deliberadamente em aberto:
-
pacotes_base_falhar_em_driftestáfalsedesde 2026-08-21. Motivo: gate novo nasce informativo, mesma regra dos checks de CI. Diferente dos bypasses de CI, aqui o baseline já nasceu limpo, os 28 pacotes instalados à mão no node estão todos classificados. Condição pra ligar: a remoção listada no item abaixo executada, porque enquantopacotes_base_a_removertiver conteúdo o gate ligado passaria a exigir que a lista continue existindo pra sempre. - Pacotes classificados como remover, ainda não removidos:
podman,podman-compose,libicu-dev,libbpfcc,libbpfcc-dev,linux-headers-6.12.48+deb13-cloud-amd64,make,liblttng-ust1,spectre-meltdown-checker,bpfcc-toolsezsh. Nenhum tem consumidor hoje (apt-cache rdepends --installedvazio nos pacotes de desenvolvimento, epodman ps -avazio sem nenhum quadlet em/etc/containers/systemd/). Junto deles saem a interfacedummy0e a linha deregistry.ladesa.com.brem/etc/hosts, que são resquício do mesmo stackpodman composedesmontado. Condição: executar o how-to de limpeza supervisionada, nunca peloansible-pull, que roda sem ninguém olhando. - Journal do node ainda ocupa mais de 4G, e o teto declarado por
sistemanasceu acima do uso atual de propósito, decisão do usuário em 2026-08-21. Motivo:SystemMaxUseabaixo do uso corrente faz o journald apagar histórico sozinho na próxima rotação, e este journal alcança 2025-09-10, mais de 11 meses e 17 boots. Baixar o teto agendaria uma exclusão silenciosa em vez de deixar a escolha com quem opera. Pela mesma razão o role não declaraMaxRetentionSec. Condição pra fechar: decidir quanto histórico de log vale a pena guardar, baixarsistema_journald_tetopro alvo, e rodar uma vez comsistema_reduzir_journal_agoraligado, com alguém acompanhando. Hoje são cerca de 3G recuperáveis num disco em 79%. - Performance Co-Pilot fica exatamente como está, decisão do usuário em 2026-08-21, registrada em Ambiente do node. O fato que justifica revisitar, a escrita contínua de métrica em
/var/log/pcp, está anotado lá. Condição: quando observabilidade virar uma decisão consciente deste cluster, adotar ou remover, em vez de seguir de pé por herança. -
protect-kernel-defaultsainda não está ligado no k3s. O rolesistemadeclara os quatro parâmetros de kernel que o guia CIS do k3s exige como pré-requisito, mas a flag em si fica pra depois de propósito: com ela ligada, o kubelet se recusa a subir se os parâmetros não estiverem valendo. Condição: confirmar no node que os quatro valores estão aplicados de verdade, e só então ligar a flag, com o ritual de mudança de risco. - Conta
apalrdcontinua existindo com sudo sem senha eauthorized_keysvazio, herdada da imagem base. Condição: rolecontas, da próxima leva. - A rotina de manutenção do node parou de rodar e ninguém notou: resolvido em 2026-08-21. O runner auto-hospedado que executava os workflows do repositório
cluster-maintenanceestava inativo desde 2026-08-12, com o registro apagado pelo próprio GitHub, e os workflows falhavam desde 2026-01-19, sete meses sem alarme nenhum. Substituído pelo rolemanutencao, que traz a coleta de lixo pra um timer do systemd no próprio node e declara a política dounattended-upgradescomo template deste repositório. Otopgradefoi aposentado, não portado, porque um atualizador que atualiza tudo o que encontra conflita com o contrato de fixar versão por checksum. Resultado da primeira execução: imagens em cache de 96 pra 34,ReplicaSetzerados apagados, e o disco do node de 81% pra 36%, de 19G livres pra 61G. O repositório foi renomeado praarquivado-cluster-maintenancee arquivado, seguindo a convenção da organização, e o runnermaintenancefoi removido do node junto dos 2G dele. - O runner
dev-deployrodava no node como root, com kubeconfig de cluster-admin: resolvido em 2026-08-22.webfoi o último repositório a migrar pro Argo CD (mesmo padrão dos outros três satélites:gitops/apps/web/comstakater/applicationcomo dependência,gitops/envs/development/applications/web.yaml,satellite-webdeclarado neste repositório). Comowebnunca teve Helm nenhum (erakubectl applydireto em três manifestos crus), o wrapper chart foi escrito do zero, não adaptado de umvalues.ymljá existente. Cutover ao vivo:Deployment/Ingressantigos apagados de propósito antes do primeiro sync (mesmo aprendizado do seletor imutável doDeployment, achado na migração do Redis, aplicado preventivamente desta vez, e oIngressantigo tinha nome diferente do novo, então ficaria órfão coexistindo com o novo se não fosse removido). Confirmado depois: podRunning,diffvazio,https://dev.ladesa.com.brrespondendo de verdade (x-powered-by: Nuxt). Zero consumidor do runner confirmado na organização inteira antes de desligar (gh api search/code). Serviço parado e desinstalado com osvc.shoficial do próprio runner, diretório apagado (/root/github-devops/actions-runner/devops/, ~188MB de RAM liberados, node sem folga). Registro do lado do GitHub (Settings, Actions, Runners) removido pelo usuário, exigeadmin:orgque a sessão não tinha. Achado à parte, não é deste item: o build de imagem dowebestá quebrado desde maio (pnpm run -w build:all, falha em todos os runs antes desta migração), não impede o deploy (usa a tag já publicada), mas precisa de atenção separada de quem mantém o código da aplicação. - Dois repositórios foram renomeados com o prefixo
arquivado-mas nunca chegaram a ser arquivados: resolvido em 2026-08-22.arquivado-esteira-ci-cdtinha dependência viva real (ver item abaixo,authentication-serviceainda usava duas actions reusáveis de lá pro job de build de imagem, achado só confirmado buscandoesteira-ci-cdno código da organização inteira, não só pelo nome do repositório sozinho). As duas actions (prepare-images-builder,image-build-push) foram inlinadas direto no workflow doauthentication-service, com os mesmos hashes de commit já usados nowebpras mesmas actions de terceiro, testado em CI real antes de considerar resolvido.arquivado-dev-kitjá não tinha consumidor nenhum. Os dois arquivados de verdade agora (archived: trueconfirmado via API). - Bug latente achado pelo gate de drift zero na adoção do SSO, em 2026-08-21, e conferido nos demais satélites depois. O manifesto do Helm declarava o backend do
Ingressapontando pra uma porta deServiceque não existe, porque usava o nome dotargetPort(a porta do container) em vez do nome da porta doService. O cluster tinha a forma correta, por número, divergindo do que o Helm achava que tinha aplicado, e sincronizar sem conferir teria derrubado o roteamento. Conferido nodocs,management-servicee, em 2026-08-22, noweb(migração pro Argo CD, ver acima): os três limpos, cada um nomeia a porta doServicediferente dotargetPortdo container (webusaweb-service/web-port), que é justamente o que evita a confusão. Os quatro satélites cobertos, nenhum caso real do bug fora do SSO original. - Registro órfão de runner na organização do GitHub: resolvido em 2026-08-22. A remoção do runner do node não apagava o registro do lado do GitHub sozinha, e isso exigia permissão de administrador da organização que a sessão não tinha; o usuário removeu direto pela interface em Settings, Actions, Runners.
- O
authentication-servicedependia de um repositório já renomeado pra arquivamento: resolvido por completo em 2026-08-22 (começou em 2026-08-21, ver histórico). A migração pro Argo CD já tinha removido o job de CD, que invocavadeploy-k8s-stakater-applicationdoarquivado-esteira-ci-cd. O job de build, que ainda usava duas actions daquele repositório (prepare-images-builder,image-build-push), foi corrigido inlinando as duas direto no workflow, mesmos hashes de commit já usados nowebpras mesmas actions de terceiro (docker/setup-buildx-action,docker/login-action,docker/build-push-action). Testado em CI real antes de considerar resolvido. Dependência zerada, repositório arquivado (ver acima). -
/etc/ssh/sshd_configfoi editado à mão e não pertence a nenhum pacoteapt, enquanto/etc/ssh/sshd_config.d/está vazio. Como o arquivo principal já tem a linha deInclude, a configuração deste repositório pode assumir por drop-in sem tocar no que está lá. Condição pra fechar: rolessh_servidor, com validação porsshd -tantes de escrever e o ritual de mudança de risco na execução, que é o mesmo caso do firewalld. Fecha junto um dos achados em aberto registrados no inventário privado. -
/etc/apt/apt.conf.d/50unattended-upgradese20auto-upgradesforam editados à mão, nenhum dos dois pertence a pacoteapt. Hoje só as origens do Debian e do Debian-Security estão declaradas, e não há política de reinício automático definida. Condição pra fechar: os dois viram template do rolemanutencao, com a decisão de reinício explícita. - Sobras de operação manual no
/root: a unitrabbitmq-cutover-revert.servicecontinua em estadofailed, resquício de um dead man switch que disparou e falhou, e seis diretórios*-backupacumulam sem regra de retenção. Condição pra fechar: o how-to de limpeza supervisionada, junto dos pacotes classificados como remover, e uma regra escrita de por quanto tempo backup manual fica no node. - Estado deixado pelo
cloud-init, que foi removido do node. O pacote está emrc, sem binário e com o serviçonot-found, mas/etc/netplan/50-cloud-init.yaml,/etc/hostse/etc/cloud/**continuam lá, sem dono nenhum. Como não há mais nada pra disputar esses arquivos, eles deixam de ser ambiente e passam a ser candidatos legítimos a declaração, ver Ambiente do node. Condição pra fechar: decidir se o Ansible assume a configuração de rede do node, e se assumir, remover os resquícios de/etc/cloud/junto, porque o aviso no topo do/etc/hostssobremanage_etc_hostshoje engana quem for editar. - Configuração de sistema que segue no padrão de fábrica, sem declaração: fuso horário, locale, e os arquivos do
systemd-timesyncde dosystemd-resolved, todos em stub vazio ou default do Debian. Nenhum está errado, e é justamente por isso que ninguém percebe que não estão declarados: uma reprovisão do zero pode cair em valores diferentes sem que nada acuse. Condição pra fechar: decidir quais desses este projeto quer fixar, e declarar no rolesistema. - Node sem swap nenhum. Não é erro, é escolha comum em node de Kubernetes, mas aqui ela se soma ao que o inventário privado registra sobre memória. Condição pra fechar: decidir explicitamente entre seguir sem swap, o que torna esgotamento de memória abrupto em vez de lento, ou configurar
zram, e declarar a decisão em qualquer um dos dois casos. - Conffile órfão de pacote já removido:
cloud-init,sysstate quatorze versões antigas delinux-imageestão em estadorc. Junto ficou/etc/cron.d/sysstat, que dispara a cada dez minutos apontando pra binário que não existe mais. É inofensivo, porque a linha se protege comcommand -ve o journal não registra erro nenhum em sete dias, mas é entulho que confunde qualquer inventário futuro. Condição pra fechar:apt purgedos pacotes emrc, junto do how-to de limpeza supervisionada. - Conta
ifroserá importada como está, com sudo sem senha, decisão do usuário em 2026-08-21. Registrado por ser um privilégio declarado sem correção, não um descuido. Condição: nenhuma automática, revisitar junto de qualquer revisão de acesso ao node.
Débito editorial: log de incidente dentro de Aprender¶
Registrado em 2026-08-21. aprender/index.md declara que a seção ensina conceitos "de forma geral, sem depender de nenhuma decisão específica de nenhum projeto", e Diátaxis reforça que fato específico de um cluster real pertence a Arquitetura, não a Explicação. Na prática aprender/argocd.md tem seis seções datadas de 2026-08-21 que são log de incidente deste cluster: o syncPolicy padronizado, a organização por categoria, o wrapper chart, a migração do cert-manager, o incidente do --prune com o CNPG, o rename de topo e a correção de causa raiz do Namespace.
- Mover o log de incidente de
aprender/argocd.mdpra Arquitetura, deixando em Aprender só a lição genérica. Não é urgente e não é exposição, são lições de engenharia legítimas, só estão no lugar errado segundo a regra que o próprio repositório declara. Condição pra fechar: decidir se viram uma página nova emarquitetura/ou se cada uma vai pro documento de arquitetura correspondente, e então mover preservando os links de entrada. O mesmo padrão já foi aplicado, em menor escala, ao tirar o achado de segurança dessas páginas.
Ferramentas e práticas candidatas, levantadas dos guias¶
Registrado em 2026-08-21, a outra metade do mesmo pedido que originou a seção de importação acima. Levantamento feito cruzando as páginas de Aprender com guias externos: o guia de hardening CIS do k3s, a documentação de segurança do GitHub Actions e do Argo CD, os princípios do OpenGitOps e o ferramental da OpenSSF.
Cada linha é candidato, não decisão tomada. Toda ferramenta nova passa antes pelos portões que este repositório já se impõe: licença conferida, versão fixada no hermit, release com no mínimo 7 dias, verbete no dicionário do cspell, e zero comentário nos arquivos de configuração. E todo check novo nasce não bloqueante, pela mesma regra que rege a seção de bypasses acima.
-
mainnão tem proteção de branch nenhuma.gh api repos/ladesa-ro/infrastructure/branches/main/protectiondevolve 404 erulesetsdevolve lista vazia, num repositório cuja tese inteira é infraestrutura revisada em pull request. Pesa mais do que parece por causa do alcance que uma permissão de commit aqui carrega, registrado no inventário privado apontado por Estado fora do git. Condição pra fechar em duas etapas: exigir revisão de CODEOWNERS agora, e exigir os checks de status só depois que eles deixarem de sercontinue-on-error, porque exigir um check que nunca falha não significa nada. - Renovate ao lado do Dependabot. O Dependabot não alcança três coisas que este repositório fixa à mão: as versões de k3s, Helm e do chart do Argo CD em
ansible/inventory/host_vars/, as oito versões dos.hcldo hermit, e otargetRevisiondos charts remotos emargocd/applications/. O manager nativo de Argo CD do Renovate casa porspec.source.chartsem exigir comentário no YAML, o que importa aqui porque comentário em YAML é proibido por regra doast-grep. Limitação a registrar antes de adotar: o Renovate não recalcula osha256dos binários fixados, então isso precisa de um passo companheiro. - Trivy com mais escopo. Já está instalado em
security.yml, rodando sóscanners: misconfig. Ligarvulnesecret, e gerar SBOM em CycloneDX, é mudança de flag numa ferramenta que já passou por todos os portões. Fecha de uma vez os itens de Supply chain e SBOM e de Vulnerability scanning, que hoje têm adoção zero apesar de terem página própria. -
step-security/harden-runnernos jobs que rodam emubuntu-latest, em modo de auditoria primeiro e com lista de egresso permitido depois. É a primeira adoção concreta de defesa de cadeia de suprimentos neste repositório. - OpenSSF Scorecard. Gratuito pra repositório público, mede o que este repositório já faz bem (action fixada por hash, CODEOWNERS, atualização automática de dependência) e nomeia o que falta, começando pela proteção de branch da primeira linha desta seção.
-
ansible-lintno perfilproduction. Hoje roda no perfil padrão. Os roles de importação do node dobraram a superfície de Ansible do repositório, então vale apertar, sempre nascendo não bloqueante. - Job de
argocd app diffem pull request. Transforma o gate de drift zero num check por PR em vez de um ritual de bootstrap. Exige acesso de leitura ao cluster a partir do CI, então precisa de uma ServiceAccount só de leitura, e não do kubeconfig de administração. -
ValidatingAdmissionPolicyem vez de Kyverno ou Gatekeeper. Policy as code argumenta que admission controller é a forma forte da garantia, porque impede aplicar direto no cluster pulando a revisão. A escolha porValidatingAdmissionPolicyé deliberada: é nativa do Kubernetes, não exige operador e não custa nenhum pod, o que importa porque este node não tem folga de memória. AplicariasecurityContextobrigatório e imagem fixada por digest no momento da admissão, atacando a raiz do bypass domisconfigregistrado no topo deste documento. -
ScheduledBackupdo CNPG pro MinIO que já roda no cluster. Foundation registra que, se o disco do node morresse, o dado seria perdido sem recuperação possível. Os dois componentes já existem e já são gerenciados por GitOps. Junto vai um how-to de teste de restauração, porque Backup e disaster recovery aponta o backup nunca testado como o ponto cego real, não a ausência de backup. - Nenhum check de CI valida âncora de link. O
mkdocs build --strictvalida que o arquivo alvo existe, não que o fragmento depois do#existe, e olycheeroda hoje sem essa verificação ativa. Isso deixou passar um link morto de verdade: renomear um cabeçalho emaprender/firewalld.mdquebrou a referência no TLDR deaprender/cloudflare.md, e todos os gates seguiram verdes. Achado em 2026-08-21, corrigido na hora, mas a lacuna que permitiu continua. Condição pra fechar: ligar a verificação de fragmento nolychee, que já está noquality.ymle já passou por todos os portões, nascendo não bloqueante como os demais. - Estágios seguintes do hardening CIS do k3s, depois do
protect-kernel-defaultsjá registrado na seção de importação:secrets-encryption, que exige reinício e recifrar os segredos que já existem no cluster, e o bloco dekube-apiserver-argcom log de auditoria, que precisa de arquivos de política no disco e de espaço pro log, e por isso depende de resolver a ocupação de disco antes.
Mapear operação de rotina/manutenção pro Ansible, considerar por último¶
Registrado em 2026-08-21, pedido explícito do usuário, deliberadamente o último item da lista (depende do resto do backlog estar resolvido primeiro, senão mapeia procedimento que ainda vai mudar). Hoje o Ansible deste repositório (ansible/roles/: k3s, firewalld, vault_repo, argocd_bootstrap, self_pull_timer) cobre só o bootstrap (rodar uma vez, do zero até o cluster de pé, ver Bootstrap mínimo na VM). A contraparte de operação contínua não existe como código nenhum, é uma sequência de comando kubectl/ssh/argocd executada manualmente e documentada em prosa (quando documentada), não repetível de forma idempotente. Isso já está parcialmente reconhecido em "Rotina de operação contínua a criar" acima, mas aquela seção é sobre decidir onde a documentação mora; este item é sobre o que dessa operação vale a pena virar Ansible de verdade, não só prosa.
Candidatos reais, levantados só de olhar pro que já foi feito manualmente nesta sessão (lista de partida, não exaustiva, o próprio item pede uma análise antes de decidir o que entra):
- Rotação de senha (Ansible Vault, deploy keys, roles do Postgres, segredos do Infisical): hoje cada rotação é uma sequência de comando ad-hoc no node (
openssl,ansible-vault encrypt,kubectlpra reconciliar). Virar um role/playbook parametrizado (ansible-playbook rotacionar-segredo.yml -e alvo=postgres-role-x) reduziria o risco de esquecer um passo no meio de uma rotação sob pressão. - Backup de datastore antes de upgrade (o padrão usado pro sqlite do k3s nesta sessão:
systemctl stop,cp -a,systemctl start): hoje é comando manual toda vez que alguém for atualizar o k3s. Virar uma task/role reaproveitável evita repetir o mesmo raciocínio (e o mesmo risco de esquecer um passo) na próxima atualização. - Sync do
root/Applicationdo Argo CD depois de umgit pull: hoje éssh+git pull --ff-only+argocd app sync root --coredigitado à mão a cada mudança, com a pegadinha real já documentada (sincronizarrootantes daApplicationfilha, ver Aprender: Argo CD) dependendo de quem está operando lembrar na hora. - Sweep de saúde pós-mudança (
kubectl get pods -Asem nada fora deRunning/Completed,argocd app list --coretudoSynced/Healthy): repetido manualmente depois de cada sync nesta sessão inteira, candidato natural a virar um único comando/task, talvez até um health-check reaproveitado pelo item "Acompanhamento de saúde" já registrado acima. - Renovação/verificação do dead man switch (
systemd-run --on-active=...): o padrão já é consistente entre os usos desta sessão (firewalld, RabbitMQ), mas cada vez foi escrito na mão no terminal; umrole/templateparametrizado (comando-de-revert,janela-de-tempo) reduziria a chance de errar a sintaxe dosystemd-runnuma hora de pressão real.
Por que só ao final: mapear procedimento operacional pro Ansible antes do resto do backlog estar resolvido significa documentar/automatizar um estado que ainda vai mudar (ex.: a reorganização de argocd/ muda o comando de sync; a auditoria de chart oficial muda o que existe pra fazer backup antes de tocar). Fazer isso por último evita reescrever o mesmo role duas vezes.
Mecanismo de rollout de imagens trocado de digest-via-PR pra Argo CD Image Updater, 2026-08-28¶
A correção registrada em "GitOps com tag mutável" foi implementada na mesma manhã como digest imutável + PR automático (GITHUB_TOKEN), e substituída na mesma tarde. Motivo: em docs e management-service, todo push em development, ambiente sem razão nenhuma pra exigir aprovação humana pra ver o próprio código rodando, passou a esperar alguém clicar em "Approve", por causa do ruleset de main que já existia antes desta correção, não introduzido por ela.
Mecanismo final: Argo CD Image Updater instalado como componente de foundation, aplicando a atualização direto na Application (write-back-method: argocd, sem commit, sem PR), com webhook do GHCR configurado pra reduzir o delay do polling padrão a segundos. Detalhe completo, incluindo a análise de segurança do endpoint exposto, em Rollout de imagens.
-
docsemanagement-servicemigrados: anotações naApplication,ImageUpdaterCR único cobrindonamePattern: "ladesa-ro-*". Confirmado ao vivo em 2026-08-28:application.deployment.image.digestaplicado via parâmetro Helm nos 3 satélites (docs,api,timetable-worker). -
webmigrado: mesmas anotações, PR web#790. -
authentication-serviceainda não migrado: mesmas anotações a replicar, nenhuma peça de infraestrutura nova necessária (oImageUpdaterCR já cobre qualquer satéliteladesa-ro-*novo). - Webhook de organização no GitHub ainda não criado: token
ghlocal não tinha o escopoadmin:org_hook; pedido de elevação (gh auth refresh -s admin:org_hook) expirou sem aprovação a tempo. Falta: aprovar o fluxo de dispositivo e rodar a criação do webhook (eventopackage, URLhttps://image-updater.ladesa.com.br/webhook?type=ghcr.io). Até lá, o Image Updater funciona só por polling (padrão do controller, tipicamente ~2min). - Webhook de organização criado: id
671516711, eventopackage,active: true. - Projeto
foundation-image-updater-w0cwcriado no Infisical, chavewebhook.ghcr-secretregistrada emprod.
Webhook do GitHub pro argocd-server, reduzindo o delay de root, 2026-08-28¶
Diferente do webhook do GHCR (que acorda o Image Updater), este é pro próprio argocd-server: reduz o delay do ciclo de polling padrão do Argo CD pra detectar mudanças em main do infrastructure (o que afeta principalmente o root, hoje sem automated.selfHeal reativo a webhook, só a polling).
-
rootganhousyncPolicy.automated:argocd/root/application.yaml, PR #7, mergeado e aplicado no cluster peloself-pull-timer.rootconfirmadoSynced/Healthycomautomated.selfHealativo. - Webhook de organização no GitHub criado: evento
push, URLhttps://argocd.ladesa.com.br/api/webhook. - Secret do webhook (
webhook.github.secretemargocd-secret) aplicado no cluster: manifest cifrado eminfrastructure-vault(secrets/k8s/argocd/webhook-github-secret.yaml), oself-pull-timerjá reconciliou sozinho, chave confirmada presente emargocd-secret.
Image Updater estava instalado mas 100% inerte desde o PR #5, achado ao vivo em 2026-08-28¶
Achado ao investigar por que o Image Updater não estava atualizando docs/api/timetable-worker apesar das anotações e do PR #6 (fix de watch.namespaces) já mergeados: a Application foundation-image-updater estava presa em OutOfSync/Failed desde a instalação, erro resource argocd-image-updater.argoproj.io:ImageUpdater is not permitted in project ladesa. Causa: argocd/root/project.yaml nunca ganhou ImageUpdater no namespaceResourceWhitelist quando o componente foi instalado. O ImageUpdater CR nunca sincronizou, e o pod, apesar de Running, nunca aplicou o watch.namespaces=argocd do PR #6, log mostrando "Watching controller namespace only" desde o dia da instalação.
- Corrigido: PR #10,
namespaceResourceWhitelistliberado, aplicado direto no cluster (kubectl apply) pra desbloquear na hora.ApplicationconfirmadaSynced, pod novo já loga"Watching specific namespaces" namespaces=argocd. - Anomalia resolvida, causa era outra: o diagnóstico anterior (controller "não listando" as 3 apps) estava errado, era o nível de log
infoescondendo as linhas de debug e mostrando só o resumo final, que por coincidência também dizia "3". Subindo pralog.level: debugde verdade (viavalues.yaml, PR #10) apareceu a causa real:"Image '...' seems not to be live in this application, skipping". Isso é um bug conhecido do próprio Argo CD (Application.status.summary.imagesvazio pra todaApplicationdo cluster, não só as 3), argoproj/argo-cd#27861. Mitigado com a anotação<alias>.force-update: "true"emdocs/api/timetable-worker/web(PRs docs#15, management-service#1101, web#790). Faltava ainda a tag explícita (:development) na anotaçãoimage-list, sem ela oupdate-strategy: digestnão tem versão pra ancorar a resolução do digest, corrigido nos mesmos PRs. Detalhe completo em Rollout de imagens.log.levelrevertido prainfodepois do diagnóstico (PR #10 já mergeada com o valor de debug; revertido em PR separada). Confirmado ao vivo funcionando de ponta a ponta pros 3 satélites (application.deployment.image.digestaplicado via parâmetro Helm). -
AppProject ladesaperdeu o whitelist doImageUpdaterde novo, sem causa raiz confirmada: depois de corrigido pelo PR #10 e aplicado com sucesso peloself-pull-timer(confirmado no log doansible-pullàs 15:31:46, diff aplicado corretamente), o campo sumiu de novo daAppProjectviva por volta de 15:57, travando ofoundation-image-updaterde novo com o mesmo errois not permitted in project ladesa. Investigado: sem segunda definição doAppProjectem outro arquivo, sem redefinição viaconfigs.projectsno Helm values do Argo CD, sem outra execução doansible-pullnesse intervalo. O histórico demanagedFieldsque poderia mostrar quem escreveu por último foi apagado sem querer pelo própriokubectl apply(client-side) usado pra corrigir na hora, tanto o meu quanto o do Ansible usam client-side apply, que sempre colapsa pra um único managerkubectl-client-side-apply. Corrigido de novo manualmente. Se acontecer de novo, capturarkubectl get appproject ladesa -o yaml(incluindomanagedFields) antes de corrigir, pra não perder o rastro de novo.