Представь, что твоя команда разрабатывает приложение, и с каждой новой фичей нужно менять структуру базы данных. Кто-то забывает применить миграцию, кто-то запускает её в неправильном порядке, и в итоге — продакшен падает. Знакомая ситуация? Именно для решения таких проблем существует Liquibase — инструмент для контроля версий и управления миграциями БД. А если добавить сюда PostgreSQL в Docker-контейнере, получится идеально воспроизводимая и изолированная среда для разработки. Давай разберем, как это всё настроить с нуля.
Что такое Liquibase и зачем он нужен с PostgreSQL в Docker?
Liquibase — это система контроля версий для твоей базы данных. Вместо ручного выполнения SQL-скриптов ты описываешь изменения (создание таблиц, добавление колонок, индексы) в декларативных файлах (XML, YAML, JSON или SQL). Liquibase отслеживает, какие изменения уже применены к БД, в специальной таблице databasechangelog, и применяет только новые. Это даёт:
- Воспроизводимость: Любой разработчик может развернуть идентичную схему БД.
- Безопасность: Исключаются человеческие ошибки при ручном запуске скриптов.
- Историю изменений: Полный аудит того, что и когда было изменено в схеме данных.
- Интеграцию с CI/CD: Миграции можно автоматически применять при деплое.
Использование PostgreSQL в Docker делает эту связку ещё мощнее: ты получаешь легковесный, изолированный и мгновенно запускаемый экземпляр базы данных, идентичный продакшену. Настройка связки liquibase postgresql docker — ключевой навык для современного DevOps-инженера.
Предварительная настройка окружения
Перед началом убедись, что у тебя установлены:
- Docker и Docker Compose (желательно последних версий).
- Java Runtime Environment (JRE) версии 11 или выше, так как Liquibase — Java-приложение.
- Текстовый редактор или IDE (VS Code, IntelliJ IDEA).
Шаг 1: Запуск PostgreSQL в Docker
Создай директорию для проекта, например, liquibase-demo. В ней создай файл docker-compose.yml для запуска контейнера с PostgreSQL.
version: '3.8'
services:
postgres:
image: postgres:15-alpine
container_name: liquibase_postgres
environment:
POSTGRES_DB: myappdb
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mysecretpassword
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U myuser -d myappdb"]
interval: 10s
timeout: 5s
retries: 5
volumes:
postgres_data:
postgres:15-alpine — он легковесный. Параметры POSTGRES_DB, POSTGRES_USER, POSTGRES_PASSWORD задают начальную базу, пользователя и пароль. Healthcheck гарантирует, что контейнер будет считаться готовым только когда БД действительно примет подключения.
Запусти контейнер командой:
docker-compose up -d
Проверь, что контейнер работает: docker ps. Ты также можешь подключиться к БД через psql: docker exec -it liquibase_postgres psql -U myuser -d myappdb.
Шаг 2: Установка и базовая настройка Liquibase
Есть несколько способов использовать Liquibase:
- CLI (JAR-файл): Скачай jar-архив с официального сайта.
- Docker-образ Liquibase: Удобно для CI/CD. Мы будем использовать этот способ, чтобы не устанавливать Java локально.
- Плагин для Maven/Gradle: Для Java-проектов.
Для нашего руководства создадим простую структуру проекта и используем Docker-образ Liquibase. Создай следующие директории и файлы:
mkdir -p liquibase/changelog
cd liquibase
Теперь создай главный конфигурационный файл Liquibase — liquibase.properties. В нём укажи параметры подключения к нашему PostgreSQL в Docker.
# Настройки подключения к PostgreSQL
url: jdbc:postgresql://localhost:5432/myappdb
driver: org.postgresql.Driver
username: myuser
password: mysecretpassword
# Логирование и управление
changeLogFile: changelog/db.changelog-master.yaml
liquibase.hub.mode: off
logLevel: info
Шаг 3: Создание мастер-файла изменений (Changelog)
Liquibase работает с мастер-файлом (чаще всего в формате YAML или XML), который ссылается на все остальные файлы изменений. Создай файл changelog/db.changelog-master.yaml.
databaseChangeLog:
- includeAll:
path: changelog/versions/
relativeToChangelogFile: true
Эта конфигурация говорит Liquibase включить все файлы изменений из директории changelog/versions/. Теперь создай саму директорию: mkdir -p changelog/versions.
Шаг 4: Написание первой миграции
Давай создадим нашу первую таблицу — например, для пользователей. Создай файл changelog/versions/001-create-user-table.yaml.
databaseChangeLog:
- changeSet:
id: 001-create-user-table
author: your_name
changes:
- createTable:
tableName: app_user
columns:
- column:
name: id
type: BIGINT
constraints:
primaryKey: true
nullable: false
autoIncrement: true
- column:
name: username
type: VARCHAR(50)
constraints:
unique: true
nullable: false
- column:
name: email
type: VARCHAR(100)
constraints:
nullable: false
- column:
name: created_at
type: TIMESTAMP
defaultValueComputed: CURRENT_TIMESTAMP
Каждый changeSet — это атомарное изменение. У него должен быть уникальный id и author. Liquibase по этим полям отслеживает, было ли изменение уже применено.
Шаг 5: Запуск миграций с помощью Docker-образа Liquibase
Теперь самое интересное — применение миграций. Мы не будем устанавливать Liquibase локально, а используем официальный Docker-образ. Из корня проекта (где лежит docker-compose.yml) выполни команду:
docker run --rm \
--network="host" \
-v "$(pwd)/liquibase:/liquibase/changelog" \
-v "$(pwd)/liquibase/liquibase.properties:/liquibase/liquibase.properties" \
liquibase/liquibase:latest \
--defaultsFile=/liquibase/liquibase.properties \
update
Разберем команду:
--network="host"— позволяет контейнеру Liquibase видеть PostgreSQL, запущенный на localhost.-v "$(pwd)/liquibase:/liquibase/changelog"— монтирует директорию с changelog-файлами внутрь контейнера.-v ".../liquibase.properties"— монтирует файл с настройками.liquibase/liquibase:latest— используем последний официальный образ.update— команда Liquibase для применения всех непримененных изменений.
docker exec -it liquibase_postgres psql -U myuser -d myappdb -c "\\dt app_user". Также проверь таблицу databasechangelog: docker exec -it liquibase_postgres psql -U myuser -d myappdb -c "SELECT * FROM databasechangelog;".
Продвинутая настройка и лучшие практики
Использование переменных окружения для безопасности
Давай улучшим наш liquibase.properties, убрав из него чувствительные данные. Перепишем его так:
url: jdbc:postgresql://${DB_HOST}:${DB_PORT}/${DB_NAME}
driver: org.postgresql.Driver
username: ${DB_USER}
password: ${DB_PASSWORD}
changeLogFile: changelog/db.changelog-master.yaml
Теперь при запуске Docker-контейнера Liquibase передавай переменные окружения:
docker run --rm \
--network="host" \
-e DB_HOST=localhost \
-e DB_PORT=5432 \
-e DB_NAME=myappdb \
-e DB_USER=myuser \
-e DB_PASSWORD=mysecretpassword \
-v "$(pwd)/liquibase:/liquibase/changelog" \
-v "$(pwd)/liquibase/liquibase.properties:/liquibase/liquibase.properties" \
liquibase/liquibase:latest \
--defaultsFile=/liquibase/liquibase.properties \
update
Интеграция в Docker Compose
Чтобы автоматически применять миграции при запуске инфраструктуры, можно добавить сервис Liquibase в тот же docker-compose.yml. Это отличная практика для локальной разработки.
version: '3.8'
services:
postgres:
image: postgres:15-alpine
container_name: liquibase_postgres
environment:
POSTGRES_DB: myappdb
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mysecretpassword
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U myuser -d myappdb"]
interval: 10s
timeout: 5s
retries: 5
liquibase-migrate:
image: liquibase/liquibase:latest
container_name: liquibase_runner
depends_on:
postgres:
condition: service_healthy # Ждем, пока БД станет здоровой
volumes:
- ./liquibase:/liquibase/changelog
- ./liquibase/liquibase.properties:/liquibase/liquibase.properties
environment:
DB_HOST: postgres # Используем имя сервиса как хост
DB_PORT: 5432
DB_NAME: myappdb
DB_USER: myuser
DB_PASSWORD: mysecretpassword
command: --defaultsFile=/liquibase/liquibase.properties update
volumes:
postgres_data:
Теперь после команды docker-compose up -d сначала запустится и "прогреется" PostgreSQL, а затем автоматически выполнятся все миграции Liquibase. Для повторного запуска миграций (если changelog не менялся) используй: docker-compose run --rm liquibase-migrate.
Сравнение способов запуска Liquibase
| Способ | Плюсы | Минусы | Использование |
|---|---|---|---|
| Docker-образ | Не требует установки Java, идеален для CI/CD, изоляция. | Нужно монтировать volumes, может быть медленнее при частых запусках. | Продакшен, CI/CD пайплайны, локальная разработка (как в нашем гайде). |
| Локальный CLI (JAR) | Быстрый запуск, полный контроль. | Требует установки Java, меньше переносимости. | Локальная разработка, если Docker не используется. |
| Плагин Maven/Gradle | Интеграция в процесс сборки Java-проекта. | Привязан к экосистеме Java. | Java/Spring Boot проекты. |
Часто задаваемые вопросы (FAQ)
Как откатить (rollback) изменения, если что-то пошло не так?
Liquibase поддерживает откаты. Для этого в changeSet можно определить тег rollback. Например, для нашей таблицы можно добавить rollback: "DROP TABLE app_user;". Чтобы откатить изменения до определённого тега, используй команду: docker run ... liquibase/liquibase rollback --tag="v1.0". Всегда тестируй откаты на стейджинге!
Можно ли использовать "чистый" SQL вместо YAML/XML в Liquibase?
Да, можно. Liquibase отлично работает с SQL-файлами. Просто укажи в мастер-файле include на конкретный SQL-файл. Это полезно для сложных миграций, которые проще написать на SQL. Однако, используя декларативные форматы (YAML/XML), ты получаешь лучшую переносимость между разными СУБД и встроенную поддержку откатов.
Как организовать работу с Liquibase в команде?
- Храни все changelog-файлы в системе контроля версий (Git).
- Используй понятную нумерацию или дату в именах файлов (например,
20240515-01-add-user-table.yaml). - Никогда не изменяй changeSet после того, как он был применён к какой-либо БД (особенно продакшену). Если нужно исправить ошибку, создай новый changeSet.
- В CI/CD пайплайне добавляй шаг проверки (validate) и обновления (update) перед деплоем приложения.
Почему Liquibase не подключается к PostgreSQL в Docker с ошибкой "Connection refused"?
Самая частая причина — неправильные настройки сети. Убедись, что:
- Контейнер с PostgreSQL запущен (
docker ps). - Ты используешь правильный хост. При запуске из другого контейнера (как в docker-compose) хост — это имя сервиса (
postgres). При запуске из хостовой системы с--network="host"хост —localhost. - Порт 5432 проброшен на хост (
ports: - "5432:5432").
Заключение
Настройка связки Liquibase, PostgreSQL и Docker — это не просто техническая задача, а инвестиция в стабильность и предсказуемость твоего проекта. Ты создал изолированную среду для БД, настроил систему контроля версий для схемы данных и автоматизировал применение миграций. Теперь ты можешь уверенно вносить изменения в базу, зная, что каждый член команды и каждый стенд будут иметь идентичную структуру. Дальнейшие шаги — это интеграция в CI/CD, работа с несколькими окружениями (dev, staging, prod) и, возможно, освоение таких команд Liquibase, как diff (для сравнения схем) и snapshot (для создания снимка состояния БД). Удачи в освоении инструментов!