はげったーを支える技術(インフラ編)
Mastodon(主にhandon.club)向けのTogetterライクなサービスとして、hagetter(はげったー)*1というサービスを開発・運用しています。個人開発ということもあり色々と新しい技術要素を取り入れながら試行錯誤しながら開発しているので、その中身について少しずつ紹介していこうと思います。
今回はインフラ編です。神の手によって創造されたソースコードがどのように実環境に反映され、1日数十もあるアクセスを捌いているのかについて紹介します。
過去の記事はこちら
はげったーのデリバリ・パイプラインについて
はげったーではGithubにソースコードをpushすると自動的にビルドされて、GCP(Google Cloud Platform)の本番環境に反映される仕組みになっています。 下はその流れを示したありきたりな図です。
以降は各パートに分けて説明します。
Cloud Build
GCPの提供するCIサービスです。ここでソースコードのビルドやDockerコンテナの作成を行い実環境にデプロイしています。
トリガはGCPのCloud Source RepositoriesだけでなくGithubやBitBucketにも対応しており、HagetterではGithubのmaster/developブランチへのpushをトリガにビルドを開始しています。
(ちなみに当時はまだGithub Actionsが無かったのでCloud Buildを利用していましたが、今ならGithub Actionsでほぼ似たようなことが出来ます)
Cloud Buildの特徴はDockerコンテナを使ったパイプライン処理です。ソースコードディレクトリを様々なDockerコンテナに順番マウントしながら処理を進めていきます。これによって、複雑なビルド環境を構築したり、ビルド前に必要となるツールを使うためだけにビルド環境を汚すといった心配事から解放されます。
よく使うコンテナについてはGCPから提供されています。 https://console.cloud.google.com/gcr/images/cloud-builders/GLOBAL
Hagetterでは以下のようなパイプライン定義となっています(パイプライン定義はソースリポジトリ内に含める必要があります)。 ちなみにブランチ毎に環境を作れるのでdevelopブランチにpushすると試験環境へ、masterブランチにpushすると本番環境に反映される仕組みになっています。この辺はサーバーレスホスティングサービスのメリットですね。
steps: # create .env file - name: 'gcr.io/cloud-builders/docker' entrypoint: bash args: ['-c', 'echo ${_B64_ENV} | base64 -d > .env'] # build the container image - name: 'gcr.io/cloud-builders/docker' args: ['build', '-t', 'asia.gcr.io/$PROJECT_ID/hagetter-$BRANCH_NAME:$SHORT_SHA', '.'] # push the container image to Container Registry - name: 'gcr.io/cloud-builders/docker' args: ['push', 'asia.gcr.io/$PROJECT_ID/hagetter-$BRANCH_NAME:$SHORT_SHA'] # Deploy container image to Cloud Run - name: 'gcr.io/cloud-builders/gcloud' args: ['beta', 'run', 'deploy', 'hagetter-$BRANCH_NAME', '--image', 'asia.gcr.io/$PROJECT_ID/hagetter-$BRANCH_NAME:$SHORT_SHA', '--region', 'asia-northeast1', '--platform', 'managed', '--allow-unauthenticated', '--quiet'] images: - asia.gcr.io/$PROJECT_ID/hagetter-$BRANCH_NAME:$SHORT_SHA
- (docker環境) Hagetterで必要となる環境変数等の準備
- (docker環境) Dockerコンテナのビルド(ビルドファイル)
- (docker環境) コンテナリポジトリにpush
- (gloud環境) Cloud Runにデプロイ
本当は1と2の間にテストフェーズを入れたいのですがまたいずれ・・
Cloud Run
Cloud RunはGoogleが2019年に始めた最強のコンテナホスティングサービスです。 オンドマンドかつオートスケールでコンテナを立ち上げてくれるので、低コストでスケールするサービスを作る事が出来ます。 HTTPSとgRPCに対応しているのでWebサイトやAPIサーバーの運用にも使えます。
レスポンスの統計の確認やコンテナへのトラフィックの段階的な振り分けなんかも出来るのでまあまあ便利です。コンソール上に出力されたログも確認出来るのでエラー時にも調査しやすいです。
Cloud Runの最大のメリットは好きなコンテナを使う事が出来ることとコストでしょう。
HagetterではNext.jsを利用していますが、Next.jsではフロントエンドとサーバーサイドの両方の処理をまとめて書けるのでNetlifyやAWS S3のような静的な環境の場合少し工夫が要ります。
Cloud RunならDockerコンテナでnext startするだけです。サーバーサイドレンダリング(SSR)についても特に苦労せず実現可能です。
Cloud Runで実行するためのDockerfileはこれだけです。
FROM node # Setting working directory. All the path will be relative to WORKDIR WORKDIR /usr/src/app # Installing dependencies COPY package.json ./ RUN npm install # Copying source files COPY . . # Building app RUN npm run build # Running the app CMD npx next start --port $PORT
もう一点のコストですが、アクセス数(応答時間)に応じた従量課金なので非常に低コストの運用が可能です。似たようなコンテナホスティングサービスだとAWS Fargateなんかもありますけど、こちらは0からスケールするようなサービスを作る事は出来ないのでCloud Run以外では同種のサービスは存在しないのではないでしょうか。(一部のランタイムであればHerokuやGAEでも似たような事は出来る)
Hagetterのようにアクセス数のあまり多くないサイトをサーバーで運営すると、サーバー費が割と勿体無かったり、かといって安いサーバーにするとパフォーマンスの問題があったりと悩ましいところですが、Cloud Runなら自動スケールするので低コスト・高性能の両面を実現可能です。まあ課金は青天井になりますが(制限はかけれる)。 今のところ無料枠内(50時間/月)に余裕で収まっています。
ちなみにみきbotも実はCloud Run上で動いていたりします。はんどんからの通知をトリガにOpenCVで画像処理を行ってMastodonAPIで処理結果を投稿しています。
Cloud Runの制約としては、コンテナはステートレスかつユーザーへのレスポンス後に処理を出来ないというものがあります。例えばユーザーにレスポンスを返した後に裏でジョブを回す、みたいな処理は出来ません。その場合はPubSub等にトピックを投げてPubSubから再度Cloud Runを呼び出す必要があります。
また、memcacheとかの機構も無いので、APIの結果をキャッシュするみたいな事が難しく性能面で若干の課題があったりします。
WebSocketのようなものも利用出来ません(ユーザー管理のGKE環境なら可能らしい)。
まあそれぞれワークアラウントもあるので個人で使う分には全く問題にならないレベルです。
Cloud Runあんまり話題にならないけど素晴しいサービスなので是非みんな使いましょう!
追記:
パフォーマンス面の改善なら以下が参考になります。
開発のヒント
まとめ
今回はHagetterのインフラ部分について紹介しました。
次回ぐらいからはやっと内部の実装について紹介していきたいと思います。
🌼🌼🌼 おまけ 🌼🌼🌼
*1:handon.club版togetterなのでhagetter