Google Cloud Run + Google Cloud FireStore + Firebase AuthenticationとGoで作る認証付きデータ保存、取得API
Firebase + Google Cloud Functions を使った認証 API の作成を以前紹介しました。
Google Cloud Functions + Google Cloud FireStore + Firebase Authentication と Go で作る認証付きデータ保存、取得 API
これを作成中、docker コンテナそのまま deploy できたら楽なのにという気持ちがありました。とはいえ、個人の遊びで kubernetes はお金の面で厳しく、AWS の Fargate という選択肢はあるものの AWS は個人利用の範囲で使った限り、GCP より高めに請求がくることもしばしば。
そんな時、GCP にも Cloud Run という docker コンテンを deploy して動かせるサービスが始まっているのを知りました。現在 beta 版なのでサービス開始後に料金がどうなるかは不明ですが、とてもおもしろそうに見えます。前回 Cloud Functions で動かしたものをそのまま Gloud Run で動かしてみました。
前提
GCP や Firebase の 登録。Firebase の Authentication の設定、それにアクセスするフロント側、あるいは API テスト用のアプリケーションなどは作成済みとします。
Google Cloud Functions + Google Cloud FireStore + Firebase Authentication と Go で作る認証付きデータ保存、取得 API
上記の記事で自分の環境は紹介しています。
実際のコード
Cloud Run のサンプルコードを参考にして、前回の Functions で使用したコードをほぼそのまま使用しています。Firebase の設定情報と GCP のプロジェクト ID をコード中に入れます。前回は環境変数からとっていましたが今回はコード中でそのまま使用しています。
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"strconv"
"strings"
"cloud.google.com/go/firestore"
firebase "firebase.google.com/go"
)
func handler(w http.ResponseWriter, r *http.Request) {
log.Print("Hello world received a request")
target := os.Getenv("TARGET")
if target == "" {
target = "World"
}
fmt.Fprintf(w, "Hello %s!\n", target)
}
func auth(w http.ResponseWriter, r *http.Request) bool {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept,Authorization")
idToken := r.Header.Get("Authorization")
splitToken := strings.Split(idToken, "Bearer")
if len(splitToken) != 2 {
return false
}
idToken = strings.TrimSpace(splitToken[1])
ctx := context.Background()
// firebase設定
conf := &firebase.Config{
ServiceAccountID: "",
ProjectID: "",
}
app, err := firebase.NewApp(context.Background(), conf)
if err != nil {
return false
}
authClient, err := app.Auth(context.Background())
if err != nil {
return false
}
_, err = authClient.VerifyIDToken(ctx, idToken)
if err != nil {
return false
}
return true
}
type User struct {
First string `firestore: "first"`
Last string `firestore: "last"`
Born int `firestore:"born"`
}
func AddUser(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
authCheck := auth(w, r)
if authCheck != true {
res := map[string]interface{}{
"status": "ng",
"message": "認証エラー",
}
json.NewEncoder(w).Encode(res)
return
}
first := r.FormValue("first")
last := r.FormValue("last")
born, err := strconv.Atoi(r.FormValue("born"))
if err != nil || first == "" || last == "" {
res := map[string]interface{}{
"status": "ng",
"message": err,
}
json.NewEncoder(w).Encode(res)
return
}
user := User{
First: first,
Last: last,
Born: born,
}
// GCPのfirestoreをおいているプロジェクトid
projectID := ""
ctx := context.Background()
client, err := firestore.NewClient(ctx, projectID)
if err != nil {
res := map[string]interface{}{
"status": "ng",
"message": "書き込みに失敗しました",
}
json.NewEncoder(w).Encode(res)
return
}
defer client.Close()
_, _, err = client.Collection("users").Add(ctx, user)
if err != nil {
fmt.Fprint(w, err)
return
}
res := map[string]interface{}{
"status": "ok",
}
json.NewEncoder(w).Encode(res)
return
}
func main() {
log.Print("Hello world sample started")
http.HandleFunc("/", handler)
http.HandleFunc("/addUser", AddUser)
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
}
dockerfile はサンプルコードに get 1つ足したものです。multistage build は古い docker だと動きません、エラーがでる場合は docker をアップデートしてください。
FROM golang:1.12 as builder
WORKDIR /go/src/github.com/knative/docs/helloworld
COPY . .
RUN go get firebase.google.com/go
RUN CGO_ENABLED=0 GOOS=linux go build -v -o helloworld
FROM alpine
RUN apk add --no-cache ca-certificates
COPY --from=builder /go/src/github.com/knative/docs/helloworld/helloworld /helloworld
CMD [ "/helloworld" ]
GCP 側の設定
課金を有効にし、サンプルコード通りなら cloud Run だけでなく Cloud build も有効にします。ローカルに gcloud をインストールしていなけれがインストールし、ターミナルから gloud に login 及びプロジェクト設定を行います。
Cloud build に docker イメージをアップロードして Cloud Run に deploy
ローカルで作成した go の実行ファイル入りの docker イメージを cloud build に登録します。登録したら、そのイメージを Cloud Run に deploy します。
gcloud builds submit --tag gcr.io/projectID/helloworld
gcloud beta run deploy --image gcr.io/projectID/helloworld --platform managed
dockerhub からでもいけそうですね。location は northasia、名前は指定せずに Enter。最初失敗しましたが、2 回目に同じコマンドを打って成功しました。
deploy に成功すると URL が表示されるので
Google Cloud Functions + Google Cloud FireStore + Firebase Authentication と Go で作る認証付きデータ保存、取得 API
ここで作成したフロントの API URL をそれに変更して動作チェックしました。
便利
Cloud Functions のコードほぼそのままコピーするだけで動作しました。Cloud Functions よりもローカル開発がしやすく、docker コンテナなので何でも動かせるのが素晴らしいですね。次はフロント入の PHP のアプリケーションを動かしてみるつもりです。