推しアイデア
1プレイヤー1GKEで破産できるところ。
1プレイヤー1GKEで破産できるところ。
Kubernetesって"コンテナオーケストレーション"って言われるものですが、実際オーケストレーションしてる感はないですよね。これはそれを解決します。
Kubernetes, gRPC
と、思っていたらアピールタイムとか雑談の中でKubernetes知ってる人がほぼ居なかったので軽く説明します。涙。
Kubernetesは"コンテナオーケストレーション"と呼ばれ、マニフェストファイルと呼ばれる定義書のようなもので決められた状態を必ず維持するという性質を持ちます。
AWS, GCPなど様々なクラウドサービスで利用可能で、もちろんローカルのマシンでも利用することができます。
マイクロサービスアーキテクチャなど複数のPOD、サービスを使用するアプリケーションのデプロイには最適なんです。
Kubernetesは"コンテナオーケストレーション"と呼ばれますが、実際あんまりオーケストレーションしてる感はありません。
ところで、オーケストレーションってなんでしょうか?

そう、指揮者なんです。インフラを触っているあなたは指揮者なんです。
でも、別にKubernetes触ってて「うお〜!指揮者してるわ〜!」とはならないと思います。指揮者とまで言わずとも、Podたちを支配している感覚はありませんよね?少なくとも僕はそう思います。
なのでその問題を解決できる方法を作りました。
Podは非常にか弱い生命です。大切にしてあげてください。 手が触れたり強い風が当たると死んでしまうかもしれません。
http://nginx-proxy-svc:80をサービスとしてアプリケーションルートに登録terraform/gcpディレクトリの中のproject, secret_manager, deployの順番でinit, apply
*.tfvars.example使ってシークレットをローカルに保存Google Cloud採用のため、Google系のAIツールを使うことで得意なのかな?とか思っていましたが、今のところそんなことは無さそうです。

Kubernetes, CI/CDのデプロイはGoogle Cloud, ネットワーク関係はCloudflareを採用しています。
いつも通りの技術スタックにはなりますが、今までよりモダンな開発はできたと思います。
今回もTerraformでIaCしました。前回はAWSだったのでGCPは初です。
GCPはサービスごとにAPIを有効にする必要がありますが、これもTerraformでApply, Destroyできたのでそうしています。
Cloud Builld --> Artifact Registry --> Cloud Deploy
Cloud Build, Cloud Runの構成は以前使ったことがありますが、Cloud Deployは初使用です。
後述しますが、Cloud DeployはSkaffoldを使用するのですが、開発環境・本番環境で差分を少なく開発ができたため読みやすくて良かったです。
環境変数に関しては、フロントエンドはCloud Buildでビルドをする際にSecret Managerから注入することで解決できますが、GKEは少し工夫をする必要がありました。
GKEにはCloudflare Tunnel用のcloudflaredサービスがいるため、開発環境・本番環境の両方でKubernetes Secretを使う方式を取っており、なるべく差分を減らしていました。
通常のGKEではGoogle Secret Managerからシークレットを格納することはできないのですが、現在プレビュー版として利用可能なシークレット同期機能を使い、Secret ManagerからGKEへ直接シークレットを同期させています。
セキュリティ的な観点で実装済みの別方法を使う方が良いそうですが、今回の用途ではベストマッチだったため採用です。
Kubernetes EngineのAutopilotクラスタを使用しています。 AutopilotにすることでPOD数、リソースの消費に合わせてインスタンスを調整してくれるため、自分でインスタンス設定をチューニングする必要がありません。同スペックでは比較的コストが高くはなるのですが、短い期間なのでないようなものです()
master-service, controller-service, slave-serviceの3つのサービスに分割し、それに加えRedis, cloudflaredサービスの系5サービスを1つのクラスタにデプロイしています。
各サービス間はすべてGoのgRPCで接続、通信をしています。
例外としてmaster-serviceは外部(Cloudflare Tunnel)と通信するため、HTTP, WebSocketでも通信できるようにしています。
マスターは外部・内部の通信の中継地点サービスです。 Goは単一のプロセスで複数の通信プロトコルを取り扱うことができたため、マスターだけは唯一HTTP, WebSocket, gRPCの3つの通信プロトコルを使用しています。
コントローラーは本アプリで遊ばれてしまう対象のPOD(slave-service)の状態・存在を管理するサービスです。
マスター・コントローラー間では直接通信を行っておらず、中間に存在するRedisを絶対情報として扱い、Redisの状態が正しいことになるため、マスターがPodを死亡としてマークすればコントローラーはそれに従ってプロセスを殺します。
このため、理論上はマスター・コントローラーのレプリケーション数を増やし、ロードバランサを適切に設定すれば理論上はRedisの情報が壊れることはないはずです()
遊ばれる対象です。プロセスの生存=Podの命であるため、コントローラーの命令でプロセスが停止されてしまった場合、Podは新たな生命に生まれ変わることを強制されます。
スレーブ自身は状態を一切持たず、コントローラーの停止命令くらいしか処理できません。
これこそまさにオーケストレーションですね()
実質的に1人プレイになるため、認証とアクセス制限をCloudflare Accessに統合しています。
Cloudflare Access経由でアクセスする専用のアプリケーショントークンが生成され、クライアント側に渡されます。 そのトークンをバックエンドでCloudflare AccessのTeamで用意されている公開鍵でJWTの検証をするこ とで実質的にバックエンドの認証をしています。
外部公開に関して、今回はクラスタにはいわゆるIngressやLBを配置しておらず、外部からの通信はできないようにしています。
そのため、Cloudflare Tunnelを使用して外部公開をしています。 Cloudflare Accessを使う都合上すごく相性が良かったので非常にLove♡
また、TunnelくんはHTTPだけじゃなくてWebSocketまで使えちゃう良い子なのでこれからも愛用していきます♡
まずはデプロイ状態から。 設計していたCI/CDパイプラインは無事に完成・動作しており、すべてのフローが問題なく動作しています。

転がってるGopherくんみたいなやつ。潰されるらしい。
WebXRのハンドトラッキングを使用して操作・インタラクションができます。


WebXRシーン上でGopherくんPODを潰すと実際にKubernetesにいる紐づいているPODで動作しているサービスに強制停止命令がgRPC経由で送信され、プロセスが死んでしまいます。そして、Kubernetesであるためプロセスの異常停止につきPODを再起動するため、また再度PODが生まれ、コントローラーに自己申告することでまた新たに登録されるようになっています。

Podを潰しすぎて大量のサービスが死んでしまいました...これは大変です... これを復旧するには人の手で作業をしないといけないかも...?

さすがKubernetes...ッ!俺たちにできないことを平然とやってのけるッ! そこに痺れる憧れるゥ!!
はい、Kubernetesはマニフェストで決められた運命(さだめ)に従うのでしばらくするときれいに元通りにしてくれます。 Kubernetesの神秘の力ですね。スタンドパワー!!
これはいつも通りなのですが、今回もアーキテクチャは自分で設計、実装はエージェントのフローを実施しました。
draw.ioカキカキ楽しい。構成図・Topa`zは一切AI使わずにやりました。手書き最高。
ただ、それとは別に新しい手法も取り入れました。 僕自身VRChatというデスクトップ・VRなど複数のデバイスで遊ぶ事ができる、メタバースのようなプラットフォームをよく使うのですが、今回そこを最終の設計仕上げに使ってみました。
ワールドのギミックとして空間に自由に描くことができるペンがよく実装されているので、それを使って無限のホワイトボードとしてたくさん描くことができます。

自由におしゃべりができるGemini Liveを片手に、色々自分の考えを確かめつつ、仮想のコワーキングスペースで設計を空間に描く。かわいい、かっこいいアバター、おしゃれだったりチルなワールドなど仮想空間特有の魅力があり、すごく楽しく作業できました。VR環境を持ってる人はぜひお試しください。
最近使い始めたObsidianも使って設計しました。 初期段階の構想レベルのものは基本ここになぐり書きしています。
ただ、Obsidianの魅力であるツリーのようなものではなく、単純にデバイスを問わず使用できるMarkdownメモとして使っています。
多分、プロダクトの設計というのは文字ベースで描くよりも、draw.ioやfigjamのような付箋を貼り付けることができるツールを使用することがメジャーだし推奨されていると思います。紙に描くのも良いらしいですね。
しかし、僕はそういったコトをするよりとりあえず思いついたことをキーボードでガリガリ書く方が早いし合っていると思ったのでこの手法を取っています。それこそObsidianやGoogle Documentのようにデバイスを問わずにいつでもメモが書けるツールがあるとすごく快適に使えて良いです。
自宅鯖にCouchDBをデプロイし、Cloudflare Zero Trust経由でVPN接続しているので、デバイス・場所を問わずにリアルタイム同期できます。
ちょうどメガロカップの数日前にリリースされたVite v8, Vite+を採用しています。
特にVite+は環境構築をかなり簡略化してくれる上、Vite v8採用のためビルドが非常に高速です。かつvp checkやその他ツールなどリンターを含め様々な開発体験を向上サせてくれるツールが含まれており、Vite+一つで開発を始められたためすごく良かったです。
OrbStackでk8sクラスタ作成、SkaffoldでGKEと同じようにテストしています。
また、クラスタのモニタリング用にk9sというCLIで利用できるツールを使っています。
Docker Desktopを使うのがメジャーですが、おま環なのか自分のMacbookでまともに動かなくなったのでOrbStackに完全移行しました。めちゃくちゃ快適。
docker composeなど既存のDocker Engine向けコマンドは大体使うことができるので移行も特に大変なこともありませんでした。
Skaffold は、継続的な開発、継続的インテグレーション(CI)、継続的デリバリー(CD)をオーケストレートすることで、デベロッパーの生産性を向上させるコマンドライン ツールです。
> Google Cloudのドキュメントから引用
Skaffoldを使うことでフロントエンド・バックエンド・その他サービスなど、本番環境へデプロイする状態とほぼ同じ環境でテストを行うことが可能で、skaffold devで起動している状態ではファイルの変更を検出し、自動で再ビルドを行いPODのイメージを更新してくれるなど、開発効率がかなり向上しました。
今回インフラにCloud Deploy, Kubernetes Engineが含まれており、Cloud DeployにはSkaffoldが必要だったため、初めて使用しました。
[#VRも使って設計] に近い話になりますが、ハッカソン当日の3日間、ImmersedというVRヘッドセット上で複数の仮想モニターを展開可能なアプリを使用し、3画面で常に作業を進めていました。
これ、ガチでいいです。
今回一人チーム(?)だったこともあり、スペースに限りはあったものの、広い作業スペースを確保して効率よく開発できて楽しかったです。^^
Chrome, ターミナル複数窓, Discord, Youtube, Draw.ioみたいな大量ウィンドウを常に開いた状態にできて神。なお、16GBのMacbook Proには荷が重かったようでSWAPが限界突破しておりました。ごめんねMacbook。
WebXRをデバッグするにはChromeの拡張を使う、エミュレータを使用するなどの方法はありますが、今回Questで直接デバッグする手法を採用しています。
しかし、Questから開発サーバーにアクセスするのには少し手間がかかってしまいます。
これを解決するために、先述したSkaffoldを使うことで、本番環境と同じようにCloudflare Tunnelで外部に出してしまうことで開発機からCloudflare、CloudflareからQuestのようにインターネットさえ繋がっていれば使えるようにしました。
テザリングでネットワーク構築したり、ハッカソン会場のネットワーク経由でローカルIPから接続するなど色々な手段はあるとおもいますが、本番環境と差分を減らしたかったため開発環境でも初めてTunnelを使っています。
以前参加したカンファレンスで知ったAIエージェントのスペック駆動開発を支援するツールを導入しました。
AGENTS.mdで明示的に使用を固定化し、基本的にすべての作業をopenspec changesやopenspec validateなどを使って自律的に要件を生成、バリデーションをし精査をしてから実装を行わせています。
時間がかかったりコンテキストを比較的圧迫してしまうなどの問題はありますが、それよりも1発でかなり思った動作になることが多く、相対的にコスト削減になっていると思います。
他にもスペックを作成して実装を行うエージェント・ツールは存在しますが、環境依存が少なく、簡単にかつ軽く使えるOpenSpecは非常に重宝しています。
Codexをメインに使っていましたが、High, Extra HIghと合わせて使うとかなりいい感じにしてくれて楽しかったです。
スレーブサービスも含めすべてのサービスがGoで動いているのですが、スレーブの再起動が早すぎて全然Podが死んでる感がありました。マシンの性能次第というのもあるとは思いますが、基本的にめちゃくちゃ早いと判断するのが良さそうです。
画像は開発中のOrbStackのActivity Monitorですが、Goで動いてるサービス達のメモリ消費量が凄まじく少ないことがわかります。gRPCしか使ってないサービスはまだわかりますが、HTTPまでやってるMasterでさえもこのメモリ消費量なのが衝撃でした。早いのも納得ですね。
今回の実装ではあまり機能量が多くなく、Goの軽さを活かせてはいませんが、もっと大規模になるとそれなりに効果が出そうで見てみたいなと思います。
一部インフラ以外はすべてCodex CLIで開発をしていますが、Codexくんあまりにもセンスがありません。Gemini Proの方が圧倒的にマシです。
Skillsの導入も考えましたが、Twitter(現X)で見た感じあんまり効果がなさそうだったので導入せず進めました。プロンプトで気合でゴリ押しています。
最初に構築したパイプラインでは4つのパイプラインが1つのGKEに向き、かつリリースの作成など一部の役割がCloud Build, Cloud Deployで重複してしまいうまく動きませんでした。
Cloud Buildはskaffold.yamlだけでデプロイができる、というのに変にCloud Buildに役割を持たせてしまったのが原因だと考えられます。
エージェントを使ってインフラを実装させているとこういった問題も発生するため、やはりドキュメントは人の手で一度読み込む、もしくはNotebookLMなどに一通りぶち込んで1つの資料として読み取れるようにするべきですね。
最初は1つのWebSocket接続につき1つのセッション、つまり1ゲームとして扱っており、WiFiが一瞬切断されることでWebSocketが切断され、セッションが終了してしまうためRedisの状態も狂い、スレーブPODが再接続できないみたいな問題が発生していました。
対応策としてはユーザーが意図的に切断した場合とそうでない場合でWebSocketの切断の方法を区別し、もし不意に切断された場合はマスターサービス側で少しだけ再接続を待機するようにしました。
まだまだWebSocketやリアルタイム通信には精通できていないので、今後も勉強していきたいです。
