Explanation
ArgoCD GitOps Workflow for GitLab BDA¶
This document explains GitLab BDA’s specific ArgoCD deployment workflow and sync wave strategy. For general GitOps concepts and ArgoCD fundamentals, see ArgoCD GitOps.
Overview¶
GitLab BDA uses a 3-wave deployment strategy to ensure proper dependency ordering:
Wave 1: Infrastructure (namespace, RBAC, S3 buckets)
Wave 2: Secrets (ExternalSecrets with credentials)
Wave 3: Applications (PostgreSQL, Redis, GitLab, Harbor)
GitLab BDA Deployment Pipeline¶
graph TD
subgraph "Development (Local)"
A[Edit TypeScript] -->|charts/*.ts| B[Source .env]
B --> C[npm run build]
C --> D{Manifests<br/>Generated}
end
subgraph "Version Control"
D -->|git commit| E[Local Git]
E -->|git push| F[Git Repository<br/>gitlab/harbor]
end
subgraph "ArgoCD (In Cluster)"
F -->|Poll every 3 min| G[ArgoCD Detects Change]
G --> H{Compare States}
H -->|Diff found| I[Calculate Changes]
I --> J[Apply Sync Waves]
end
subgraph "Kubernetes Cluster"
J -->|Wave 1| K[Infrastructure<br/>Namespace, RBAC, S3]
K -->|Wave 2| L[Secrets<br/>ExternalSecrets]
L -->|Wave 3| M[Applications<br/>GitLab, Harbor]
M --> N[Running Pods]
end
N -.->|Monitor| G
style G fill:#f96
style F fill:#fc6
style N fill:#6f6
Step-by-Step: Making a Change¶
1. Developer edits configuration:
cd dp-infra/gitlabbda
vim charts/constructs/database.ts
# Change PostgreSQL storage from 10Gi to 20Gi
2. Build manifests:
# CRITICAL: Source .env to load credentials
bash -c 'source .env && npm run build'
This generates updated manifests/gitlab.k8s.yaml with changes.
3. Review changes:
git diff manifests/gitlab.k8s.yaml
# Shows PostgreSQL PVC size change:
- storage: 10Gi
+ storage: 20Gi
4. Commit and push:
git add manifests/ charts/
git commit -m "Increase PostgreSQL storage to 20Gi for growth"
git push origin main
5. ArgoCD automatically syncs:
ArgoCD polls git every 3 minutes (configurable)
Detects manifest changes
Applies changes to cluster
Reports sync status
6. Verify deployment:
# Check ArgoCD Application status
kubectl get application gitlab-bda -n argocd
# Should show:
# SYNC STATUS: Synced
# HEALTH STATUS: Healthy
GitLab BDA Sync Waves¶
ArgoCD applies resources in waves (numbered order) to respect dependencies:
Wave 1: Infrastructure¶
Resources:
Namespace (
gitlabbda)ServiceAccounts, Roles, RoleBindings
Crossplane ProviderConfig (S3)
S3 Buckets (8 buckets: artifacts, uploads, LFS, pages, registry, backups, postgresbackups, cache)
Why wave 1:
Must exist before secrets and applications
S3 buckets must be created before PostgreSQL backups configured
RBAC must exist before applications create pods
Example:
// charts/constructs/namespace.ts
new kplus.Namespace(this, 'namespace', {
metadata: {
name: 'gitlabbda',
annotations: {
'argocd.argoproj.io/sync-wave': '1',
},
},
});
Wave 2: Secrets¶
Resources:
ClusterSecretStore (ESO)
ExternalSecrets (GitLab secrets, PostgreSQL credentials, S3 credentials)
Why wave 2:
Must exist after infrastructure (wave 1)
Must exist before applications (wave 3) that reference them
PostgreSQL needs credentials before starting
GitLab needs secrets before pods start
Example:
// charts/constructs/app-secrets.ts
new ExternalSecret(this, 'gitlab-secrets', {
metadata: {
namespace: 'gitlabbda',
annotations: {
'argocd.argoproj.io/sync-wave': '2',
},
},
spec: {
secretStoreRef: {
name: 'gitlabbda-app-secrets-store',
kind: 'ClusterSecretStore',
},
// ...
},
});
Wave 3: Applications¶
Resources:
PostgreSQL Cluster (CloudNativePG)
Redis StatefulSet
GitLab Helm release
Harbor Helm release
Why wave 3:
Depends on secrets (wave 2)
Depends on S3 buckets (wave 1)
Can start safely after all dependencies ready
Example:
// charts/constructs/database.ts
new cnpg.Cluster(this, 'postgres', {
metadata: {
namespace: 'gitlabbda',
annotations: {
'argocd.argoproj.io/sync-wave': '3',
},
},
spec: {
instances: 2,
storage: {
storageClass: 'longhorn-redundant-app',
size: '10Gi',
},
backup: {
barmanObjectStore: {
destinationPath: 's3://gitlab-postgresbackups-gitlabbda-kup6s/',
// S3 credentials from ExternalSecret (wave 2)
},
},
},
});
GitLab BDA Sync Wave Summary¶
Wave |
Component |
Resources |
Depends On |
|---|---|---|---|
1 |
Infrastructure |
Namespace, RBAC, ProviderConfig, S3 Buckets |
None |
2 |
Secrets |
ClusterSecretStore, ExternalSecrets |
Wave 1 (namespace, S3) |
3 |
Applications |
PostgreSQL, Redis, GitLab, Harbor |
Wave 2 (secrets), Wave 1 (S3) |
ArgoCD Application Manifest¶
The ArgoCD Application for GitLab BDA:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: gitlab-bda
namespace: argocd
spec:
project: default
source:
repoURL: https://git.bluedynamics.eu/kup6s/dp/gitlab.git
targetRevision: main
path: manifests # ArgoCD reads from this directory
destination:
server: https://kubernetes.default.svc # This cluster
namespace: gitlabbda
syncPolicy:
automated:
prune: true # Delete resources removed from git
selfHeal: true # Auto-revert manual kubectl changes
syncOptions:
- CreateNamespace=true # Create namespace if missing
- ApplyOutOfSyncOnly=true # Only update changed resources
Key Settings:
automated.prune: true- Removes resources deleted from git (keeps cluster clean)automated.selfHeal: true- Reverts manualkubectlchanges (enforces GitOps)source.path: manifests- ArgoCD reads generated manifests (not CDK8S charts/)
See ArgoCD Configuration Reference for complete manifest.
GitLab BDA Specific Patterns¶
1. Multi-Bucket Coordination¶
Challenge: GitLab needs 8 S3 buckets, all must exist before GitLab starts
Solution: All buckets in wave 1, GitLab in wave 3
// All 8 buckets created in wave 1
new Bucket(this, 'artifacts', {
metadata: { annotations: { 'argocd.argoproj.io/sync-wave': '1' } },
spec: { forProvider: { bucket: 'gitlab-artifacts-gitlabbda-kup6s' } },
});
// GitLab in wave 3, buckets guaranteed to exist
new GitlabHelmConstruct(this, 'gitlab', {
metadata: { annotations: { 'argocd.argoproj.io/sync-wave': '3' } },
// ...
});
2. PostgreSQL with Barman Backup¶
Challenge: PostgreSQL backup configuration references S3 bucket and credentials
Solution:
S3 bucket in wave 1
S3 credentials in wave 2 (ExternalSecret)
PostgreSQL in wave 3
// Wave 2: ExternalSecret with S3 credentials
new ExternalSecret(this, 's3-creds', {
metadata: { annotations: { 'argocd.argoproj.io/sync-wave': '2' } },
// ...
});
// Wave 3: PostgreSQL references credentials
new cnpg.Cluster(this, 'postgres', {
metadata: { annotations: { 'argocd.argoproj.io/sync-wave': '3' } },
spec: {
backup: {
barmanObjectStore: {
destinationPath: 's3://gitlab-postgresbackups-gitlabbda-kup6s/',
s3Credentials: {
accessKeyId: { name: 's3-creds', key: 'access-key' },
secretAccessKey: { name: 's3-creds', key: 'secret-key' },
},
},
},
},
});
3. ExternalSecrets with ClusterSecretStore¶
Challenge: ExternalSecret needs ClusterSecretStore to exist first
Solution: Both in wave 2, but use sub-ordering (ArgoCD sorts alphabetically within wave)
// Created first (alphabetically: "a-...")
new ClusterSecretStore(this, 'a-secret-store', {
metadata: { annotations: { 'argocd.argoproj.io/sync-wave': '2' } },
// ...
});
// Created second (alphabetically: "z-...")
new ExternalSecret(this, 'z-app-secrets', {
metadata: { annotations: { 'argocd.argoproj.io/sync-wave': '2' } },
spec: { secretStoreRef: { name: 'gitlabbda-app-secrets-store' } },
});
Monitoring GitLab BDA Deployment¶
# Check overall status
kubectl get application gitlab-bda -n argocd
# Check which wave is currently applying
argocd app get gitlab-bda --show-operation
# View sync history
argocd app history gitlab-bda
# Check for drift
argocd app diff gitlab-bda
Troubleshooting¶
Application Stuck on Wave 2¶
Symptoms:
argocd app get gitlab-bda --show-operation
# Shows: Waiting for Wave 2 resources
Solution: Check ExternalSecrets synced
# Check ExternalSecret status
kubectl get externalsecret -n gitlabbda
# Check if secrets created
kubectl get secret -n gitlabbda | grep gitlab
# If not synced, check ClusterSecretStore
kubectl get clustersecretstore gitlabbda-app-secrets-store
PostgreSQL Pods CrashLoopBackOff¶
Symptoms:
kubectl get pods -n gitlabbda | grep postgres
# postgres-1 CrashLoopBackOff
Common causes:
S3 credentials missing (wave 2 not complete)
S3 bucket doesn’t exist (wave 1 failed)
Barman backup configuration invalid
Solution:
# Check secrets exist
kubectl get secret -n gitlabbda | grep s3
# Check S3 buckets created
kubectl get bucket -A | grep gitlab
# Check PostgreSQL logs
kubectl logs -n gitlabbda postgres-1
GitLab Pods Pending¶
Symptoms:
kubectl get pods -n gitlabbda | grep gitlab
# gitlab-webservice-xxx Pending
Common causes:
PostgreSQL not ready (wave 3 dependency)
Redis not ready (wave 3 dependency)
Secrets missing (wave 2 not complete)
Solution:
# Check PostgreSQL ready
kubectl get cluster -n gitlabbda gitlab-postgres
# Check Redis ready
kubectl get statefulset -n gitlabbda redis
# Check secrets exist
kubectl get secret -n gitlabbda | grep gitlab-secrets
Best Practices¶
1. Always Build Before Commit¶
Good:
bash -c 'source .env && npm run build'
git add charts/ manifests/
git commit -m "Update PostgreSQL storage"
Bad:
# Forgot to rebuild manifests
git add charts/
git commit -m "Update PostgreSQL storage"
# manifests/ out of sync!
2. Descriptive Commit Messages¶
Good:
feat: Increase PostgreSQL storage to 20Gi
Anticipating growth from 5 to 10 users over next quarter.
Storage usage currently at 7Gi, will hit limit in ~2 months.
Affects:
- PostgreSQL PVC size
- Barman backup configuration
See: #123
Bad:
Update config
3. Verify Sync Waves in Manifests¶
# After build, check sync waves are correct
grep "sync-wave" manifests/gitlab.k8s.yaml | sort -u
# Should show:
# sync-wave: "1" # Infrastructure
# sync-wave: "2" # Secrets
# sync-wave: "3" # Applications
4. Monitor Deployment Progress¶
# Watch deployment progress
watch kubectl get application gitlab-bda -n argocd
# Check pod status during deployment
watch kubectl get pods -n gitlabbda