Cloud RunでPiping Serverを立てる方法と注意点と解決策
やりたいこと
Google Cloud Platform (GCP) の Cloud RunでPiping Serverを立てること。
きっかけ: @peaceiris氏のこちらのツイート。
@peaceiris氏は「書くことが全然ないくらい簡単」にできたようだが、少しハマったのでやり方も書きたい。
もっとシンプルにデプロイする方法があるかもしれない。
やり方
追記: コマンドだけでデプロイする方法: 「Cloud RunにPiping Serverをコマンドだけでデプロイする」
以下の環境変数$PORTでHTTPをリッスンするようなDockerfileを書く。
code:Dockerfile
FROM nwtgck/piping-server
LABEL maintainer="Ryo Ota <nwtgck@nwtgck.org>"
CMD "--http-port=$PORT"
以下のコマンドを叩く。プロジェクトIDは自分のものに置き換える。
code:console
$ gcloud auth configure-docker
$ docker build -t gcr.io/プロジェクトID/piping-server .
$ docker push gcr.io/プロジェクトID/piping-server
あとは、gcr.io/プロジェクトID/piping-serverを"Container image URL"に記述してCreate するだけ。
https://gyazo.com/a168acbd36f80e78094cee19756ecf42
ちゃんと以下のようにPiping Serverが立ち上がっていることが確認できる。
https://gyazo.com/4186bd13f8d5200b85a9581c64cc5704
そして嬉しいことにHTTP/3 (HTTP over QUIC)で動いていることが確認できる。
https://gyazo.com/95d54defada45fd18ac5cbb7895de247
ハマったところ
この公式説明(Quickstart: Build and Deploy  |  Cloud Run Documentation  |  Google Cloud)通りgcloud builds submit --tag gcr.io/プロジェクトID/piping-serverをすると、以下のようにエラーをした。
code:エラーメッセージ
....
f1b5933fe4b5: Waiting
denied: Token exchange failed for project 'プロジェクトID'. Caller does not have permission 'storage.buckets.create'. To configure permissions, follow instructions at: https://cloud.google.com/container-registry/docs/access-control
....
調べてみると「google cloud platform - gcloud docker push results in "denied: Token exchange failed for project 'gcp-project-id-example'." - Stack Overflow」という情報が見つかった。
ここでgcloud auth configure-dockerをした後に、docker pushを使って直接gcr.ioにpushすれば良いということが分かった。
そもそもDocker Hubに上がっているnwtgck/piping-serverを直接使いたかったが、gcr.ioからじゃないとCloud Runは受け付けてくれなかった。「Dockerfileなしで標準入力からdocker build」の方法を使えばファイルを経由せずにdocker buildからdocker pushまでできそう。
Piping Server on Cloud Runの注意点
Piping Serverの最大の特徴であるストリーミングしながらのアップロードとダウンロードができない。
例えば、大きなファイルをアップロードしながら同時にダウンロードもすることができない。これはCloud RunはHTTPでFaaS的なことをすることを想定しているからだと思う。REST APIやGraphQLなどのAPIサーバーのDockerイメージを手軽にデプロイできるのが売りだと思う。これに関しては下記に解決策を載せた。
だがテキストや画像など小さなデータを送る用途では問題になく使える。
ステートレスを想定している。
Piping Serverは外部ストレージを要求はしないが同じパスでステートを持つ設計になっている。一台のコンテナなら問題ないが複数のPiping Serverがあってロードバランシングされるような状況があるならパスベースのロードバランシングが必要になる。このL7でのパスベースのロードバランシングはずっと関心があるところでPiping Server on Kubernetesをするために定期的に情報を集めているがまだその方法を得ていない。すでに実現できているパスベースのロードバランシングに関しては、HAProxyにはパスベースのロードバランシングの機能がありロードバランサーでPiping Serverを負荷分散をするで紹介している。
リクエストは最大900秒 = 15分。
https://gyazo.com/a2fa019ab899e4bf94bce74ca4c39648
Piping Serverは何十日と起動転送し続けテラバイト(TB)やペタバイト(PB)単位で転送可能だが、Cloud Runだとリクエスト時間が制限されている。FaaSだと思うので自然なこと。上記のストリーミングできない時点で必然的にリクエストに時間けれなくはなるとは思うが。下記の解決策はこれに関しても解決する。
転送量課金。
.run.appのURLを公開しなければ自分だけで公開Piping Serverを使えそうなので心配はいらなさそう(GCPの仕組み上URLがどこかに公開さるような仕組みになっていないかはちゃんと調べる必要はありそう)。みんなに教えるときはちゃんとアラートつけるとか考える。
ストリーミングできないことの解決策
「ストリーミングしながらのアップロードとダウンロードができない」に関しては、一定量でチャンクに分けてで転送することで無限にアップロードしたりダウンロードできるようになる。すでに存在するクライアントとしてPiping Chunkがある。
Piping Chunkに関しては「Web上でPiping Server経由のエンドツーエンド暗号化したファイル転送」が詳しい。任意の数同時にリクエストできるようになっている。複数リクエストでもCloud RunだとHTTP/2が使えるためTCPのコネクションは1つになりリクエストを同時にしてコネクションが増えてしまう心配もないと思う。
Cloud Runに限らずREST APIや通常のWebページを想定したデプロイ環境は多いためこういう方法は積極的に使って良いと思っている。