GCPにコンテナで作ったAPIを公開する。認証も付ける。(Cloud Run + API Gateway + Firebase Auth)
はじめに
GCPにコンテナで作ったAPIをDeployして、API GatewayとFirebaseで認証つけてみたのでメモ。
GCP初心者なので、多少のミスは許してもらえると助かります。
構成図
こんな感じの構成。
作り方
API作ってコンテナ化
Dockerfileを書いておきました(なくてもDeployできたかもしれないけど、一応)
FROM node:14.15.3-buster-slim
WORKDIR /app
COPY . .
EXPOSE 3000
RUN npm install
ENTRYPOINT ["npm", "start"]
Cloud Runにデプロイ
- GCPのコンソールからCloud Runのページにいって、新規サービスを作成。
- 「ソースリポジトリから新しいリビジョンを継続的にデプロイする」を選ぶ
- 「SET UP WITH CLOUD BUILD」を選ぶ
- GitHubとの連携の設定をする
- 連携対象のレポジトリを選ぶ
- ブランチを選んでDockerfileを指定
- Ingressは「全てのトラフィック」、認証は「認証が必要」にする
- 認証については最初は未認証も許可しておいて動作確認したほうがよいかもです。でないとAPI Gatewayの設定をするまで403になってしまい動作確認出来ないので。
- 認証については最初は未認証も許可しておいて動作確認したほうがよいかもです。でないとAPI Gatewayの設定をするまで403になってしまい動作確認出来ないので。
- 他を適当に設定して、「作成」を押せばコンテナをデプロイ出来るはず
Firebaseの設定
- Firebaseにログインしてプロジェクトを作る。このとき、Cloud Runを作ったGCPのプロジェクトと紐付けておく
- Authenticationに入って、適当にパスワード認証のユーザーを作っておく(テスト用)
API Gatewayを作る
-
GCPのコンソールからAPI Gatewayのページにいって、新規ゲートウェイを作成
-
入力項目は「API仕様のアップロード」以外は適当にいれればOK
- サービスアカウントはCloud Runと同じにした。本当はAPI Gateway用に作ったほうが良いのかも。
- サービスアカウントはCloud Runと同じにした。本当はAPI Gateway用に作ったほうが良いのかも。
-
API仕様のアップロード用のSwagger仕様を書くのが厄介
- 仕様がSwagger2.0で書かないと行けない。OpenAPI3の仕様で書くと弾かれる
- Cloud RunへのURLを決められた書式で埋め込む必要あり
- Firebase認証の設定をいれる
-
とりあえず下記のように書きました
swagger: '2.0'
info:
title: sample-api
description: Sample API on API Gateway with a Google Cloud Functions backend
version: 1.0.0
# ここにCloud RunのURLを入れる
x-google-backend:
address: https://xxxxxxx.a.run.app
# firebase認証の設定
securityDefinitions:
firebase:
authorizationUrl: "" # ここは空のままでOK
flow: "implicit"
type: "oauth2"
x-google-issuer: "https://securetoken.google.com/(firebaseのプロジェクトid)" # ここ書き換える
x-google-jwks_uri: "https://www.googleapis.com/service_accounts/v1/metadata/x509/securetoken@system.gserviceaccount.com"
x-google-audiences: "(firebaseのプロジェクトid)" # ここ書き換える
security:
- firebase: []
paths:
/:
get:
summary: hello
operationId: hello
responses:
'200':
description: A successful response
あとは、「ゲートウェイを作成」を押して、しばらくすればAPI Gatewayが立ち上がります。
動作確認
-
Cloud Runに直接アクセスしてみる
- 認証してないので403エラー(ネットワーク的には繋がるがIAMで弾いているので401や404でなく403になる)
- 認証してないので403エラー(ネットワーク的には繋がるがIAMで弾いているので401や404でなく403になる)
-
API Gatewayに直接アクセスしてみる
- トークンが無いので401エラー
- トークンが無いので401エラー
-
API GatewayにFirebaseで認証して取得したidトークンをつけてアクセスしてみる
- 認証されているので、200 OK
※ やたら時間がかかっているのは、お金をケチってコールドスタンバイだからです。
- 認証されているので、200 OK
ということで、GCPにコンテナで作ったAPIを公開して認証を付けることが出来ました。
余談
Cloud RunのIngressと認証の設定について
Cloud RunのIngressの設定で「全てのトラフィック」を許可しました。
「API Gatewayからの通信だけを許可すればいいので、制限するべきでは?」と思う方がいるかも知れません。というか、私はそう思いました。
しかし、実際には通信を制限すると、API Gatewayからの通信も制限されて繋がらなくなってしまいます。
これについてはこちらのstackoverflowの回答が参考になりました。
- https://stackoverflow.com/questions/67951166/gcp-access-from-api-gateway-to-google-cloud-run-backend
つまり、少なくとも現時点では、Cloud Runへの通信をAPI Gatewayから制限することはネットワーク設定では無理なので、IAMで制限しましょうってことみたいです。
この手のマネージドサービスは、公開が簡単な代償にネットワークの制限をかけるのが大変な印象があります。AWSのAppRunnerもAzureのWebAppsもネットワーク周りの設定辛いんですよね。GCPお前もかって感じです。
「Firebaseで認証してidトークン取ってくる」ってどうすればいいの?という話
idトークンをつければAPIに接続できるよ書きましたが、つまりidトークン取ってこないといけないです。取ってくるコードを雑に書いたので貼っておきます。
-
npm install firebase
して使ってください -
firebaseConfig
はFIrebaseのコンソールで適当にアプリ作ると表示されるサンプルコードからコピペしてください - ユーザーとパスワードは、Firebase Authentication上に作ったユーザーのやつを設定してください
import { initializeApp } from "firebase/app";
import { getAuth, signInWithEmailAndPassword, getIdToken } from "firebase/auth";
// ここをFirebaseのコンソールから取れるソースコードの内容で上書きする
const firebaseConfig = {
apiKey: "",
authDomain: "",
projectId: "",
storageBucket: "",
messagingSenderId: "",
appId: "",
measurementId: ""
};
// firebaseに登録しているユーザーのメールアドレスとパスワード
const email = '';
const password = '';
// Initialize Firebase
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
async function run() {
// メールアドレスとパスワードを使って認証
const credential = await signInWithEmailAndPassword(auth, email, password);
// IDトークン(JWT)取得
const token = await getIdToken(credential.user, true);
console.log(token);
}
run();
何故か時々500エラーになる
- Cloud Runのインスタンス数の最小を0にすると、Api Gateway経由で呼び出す時に立ち上げを待てずに500エラーになることがあるみたいです
- Cloud Run側は起動に時間がかかるものの正常に動いていて10秒弱で200 OKを返しているのですが、Api Gateway側が8秒ぐらいでエラーと判断してしまうみたいです。タイムアウト時間を変更しても変わりませんでした
- ググってみたけど詳細はよく分かりませんでした
- Cloud Runの最小インスタンス数を1にすると500エラーがでなくなったので、とりあえず1にしています
- 料金が1日20円くらい増えました
使った記憶がないのにStorageがやたら使われている
別記事に書きました
NCDC株式会社( ncdc.co.jp/ )のエンジニアチームです。 募集中のエンジニアのポジションや、採用している技術スタックの紹介などはこちら( github.com/ncdcdev/recruitment )をご覧ください! ※エンジニア以外も記事を投稿することがあります
Discussion