Когда я начал поднимать PostgreSQL через Docker для своих проектов, всё выглядело просто: описал сервис в docker-compose.yml, запустил контейнер - база доступна.
Проблемы начались, когда я начал запускать миграции вместе с контейнерами. Иногда миграции стартовали раньше чем PostgreSQL успевал принять подключения, и приложение падало с ошибкой подключение к базе данных.
С опытом я пришел к более удобному варианту. Сначала запускал сам контейнер с базой и уже далее делал миграции. Так проще контролировать процесс, легче отлаживать ошибки и понятнее, что именно происходит с базой. Для миграций в своих Go-проектах я использовал goose.
goose - это инструмент для работы с миграциями базы данных. Его можно использовать как CLI-утилиту из терминала или подключить как Go-пакет и запускать миграции из кода. В этой статье я покажу вариант с запуском миграций через CLI-утилиту.
Для начала установим goose как CLI-утилиту:
go install github.com/pressly/goose/v3/cmd/goose@latest
После установки можно проверить, что команда доступна:
goose --version
Далее рассмотрим такую структуру проекта и покажу где я буду хранить миграции и как создать файлы для миграций:
my-app/
├── cmd/
│ └── app/
│ └── main.go
├── migrations/
├── docker-compose.yml
├── Makefile
├── go.mod
└── go.sum
В папке migrations будут лежать SQL-файлы миграций. Каждый такой файл описывает изменение схемы базы: создание таблицы, добавление колонки, создание индекса и так далее.
goose -dir migrations create create_users sql
Флаг -dir указывает папку, где нужно создать файл миграции. create_users — это название миграции, а sql означает, что миграция будет написана обычным SQL.
В docker-compose.yml опишем вот такой контейнер для запуска PostgreSQL:
services:
postgres:
image: postgres:16-alpine
container_name: go_postgres
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: app_db
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
Сам запуск контейнера здесь подробно разбирать не буду: для этого достаточно выполнить docker compose up -d. Основной фокус статьи — запуск миграций. Запускать свои миграции я буду через Makefile вот так он выглядит внутри:
DB_URL=postgres://postgres:postgres@localhost:5432/app_db?sslmode=disable
MIGRATIONS_DIR=migrations
.PHONY: migrate-up migrate-down migrate-status migrate-create
migrate-up:
goose -dir $(MIGRATIONS_DIR) postgres "$(DB_URL)" up
migrate-down:
goose -dir $(MIGRATIONS_DIR) postgres "$(DB_URL)" down
migrate-status:
goose -dir $(MIGRATIONS_DIR) postgres "$(DB_URL)" status
migrate-create:
goose -dir $(MIGRATIONS_DIR) create $(name) sql
В моём случае goose запускается с хост-машины, поэтому в DB_URL я использую localhost. Если запускать миграции из другого контейнера внутри docker-compose, вместо localhost нужно будет указать имя сервиса — postgres.
Далее после запуска нашего контейнера базы данных мы уже применить наши миграции. и мы просто должны написать:
make migrate-up
После запуска можете проверить статус:
make migrate-status
Теперь миграции запускаются отдельным шагом после старта PostgreSQL. Поэтому мы не попадаем в ситуацию, когда goose пытается подключиться к базе раньше, чем она готова принимать соединения..
Автор: albatomm
