Firebase Hosting と Cloud Run で GitHub の芝生を植える
serverless GCP · Firebase · Cloud Run
はじめに
Firebase Hosting や Netlify 等、静的サイトのホスティングサービスは多数あります。 この2つは FaaS (Function as a Service) 機能により、動的なコンテンツの配信にも対応しています。
それぞれ、次の機能が利用できます。
Service | FaaS | CaaS |
---|---|---|
Firebase Hosting | Firebase Functions (Cloud Functions) | Cloud Run |
Netlify | Netlify Functions (AWS Lambda) |
今回は、このサイトが Firebase Hosting で稼働していること、また Cloud Run という Container as a Service が利用できることから、Firebase Hosting を例にして紹介します。
対象読者
- 静的サイトのホスティングサービスで、一部動的なコンテンツを扱いたい
- 静的サイトのホスティングサービスに興味がある
- 自分のサイトにも GitHub の芝生を植えたい
TL;DR
- GitHub から芝生の箇所 (svg) だけをスクレイピングします。
- Same Origin Policy の問題回避とデータ整形のために BFF として Cloud Run を導入します。
- Firebase Hosting と Cloud Run を統合することで、動的コンテンツを CDN でキャッシュします。
GitHub の芝生とは
GitHub Contributions Graph といいます。 GitHub のプロフィールページにある、緑色の芝生です。
GitHub の芝生を自分のサイトにも植えたいという物好きな人は中々いないと思うので、一般の方は Firebase Hosting と Cloud Run を統合して動的なコンテンツを生成する際の参考情報としてご活用ください。
ちなみに、今回植える芝生はこちらです。Cloud Run で GitHub から取得してきている SVG 画像なので、毎日内容が更新されます。
Cloud Run が必要な理由
Same-Origin Policyによりフロントエンドで GitHub から直接取得することはできません。 そこで BFF (Backends For Frontends) が必要になります。
BFF とはドメインロジックを担うバックエンドとは別に、フロントエンドのために API の集約、データ整形、プロトコル変換、SSR などの役割を担う、バックエンドとフロントエンドの橋渡しのような存在です。
今回は、BFF のために Cloud Run を採用し、CDN 対応のための Cache-Control ヘッダー付与と、データ整形を行う API を稼働させます。 Cloud Functions ではなく Cloud Run を選ぶ利点としては、Docker Image としての可搬性、再利用性が大きく、ローカル環境でも容易に動作確認ができる事です。さらにベンダーロックインも、ランタイムの制限もないため、好きな言語を採用できます。
今回は、Cloud Functions や Netlify Functions でも使える Go 言語で実装します。
全体図
図の通り、Client は Firebase Hosting 経由で Cloud Run を実行します。これにより CDN によるキャッシュが利用できるので、すでにキャッシュに存在する場合は Cloud Run を起動せず、Firebase Hosting から直接返却されます。
Cloud Run で稼働させるアプリケーションから、GitHub へ HTTP Request を送信します。
Cloud Run を利用するには Docker image を事前に Docker Hub や Cloud Container Registry に登録する必要があるので、今回は Cloud Container Registry を利用しています。
GitHub の芝生を取得するプログラム
コードを見ていただいた方が早いと思うのでリポジトリへのリンクを掲載します。誰も使わないと思いますが、MIT ライセンスの下で利用できます。
やっていることはシンプルです。
- GitHub から GitHub Contributions Graph の SVG 画像を取得
- inline SVG 画像を含む HTML を取得
- SVG のみ抽出
- SVG の修正と style の追加
- HTTP Server で配信
- Cache-Control header の返却
GitHub から GitHub Contributions Graph の SVG 画像を取得
次の URL から GitHub Contributions Graph を含む HTML が取得できます。
https://github.com/users/${github_username}/contributions
golang.org/x/net/html を用いて svg 要素のみを抽出する parser を実装します。
抽出した svg 要素には style が当たっていない、xmlns
属性が足りない等、単体の SVG 画像として扱うには不都合があるため、修正します。
HTTP Server で配信
Firebase Hosting は CDN によるキャッシュが効くので、Cache-Control header を返却することは、コストやパフォーマンスの面で重要です。今回は1日キャッシュされるように設定しています。
Firebase Hosting のキャッシュの動作については、 こちら に記載されています。
Deploy
Cloud Container Registry への push
API を有効化します。
$ gcloud services enable containerregistry.googleapis.com
Cloud Container Registry へ docker image を push できるように、認証を済ませておきます。
cf. https://cloud.google.com/sdk/gcloud/reference/auth/configure-docker
$ gcloud auth configure-docker
docker image を build & push します。
${PROJECT_ID}
の箇所は実際の GCP Project ID で置き換えてください。
$ docker build -t asia.gcr.io/${PROJECT_ID}/lawn:latest .
$ docker push asia.gcr.io/${PROJECT_ID}/lawn:latest
Cloud Run への deploy
API を有効化します。
$ gcloud services enable run.googleapis.com
gcloud command で deploy する場合
${PROJECT_ID}
の箇所は実際の GCP Project ID に、 ${GITHUB_USERNAME}
の箇所は実際の GitHub ユーザー名で置き換えてください。
$ gcloud run deploy lawn \
--image="asia.gcr.io/${PROJECT_ID}/lawn:latest" \
--region=asia-northeast1 \
--platform=managed \
--max-instances=1 \
--memory=128Mi \
--allow-unauthenticated \
--set-env-vars \
GITHUB_USERNAME=${GITHUB_USERNAME}
terraform で deploy する場合
こちらを参考にしてください。
https://github.com/ww24/lawn/tree/master/terraform
Firebase Hosting の設定
firebase.json
に以下のように rewrite rule を追記し、deploy します。
serviceId
には Cloud Run への deploy 時に指定した service id を指定してください。
{
"hosting": {
"rewrites": [ {
"source": "/lawn.svg",
"run": {
"serviceId": "lawn",
"region": "asia-northeast1"
}
} ]
}
}
次のコマンドで deploy します。
$ firebase deploy
注意点
1. Firebase Hosting と Cloud Run を統合すると Firebase のプランは Blaze になる
Spark と無料枠は変わりませんが、無料枠を超過した分は従量課金になります。
- Firebase Hosting と Cloud Run を統合するには、同一の GCP プロジェクトに存在する必要がある。
- Cloud Run を利用するには、無料枠はあるものの GCP プロジェクトで課金を有効にする必要がある。
- Firebase と紐付いている GCP プロジェクトで課金を有効にすると Firebase のプランは自動的に Blaze にアップグレードされる。
2. Cloud Run 作成時のデフォルトでは 403 が返る
Cloud Functions と異なり、Cloud Run では認証済みリクエストのみ受け付ける設定がデフォルトです。 そのため、Firebase Hosting から呼び出す際は未認証リクエストを許可する設定が必要です。
- gcloud command の場合は
--allow-unauthenticated
オプションを指定します。 - terraform の場合は
google_cloud_run_service_iam_policy
resource でallUsers
に対してroles/run.invoker
を付与するgoogle_iam_policy
data source を紐付けます。
※ Firebase Hosting から呼び出す際のサービスアカウントがあれば、そのサービスアカウントに Cloud Run 起動元ロール (roles/run.invoker
) を付与することで認証済みリクエストに対応できるのですが、現在 (2020/3) ドキュメントに記載はなく、未認証リクエストを許可するしかないようです。
3. Firebase Hosting の rewrite rule で path は rewrite できない
たとえば、次のように /lawn
というリクエストが Cloud Run にルーティングされるように rewrite rule を記述した場合、Cloud Run にはそのまま /lawn
という path でリクエストが飛びます。/lawn
を /
に rewrite することはできないので、source に指定する path が変わる際は、Cloud Run 側で動かすアプリケーションの実装にも修正が必要になる場合があります。
{
"hosting": {
"rewrites": [ {
"source": "/lawn",
"run": {
"serviceId": "lawn",
"region": "asia-northeast1"
}
} ]
}
}
まとめ
Firebase Hosting と Cloud Run を統合することで、動的なコンテンツも配信できるようになりました。CDN によるキャッシュも利用できるため、ページが開かれる度に Cloud Run が起動することもなく、直接 Cloud Run の URL を公開する場合に比べてコストも抑えられます。
今回取得した GitHub の芝生は トップページ下部 Activities に掲載しています。