shibatch's journey

日々考えていることをつらつら書くだけです

Herokuのデータベースを(Kubernetes Podを経由して)Amazon Auroraにデータ移行してみた

この記事はGMOペパボエンジニア Advent Calendar 2023の10日目になります🎄

最近PaaSであるHerokuにあるサービスをAWS(Amazon EKS on Fargate)で動かそうとしています。まだ検証段階ではあるものの、動作するところまではこぎつけました。HerokuとAWSを以下の組み合わせで移設しようとしています。

どれもいざやってみるとさほど困難さはなかったのですが(最初にEKSを構築してIngressを動かすほうがよほど手間がかかった)、Herokuで採用しているデータベースであるPostgreSQLに関しては知識ゼロから始めたもので学びが多かったのでやったことを書いていきます。雰囲気だけでも感じていただけたら嬉しいです!

なお、移行して動いた、という段階なのでパフォーマンスがどうなったといった視点は今回はないです…ご了承ください。

Auroraの構築については省略します😃他にも解説記事はたくさんあるかと思いますので

下準備

※私は作業PCがMacなのでMacの場合です

Herokuでデータベースのバックアップを取得するためにはHerokuのコマンドを実行することになるのですが、内部的にPostgreSQLCLIコマンド─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の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

これは内部的にはPostgreSQLpg_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_userapproleの権限を付与します。

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にデータ移行できたよ〜というものを紹介してみました。ここにある手順ですがまだ荒削りなところがあります。具体的には先ほど言及したもっと大きなサイズのデータ移行の場合どうするかということと、あと移行後のデータの完全性をちゃんと知りたいなぁと感じており、今後の課題です。

意外にすんなりできた、というのが感想です。本気でデータ移行する場合は書き込みできない状態にして実施することになるなどもっと細かい点を詰めなければなりませんが、何かしらの参考になれば幸いです😃