From 25b82848d610bf87731c08b2551102d15e1ca822 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Sat, 3 Jan 2026 16:09:31 -0600 Subject: [PATCH] Run migrations on app start instead of separate job - Migrations now run in Application.start/2 before starting services - Ecto's advisory locks prevent concurrent migrations in clustered setup - Simplified deployment - no separate migration job needed - Removed migrate-job.yaml manifest --- .gitlab-ci.yml | 13 +------- k8s/migrate-job.yaml | 59 ------------------------------------- lib/towerops/application.ex | 5 ++++ 3 files changed, 6 insertions(+), 71 deletions(-) delete mode 100644 k8s/migrate-job.yaml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 94349e8a..69a5031d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -43,18 +43,7 @@ deploy: script: - kubectl config get-contexts - kubectl config use-context graham/towerops:towerops - # Run database migrations before deploying new version - - | - # Create unique job name using commit SHA (truncated to fit k8s naming limits) - JOB_NAME="towerops-migrate-${CI_COMMIT_SHORT_SHA}" - # Generate job manifest with correct image tag and unique name - sed -e "s|IMAGE_TAG|${CI_COMMIT_SHA}|g" \ - -e "s|towerops-migrate-TIMESTAMP|${JOB_NAME}|g" \ - k8s/migrate-job.yaml | kubectl apply -f - - # Wait for migration to complete (max 5 minutes) - kubectl wait --for=condition=complete --timeout=5m job/${JOB_NAME} -n towerops - echo "Database migration completed successfully" - # Deploy new version + # Deploy new version (migrations run on app start) - kubectl set image deployment/towerops towerops=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA -n towerops - kubectl rollout status deployment/towerops -n towerops --timeout=5m environment: diff --git a/k8s/migrate-job.yaml b/k8s/migrate-job.yaml deleted file mode 100644 index 3d085805..00000000 --- a/k8s/migrate-job.yaml +++ /dev/null @@ -1,59 +0,0 @@ ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: towerops-migrate-TIMESTAMP - namespace: towerops - labels: - app: towerops - component: migration -spec: - # Clean up completed jobs after 1 hour - ttlSecondsAfterFinished: 3600 - backoffLimit: 3 - template: - metadata: - labels: - app: towerops - component: migration - spec: - restartPolicy: Never - imagePullSecrets: - - name: gitlab-registry - securityContext: - runAsNonRoot: true - runAsUser: 65534 - fsGroup: 65534 - seccompProfile: - type: RuntimeDefault - containers: - - name: migrate - image: registry.gitlab.com/graham/towerops:IMAGE_TAG - command: ["/app/bin/towerops", "eval", "Towerops.Release.migrate()"] - securityContext: - allowPrivilegeEscalation: false - runAsNonRoot: true - runAsUser: 65534 - capabilities: - drop: - - ALL - seccompProfile: - type: RuntimeDefault - env: - - name: MIX_ENV - value: "prod" - - name: RELEASE_COOKIE - valueFrom: - secretKeyRef: - name: towerops-secrets - key: RELEASE_COOKIE - envFrom: - - secretRef: - name: towerops-db - resources: - requests: - memory: "128Mi" - cpu: "100m" - limits: - memory: "256Mi" - cpu: "500m" diff --git a/lib/towerops/application.ex b/lib/towerops/application.ex index 78d680d9..a7eca3c7 100644 --- a/lib/towerops/application.ex +++ b/lib/towerops/application.ex @@ -7,6 +7,11 @@ defmodule Towerops.Application do @impl true def start(_type, _args) do + # Run migrations on startup (Ecto handles locking for concurrent runs) + unless Application.get_env(:towerops, Towerops.Repo)[:database] == "towerops_test" do + Towerops.Release.migrate() + end + topologies = Application.get_env(:libcluster, :topologies, []) children = [