はじめに
「この記事は苫小牧高専アドベントカレンダー2022 n日目の記事です。」という書き出しで投稿しようとネタを仕込んでおいたら今年はやらんのかいとなりました。
こんにちは、mktakuyaです。情報工学科(2012年入学)、電子・生産システム工学専攻(2017年入学)出身です。いまは都内でITエンジニア・プログラマをしています。Rubyが好きですが最近はPythonとTypeScriptを書くことが多いです。
この記事では、学生の頃に作ってから動かし続けている苫小牧高専News Botを、サーバーレス技術で作り直してみた話を書いていこうと思います。
苫小牧高専News Bot
苫小牧高専News Botは、苫小牧高専のニュースをお届けするTwitter Botです。
2014年3月(高専2年→3年の春休み)にPythonで作りました。この頃は入門レベルのC言語とPythonしか出来なかったですね。今はRuby / RailsにTypeScript / Vue / ReactそしてPythonでご飯を食べています。ナイス進化
その後、Ruby(非Rails)で作り直してメール配信に対応させたり、ニュースを電話でいち早く受け取ることが出来るようにしたり、FAXで受け取ることが出来るようにしたりと、その時見つけたおもしろ技術の実験台として活用してきました。
社会人1年目の2019年にRailsで作りなおしてHerokuにデプロイして以降は、完全放置モード。卒業した学校のNews Botを作るよりも本業と趣味のPodcast周りのシステム開発に明け暮れる日々でした。
Heroku 無料プラン廃止
そんなある日僕のもとに届いた衝撃的なニュース。Herokuの無料プラン廃止です。
もともとWeb / Worker dynoにはお金を払っており、無駄に月14ドルくらい掛かっていたこのBot。今まで無料で使えていたDBとRedisに追加で24ドルもかかるというではありませんか。
正直Zapierに毛が生えたくらいの機能しかない単発アプリに対して月38ドルも払いたくありません。*1
それもこれも、こんなシンプルなアプリを作るのに無駄にフルスタックなWebフレームワークであるRailsを使っているせいです。
サーバーレスで作り直してみた
というわけで、Railsを使わずに作りなおしてみることにしました。4度目のRebuildです。学校のニュースをツイートするということにここまで情熱をかけた男が他にいるでしょうか。だからZapierでいいだろと
僕は学生時代、Railsだけを極めればあらゆる(Web寄りな)プロダクト開発に対応することが出来ると思っていました。わりとガチで。ところがその幻想は社会人1年目で打ち砕かれることになります。いや打ち砕かれて本当によかった。
もちろん、今でもRailsをメイン技術とする会社に在籍しています。が、ここ最近の開発トレンドとして、メインのRailsアプリにどんどん機能を足さず、周辺に小さく処理を切り出していくということが多いです。Rails上に実装されたWeb APIからデータを引っ張って、諸々の計算処理をPython on LambdaまたはECSなどで計算してやってまたRailsのDBに戻すだとか、バッチ処理は外に切り出されているだとか。
そのときに便利なのがAWS LambdaやECS Task、そしてそれらをいい感じに接続するStepFunctionsやEventBridgeです。こうした技術を僕は「サーバーレス技術」と呼んでいます*2。こうしたサーバーレス技術をゴリゴリ使って仕事を進めていくのは実は現職がはじめてです。
僕が技術勉強をする際の鉄板は、仕事の技術を使って趣味のプロダクトを作る、という手法です。というわけで、同じ技術を使って苫小牧高専News Botを作り直してみました。
システム概要はこんな感じ。左側が全体のフローチャート、右側がフローチャート内で「新着ニュース通知ステートマシン」と記載されているもの(StepFunctionsのステートマシン)です。
ステートマシンの中のひとつひとつの箱をステートと呼びますが、ここにはAWS Lambdaの関数やECSタスク、その他諸々のAWSのリソースを当てはめることが出来ます。このステートマシンでは、最新ニュースのJSONを読み込んだあと、点線で囲まれた部分にあるステートをそれぞれ呼び出します。今はツイート通知とメール通知しかいませんが、ここに電話やFAX、SlackやDiscordへの通知などプログラムで書いてLambdaやECS Taskで包める処理ならなんでもおけます。めっちゃおもしろい。
ニュースを取得してS3に保存するとか、ニュース情報をツイートするみたいな部分はPythonで書いてLambdaに載せています。そしてそのLambda関数というリソース自体や、「3分に1回、最新ニュース取得Lambdaを呼び出す」「S3に新オブジェクトが置かれたらStepFunctionsのステートマシンを呼び出す」といったEventBridgeのルールはAWS CDKで記述しています。ふつうはAWSのWebコンソールでポチポチしてLambdaなどのリソースを作成すると思いますが、TypeScriptなどお好きな言語でリソースを定義出来ます。流行りのIaC (Infrastructure as Code)ってやつです。
あ、あとフロントはNext.jsで作ってます。まぁ大したことはしてなくて、 tmnct-news.m6a.jp/n/a7b51533b0 みたいなURLを踏まれたら元のニュースのURLである www.tomakomai-ct.ac.jp/news/19167.html にリダイレクトしてるだけですね。
今後
リリースしてから1週間ほどでしょうか。とりあえずは安定して動いてくれているようです。ミスって過去ニュースを100件ツイートするという事故も起こしましたが。。。過去データのインポート前に、「S3のPutEventを拾ってステートマシンを起動する」というルールを無効化する必要があったのですが、寝ぼけてて「3分に1回新着ニュース取得Lambdaを起動する」方のルールを無効化してました。
やらかしたわ。EventBridgeのルールを使って、S3のPutObjectを拾ってStepFunctionsのステートマシンを起動しているのだけど、過去ニュースのインポート時に止めるべきルールを間違えた。。。https://t.co/nR3vHHXT6R
— mktakuya (@mktakuya) 2022年12月10日
エラー通知とかロギングとか、モノリスRailsだと当たり前にやっていたことをサーバーレス技術ベースだとどうすればいいんだろうとか思ってます。とりあえずSentry入れましたがLambda関数の起動自体に失敗したらSentryに通知すらいきません。CloudWatch Alarmとかいうのを使うんかな?とかとか。サーバーレス赤ちゃんなので学んでいきたい。
また記事内では書かなかった個人的なチャンレジとして、クリーンアーキテクチャっぽいコードを書いてみるというのもありました。ただこれは他のプロジェクトを見てディレクトリ構成などを見様見真似でやってみただけで、正直ちゃんと出来ている自信がまったくありません。本読んで勉強してまたリファクタしようかなと。
おわりに
そんなわけで、苫小牧高専News 4度目のRebuildの記録でした。
LambdaもStepFunctionsも本当に安くて、今まで月14ドルかかっていたのが1ドル未満になりました。S3のディスク容量だけに課金されていて、ほかはすべて永年無料枠で運用できています。
技術の素振りとしてはなかなか良い苫小牧高専Newsですが、卒業して5年近く経つ僕がずっと運用し続けるのもどうよという感じがしています。一応CDKでリソース定義しているので、別のAWSアカウントで動かすことも可能です。この記事を読んでいる在学生の方、ソース一式渡すでもいいし、ゼロから作るでもなんでも良いので、誰か引き継いぎませんか?