Миграции в Go-проекте: PostgreSQL в Docker и goose на практике

от автора

Когда я начал поднимать 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/datavolumes:  postgres_data:

Сам запуск контейнера здесь подробно разбирать не буду: для этого достаточно выполнить docker compose up -d. Основной фокус статьи — запуск миграций. Запускать свои миграции я буду через Makefile вот так он выглядит внутри:

DB_URL=postgres://postgres:postgres@localhost:5432/app_db?sslmode=disableMIGRATIONS_DIR=migrations.PHONY: migrate-up migrate-down migrate-status migrate-createmigrate-up:goose -dir $(MIGRATIONS_DIR) postgres "$(DB_URL)" upmigrate-down:goose -dir $(MIGRATIONS_DIR) postgres "$(DB_URL)" downmigrate-status:goose -dir $(MIGRATIONS_DIR) postgres "$(DB_URL)" statusmigrate-create:goose -dir $(MIGRATIONS_DIR) create $(name) sql

В моём случае goose запускается с хост-машины, поэтому в DB_URL я использую localhost. Если запускать миграции из другого контейнера внутри docker-compose, вместо localhost нужно будет указать имя сервиса — postgres.

Далее после запуска нашего контейнера базы данных мы уже применить наши миграции. и мы просто должны написать:

make migrate-up

После запуска можете проверить статус:

make migrate-status

Теперь миграции запускаются отдельным шагом после старта PostgreSQL. Поэтому мы не попадаем в ситуацию, когда goose пытается подключиться к базе раньше, чем она готова принимать соединения..

ссылка на оригинал статьи https://habr.com/ru/articles/1040802/