この記事はGMOペパボエンジニア Advent Calendar 2023 の10日目になります🎄
最近PaaSであるHerokuにあるサービスをAWS (Amazon EKS on Fargate)で動かそうとしています。まだ検証段階ではあるものの、動作するところまではこぎつけました。HerokuとAWS を以下の組み合わせで移設しようとしています。
どれもいざやってみるとさほど困難さはなかったのですが(最初にEKSを構築してIngress を動かすほうがよほど手間がかかった)、Herokuで採用しているデータベースであるPostgreSQL に関しては知識ゼロから始めた もので学びが多かったのでやったことを書いていきます。雰囲気だけでも感じていただけたら嬉しいです!
なお、移行して動いた、という段階なのでパフォーマンスがどうなったといった視点は今回はないです…ご了承ください。
Auroraの構築については省略します😃他にも解説記事はたくさんあるかと思いますので
下準備
※私は作業PCがMac なのでMac の場合です
Herokuでデータベースのバックアップを取得するためにはHerokuのコマンドを実行することになるのですが、内部的にPostgreSQL のCLI コマンド─psql ─をKickするため、最初にpsql コマンドを利用できるようにする必要があります。psql コマンドを使うためには以下からHerokuに構築しているPostgreSQL のバージョンに合ったものをインストールします。
postgresapp.com
インストールが終わったら、psql コマンドを使うためにパスを通してあげます。自分は雑にzshrcに以下を入れました。
% tail -3 ~/.zshrc
# psql PATH
export PATH=${PATH}:/Applications/Postgres.app/Contents/Versions/latest/bin
psql コマンドが打てるようになったら準備完了です😎
% which psql
/Applications/Postgres.app/Contents/Versions/latest/bin/psql
ではHerokuのPostgreSQL をちょこっと覗いてみましょう。ログインはheroku pg:psql です。オプションを指定しない場合はHerokuでサービスが参照しているデータベースの所有者ユーザーになります。
% heroku pg:psql
--> Connecting to postgresql-example-98765
psql (13.13, server 13.12 (Ubuntu 13.12-1.pgdg20.04+1))
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)
Type "help" for help.
heroku-database::DATABASE=>
PostgreSQL ではメタコマンドと言って短いコマンドでデータベースの内容をわかりやすく出力してくれる仕組みがあります。データベース一覧とその所有者を表示する\lを打ってみます。
heroku-database::DATABASE=> \l
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
----------------+----------------+----------+-------------+-------------+--------------------------------------------------
ma72h7wq4Z4uhlk | bkreikhp6iwaly | UTF8 | en_US.UTF-8 | en_US.UTF-8 | bkreikhp6iwaly=CTc/bkreikhp6iwaly +
| | | | | internal_utility_u1jk56dcsvf7q8=c/bkreikhp6iwaly
postgres | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | postgres=CTc/postgres
template0 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/postgres +
| | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | postgres=CTc/postgres
(4 rows)
※データベース名、ユーザー名は架空のものです
いちばん上に表示されているma72h7wq4Z4uhlkが実際に使用しているデータベース、つまり今回データ移行するものです。HerokuのPostgreSQL のデータベース名はランダム文字列なんですね。右隣に表示されているOwnerの名前もランダム文字列です。
このデータベース名は後ほど使うので控えておきます。
バックアップ(スナップショット)をとる
それではリストア元のデータベースのバックアップを取得します。一般的な話になるのですが、データベースのバックアップをバックアップは2種類に大別できまして、コールドバックアップとホットバックアップがあります。コールドバックアップはデータベースのソフトウェア(今回の場合はPostgreSQL )を停止させてデータのバックアップを取得するものですが、PaaSであるHerokuはコールドバックアップは想定していないようです。
そこでホットバックアップを取得するのですが、これはとても簡単で以下のコマンドを実行するだけです。
% heroku pg:backups:capture
これは内部的にはPostgreSQL のpg_dumpコマンドを実行し、先ほど確認したma72h7wq4Z4uhlkデータベースの論理バックアップを取得しています。バックアップファイルはHeroku内に保管されます。
コマンド実行後Webコンソールでみても取得されていることがわかります。
Heroku backup
※今回の場合は2MBと極小のデータですが、20GB以上のデータだとタイムアウト する可能性があるからデータベースのブランチ(耳慣れない表現ですがデータベースの書き込みがないレプリカ)を作成することを推奨しています。
バックアップファイルを取得する
Herokuに格納されたバックアップデータをローカルPCにダウンロードします。出力ファイル名を指定しない場合はlatest.dumpという名前のファイルがダウンロードされます。
% heroku pg:backups:download
Getting backup from ⬢... done, #1
Downloading latest.dump... ████████████████████████▏ 100% 00:00 1.94MB
% ls -l latest.dump
-rw-r--r-- 1 shibatch staff 2030927 11 29 19:04 latest.dump
shibatch@PM-GMXJX5060K sandbox-eks % ls -lh latest.dump
リストア用のPodを立てる
今回、EKSとAuroraはサブネットを分けている構成にしています。
構成図
今回はEKSにHerokuのWeb Dynosに相当するPodを構築するため、EKSのあるサブネットからAuroraのあるサブネットに疎通する必要があります。
そこで疎通のテストも兼ねて、EKS上にAurora(PostgreSQL )をCLI 操作するためのPodを立てて、そのPodにバックアップデータを転送してリストアすることにしました。
EKSに以下の通り、PostgreSQL が使えるPodを立てます。このコンテナイメージは内部でPostgreSQL を起動するためパスワードの環境変数 指定が必要ですが、今回psql コマンドを打ちたいだけなのでパスワードはなんでもよいです。
% kubectl run postgres-cli --image=postgres:13-alpine --env="POSTGRES_PASSWORD=test"
% kubectl get pod
NAME READY STATUS RESTARTS AGE
postgres-cli 1/1 Running 0 2m49s
Podにリストアしたデータを転送する
Kubernetes にはPodに対してファイルを転送するコマンドがあるので今回はそのコマンドで転送します(こんなコマンドあるの知らなかった)。
転送完了したらPodにログインしてファイルの存在確認します。
% kubectl cp ./latest.dump postgres-cli:/tmp/sandbox-pg.dump
% k exec -it postgres-cli -- /bin/bash
postgres-cli:/# ls -l /tmp
total 1984
-rw-r--r-- 1 502 dialout 2030927 Nov 29 10:12 sandbox-pg.dump
カンのいい読者なら察したでしょうが、このコピーは検証用データベースの2MBのバックアップデータだからシュッとできたものの、本番用のGB単位のデータだったら転送に時間がかかる&Podのtmp領域の空き容量の関係でこんなうまくいかない可能性があります。この問題はこれから考えます😃
Auroraにデータベースのあれこれを作成する
あとはリストアしていくだけ…と言いたいところですが、Auroraにあらかじめリストアするデータベースを作成しておかないといけないようでした。
まずはログインしたPodを踏み台にしてAuroraのプライマリインスタンス にログインします。初回ログインではスーパーユーザーになるでしょう。下記ではデフォルトのpostgresユーザーとしています。
postgres-cli:/# psql --host=aurora-postgres.cluster-bkreikhp6iwaly.ap-northeast-1.rds.amazonaws.com --port=5432 --username=postgres --password -d postgres
Password:
psql (13.13, server 13.12)
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)
Type "help" for help.
とにもかくにもHeroku PostgreSQL にあったデータベース名でデータベースを作ってみます。
postgres=> CREATE DATABASE ma72h7wq4Z4uhlk;
CREATE DATABASE
postgres=> \l
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
----------------+------------------+----------+-------------+-------------+---------------------------------------
ma72h7wq4Z4uhlk | bkreikhp6iwaly | UTF8 | en_US.UTF-8 | en_US.UTF-8 |
<snip>
スーパーユーザーで運用するのはセキュリティ上望ましくないので、運用のためのロールとユーザーを作成します。
まずはroleから。今回はappというロール名にしました。
postgres=> CREATE ROLE app;
CREATE ROLE
作成したapproleにデータベースへのアクセス権を付与していきます。
postgres=> GRANT CONNECT ON DATABASE ma72h7wq4Z4uhlk TO app;
GRANT
postgres=> GRANT ALL PRIVILEGES ON DATABASE ma72h7wq4Z4uhlk TO app;
GRANT
ログインユーザーを作成します。今回はapp_userとしました。
postgres=> CREATE ROLE app_user LOGIN PASSWORD 'なにがし';
CREATE ROLE
app_userにapproleの権限を付与します。
postgres=> GRANT app TO app_user;
GRANT ROLE
一旦postgresのコンソールを抜けて、ma72h7wq4Z4uhlk databaseへログインしなおします。
postgres-cli:/# psql --host=aurora-postgres.cluster-bkreikhp6iwaly.ap-northeast-1.rds.amazonaws.com --port=5432 --username=postgres --password -d ma72h7wq4Z4uhlk
app roleに対して、publicスキーマ のテーブル、シーケンスに対する権限を付与します。
ma72h7wq4Z4uhlk=> GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO app;
GRANT
ma72h7wq4Z4uhlk=> GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO app;
GRANT
ma72h7wq4Z4uhlk=> ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON TABLES TO app;
GRANT
リストアを実施する
Podログインした状態からバックアップファイルを指定してリストアします!リストア先は先ほど作成したデータベース名です。
postgres-test:/# pg_restore --verbose --clean --no-acl --no-owner -h aurora-postgres.cluster-bkreikhp6iwaly.ap-northeast-1.rds.amazonaws.com -U postgres -d ma72h7wq4Z4uhlk /tmp/sandbox-pg.dump
<snip>
これでリストアは完了ですね!
この状態でPodのコンテナに設定するプライマリデータベース接続用の環境変数 (Herokuからそのまま使う場合はDATABASE_URL)をAuroraのものに変更するとちゃんと接続してPodがRunningになってくれました。
DATABASE_URL: postgres://app_user:password@aurora-postgres.cluster-bkreikhp6iwaly.ap-northeast-1.rds.amazonaws.com:5432/ma72h7wq4Z4uhlk primary
今後の課題
というわけで、これでHeroku PostgreSQL →Auroraにデータ移行できたよ〜というものを紹介してみました。ここにある手順ですがまだ荒削りなところがあります。具体的には先ほど言及したもっと大きなサイズのデータ移行の場合どうするかということと、あと移行後のデータの完全性をちゃんと知りたいなぁと感じており、今後の課題です。
意外にすんなりできた、というのが感想です。本気でデータ移行する場合は書き込みできない状態にして実施することになるなどもっと細かい点を詰めなければなりませんが、何かしらの参考になれば幸いです😃