Automatisasi Database Migration dengan Docker: Backup, Restore, dan Versioning

Lhuqita Fazry
Docker & DevOps Docker PostgreSQL Database Migration Backup
Automatisasi Database Migration dengan Docker: Backup, Restore, dan Versioning

Konsep Database Migration dalam Lingkungan Container

Database migration adalah proses mengelola perubahan skema database secara terstruktur — bukan sekadar menyalin data dari satu tempat ke tempat lain. Migration mencakup penambahan kolom baru, perubahan tipe data, pembuatan tabel, hingga modifikasi constraint yang harus diterapkan secara terkontrol di setiap environment.

Dalam lingkungan container, strategi migration memerlukan perhatian lebih. Container bersifat ephemeral: ketika container dihentikan atau dihapus, seluruh data di dalamnya ikut hilang kecuali kita mengonfigurasi volume persistent. Tanpa strategi yang tepat, perubahan skema bisa hilang begitu container restart.

Kita akan membahas tiga pilar utama dalam mengelola database migration di Docker. Pertama, backup — membuat salinan aman sebelum setiap perubahan skema sebagai safety net. Kedua, restore — kemampuan mengembalikan database ke state sebelumnya jika migration gagal. Ketiga, versioning — melacak setiap perubahan skema agar kita tahu persis versi mana yang sudah diterapkan di setiap environment.

Menyiapkan Lingkungan Docker untuk Database PostgreSQL

Langkah pertama adalah menyiapkan struktur proyek dan konfigurasi container. Kita akan menggunakan docker-compose.yml untuk mendefinisikan service PostgreSQL dengan volume persistent.

Buat struktur folder berikut:

text
project/
  docker-compose.yml
  migrations/
  backups/

Folder migrations/ akan menyimpan file SQL untuk perubahan skema, sementara backups/ digunakan untuk menyimpan hasil dump database sebelum migration dijalankan.

Berikut konfigurasi docker-compose.yml:

yamlyaml
version: "3.8"

services:
  db:
    image: postgres:16-alpine
    container_name: postgres_db
    environment:
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: secretpass
      POSTGRES_DB: appdb
    ports:
      - "5432:5432"
    volumes:
      - pgdata:/var/lib/postgresql/data
      - ./backups:/backups
      - ./migrations:/migrations

volumes:
  pgdata:

Konfigurasi ini menggunakan named volume pgdata untuk persistensi data database. Folder backups/ dan migrations/ di-mount sebagai bind mount agar bisa diakses dari host maupun container. Base image postgres:16-alpine dipilih karena ukurannya ringan.

Arsitektur Docker: interaksi antara Docker Client, Docker Host, dan Registry

Gambar: Diagram arsitektur Docker yang menunjukkan hubungan antara Docker Client, Docker Host (termasuk containers dan volumes), serta Docker Registry — Sumber: [Docker Docs](https://docs.docker.com/get-started/overview/)

Setelah file siap, jalankan container:

bashbash
docker-compose up -d

Verifikasi koneksi dengan:

bashbash
docker exec -it postgres_db psql -U appuser -d appdb -c "SELECT version();"

Output akan menampilkan versi PostgreSQL yang berjalan, menandakan database sudah siap digunakan.

Membangun Script Backup dan Restore Database

Data Science with Python
Data Science • Beginner

Data Science with Python

Master the art of data analysis, visualization, and predictive modeling.

Daftar

Backup adalah langkah wajib sebelum menjalankan migration. Jika perubahan skema menyebabkan error, kita bisa mengembalikan database ke state sebelum migration menggunakan file backup tersebut.

Script backup menggunakan pg_dump untuk menghasilkan file SQL dengan timestamp sebagai nama file. Flag --clean dan --if-exists memastikan script hasil dump bisa dijalankan ulang tanpa error — statement DROP akan menggunakan IF EXISTS. Flag --no-owner menghilangkan informasi kepemilikan agar file dump bisa di-restore di environment berbeda tanpa konflik permission.

Berikut script backup (backup.sh):

bashbash
#!/bin/bash

TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
BACKUP_DIR="/backups"
DB_NAME="appdb"
DB_USER="appuser"
BACKUP_FILE="${BACKUP_DIR}/backup_${DB_NAME}_${TIMESTAMP}.sql"

echo "[$(date)] Starting backup for database: ${DB_NAME}"

pg_dump -U "${DB_USER}" -d "${DB_NAME}" \
  --clean \
  --if-exists \
  --no-owner \
  --file="${BACKUP_FILE}"

if [ $? -eq 0 ]; then
  echo "[$(date)] Backup successful: ${BACKUP_FILE}"
  echo "File size: $(du -h ${BACKUP_FILE} | cut -f1)"
else
  echo "[$(date)] Backup FAILED for database: ${DB_NAME}"
  exit 1
fi

Script restore (restore.sh) membaca file dump dan mengembalikan state database:

bashbash
#!/bin/bash

BACKUP_DIR="/backups"
DB_NAME="appdb"
DB_USER="appuser"

LATEST_BACKUP=$(ls -t ${BACKUP_DIR}/backup_${DB_NAME}_*.sql 2>/dev/null | head -1)

if [ -z "${LATEST_BACKUP}" ]; then
  echo "No backup file found in ${BACKUP_DIR}"
  exit 1
fi

echo "[$(date)] Restoring from: ${LATEST_BACKUP}"

psql -U "${DB_USER}" -d "${DB_NAME}" -f "${LATEST_BACKUP}"

if [ $? -eq 0 ]; then
  echo "[$(date)] Restore completed successfully"
else
  echo "[$(date)] Restore FAILED"
  exit 1
fi

Workflow yang direkomendasikan: jalankan backup.sh sebelum mengeksekusi migration baru. Jika migration berhasil, backup lama bisa dihapus. Jika migration gagal, jalankan restore.sh untuk kembali ke state sebelum perubahan.

Korelasi antara pg_dump, psql, dan pg_restore dalam proses backup dan restore database PostgreSQL

Gambar: Diagram yang menunjukkan korelasi antara pg_dump untuk membuat backup, psql untuk restore dari plain-text SQL dump, dan pg_restore untuk restore dari archive format — Sumber: [Wikimedia Commons](https://commons.wikimedia.org/wiki/File:PostgreSQL_dump_restore.svg)

Untuk menjalankan script dari host:

bashbash
docker exec postgres_db bash /backups/backup.sh
docker exec postgres_db bash /backups/restore.sh

Strategi Versioning untuk Database Migration

Versioning memungkinkan kita melacak perubahan skema secara sistematis. Pendekatan paling sederhana adalah memberi nama file migration dengan prefix nomor versi, misalnya:

text
migrations/
  V001__create_users_table.sql
  V002__add_email_column.sql
  V003__create_orders_table.sql

Setiap file berisi statement SQL yang merepresentasikan satu unit perubahan. File dijalankan secara berurutan berdasarkan nomor versi.

Agar proses ini bisa dilacak, kita memerlukan tabel schema_migrations di database:

sqlsql
CREATE TABLE IF NOT EXISTS schema_migrations (
    version     VARCHAR(10) PRIMARY KEY,
    filename    VARCHAR(255) NOT NULL,
    applied_at  TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Tabel ini mencatat migration mana yang sudah dijalankan. Sebelum mengeksekusi file migration, script akan memeriksa apakah versi tersebut sudah ada di tabel — jika sudah, file akan dilewati. Inilah yang membuat proses migration menjadi idempotent: bisa dijalankan berkali-kali tanpa menghasilkan error duplikasi.

Berikut script Python sederhana yang mengorkestrasi proses ini:

pythonpython
!pip install psycopg2-binary

import os
import psycopg2

MIGRATIONS_DIR = "/migrations"
DB_CONFIG = {
    "host": "localhost",
    "port": 5432,
    "dbname": "appdb",
    "user": "appuser",
    "password": "secretpass",
}

conn = psycopg2.connect(**DB_CONFIG)
cur = conn.cursor()

cur.execute("""
    CREATE TABLE IF NOT EXISTS schema_migrations (
        version VARCHAR(10) PRIMARY KEY,
        filename VARCHAR(255) NOT NULL,
        applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    );
""")
conn.commit()

cur.execute("SELECT version FROM schema_migrations")
applied = {row[0] for row in cur.fetchall()}

migration_files = sorted(
    [f for f in os.listdir(MIGRATIONS_DIR) if f.endswith(".sql")]
)

for filename in migration_files:
    version = filename.split("__")[0]
    if version in applied:
        print(f"[SKIP] {filename} — already applied")
        continue

    with open(os.path.join(MIGRATIONS_DIR, filename)) as f:
        sql = f.read()

    cur.execute(sql)
    cur.execute(
        "INSERT INTO schema_migrations (version, filename) VALUES (%s, %s)",
        (version, filename),
    )
    conn.commit()
    print(f"[DONE] {filename} — applied successfully")

cur.close()
conn.close()

Script ini membaca folder migrations/, membandingkan dengan isi tabel schema_migrations, dan hanya mengeksekusi file yang belum diterapkan. Pendekatan ini cocok untuk proyek skala kecil hingga menengah. Untuk proyek yang lebih kompleks, tools seperti Flyway atau Alembic menyediakan fitur tambahan seperti rollback otomatis dan checksum validation.

Mengotomatisasi Pipeline dengan Docker Entrypoint Script

Kita bisa mengintegrasikan seluruh workflow — backup, migration, dan verifikasi — ke dalam satu entrypoint script yang berjalan saat container startup. Dengan cara ini, proses migration menjadi bagian dari siklus hidup container.

Berikut entrypoint.sh:

bashbash
#!/bin/bash
set -e

echo "=== Starting Database Migration Pipeline ==="

if [ "${AUTO_MIGRATE}" = "true" ]; then
  echo "[1/3] Running pre-migration backup..."
  bash /backups/backup.sh

  echo "[2/3] Running database migrations..."
  python3 /migrations/run_migrations.py

  echo "[3/3] Verifying latest migration..."
  psql -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" -c \
    "SELECT version, filename, applied_at FROM schema_migrations ORDER BY version DESC LIMIT 1;"

  echo "=== Migration Pipeline Complete ==="
else
  echo "AUTO_MIGRATE is not set to 'true'. Skipping migration."
fi

exec "$@"

Environment variable AUTO_MIGRATE=true berfungsi sebagai safety switch. Di environment production, kita tidak ingin migration berjalan otomatis setiap container restart. Flag ini memastikan migration hanya berjalan ketika secara eksplisit diaktifkan.

Integrasikan entrypoint ke dalam Dockerfile:

dockerdockerfile
FROM postgres:16-alpine

COPY entrypoint.sh /usr/local/bin/entrypoint.sh
COPY backups/backup.sh /backups/backup.sh
COPY backups/restore.sh /backups/restore.sh
COPY migrations/ /migrations/

RUN chmod +x /usr/local/bin/entrypoint.sh \
    && chmod +x /backups/backup.sh \
    && chmod +x /backups/restore.sh \
    && apk add --no-cache python3 py3-pip \
    && pip3 install psycopg2-binary --break-system-packages

ENTRYPOINT ["entrypoint.sh"]
CMD ["postgres"]

Saat container dijalankan dengan AUTO_MIGRATE=true, pipeline akan mengeksekusi backup terlebih dahulu, lalu menjalankan seluruh file migration yang belum diterapkan, dan terakhir memverifikasi hasilnya dengan query ke tabel schema_migrations. Query verifikasi menampilkan migration terakhir yang sukses — memastikan tidak ada file yang terlewat atau gagal.

Kuasai skill DevOps dan containerization secara menyeluruh melalui program Docker & DevOps di Rumah Coding. Program ini mencakup CI/CD pipeline, container orchestration dengan Docker Swarm dan Kubernetes, serta infrastructure as code menggunakan Terraform — semua dengan studi kasus production-ready.

Kursus Terkait

E-commerce Sales Dashboard
Kursus Premium Data Science

Data Science with Python

Master the art of data analysis, visualization, and predictive modeling.

Proyek Akhir

E-commerce Sales Dashboard

  • Data Cleaning Pipeline
  • Interactive Charts
  • Sales Forecasting Model
7 Weeks Beginner
Lihat Detail Kursus

Artikel Terkait