こんにちは。KOUKIです。
Go言語とPostgreSQLとDockerを使ったアプリケーション開発を記事にしています。
前回は、開発環境の構築について触れました。
今回は、マイグレーションについて触れます。
<目次>
そもそも何故マイグレーションが必要なのか
現在、データベースのスキーマーは、コンテナ起動時に自動的に作られるように設定しています。
しかし、アプリケーションの仕様の変更やデータベースの致命的な欠陥が見つかったりなどして、データベースのスキーマー変更が求められることがあります。
現状だと、コンテナを一旦削除して新たに作り直すなどしないとスキーマーをアップデートする術がなく、結構不便です。
これを解決する手段がマイグレーションです。
マイグレーションについて、ハンズオンで学びましょう。
golang-migrate/migrateライブラリの導入
Go製のツールで、golang-migrate/migrateがあります。これをインストールしましょう。
私の環境はMacなので、brewを使ってインストールします。
1 |
brew install golang-migrate |
スキーマーの作成
golang-migrateを導入できたら、migrateコマンドが使用できるようになります。
次のコマンドで、スキーマーを作成してみましょう。
1 2 3 4 5 6 7 |
# スキーマー作成 migrate create -ext sql -dir db/migration -seq init_schema db └── migration ├── 000001_init_schema.down.sql └── 000001_init_schema.up.sql |
2種類のファイルが作成されました。
upファイルの方は、「migrate up」で読み込まれるファイルです。downファイルの方は、「migrate down」で読み込まれます。
これらのファイルは、最初は空ファイルになっているので、sqlを書き込む必要があります。
upファイルの方に、前回作成した「Simple bank.sql」ファイルの中身をコピーして貼り付けてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
CREATE TABLE "accounts" ( "id" bigserial PRIMARY KEY, "owner" varchar NOT NULL, "balance" bigint NOT NULL, "currency" varchar NOT NULL, "created_at" timestamptz NOT NULL DEFAULT (now()) ); CREATE TABLE "entries" ( "id" bigserial PRIMARY KEY, "account_id" bigint NOT NULL, "amount" bigint NOT NULL, "created_at" timestamptz NOT NULL DEFAULT (now()) ); CREATE TABLE "transfers" ( "id" bigserial PRIMARY KEY, "from_account_id" bigint NOT NULL, "to_account_id" bigint NOT NULL, "amount" bigint NOT NULL, "created_at" timestamptz NOT NULL DEFAULT (now()) ); ALTER TABLE "entries" ADD FOREIGN KEY ("account_id") REFERENCES "accounts" ("id"); ALTER TABLE "transfers" ADD FOREIGN KEY ("from_account_id") REFERENCES "accounts" ("id"); ALTER TABLE "transfers" ADD FOREIGN KEY ("to_account_id") REFERENCES "accounts" ("id"); CREATE INDEX ON "accounts" ("owner"); CREATE INDEX ON "entries" ("account_id"); CREATE INDEX ON "transfers" ("from_account_id"); CREATE INDEX ON "transfers" ("to_account_id"); CREATE INDEX ON "transfers" ("from_account_id", "to_account_id"); COMMENT ON COLUMN "entries"."amount" IS 'can be negative or positive'; COMMENT ON COLUMN "transfers"."amount" IS 'must be positive'; |
dropファイルの方は、table削除コマンドを書いておきましょう。
1 2 3 |
DROP TABLE IF EXISTS entries; DROP TABLE IF EXISTS transfers; DROP TABLE IF EXISTS accounts; |
マイグレーションの実行
いよいよマイグレーションを実行します。
postgresコンテナの確認
最初に、postgresコンテナが起動しているか確認してください。
1 2 3 |
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES fba6eb6a159d golang-with-postgres_postgres "docker-entrypoint.s…" 19 hours ago Up 6 seconds 5432/tcp postgres |
もし起動していない場合は、「docker-compose up」コマンドで起動しておいてください。
復習ですが、docker-compose.ymlのenvironmentに以下の設定をしているので、コンテナ作成時にpostgresユーザー、simplebankデータベースが作成されています。
1 2 3 4 5 6 |
postgres: ... environment: - POSTGRES_USER=postgres - POSTGRES_PASSWORD=secret - POSTGRES_DB=simplebank |
一応、コンテナ内に入って、DBを確認してみましょう。
1 2 3 4 5 6 7 8 |
# コンテナ内に入る docker container exec -it postgres /bin/sh # simplebankを確認 psql simplebank -Upostgres # プロンプトがsimplebankに変わる simplebank=# |
データベースは、存在するようですね。確認ができたので、exit * 2でコンテナから抜けてください。
Makefileの作成
Makefileを作成して、コマンドを追加しておきましょう。
1 |
tourch Makefile |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# コンテナ起動 up: docker-compose up # DBを作成 createdb: docker exec -it postgres createdb --username=postgres --owner=postgres simplebank # DB削除 dropdb: docker exec -it postgres dropdb -Upostgres simplebank .PHONY: createdb dropdb up |
DB作成/削除、コンテナ起動コマンドを追加しました。
削除コマンドの実行
先に削除コマンドを実行してみましょう。
1 2 3 4 5 6 |
$ make dropdb docker exec -it postgres dropdb -Upostgres simplebank dropdb: error: database removal failed: ERROR: database "simplebank" is being accessed by other users DETAIL: There is 1 other session using the database. make: *** [Makefile:6: dropdb] Error 1 |
「DETAIL: There is 1 other session using the database.」と出力され、削除できませんでした。ゾンビプロセスが残っているため、削除できないようです。(超めんどくさい)
いくつか解決策があるようですが、この記事の解決策を参考にしてみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# コンテナに入る docker container exec -it postgres /bin/sh # postgresユーザーでログイン psql -Upostgres # REVOKEコマンドを実行 REVOKE CONNECT ON DATABASE simplebank FROM public; # ゾンビプロセス削除 SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = 'simplebank'; # コンテナを抜ける exit exit |
改めて、make dropdbコマンドを実行します。
1 2 |
$ make dropdb docker exec -it postgres dropdb -Upostgres simplebank |
作成コマンドの実行
次は、DBを作成しましょう。
1 |
make createdb |
UP マイグレーション
DBを再作成したので、upファイルの記述情報を元にマイグレーションしてみましょう。
1 2 3 4 5 6 7 8 9 |
# upマイグレーション実行 migrate -path db/migration -database "postgresql://postgres:secret@localhost:5432/simplebank?sslmode=disable" -verbose up 2020/12/23 10:12:55 Start buffering 1/u init_schema 2020/12/23 10:12:55 Read and execute 1/u init_schema 2020/12/23 10:12:55 Finished 1/u init_schema (read 5.479877ms, ran 33.855413ms) 2020/12/23 10:12:55 Finished after 43.659862ms 2020/12/23 10:12:55 Closing source and database |
「http://localhost:9232/」にアクセスするとテーブルが作られていることがわかります。

注意事項
注意事項が一つだけあります。ローカルでPostgresが5432ポートで動いている場合、localhost:5432へアクセスするとローカルのPostgresへ繋ぎに行ってしまいます。
かくゆう私も、ローカル上でpostgresqlを動作させていたので、マイグレーションを実行したときに以下のエラーに苦しめられました。
1 2 3 |
# 発生したエラー dropdb: error: could not connect to database template1: FATAL: role "postgres" does not exist |
認証情報を正しく設定しているにも関わらず、上記のエラーが出ていたのでしばらく悩んでいたのですが、ローカル上でpostgresが動いているのではないかと思い、以下のコマンドを実行しました。
1 2 3 4 |
$ sudo lsof -i:5432 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME postgres 58 postgres 4u IPv6 0x5b4f30c244eb7179 0t0 TCP *:postgresql (LISTEN) postgres 58 postgres 5u IPv4 0x5b4f30c244ebace9 0t0 TCP *:postgresql (LISTEN) |
案の定、ローカルでpostgresが動いていましたね…
これをKillするとマイグレーションが可能になります。
1 2 |
# プロセスをkillする sudo kill 58 |
DOWN マイグレーション
次は、downファイルを元にマイグレーションを実行してみましょう。
1 2 3 4 5 6 7 8 9 10 11 |
# downマイグレーションを実行 migrate -path db/migration -database "postgresql://postgres:secret@localhost:5432/simplebank?sslmode=disable" -verbose down 2020/12/23 10:16:46 Are you sure you want to apply all down migrations? [y/N] y 2020/12/23 10:16:49 Applying all down migrations 2020/12/23 10:16:49 Start buffering 1/d init_schema 2020/12/23 10:16:49 Read and execute 1/d init_schema 2020/12/23 10:16:49 Finished 1/d init_schema (read 10.813762ms, ran 11.72919ms) 2020/12/23 10:16:49 Finished after 2.646654009s 2020/12/23 10:16:49 Closing source and database |
OKですね。
最後に、Makefileを更新しておきましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# コンテナ起動 up: docker-compose # DBを作成 createdb: docker exec -it postgres createdb --username=postgres --owner=postgres simplebank # DB削除 dropdb: docker exec -it postgres dropdb -Upostgres simplebank # マイグレーション migrateup: migrate -path db/migration -database "postgresql://postgres:secret@localhost:5432/simplebank?sslmode=disable" -verbose up migratedown: migrate -path db/migration -database "postgresql://postgres:secret@localhost:5432/simplebank?sslmode=disable" -verbose down # スキーマーの作成 createsc: migrate create -ext sql -dir db/migration -seq ${name} .PHONY: up createdb dropdb migrateup migratedown createsc |
次回
次は、CRUDを作成しましょう!
コメントを残す
コメントを投稿するにはログインしてください。