こんにちは。ひぃです。
2022年11月28日に、HerokuのFreeプランの提供が終了しました。
これに伴い、自分のDiscordサーバで運用をしているお手製bot(Node.js + Postgresql)をHerokuからFly.ioに移行したのでその時の話を書いていこうと思います。
なお、この記事は備忘録のついでに誰かの参考になればと思って書いています。
現時点では未決の課題や、よくわかってないままテキトーに終わらせてる部分もあると思います。ご了承ください。
何故Fly.ioなのか
自分が移行したのはFreeプラン終了から1ヶ月も経過した後だったので、検索すると既にHeroku Freeプランからの移行先の候補をまとめた記事が沢山ありました。 先人たちの知恵に感謝。
沢山の記事をナナメ読みしていく中で、良さそうな物から試していた結果、2つ目に試したFly.ioが気に入ったという感じです。
無料プランが充実してるし、公式ドキュメントも豊富。なんならHerokuからFly.ioへのmigrateガイドすらある。
ちなみに、1つ目はGCP(Google Cloud Platform)でした。
こっちはGoogleのサービスだから安心度は高そうだと思ったのですが、Node.jsアプリを動かすのにはあんまり適してなさそうな感じがしました。
知識としては浅瀬も浅瀬なので知らないだけかもしれませんが、常時サービスを起動する為にはダミー用のフロントページが必要になるとかなんとか。
あとはGoogleが巨大な為に全て自社サービスを使わせようとしてくるのも面倒臭いポイントでした。 一から構築するなら別にそれでもいいんですが、移行の場合はシンプルに勉強コストや移行の手間がデカすぎる。
Node.jsの移行
Fly.ioアカウントの登録
とりあえず公式サイトからアカウントを作りましょう。自分は面倒臭かったのでGithub連携で登録しました。
Deploy app servers close to your users · Fly
Flyctlのインストール
大体は公式ドキュメントに書いてある通りです。
自分はWindowsなので、Windows Power Shellを使ってインストールをしました。 インストールさえできればコマンドプロンプトからもFlyctlは実行できます。
アプリケーションの作成
また公式ドキュメントです。ほんま助かる。
朧気な記憶ですが、確かこれ。
flyctl create -a [app name]
Secretsの登録
ブラウザからではなくコマンドプロンプトから実行します。
flyctl secrets set -a [app name] [key]=[value]
ぱっと見ですが、現在のsecretsを確認する手段はなさそう? 同一のkeyでsetを再実行して上書きとか、unsetで削除はできます。
flyctl secrets unset -a [app name] [key]
なお、DATABASE_URLは後程別の方法で紐づけるのでこの段階で登録しないでください。 自分はこの段階で登録してしまった為に上手くいかず、後でDB作り直したり色々試行錯誤するハメになりました。
アプリケーションのデプロイ
デプロイする前にFly.io用のコンフィグを作成する必要があります。 初回はlaunchを実行します。2回目からは普通にdeployで大丈夫です。
flyctl launch flyctl deploy -a [app name]
一応手動でコンフィグファイルであるfly.tomlを作成することもできます。 正直launchを実行した記憶がないので、自分は多分手動で作ったんでしょう。
Node.js向けのSpeedrun解説ページに色々書いてあるのでこっちも参考になるかも。
JavaScript on Fly.io · Fly Docs
デプロイは結構時間かかります。10分くらいかしら。お皿を洗ったり、いっそお風呂に入っちゃうか~くらいの感覚。
ログを確認する
デプロイが完了したらログを見ましょう。エラーが無ければDiscord botもオンラインになってるはず。
flyctl logs -a [app name]
自分のbotは開幕からDBにアクセスする処理があったのでDB見つからないぞ!って怒られてました。 が、取り敢えず立ち上がりはしてたのでOK。
Postgresqlの移行
DBの作成
ここがちょっと躓きました。
普通にDBを作るだけなら下記手順で作れます。
Create a Fly Postgres Cluster · Fly Docs
が、そのDBに接続しようとした所SSL接続エラーが出ました。 どうやらFly.ioではSSL接続する為にアプリケーションとdbを紐づける必要があるみたいです。
だったら紐づければいいだけの話なんですが、紐づけるコマンドがdbを同時に作成もしてくれるので最初からそちらだけ行うのが良いと思います。 同時に作成してくれたと思うけど、勘違いだったらごめんなさい。どっちもやってください。
flyctl postgres attach --app [app name] [db name]
Attach or Detach a Fly App · Fly Docs
DBを作るとこんなメッセージが出ると思います。
これは一度しか出ないのでちゃんとメモっておきましょう。特にConnection string。
Postgres cluster testdb created Username: postgres Password: 111222xxxYYYzzz Hostname: testdb.internal Proxy port: 5432 Postgres port: 5433 Connection string: postgres://postgres:111222xxxYYYzzz@testdb.internal:5432
なお、attachを実行する時点で既にアプリケーションに紐づくDATABASE_URLがある場合、エラーが出ます。
恐らくunsetでDATABASE_URLのsecretsを削除すれば問題なく通ると思うんですが、自分は英文をナナメ読みして「作り直さないとダメなんか!?」って勘違いしてDB作り直しました。
DBを整備する
コマンドプロンプトからコンソールにアクセスしてsql文を叩きましょう。
アプリとDBを紐づけていれば下記コマンドで一発でログインできます。
flyctl postgres connect -a [db name]
なお、初期はpostgresというデータベースに接続しています。
紐づけたアプリから接続するのはアプリの名前のデータベースですので、忘れずに切り替えておきましょう。
\c [app name]
Connect With flyctl · Fly Docs
heroku postgresから移行する手段もあるみたいです。公式ドキュメントが有能すぎる。
が、自分はherokuのDBが消し飛んでたので試していません。
Migrate a Postgres Database from Heroku · Fly Docs
また、heroku postgresでは問題なく動いていたのですが、DB名前を大文字で作成したが為にテーブルがあるはずなのに見つからない『relation "xxxxx" does not exist』が出てました。
良い子のみんなはテーブルは全部小文字で作成しましょう。
ここまで来たら問題なく動くはず。
課題
Githubからの自動デプロイ
公式ドキュメントはあるんですが、上手く動作してくれませんでした。
きっと何か変な勘違いをしてるんだと思います。間違うのはいつも人間。気が向いたらやります。
Continuous Deployment with Fly.io and GitHub Actions · Fly Docs
インスタンスが複数立ち上がってる?
ログが謎に2回分吐き出されてたり、1通しか来ないはずのメッセージが2通来てたりしてるので、インスタンスが2つ立ち上がってる場合があるっぽいです。
1通しか来ない時は来ないので、デプロイ直後とかそんなんなのかな。イマイチよくわかってないので気が向いたら調べます。
【2023/01/07 追記】
普通に現行バージョンと過去バージョンで2つインスタンスが立ち上がってました。
下記手順で過去バージョンを停止できます。
flyctl status flyctl vm stop [vm id]
Twitter APIのせいでデプロイできない場合がある
自分のbotはTwitterから #VRChat_world紹介 タグのついたツイートを一定条件で収集する機能がついてます。 なんですが、Twitter APIがちょっと面倒臭い子でして、直前までstreamでツイートを収集しているとデプロイ時に「API使いすぎだよ!時間空けてね!」って主旨のエラーを吐く時があります。
なのでheorkuで運用していた時はstreamが取得できていない場合は5分空けて再度streamを拾ってくる処理を付けたんですが、Fly.ioでは「デプロイしたけどエラー吐いたから前バージョンに戻しておいたよ!」って言われます。一応もう一回デプロイすれば通ります。
この処理はtry catchしてるはずなんですが、ちゃんと拾えてないエラーがあるんですかね。
間違うのはいつも人間。きっと何かミスってるんでしょう。
気が向いたら直します。
【2023/01/08 追記】
別にデプロイに失敗してるわけではなく、『デプロイはしたけど、エラー吐いたから過去バージョンで起動しておくよ!』ってことでした。なので、普通にアプリケーション再起動かければOKでした。
flyctl apps restart [app name]