iGRPIGRP 3.0Documentation

Instalação via ArgoCD (GitOps)

Deployment do igrp-stack por GitOps — repositório de deployment, modelo de segredos por camadas e as armadilhas do sync.

Modelo de deployment em que se copia o Chart.yaml e um values.yaml para um repositório git de deployment, se editam os parâmetros do ambiente, e o ArgoCD sincroniza com o cluster. Os subcharts são resolvidos do registry OCI.


1. Pré-requisitos

Subcharts publicados no registry

Todos os subcharts igrp-* e o umbrella têm de existir em oci://registry.nosi.cv/igrp-charts. O CI do repositório do chart trata disso: job manual publish-all para bootstrap, mais publicação automática no merge.

Registry OCI registado no ArgoCD

Sem isto, o helm dependency build do repo-server falha ao puxar os subcharts.

argocd repo add registry.nosi.cv --type helm --enable-oci \
  --name igrp-charts --username <user> --password <pass>

Ou declarativo, como Secret no namespace do ArgoCD:

apiVersion: v1
kind: Secret
metadata:
  name: igrp-charts-oci
  namespace: argocd
  labels:
    argocd.argoproj.io/secret-type: repository
stringData:
  name: igrp-charts
  url: registry.nosi.cv
  type: helm
  enableOCI: "true"
  username: <user>
  password: <pass>

Cluster

Operador cloudnative-pg, StorageClass (Longhorn / Azure Disk) e nginx IngressController — ver a visão geral.


2. Repositório de deployment

Estrutura mínima por ambiente:

deployments/
└── igrp-stack/
    ├── Chart.yaml          ← CÓPIA do Chart.yaml do igrp-stack (não editar deps)
    ├── values.yaml         ← parâmetros do ambiente (domínios, réplicas, toggles…)
    └── sealed-secrets/     ← só em produção

O Chart.yaml copia-se tal e qual: as dependências apontam para o OCI com versão fixa, e assim os paths dos values ficam idênticos aos de toda a documentação (igrp-access-management.oauth…). Para o values.yaml, parte dos ficheiros do chart e ajusta os três domínios global.*.

A Application

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: igrp-stack
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://<servidor-git>/<grupo>/<repo-deployments>.git
    targetRevision: main
    path: deployments/igrp-stack
  destination:
    server: https://kubernetes.default.svc
    namespace: igrp
  syncPolicy:
    syncOptions:
      - CreateNamespace=true
      - ServerSideApply=true        # ConfigMap grande do realm do Keycloak
    # automated: { prune: true, selfHeal: true }   # opt-in
  ignoreDifferences:
    - group: postgresql.cnpg.io
      kind: Cluster
      jsonPointers: [ /status ]

Comportamento dos Jobs no primeiro sync

O ArgoCD converte hooks post-install em PostSync, que só corre quando todos os recursos estão Healthy. Num sync inicial de raiz, os process backends não ficam Healthy sem os Secrets e BDs que estes Jobs criariam — pode ficar circular. O chart já mitiga isto com argocd.argoproj.io/hook: Sync e a coreografia de waves. No fluxo helm upgrade --install pelo CLI esta limitação não existe.


3. Segredos — modelo por camadas

O chart cria por default os Secrets a partir dos values (create: true). Para GitOps há toggles que desligam essa criação, e o chart passa a apenas referenciar Secrets pré-existentes.

SecretToggle nos valuesChaves esperadas
igrp-*-db-user (4, ou 6 com notificações)igrp-pg-cluster.dbUsers.createSecrets: falseusername/password, type kubernetes.io/basic-auth, label cnpg.io/reload: "true"
igrp-access-management-secretsigrp-access-management.secret.create: falsesmtp-password
igrp-access-management-oauth-clientigrp-access-management.oauthClient.create: falseclient-id/client-secret
igrp-access-management-oauth-keysjá é sempre existingSecretpublic.pem/private.pem — via scripts/generate-oauth-keys.sh
igrp-minio-credsigrp-minio.credsSecret.create: falseMINIO_ROOT_USER/MINIO_ROOT_PASSWORD
igrp-app-center-nextauthigrp-app-center.nextauth.existingSecretnextauth-secret — partilhado por TODOS os frontends Next.js
nextauth das process UIsnenhum — apontam por default ao Secret do app-centernextauth-secret
igrp-keycloak-adminigrp-keycloak.keycloak.auth.existingSecretadmin-password
igrp-redis-authigrp-redis.authSecret.create: false (prod) ou password explícita (dev)redis-password
igrp-process-*-oauth-client (4)já é sempre existingSecret, ou Job m2m.autoRegisterclient-id/client-secret
igrp-notification-{api,ui}-oauth-client (2)idemclient-id/client-secret

Sem authSecret explícito, a password do Redis roda a cada sync

O lookup + random do wrapper gera uma password nova em cada sincronização do ArgoCD. Define authSecret.create: false (produção) ou authSecret.password (dev).

O mapa dbUsers.users exige password mesmo com o módulo desligado

As entradas notification e notificationTemporal vêm declaradas no values.yaml. A validação validate-db-users exige password a cada entrada do mapa mesmo com o módulo de notificações desligado, e um null no values do ambiente não as remove. Em GitOps: dbUsers.createSecrets: false e pré-criar os dois Secrets, ou dar-lhes password no values do ambiente.

Excepção: o clientSecret do Keycloak não pode vir de um Secret

O igrp-keycloak.igrp.clientSecrets.accessManagement é renderizado para dentro do realm_config.json, que é um ConfigMap. Só é usado no modo de federação Keycloak. As opções são: aceitar este único valor no values, ou importar o realm com um valor temporário e definir o secret real na consola do Keycloak (kcadm) depois do primeiro arranque.

3.1 Dev e stage — segredos nos values

Manter os defaults (create: true) e preencher os valores no values.yaml do repo de deployment. Aceitável apenas porque o repo é privado e as credenciais são de ambientes de teste. Nunca com credenciais de produção.

3.2 Produção — Sealed Secrets

Ver Instalação de produção para o procedimento completo. Em resumo: instalar o controller, guardar o backup da chave privada fora do git, gerar os SealedSecrets com scripts/bootstrap-secrets.sh --seal, commitar a pasta cifrada, e ligar os toggles no values de produção.

O ArgoCD não mistura chart e manifests soltos no mesmo source

Se a pasta sealed-secrets/ estiver dentro do path de um Application com chart Helm, cria um segundo Application (ou um segundo source) para ela.

3.3 Sem ferramenta — bootstrap manual

O mesmo script sem --seal cria os Secrets directamente no cluster, uma vez por ambiente, antes do primeiro sync. Os values usam os mesmos toggles.

3.4 Futuro — External Secrets Operator

O contrato "Secrets pré-existentes com estes nomes e chaves" é exactamente o que o ESO cumpre. Quando se adoptar Azure Key Vault, o bootstrap ou os SealedSecrets são substituídos por recursos ExternalSecret que criam os mesmos Secrets — sem alterações ao chart nem aos values.


4. Checklist de primeiro deploy

  1. publish-all corrido no CI (registry populado);
  2. registry OCI registado no ArgoCD;
  3. Secrets do ambiente criados + chaves RSA (generate-oauth-keys.sh);
  4. Chart.yaml + values.yaml no repo de deployment;
  5. (CNPG < 1.25) igrp-pg-cluster.cluster.databases: [] nos values, e criar access-manager-db / keycloak à mão depois do PostgreSQL subir — as BDs dos process-* são criadas pelo Job db-init em qualquer versão do CNPG;
  6. criar o Application e sincronizar, acompanhando a ordem: pg-cluster → redis/minio/keycloak → access-management → app-center/gateway → process-*;
  7. registar os clientes OAuth dos process-* (Job m2m.autoRegister ou manualmente);
  8. rollout restart do gateway se serviços novos não aparecerem nas rotas.

5. Armadilhas de uma instalação de raiz

Validado num cluster novo, apagando tudo e sincronizando só pelo ArgoCD. Correu — mas só depois destas quatro coisas.

5.1 O sync tem de ser disparado pelo caminho oficial

kubectl patch do .operation corre com skipHooks=true

Nenhum hook do chart é executado — nem db-init, nem oauth-register, nem catalog-seed, nem oauth-keys-init.

O sintoma não parece um problema de sync, parece um chart partido: o Temporal em CrashLoop porque as suas BDs não existem, e cinco apps em CreateContainerConfigError à espera dos Secrets *-oauth-client. E a instalação fica em ciclo — a operação espera que a wave 3 fique Healthy, e a wave 3 depende de hooks que não correram.

Dois agravantes:

  • Se já houver uma operação em Running, o controller retoma-a (Resuming in-progress operation) e ignora a nova. Para a substituir é preciso terminá-la primeiro e esperar que saia de Running/Terminating.
  • syncStrategy: {apply:{}} é apply-only e salta hooks por definição. Um merge-patch não remove uma estratégia já lá posta — fica {apply:{},hook:{}}, e o ArgoCD escolhe apply.

Usa o botão SYNC da UI, ou argocd app sync. Diagnóstico rápido:

kubectl logs -n argocd sts/argocd-application-controller --since=5m \
  | grep "application=<app>" | grep -o 'skipHooks=[a-z]*' | sort | uniq -c

5.2 Um repo que redeclara o umbrella não herda o values.yaml dele

Se o repo de deployment tem um Chart.yaml próprio com os subcharts como dependências, em vez de depender do umbrella publicado, não há camada base de values — todo o wiring tem de ser reproduzido.

O que mais escapa: igrp-pg-cluster.cluster.cluster.roles e cluster.databases. O access-management e o Keycloak não têm Job db-init — as suas roles e BDs são declarativas (CNPG ≥ 1.25). Sem esse bloco o Keycloak entra em CrashLoop com password authentication failed for user "keycloak_user" e o Access Management fica preso no initContainer sas-schema-init.

Verificação: renderizar o chart do repo de deployment e comparar o conjunto de recursos com um render do umbrella publicado.

5.3 Renderizar sem --namespace envenena o RBAC

helm template <release> . deixa .Release.Namespace em default. Os RoleBinding dos hooks passam a apontar a ServiceAccounts em default/… e os recursos namespaced saem com metadata.namespace: default. Os manifestos parecem bem; os Jobs falham em runtime com Forbidden … cannot get resource. Ao comparar com o cluster, renderiza sempre com --namespace <ns>.

5.4 Chaves RSA — manual ou automático, mas conscientemente

No subchart e no values.prod.yaml, o Secret igrp-access-management-oauth-keys é criado por ops. Enquanto não existir, o pod do Access Management fica em FailedMount indefinidamente, porque o Deployment monta-o como volume.

Para GitOps de raiz sem passos manuais: oauth.keys.generate.enabled: true (já ligado no values de teste), que corre o Job oauth-keys-init na sync-wave -5. Gera só se o Secret não existir, por isso não roda chaves em upgrades. Em produção continua com o Secret de ops — a chave privada fica gerível fora do cluster e sobrevive a um helm uninstall.

5.5 Uma sync não basta para convergir

A instalação de raiz convergiu ao fim de três argocd app sync seguidos. Cada um avança as waves, mas termina em Failed porque Deployments de waves posteriores ainda não têm as dependências e batem no progress deadline. Com syncPolicy.automated + retry na Application, o ArgoCD faz esses ciclos sozinho e uma única acção chega.


6. Armadilhas de um upgrade

6.1 Republicar a mesma versão: são DUAS caches, não uma

A política do chart permite corrigir in place, sem bump de versão. Mas o rollout restart do repo-server — que era a receita documentada — não chega:

CacheO que guardaComo se limpa
repo-server (disco do pod)os .tgz dos subcharts OCI, por versãokubectl rollout restart deploy/<release>-argocd-repo-server -n argocd
manifestos gerados (Redis)o YAML renderizado, indexado pela revisão git do repo de deploymenthard refresh — a revisão não mudou, por isso a cache continua válida
kubectl rollout restart deploy/<release>-argocd-repo-server -n argocd
kubectl rollout status  deploy/<release>-argocd-repo-server -n argocd
kubectl exec -n argocd sts/<release>-argocd-application-controller -- \
  sh -c 'argocd --core app get <app> --hard-refresh'
argocd app sync <app>

Synced + Healthy pode estar a mentir

Com só o restart, o sync reportou Synced e Healthy enquanto aplicava o render antigo. E parecia meio aplicado, porque annotations postas à mão sobrevivem ao merge patch. Confirma sempre num campo que o chart novo mudou — nunca no estado Synced.

6.2 Correr o CLI argocd quando não está instalado no nó

Usa o binário que já vive no pod do application-controller. O do argocd-server não tem RBAC para o modo --core:

kubectl exec -n argocd sts/<release>-argocd-application-controller -- \
  sh -c 'argocd --core app sync <app> --timeout 600'

6.3 O ArgoCD quer podar PVCs que ficaram como backup

Um PVC criado por uma versão anterior do chart mantém o argocd.argoproj.io/tracking-id e aparece em requiresPruning: true — um sync com prune apaga os dados. Para o preservar, tira-o do tracking (não apaga nada):

kubectl label    pvc <pvc> -n <ns> app.kubernetes.io/instance-
kubectl annotate pvc <pvc> -n <ns> argocd.argoproj.io/tracking-id-

6.4 Recursos com campos imutáveis

O spec.selector de Deployments e StatefulSets não se altera. Quando um subchart muda de esqueleto, o sync falha com field is immutable e o recurso tem de ser apagado à mão antes. Despiste genérico, a correr antes de um upgrade que troque a origem de um subchart:

# comparar os selectors renderizados com os que estão vivos
helm template <rel> . -n <ns> | grep -A4 'matchLabels'
kubectl get deploy,sts -n <ns> -o custom-columns='NAME:.metadata.name,SEL:.spec.selector.matchLabels'
How is this guide?

Última atualização: