ひぃの雑多書き

日記からお役立ち情報まで色んなことを書きます。

Discordjsで作ったbotをHerokuからFly.ioに移行した話

こんにちは。ひぃです。

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のインストール

大体は公式ドキュメントに書いてある通りです。

Install flyctl · Fly Docs

自分はWindowsなので、Windows Power Shellを使ってインストールをしました。 インストールさえできればコマンドプロンプトからもFlyctlは実行できます。

アプリケーションの作成

また公式ドキュメントです。ほんま助かる。

Fly Launch · Fly Docs

朧気な記憶ですが、確かこれ。

flyctl create -a [app name]

Secretsの登録

ブラウザからではなくコマンドプロンプトから実行します。

fly secrets · Fly Docs

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]

fly vm stop · Fly Docs

Twitter APIのせいでデプロイできない場合がある

自分のbotTwitterから #VRChat_world紹介 タグのついたツイートを一定条件で収集する機能がついてます。 なんですが、Twitter APIがちょっと面倒臭い子でして、直前までstreamでツイートを収集しているとデプロイ時に「API使いすぎだよ!時間空けてね!」って主旨のエラーを吐く時があります。

なのでheorkuで運用していた時はstreamが取得できていない場合は5分空けて再度streamを拾ってくる処理を付けたんですが、Fly.ioでは「デプロイしたけどエラー吐いたから前バージョンに戻しておいたよ!」って言われます。一応もう一回デプロイすれば通ります。

この処理はtry catchしてるはずなんですが、ちゃんと拾えてないエラーがあるんですかね。
間違うのはいつも人間。きっと何かミスってるんでしょう。 気が向いたら直します。

【2023/01/08 追記】
別にデプロイに失敗してるわけではなく、『デプロイはしたけど、エラー吐いたから過去バージョンで起動しておくよ!』ってことでした。なので、普通にアプリケーション再起動かければOKでした。

flyctl apps restart [app name]