Cloud RunでgRPC streamingができるようになったので動かしてみたりした
あけましておめでとうございました。 年明けてから更新さぼってましたごめんなさい。
最近Cloud Runが変態じみたアップデートを繰り返しているので、遊んでみたメモです。
今回作ったもののソースはこちら。
TypeScriptに書き換える作業はそのうちやります。 突貫工事なので許してください。
Cloud RunにデプロイするのにDockerfile書かなくて良くなってた
たぶん最近のアップデートと思われますが、Deploy from source codeってのができるようになってました。
ソースコード一式をgcloud run deploy
すると、Cloud Buildの上でBuildpacksを使って勝手にDockerコンテナ化してくれる機能らしいです。
Google Cloud Buildpacksのもうちょい詳しい説明はここにあります。 Goのサンプルは公式にも転がっているので、今回はNode.jsでやってみました。
Node.jsでは、npm ci
をしたあとにnpm run start
を撃ってくれるようです。
今のところこのプロセスではnpm run build
などのカスタムビルド処理はしてくれないので、
TypeScriptでトランスパイルが必要とかバンドラでビルドが必要とかそういう場合はgcloud run deploy
の前にやっておく必要があるみたいです。
したがって、自動デプロイを設定する際は適宜.gcloudignoreを用いてビルド生成物だけをCloud Runに乗せるようにする必要があります。
これをやるのとDockerfileを書くのとどっちが手間かでいうと結構悩ましいところです。
普通に不便なので、npm run build
くらいはやってほしいと思いました。
一方で、Goで書かれたアプリケーションとかはマジで一瞬でうごくのですごく便利です。 Rustとかにも対応してくれたら個人的には嬉しいです。
gRPC streamingで遊んでみた
さて、こちらが今回の本題になります。 最近のアップデートで、Cloud RunでgRPCのbidirectional streamingがサポートされたらしいので動かしてみました。
詳細は先頭に貼ったコードを見てもらうことにして、結論だけ書くと本当に動きました。
注意すべきポイントとしては、Cloud Runのデプロイ時にHTTP/2を有効にするのを忘れないことかなと思います。 GCloud SDKによるデプロイコマンドは以下のようになります。
gcloud beta run deploy grpc-server --source grpc-server --platform managed --region asia-northeast1 --use-http2 --allow-unauthenticated
--use-http2
がHTTP/2を有効にするためのフラグです。
Cloud RunにホストしたgRPCサービスに接続するNode.jsのコードは以下のようになります。
const grpc = require('grpc')
const protoLoader = require('@grpc/proto-loader')
const PROTO_PATH = __dirname + '/../grpc-server/sample.proto'
const packageDefinition = protoLoader.loadSync(
PROTO_PATH,
{
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
}
)
const proto = grpc.loadPackageDefinition(packageDefinition)
// ここでCloud RunのURLを指定(スキーマ/ポートは不要)
const client = new proto.sample.Sample('sample-service.a.run.app', grpc.credentials.createSsl())
// streamじゃないほう
client.get({ id: 1, name: '甘雨' }, (error, response) => {
if (!error) {
console.log(response.message)
} else {
console.error(error)
}
})
// stream
const channel = client.stream({ id: 1, name: '甘雨' })
channel.on('data', (response) => {
console.log(response)
})
channel.on('end', () => {
console.log('end')
})
clientを作るときに、Cloud Runのサービスを指すURLのホストを渡しながらgrpc.credentials.createSsl()を指定すればOKです。 サーバ側はCloud Runの通常のお作法通り、0.0.0.0:<PORT>でリクエストを受けるようにします。 ポート番号は環境変数PORTからとれる値を使えばOK。
まとめ
ソースコードからCloud Runへのデプロイは非常に便利ですが、残念ながらNode.jsのサポートはまだまだと言わざるを得ませんね。 カスタムビルドコマンドがなにか動かせるようになったら随分話は変わりそうですが、現時点ではGo以外はおとなしくDockerfileを書くほうが良さそうな気がします。 アップデートに期待しながらDockerfileを捨てるのもありですが、そのへんはローカル環境での実行との兼ね合いですかね。
gRPCは*.protoファイルをクライアントとサーバ間でどう共有するのがいいのかよくわかりませんが、 Cloud Runのようなサーバレス環境で、しかもめんどくさい設定をほとんど必要とせずにstreamingができるようになったのは結構大きな進歩だと思います。 一般的なアプリケーションだとまだまだFirebaseでいいじゃんってなる場面が多そうですが、 Firestoreのリアルタイムアップデートでは用が足りなくなったときの手段としてはかなり有効そうです。
そのうちGraphQLとかも試してみようと思います。