この記事はGMOペパボエンジニア Advent Calendar 2023の10日目になります🎄
最近PaaSであるHerokuにあるサービスをAWS(Amazon EKS on Fargate)で動かそうとしています。まだ検証段階ではあるものの、動作するところまではこぎつけました。HerokuとAWSを以下の組み合わせで移設しようとしています。
- Heroku Dynos → Amazon EKS
- Heroku Postgres(Add-on) → Amazon Aurora PostgreSQL
- Redis Cloud(Add-on) → Amazon ElastiCache for Redis
どれもいざやってみるとさほど困難さはなかったのですが(最初にEKSを構築してIngressを動かすほうがよほど手間がかかった)、Herokuで採用しているデータベースであるPostgreSQLに関しては知識ゼロから始めたもので学びが多かったのでやったことを書いていきます。雰囲気だけでも感じていただけたら嬉しいです!
なお、移行して動いた、という段階なのでパフォーマンスがどうなったといった視点は今回はないです…ご了承ください。
Auroraの構築については省略します😃他にも解説記事はたくさんあるかと思いますので
下準備
Herokuでデータベースのバックアップを取得するためにはHerokuのコマンドを実行することになるのですが、内部的にPostgreSQLのCLIコマンド─psql─をKickするため、最初にpsqlコマンドを利用できるようにする必要があります。psqlコマンドを使うためには以下からHerokuに構築しているPostgreSQLのバージョンに合ったものをインストールします。
インストールが終わったら、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
これは内部的にはPostgreSQLのpg_dump
コマンドを実行し、先ほど確認したma72h7wq4Z4uhlk
データベースの論理バックアップを取得しています。バックアップファイルはHeroku内に保管されます。
コマンド実行後Webコンソールでみても取得されていることがわかります。
※今回の場合は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
作成したapp
roleにデータベースへのアクセス権を付与していきます。
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
にapp
roleの権限を付与します。
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にデータ移行できたよ〜というものを紹介してみました。ここにある手順ですがまだ荒削りなところがあります。具体的には先ほど言及したもっと大きなサイズのデータ移行の場合どうするかということと、あと移行後のデータの完全性をちゃんと知りたいなぁと感じており、今後の課題です。
意外にすんなりできた、というのが感想です。本気でデータ移行する場合は書き込みできない状態にして実施することになるなどもっと細かい点を詰めなければなりませんが、何かしらの参考になれば幸いです😃