#えむけーろぐ

間違った事を書いていたらやさしく教えてください

苫小牧高専News Botをサーバーレス技術で作り直してみた

はじめに

「この記事は苫小牧高専アドベントカレンダー2022 n日目の記事です。」という書き出しで投稿しようとネタを仕込んでおいたら今年はやらんのかいとなりました。

こんにちは、mktakuyaです。情報工学科(2012年入学)、電子・生産システム工学専攻(2017年入学)出身です。いまは都内でITエンジニア・プログラマをしています。Rubyが好きですが最近はPythonとTypeScriptを書くことが多いです。

 

この記事では、学生の頃に作ってから動かし続けている苫小牧高専News Botを、サーバーレス技術で作り直してみた話を書いていこうと思います。

twitter.com

苫小牧高専News Bot

苫小牧高専News Botは、苫小牧高専のニュースをお届けするTwitter Botです。

2014年3月(高専2年→3年の春休み)にPythonで作りました。この頃は入門レベルのC言語とPythonしか出来なかったですね。今はRuby / RailsにTypeScript / Vue / ReactそしてPythonでご飯を食べています。ナイス進化

blog.m6a.jp

その後、Ruby(非Rails)で作り直してメール配信に対応させたり、ニュースを電話でいち早く受け取ることが出来るようにしたり、FAXで受け取ることが出来るようにしたりと、その時見つけたおもしろ技術の実験台として活用してきました。

社会人1年目の2019年にRailsで作りなおしてHerokuにデプロイして以降は、完全放置モード。卒業した学校のNews Botを作るよりも本業趣味のPodcast周りのシステム開発に明け暮れる日々でした。

Heroku 無料プラン廃止

そんなある日僕のもとに届いた衝撃的なニュース。Herokuの無料プラン廃止です。

forest.watch.impress.co.jp

もともと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を作り直してみました。

github.com

システム概要はこんな感じ。左側が全体のフローチャート、右側がフローチャート内で「新着ニュース通知ステートマシン」と記載されているもの(StepFunctionsのステートマシン)です。

苫小牧高専News サーバーレス版の概略図

ステートマシンの中のひとつひとつの箱をステートと呼びますが、ここには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を起動する」方のルールを無効化してました。

エラー通知とかロギングとか、モノリスRailsだと当たり前にやっていたことをサーバーレス技術ベースだとどうすればいいんだろうとか思ってます。とりあえずSentry入れましたがLambda関数の起動自体に失敗したらSentryに通知すらいきません。CloudWatch Alarmとかいうのを使うんかな?とかとか。サーバーレス赤ちゃんなので学んでいきたい。

また記事内では書かなかった個人的なチャンレジとして、クリーンアーキテクチャっぽいコードを書いてみるというのもありました。ただこれは他のプロジェクトを見てディレクトリ構成などを見様見真似でやってみただけで、正直ちゃんと出来ている自信がまったくありません。本読んで勉強してまたリファクタしようかなと。

おわりに

そんなわけで、苫小牧高専News 4度目のRebuildの記録でした。

LambdaもStepFunctionsも本当に安くて、今まで月14ドルかかっていたのが1ドル未満になりました。S3のディスク容量だけに課金されていて、ほかはすべて永年無料枠で運用できています。

技術の素振りとしてはなかなか良い苫小牧高専Newsですが、卒業して5年近く経つ僕がずっと運用し続けるのもどうよという感じがしています。一応CDKでリソース定義しているので、別のAWSアカウントで動かすことも可能です。この記事を読んでいる在学生の方、ソース一式渡すでもいいし、ゼロから作るでもなんでも良いので、誰か引き継いぎませんか?

*1:14ドル払っていたのもやめたいと思っていたし

*2:正しい呼び方かは知らない

インターネット飲み会

昨日、学生時代からのファンだったuiureoさん・hidesysさんと目黒で飲んだ。

お二人をウォッチしはじめたのはたしか2014年頃、高専3年生の終わりくらいのはずだ。いわゆる京都のインターネット界隈に属する彼らをウォッチする中で、趣味でWebサービスを作ること、スタートアップ界隈の情報を追っていくこと、自分の考えや日記、特になんというかディープな気持ちを書くということといった、いわゆるインターネットしぐさ(?)のようなものを身につけることが出来た気がする。

まさかそんなお二人と会って、飲みに行けるだなんて!

モツ鍋屋に行ったりバーに行ったりした。今どんな仕事をしているのかだとか、インターネット・スタートアップ界隈の話だとか、Webサービスの技術・アイデアについて語り合ったりだとか。

めちゃくちゃ楽しかった。改めてやっていこうという気持ちになった。

いや、やる、か。

 

近況

今月も月末ギリギリ更新だ!!!

blog.m6a.jp

2022年10月はこんな感じであった。

  • 新卒採用イベント参加 × 2
  • サバゲー × 2
  • コロナ感染

新卒採用

会社でエンジニア採用に関わっている。

事業に対するインパクトスケールを上げていくということで、担当チーム・プロダクト以外にも貢献していきましょうという話をマネージャーとしていて、まぁ手っ取り早く(?)広く貢献出来るものとして採用貢献をやってみている。

中途の面接自体は某スタートアップのA社で経験済なのだが、エンジニア志望の学生と会社の中の人としてお話してアトラクトするというのはそんなに経験が無いので良い経験だ。

また面接の方にも携わっていっているが、あの短い時間の中で人を見極めるというのはなかなか難しい。

あんまり数をこなしていない今の時点であんまり感想など書くと良くないので、少し経験を積んだらまた考えたことを書いたり話したりしてみようかなぁと思っている。

サバゲー

ブログで言及するのは初めてかも。実は今年2022年の7月くらいからサバゲーにハマっている。

夏は暑いので室内フィールド + ガスハンドガンで楽しみつつ、涼しくなってきたということで長物(いわゆるライフルタイプのもの)を購入してアウトドアフィールドにもチャレンジしてみた。

11月にも2回くらいはサバゲー行きたい。

コロナ感染

とうとう自分にも順番がまわってきた。

とはいえ幸い軽症で、デルタ株の頃に言われていた「入院するほどではないという意味では軽症だけど死ぬかと思った」というような感じもなく、症状的には風邪の延長、インフルエンザにでも罹患したときのような感じであった。

まぁワクチンを3回打っていることと、単に運が良かったということなのだろう。

その他

その他、なんだか仕事が忙しかったり、Podcastのサポーター向けSlackを開設したりという感じの10月であった。

 

何かを続けるということについて

このブログも2018年10月から毎月更新をしている*1ようで、今日の投稿を逃すとそれが途絶えてしまうということに気がつき慌てて更新している。

小さい頃からとにかく何かを続けるのが苦手だったし、今でもそうだと思っている。しかしながら、ここ数年は趣味のPodcastを筆頭に、個人開発やブログなど、なんとなく「何かを続ける」ということが出来るようになりつつあるのかなと思うようになってきた。

趣味で友人達とやっている「ゆるふわPodcast」は、2020年5月8日のEP26から今日時点でEP156まで2年4ヶ月も欠かさず毎週配信を継続することが出来ている。

yuru28.com

ブログも、まぁこうやってギリギリに書いているものだったり、「そもそもそれTwitterでいいんじゃねえの140文字以下だし」という記事を多分に含みつつも4年近く毎月更新をすることが出来ている。

そういうわけで、なぜ続けることが出来ているのか?ということについて考えを書いてみようと思う。

続けるの周期をゆるくする

いきなりしょぼいことを書いてしまうのだが、続けるの周期をゆるめにしてやれば良かったりする。

例えばこのブログがそうだ。偉そうに「4年近く毎月書いている!」なんて言っているが、毎週、毎日ブログを書いている人からしたらそんなのしょぼくて笑いが出てしまうことだろう。

実際、12月1日から25日まで一人(?)でブログを書き続ける一人アドベントカレンダーについては、2018年2019年はなんとか達成しつつもその後2020年2021年はエターナってしまっている。4年間毎月ブログ更新を続けて、2年半近く毎週Podcast更新を続けられる男が、たった25日間毎日ブログを更新することすら出来ないのだ。

でも記事を書くことそのものが仕事であるとかじゃない限り、ブログを更新する周期について人と競ったり厳しいノルマを課す必要なんて無いはずだ。なんとなく、「この内容が、このくらいの周期で、これだけ続けば自分的には続いてるって言えるな〜」と思えればそれで良いのだ。

続けるための仕組みをつくる

なんだか自己啓発っぽい見出しになってしまったが、続けるための仕組みをつくるのも重要だ。

Podcastに関しては、過去に勉強会で発表したこともあるのだけど、以下の3点を仕組み化して継続することが出来ている。

  • 収録を定期イベント化すること
  • 日々の情報収集の仕組みをつくること
  • 1エピソード30分以内に収めること
ゆるふわなPodcastのすすめ / kichijojipm-22 - Speaker Deckより

また業務で作ったイケてる仕組みとかでない限り、いや業務で作った仕組みですらたまにうまく動かないということは絶対にあるので、そうなった時にも破綻しない(=なんとか続けられる)ようにしておくことも大切だ。

プランBを考えよ、ということもあるが、それよりはもっとレベルが低い話で、例えばPodcastなんかでは「予定が合わなくても必ず決めた曜日時間にやる(欠員OKにする)」「誰も予定が合わなかったら一人回も辞さない」という割り切りをしていたりする。

気がついたら続いていた、という状態にしてみる

これはPodcastではなくブログの話になるのだが*2、ブログに関しては別に最初から毎月更新しようと思ってしたわけではない。

もちろんこうして月末に慌てて記事を書いているように、今は毎月更新しようとしてやっているものの、いつだったかに「あ、これ毎月更新してるじゃん」と気がつくまでは本当にナチュラルに毎月ブログを書いていた。

なにか思うところがあったりだとか、140文字以上だったり以下だったりのことを書きたいとなったときにブログを書いていたのだけど、それがたまたま1ヶ月周期で続いていたのだ。

なんだかこういう物事の続け方って理想的な気がしている。そういう状態になれるようなことがたくさんと言わずともいくつかあると、人生って豊かになるんでしょうね。

おわりに

もともと続けるのが苦手、とか根性がない、とかがある意味コンプレックスだったのだが、東京に出てきてからずっとお世話になっている坂上さん @madai0517 に「そんなことないよ、mkくんはブログにPodcastに色々と継続できてるじゃん!」と言っていただいてから少し自信がついてきた。「そうか〜たしかに考えてみれば色々続けられるようになってるな、自分」と思えるようになったのであった。

そういう気づきを与えてくれる人に恵まれたことに感謝しつつ、これからもいろいろきつくない程度に、楽しく何かを継続してやっていけたら良いなぁと思っています。

これで9月のノルマは達成です。

*1:Mediumにあるものも入れたらもっと長いかも。

*2:PodcastはEP26で毎週配信します宣言をした上でやっている

「ジョン・ウィリアムズ」ウインド・オーケストラ・コンサート

行ってきた。

prtimes.jp

今年の2月くらいにたまたまTwitterの広告に流れてきて一目惚れ。最近はTwitterの広告も良い仕事するようになってる。

ジョン・ウィリアムズはスターウォーズやハリーポッターなど有名な映画音楽の作曲家。コンサート内では他にもインディ・ジョーンズやジュラシックパーク、これは全く知らなかったけど11人のカウボーイという映画の曲も演奏されていた。

いやいやスターウォーズやハリーポッターはもともと好きなので約束された感動なのは当然として、他の映画音楽の演奏も大変よかったです。インディ・ジョーンズとジュラシックパーク、このコンサートに行くまでに観ようと思って観れてなかったんだけど、この気持ちが熱くノッているうちに観てしまおうと思う。

スターウォーズのメインテーマが流れたときは感動で泣きそうになった。鳥肌が立つっていうネットスラングがあって、本当は褒めるときに使うものじゃないらしいけど、あえて使うとまさに鳥肌モノだった。

 

オーケストラ・コンサートなんて小学校だか中学校だかの社会科見学以来なのだが、かなり楽しかった。

こういう、文化に触れて感情を動かすという経験はたまにしておくと良いですね。

Vue 3の素振り(Podcast管理画面 リプレイス)

はじめに

↓の続き。

blog.m6a.jp

プロフィールサイトを作って感覚を覚えたことだし、お次はユーザー認証やCRUD操作のあるアプリを作ってみようということで、趣味でやっているPodcastの管理画面をリプレイスしてみた。

できたもの

できたものはこんな感じ。

もともとRailsのMPAで作っていたものをコピーしただけなので特に代わり映えはない。

Vue 3でリプレースしたPodcast管理画面

SPAにしたので、エピソード公開時の体験は改善した気がする。エピソード一覧の画面描画時に /episodes を叩き、その後 /episodes/:id/publishing にPOSTする的な。

エピソード公開時の挙動

API: Rails

もともとRailsを使っていたので、APIはRailsで作った。

GraphQL APIを生やしても良いかなと思ったけど、そもそもPodcast画面をリプレイスする目的は仕事の技術で素振りすることだったので、仕事で使っているREST APIに。

2022年、RailsでREST APIを作るときの選択肢は色々あるけど、ひとまずRailsのController + jbuilderに。

デプロイ先: S3 + CloudFront

プロフィールサイトはVercelで作ったけど、こっちはS3 + CloudFrontに。仕事で云々、というのもあるのだけど、それ以上にVercelのProプランが20ドル/月とお高めだからというのがある。

一応無料のHobbyプランがあり十分使えるものであるものの、非営利の個人利用に限定されており、3人で有料サポータープログラムなんかも提供しているこのPodcastを商用ではないと言い張るのはフェアじゃないかなと。

さてS3へのデプロイはとてもかんたんで、GitHub Actions上でvite buildして出来た成果物(distディレクトリ以下のすべて)をs3 syncするだけ。

ブログ用

お恥ずかしながら、S3のバケットやCloudFrontの設定はAWSのWebコンソールでポチポチしました。本当はCDKとか使いたいネ……。

失敗

SPAやモバイルアプリ向けのWeb APIを作る上で考えなければいけないのが、ファイルアップロード。

S3にダイレクトアップロードするとか、ファイルアップロードがある画面のAPIだけmultipart/form-dataにするか、などいろいろあると思う。

めんどくさいのでBase 64エンコードしたMP3ファイルをJSONに詰めて送るようにしたら、無事Herokuのdynoがメモリ不足で落ちました(´・_・`)

20MB程度のMP3ファイルをBase 64エンコードしてJSONに詰めたらサーバーが落ちた様子

ちょっとした画像ファイルならともかく、20MB程度のMP3ファイルをボディに詰めるなんて無謀ですね。1リクエストで20MB相当のBase 64文字列がログにボンと流れる様子はなかなか爽快でした。

おわりに

仕事で作ってた社内向けアプリと並行してPodcastの管理画面を作っていたおかげで、プライベートの開発・仕事の開発それぞれで得た知見を双方向に適用出来てなかなか楽しかったです。良い勉強になりました。

とりあえずこの記事を公開したら、ファイルアップロードまわりをなんとかしよう……。

はてなブログのGA4対応

いつの間にかはてなブログがGA4対応していたので、設定した。

staff.hatenablog.com

さすがはてな社の解説記事だけあって、記事のとおりにやればかんたんに設定できた。追加のメタデータを送信してくれるのだが、そこはGA4上でカスタムディメンションの設定をしなければならないので注意。(これも上記記事に書いてある。)

ついでに、BigQueryにイベントデータを流す設定もしておいた。RedashからBigQueryにつなぎにいって、適当なクエリを書き、Zapier連携して毎日ブログがどれくらい読まれたかをメール送信する、なんてことができそう。

 

せっかくGA4を設定したのにこういうこと言うのもなんだけど、仕事でNext.jsを触ることになりそうなので、ブログを自作してみようかなぁという気持ちになっている。Podcastの画面を作るので忙しかった*1こと、Headless CMSでやるかCMSはRailsで自作するか迷っていることなどから、なかなか着手出来ていないのだが。