前置き
以前にTypeScriptで作った常時起動型のBotを、クラウド運用コストの都合上、ローカルのDocker環境でずっと動かしてきた。今回はそのBotをDenoに移行しつつ、無料でクラウド環境にデプロイできるようにしてみた話である。
気になる方は前回の記事を読んでみて欲しい。
Denoとは
歴史の長いNode.jsにおける技術的負債を解消するため、Node.jsの開発者が新たに開発した最新のJavaScript実行環境です。
TypeScriptを標準サポートしており、その他開発者向けの拡張機能を搭載しています。
- リンター
- フォーマッター
- テスト
これまでTypeScript, ESLint, Prettier, Jestなどを個別にインストールして設定ファイルを作っていたのが、Denoを使うだけで簡単に実行できるようになります。
特にtsconfig.jsonやESLintのルールについては、どこかのネット記事で見つけた推奨設定をそのままコピペして使ってる人も多いんじゃないでしょうか。
その推奨設定は予めDenoに設定されているので、拘りが無い限りはオプション設定ファイルを書かなくても良いです。
Deno環境であれば誰でもすぐに厳格なコード記述で開発することができます。
…と、ここまではよくあるDenoの説明ですが、個人的にイマイチだと思った点。
VSCodeで開発をするにあたって、Deno公式の拡張機能をインストールするだけではTypeScript構文エラーが大量発生します。Deno本体もPCにインストール&パス設定する必要があり、Docker起動だけで環境構築OKとはならないところでしょうか。
Denoへの移行
じゃあNode.js(TypeScript)のプロジェクトを全部Denoに移行して、新規プロジェクトもDenoにすれば良いとね! とは残念ながらなりません。
Node.jsとの互換機能についてはv1.31でサポートされましたが、Node.jsで使えていたサードパーティー製のライブラリ全てがDenoで対応できているわけではないようです。
移行前のモジュール類(node_modules)を削除した上で、Denoを実行したとしても依存関係やパスのエラーが発生する事象が発生しました。
相性もあるのかもしれませんが、必要に応じて代わりのライブラリか機能の自作をする必要があるかもしれません。
またDeno専用のサードパーティを検索できる公式ページもありますが、歴が浅いものや長期間メンテナンスされていないものが多いイメージです。利用できるならばnpmで有名なライブラリをそのまま使う方が望ましいと考えます。
discord.js を Discordenoへ
channel.send()を実行した際に発生したエラーをそのまま貼り付けておきます。
TypeError: isJSONEncodable is not a function
at file:///deno-dir/npm/registry.npmjs.org/discord.js/14.7.1/src/structures/MessagePayload.js:202:9
DiscordAPI に送信する JSON データの処理で利用しているメソッドが正常に読み込めていないのが原因だと思われます。
const { isJSONEncodable } = require('@discordjs/builders');
/* 中略 */
embeds: this.options.embeds?.map(embed =>
isJSONEncodable(embed) ? embed.toJSON() : this.target.client.options.jsonTransformer(embed),
),
サードパーティーで最新の更新も9ヶ月前で少し心配ですが、こちらを利用することとします。使用感はdiscord.jsと若干異なりますが、移行自体は難しくありませんでした。
Deno Deployとは
Deno社が提供するDenoに特化したサービスを利用してみました。
執筆時点で、個人開発程度ならばクレカ登録なし&無料で利用できるのがメリットです。
(今はもう「Heroku」を無料で使えないんですよね
エッジコンピューティング
自動で負荷分散スケーリングを行い、世界規模でアクセス元から一番近いデータセンターで実行されます。
良いこと書いてはいるのですが、趣味の個人開発のレベルではオーバースペックなので、今後料金体系の見直しがある際にはオプションにして欲しい内容です。
詳細は後に書きますが、複数リージョンでアクセスするとその数だけサーバーが立ち上がります。またリクエストベースで管理しており、数分リクエストがない場合に自動でスケールダウンする仕様のようです。
GitHub連携
ビルド用の設定ファイルを準備する必要もなく、DenoとGitHubとで認証さえ通せば、push時に自動で環境にデプロイされます。
デプロイしてみる
ここからDeno Deployを使ってみた際の課題点を記載します。
npm識別子未対応
Deno自体にNode.jsとの互換性ができたとはいえ、Deno Deploy環境においてはまだ未対応のようです。
npmパッケージを読み込ませる記述があるとデプロイエラーとなります。まだ発展途上ということでしょうか。
デプロイする時になって初めて知ったため、該当ライブラリの代替を探して置換作業が必要になりました。
npm: specifiers are not yet supported on Deno Deploy
※ cdn.skypack.devやesm.shなどのCDNから直接npmパッケージを読み込む場合は有効です
双方向通信を維持できない
Deno Deployではリクエストによる自動スケーリングを行ってくれるのですが、BotとDiscordサーバーはリクエスト&レスポンスを繰り返しているわけではないため、自動でスケールダウン(停止)されてしまいます。
双方向通信を行っていて、実行環境とDiscordサーバーでやり取りは発生しているはずなのですが、残念ながら対象外のようです。
公式に記載されているものが一般的な使い方のようです。
DiscordスラッシュコマンドなどAPIベースのBotであるならば、双方向通信の問題は発生しないはずです。
継続的なリクエストを維持する
「双方向通信を維持できない」が維持できないわけではありません。要はスケールダウンされる前に新しいリクエストを送り続ければいい。
イメージ的には死活監視のシステム。ドメイントップにリクエストを送ってステータス200が帰ってくればヨシ!ってやつです。
【GCP】 Cloud Monitoring
「稼働時間チェック」を使うと指定したエンドポイントに向かって定期的にリクエストを送信、条件においじて通知してくれます。
この機能では最低3つのリージョンからリクエストを送信するため、Deno Deployのエッジコンピューティングが発揮されて常に3つのリージョンでBotが起動するようになりました。
3つ同時にDiscordサーバーと双方向通信を行うことで、Botに設定したアクションも3回発生することになります。これは想定していない挙動なので、残念ながら利用できません。
【GCP】 Cloud Scheduler
cronジョブを作成できる機能で、Deno Deployのエンドポイントに向かってリクエストを投げるように設定します。リージョンも1つのみ(asia-northeast1)で設定できるため、複数リージョンリクエストも発生しません。
現状2分ごとに送信していればスケールダウンは発生していません。かつBotは深夜利用しないため0 ~ 6時は停止しています。
(つけっぱなしでも良いのですが、利用状況を鑑みて料金体系変えられたら困るので
一応注意点としてCloud Schedulerは、実行回数での課金はされませんが、請求先アカウントあたり3ジョブまでしか無料枠がありません。個人開発ならば枠はどうとでもなるでしょう。
まとめ
まだ発展途上ですが、Node.jsの開発環境が一新される可能性を感じます。
一方、Denoサードパーティー製のライブラリが本当に安全なのかわからない点に不安があります。個人開発なら自己責任ですが、企業やチーム所属で何かしらトラブルが発生した場合のリスクを考える必要があります。
npmパッケージも全て使えるわけではないので、代替案を探したりして工数(残業)が増加する可能性もあります。その他解決すべき内容も英語ドキュメントなので、自力での解決能力が求められます。
個人的には使っていきたいと考えていますが、チームでとなるとNode.jsを採用することになる…んじゃないかな。
Deno Deployについては料金体系が変わらない限り使い倒してみようと思います。