2023/01/07

「下高井戸・明大前問題」 (GKEに無料でStreamlitアプリをデプロイした話)

皆様、あけましておめでとうございます。
年末年始、冬休みの自由研究ということでGKEの無料枠にStreamlitで作ったちょっとしたwebアプリを置いてみました。

「下高井戸・明大前問題」


今回やりたいことは「下高井戸・明大前問題」の解決です。
この問題は今まで私から1000円ぐらいの懸賞金がかかっている未解決問題でした。

「下高井戸・明大前問題」

悪魔の領域への引っ越し


最近引っ越しをしました。
いい物件があったので京王線 下高井戸駅と明大前駅の間(明大前が新宿側)、下高井戸寄りに引っ越すことに。
しかし、こんなに電車に乗るのが難しい地域だったとは…

(実際には静かな住宅街で世田谷区を感じるいい場所です)

悪魔ポイント①:最寄り駅が各停


下高井戸駅は各停駅です。
実際には快速も止まりますが京王線の快速はほぼ京王新線を通る(停車駅が2つ増える)ので微妙な感じです。
ですがこれはまあ許容できます。下高井戸は割と新宿に近く、一番早い特急と比べても新宿との間で1駅しか変わらないので所要時間はそれほど問題になりません。
しかし悪魔ポイントはそれだけでは無いのです…

悪魔ポイント②:京王線 特急多すぎ


さて、京王線ユーザーとなったわけですが時刻表を見てみましょう。
まずは明大前。

京王線 明大前 平日 上り

さすが東京。ラッシュ時でなくても1時間に20本以上電車があります。
下高井戸はどうでしょう。

下高井戸 平日 上り

凡例

え!各駅停車少なすぎ!!!!!!!!!!!!!!!!????????????

特急、区間急行ばっかりで各駅停車が全然ありません。
下高井戸に停車する電車と電車の間に3本通過があったりします。

つまり、最寄りの下高井戸よりも明大前まで歩いて電車に乗ったほうが早く移動できる場合が結構あります。そんなあ…

悪魔ポイント③:どっちに行けばいいのか分からん


下高井戸は明大前より家から近いので、各駅停車に乗るなら下高井戸に歩くことになります。
しかし特急が止まらないため、各停が無い時間だと多少時間がかかっても特急の止まる明大前に歩いたほうが良いです。

要はこれが解決したい問題なのですが、下高井戸か明大前か、どちらに歩けばいいのかは現在時間と徒歩分数によって変わってくるので、家を出た瞬間にどっちに行けばいいか教えてくれるモノが必要です。

家でたらGoogle Maps開けば良くない?と思わないでください。

Webアプリの作成


ということで、家を出るタイミングでどちらに歩けばよいかを教えてくれるwebアプリを作っていきます。
自由研究なので時間をかけずサクッと簡単に作っていきます。

完成品


こちらが完成品になります。

メイン


ブラウザからアクセスすると…


でました。この時間だと下高井戸に向かって歩けば良いみたいです。
時間によっては10分も到着に差があったりします。

設定画面


左上の三角を押すとサイドバーが出てきて設定ができます。


2択になっている駅や目的の駅を選択できます。(自分は使わないけど)
さらに、気分によってはゆっくり歩きたかったり、一刻も早く目的地に向かいたかったりするので、各駅への徒歩分数を3パターンプリセットできます。

これらの設定はすべてURLにパラメータとして反映されるので、設定が終わった後にURLをお気に入りに登録したりすることで同じ設定を再現できます。

私は携帯のホーム画面にChromeで開くボタンを置きました。


Streamlit


今回Streamlitを使って面を作ってみました。
StreamlitはhtmlのdivがPythonのObjectに抽象化されていて、そこに別のObjectを追加していく感覚で面が作れます。(すごく簡単に書いたので正しくないかも)

Streamlitについては公式のdocsがしっかりしているので、これぐらいのモノならほとんど詰まりません。素人ですがオンボーディング含めて2時間ぐらいで面ができました。
HTMLもCSSも書かずそれっぽい画面になります。すごい!

今回は時刻を出すだけでしたが、本来はグラフなどを綺麗に+インタラクティブに表示するようなときに使うものなので、ちょっと無駄かも。
必要なリソースもこの程度の内容にしては大きく、後述しますが無料枠のGKEにデプロイするのはちょっと大変でした。
ですが本当に簡単に面が作れるので自由研究にはとても便利でした。

ダイヤからのルート検索


毎回乗り換え含めた最速ルートを検索しています。
同じ各停、同じ区間でも所要時間が違ったりしていたので時刻表だけじゃ到着時刻がわかりませんでした。
具体的にどうやってるかはひみつ。

GKE無料枠へのデプロイ


モノを作るよりこっちの方が大変でした…

GCPの無料枠について


2022年、herokuから無料プランがなくなりましたね…
herokuでは音割れブリーチbotというモノを動かしていましたがちょっと前にGKEに引っ越ししました。
間違えて何回か5ドル払っちゃったんですが、これのために5ドルはアホすぎる…

Google様のGCPですが、各サービスで無料枠が設定されています。太っ腹ですね。
kubernetesのクラスターを建てるのも無料で試せます。
うろ覚えですが今回使わせて頂いているサービスはこんな感じで無料になります。すごい。
  • Cloudbuild 
    • 1日120分ビルド無料。
  • Container Resistry (GCR)
    • ここはGCSなので保存した分だけ課金だったはず
  • GCRからのPull
    • regionが同じじゃないと無料にならないはずなので注意
  • Kubernetes Engine (GKE)
    • autopilotでもstandardでも1zonalクラスタ管理費無料
    • 知らなかったけどautopilotというもっとmanagedなclusterが建てられるようになっていた
    • 試してないけどautopilotだとnodeがGCEの無料枠から出るのでお金かかりそうな気配がする
  • Compute Engine (GCE)
    • GKEノードの実態
    • e2-micro1台が無料
    • e2-microは0.5CPU(ただしバースト2.0CPU)と1Gi RAM
      • しょぼい
  • SSL証明書
    • マネージドなSSL証明書が100個とかまで無料なはず
    • GKEにManagedCertificateというリソースを置くとSSL証明書を作ってくれてIngressでnameから指定できるので管理しやすい
  • 静的IPアドレス
    • 使ってなかったら有料だけど使ってたら無料だったはず
  • 外とのネットワーキング
    • 多分egressだけお金かかるけどほぼ無料
  • ドメイン
    • 1000円強/年, 前からもってた
  • Logging
    • GCSに保存するとお金がかかる。loggingのAPIを叩くだけなら無料
なんか色々無料で使える!すごい!と思いますがメインのGCEはe2-microなんですよね。メモリ1GiBです。こんなのにk8sを乗せるとどうなるのか…!

Kubernetesシステムを痩せさせる


とりあえず無料枠に収まるようにGKEでk8s clusterを立ててみます。


立ちました。もちろん1ノード。
何も触っていない状態でノードの中身を見てみます。


おや?何もdeployしていないのに0.5GiBもメモリをrequestしています!ノードに1GiBしかないのに!
limitが1.3GiBなのでこの時点で既にシステムのOOMの可能性があります。(ちなみにCPUも0.53v requestしていたのでパンパンです。)

困りました。
ブリーチbotは置けましたが、今回ビルドしたイメージを置いたところ、リソースが足りずにnodeが死んでしまいました。
ということで、なんとかしてk8s自体が使っているリソースを削減したいところです。
ここでググってみると公式でそれっぽいガイドがありました。すごい。

nodeで動いているpodを見てみます。


fluentbit-gkeというpodがCPU 100mとメモリ 209MiBをrequestしています。
調べてみるとfluentbitというのはloggingのパイプライン化をするツールらしいです。ですのでコンソールをポチポチしてCloud Loggingをオフにします。
これでCPU 100mとメモリ 209MiBを節約できました。ログは保存されませんが無料なので我慢します。

さらにkube-dnsなどのautoscalerを消します。このあたりはdeploymentで管理されているので直接editしてreplicaを0にします。

もう少し開けたい気がするので、多少動作が不安定になるのを我慢してkube-dnsなどのリソースでrequestを大丈夫そうなところまで手動で下げます。
加えて突然死を防ぐため、podでメモリのlimitとrequestがずれているものをlimit=requestにしてしまいます。

こんな感じでkubenetesのダイエットを行ったところ、メモリのrequestを大幅に減らすことができました。
これでやっとデプロイできます!

普通にデプロイする


ノードに(ほんの少し)余裕ができたのでGCPのガイドを見ながら普通にデプロイしていきます。
yamlを書いてDeployment, Service, ManagedCertificate, Ingressをapplyします。
GCPコンソールから静的IPを予約して、ドメインにレコードを設定。Ingressでこれを指定します。
全部GCPなのでマニフェストのannotationsでリソースの名前を指定するだけでGKEからIPアドレスなどGCPのリソースがつながって体験がいいです。

おわり


ということで、ほぼ無料でお手軽に問題が解決できました。やったね!

ちなみにブリーチbotだけ動いている状態では月におよそ10円前後しかかかっていません。
去年12月の請求がこちら。

2022年12月の請求

10,614円分使って無料枠が10,605円なので9円です。すごい。

皆様もGoogle様に感謝して使わせてもらいましょう。