Java.DBMigrationTools.How do you use migration tools in GitOps-based workflows?

In GitOps, the key shift is: Git is the desired state, and your CD controller (Argo CD / Flux) is the executor. So migrations must be declarative, repeatable, observable, and safe under retries.

Core principles

  • Immutable migrations: never edit an applied migration; fix-forward with a new one.
  • Idempotent execution: the migration runner can be retried safely (GitOps controllers will retry).
  • Single writer: only one actor runs migrations at a time per DB.
  • Separation of concerns: app rollout and schema rollout are related, but not the same thing.

Common GitOps patterns (what actually works)

Pattern A — “Migration Job as a sync step” (most common)

You commit a Kubernetes Job that runs Flyway/Liquibase, and Argo/Flux applies it.

Argo CD

  • Use sync waves so DB migration runs before app deployment.
  • Optionally use a PostSync hook if you want migrations after the app (rare; usually wrong for breaking changes).

Pros

  • Clear audit trail in Git
  • Argo shows success/failure
  • Controlled ordering

Cons

  • Jobs must be carefully designed to be re-runnable and not spam multiple runs

Practices

  • Job name includes release version/hash so each deploy is a new job:
    • migrate-<appVersion> (or commit SHA)
  • Use a dedicated migration container image that contains:
    • your migration scripts
    • the exact tool version (Flyway/Liquibase)
  • Migration job uses the migration role credentials via Secret

Pattern B — “Init container in the app Deployment” (simpler, riskier at scale)

The app pod includes an initContainer that runs migrations before the app container starts.

Pros

  • Easy to wire
  • No extra object

Cons

  • Concurrency: many replicas start → many attempt migrations (you must gate with lock/leader)
  • Harder to manage failures vs app rollout
  • Less control in multi-service/shared DB cases

If you do this, enforce:

  • replicas=1 during migration, or
  • leader election / single-runner, and rely on Liquibase/Flyway locking

Pattern C — “Platform pipeline runs migrations, GitOps only deploys app” (very stable)

The pipeline runs migrations against the target environment first, then commits/promotes the app manifest.

Pros

  • Great control and observability
  • Prevents “controller retries causing re-execution surprises”

Cons

  • Slightly less “pure” GitOps; execution not exclusively from cluster pull

In practice, many mature orgs do this because DB changes are high blast-radius.


How you keep it safe: Expand–Migrate–Contract + GitOps promotion

GitOps shines when you promote commits between envs (dev → stage → prod).

Recommended sequence

  1. Expand schema (additive, backward compatible) — commit A
  2. Deploy app that can handle old+new — commit B
  3. Backfill (separate migration or batch job) — commit C
  4. Switch reads via feature flag — commit D
  5. Contract (drop old) later — commit E

This prevents “Argo applied everything, but pods restarted mid-way and now we’re broken”.


Concrete mechanics you should mention in interviews

Ordering and gating

  • Argo CD: sync waves (DB job wave -1, app wave 0)
  • Flux: use Kustomize/Helm ordering (less expressive than Argo waves, but doable via separate Kustomizations with dependsOn)

Concurrency control

  • Liquibase: DATABASECHANGELOGLOCK (monitor/alert for stuck locks)
  • Flyway: locks via schema history / DB locking behavior
  • Still best to design “one job per deploy” and let the tool lock be the final safety net.

Retry semantics (GitOps controllers retry)

Your migration execution must be:

  • safe to re-run (if job gets restarted)
  • not doing huge non-idempotent DML without guards
  • not combining transactional + non-transactional DDL wrongly (e.g., Postgres CREATE INDEX CONCURRENTLY cannot be inside BEGIN/COMMIT)

Observability

  • Emit logs + metrics from the migration job (duration, success/failure, target version).
  • Dashboards query:
    • Flyway: flyway_schema_history
    • Liquibase: DATABASECHANGELOG + DATABASECHANGELOGLOCK

Rollback reality

  • In GitOps, rollback is “revert Git commit”, but DB rollback is rarely automatic.
  • So you design migrations as forward-only and rely on:
    • backups
    • restore procedures
    • compatibility rollout patterns (expand/contract)

How I would describe a “good” GitOps setup (short)

“In GitOps, migrations are applied as a dedicated Kubernetes Job managed by Argo/Flux, ordered before the app rollout via sync waves/dependsOn. The job uses a pinned tool version and immutable migration artifacts, is safe under retries, and we monitor schema history tables plus job status. We use expand–migrate–contract with feature flags to tolerate mixed versions during rollout, because ‘git revert’ doesn’t undo database state.”

This entry was posted in Без рубрики. Bookmark the permalink.