クラウド・ランの32 MB要求制限を克服する方法


Cloud Run Google Serverによって提供される素晴らしいServerlessな製品は、しばしばコンテナ化されたウェブサービスを走らせるのにぴったりです.これは、自動スケーリング、ローリングの更新、Autorestartなどの多くの利点を提供し、スケール0にわずか数名に.すべてのクラスタのプロビジョニングと管理の面倒なし!
この製品は、PythonのフラスコREST APIとして、次のようなデザインで確実に選択します.

1 -残りのエンドポイントにHTTP POSTでデータファイルをアップロードする
2 -プロセスファイル
3 -クライアントlibを使用してBigQueryにデータを挿入する
これは完全に罰金です.32 MBより大きいデータファイルを扱うことができない限り!
実際、クラウドランはこのような大きなファイルをアップロードしません.代わりに、エラーメッセージを取得します.

413: Request entity too large


おめでとう、あなただけのハードヒットsize limit of Cloud Run inbound requests .
しかし、心配しないで、以下の改善されたデザインを適用するならば、あなたはあなたのサービスのために雲ランを使用し続けることができます:

改善されたデザインは、クラウドストレージ、署名のURLとPubSub通知


制限を回避するには、ソリューションを設計することができますCloud Storage signed urls :

今回、ファイルは残りのエンドポイントに直接アップロードされませんが、代わりにクラウドストレージにアップロードされます.
このプロセスの欠点は、クライアントが1つの代わりに2つの要求をしなければならないということです.したがって、全く新しいシーケンスは次のようになります.
1 -クライアントが符号化されたURLを
2 -クラウドストレージクライアントを使用して、Webサービスは、署名されたURLを生成し、クライアントにそれを返します
3 -クライアントは、ファイルを直接クラウドストレージバケットにアップロードします( HTTPは署名されたURLに置かれます).
4 -ファイルアップロードの終わりに、通知OBJECT_FINALIZE を送信する
5 -通知は、その後、サブスクリプションを介してクラウドランでWebサービスにプッシュされます
6 - Webサービスは、ファイルをダウンロードして通知に反応する
7 -ウェブサービスは、ファイルを処理することができます.
8 -同様に、データはbigqueryに挿入されます
このデザインは全く無価値で、失敗のどんな一点もなしできちんとスケールします.さて、より詳細にそれを実装する方法を見てみましょう.

クラウドランから署名URLを作成する


ちゃ!クラウドランサービスには、その役割が必要ですroles/iam.serviceAccountTokenCreator 署名されたURLを生成できるようにします.それは本当に文書化されません、そして、あなたがそれを与えないならば、あなたは多くの情報なしでHTTPエラー403を得ます.
このPythonコードは、this blog post by Evan Peterson , クラウドのWebServiceの既定のサービスアカウントで、署名されたURLを生成する方法を公開します
from typing import Optional
from datetime import timedelta

from google import auth
from google.auth.transport import requests
from google.cloud.storage import Client


def make_signed_upload_url(
    bucket: str,
    blob: str,
    *,
    exp: Optional[timedelta] = None,
    content_type="application/octet-stream",
    min_size=1,
    max_size=int(1e6)
):
    """
    Compute a GCS signed upload URL without needing a private key file.
    Can only be called when a service account is used as the application
    default credentials, and when that service account has the proper IAM
    roles, like `roles/storage.objectCreator` for the bucket, and
    `roles/iam.serviceAccountTokenCreator`.
    Source: https://stackoverflow.com/a/64245028

    Parameters
    ----------
    bucket : str
        Name of the GCS bucket the signed URL will reference.
    blob : str
        Name of the GCS blob (in `bucket`) the signed URL will reference.
    exp : timedelta, optional
        Time from now when the signed url will expire.
    content_type : str, optional
        The required mime type of the data that is uploaded to the generated
        signed url.
    min_size : int, optional
        The minimum size the uploaded file can be, in bytes (inclusive).
        If the file is smaller than this, GCS will return a 400 code on upload.
    max_size : int, optional
        The maximum size the uploaded file can be, in bytes (inclusive).
        If the file is larger than this, GCS will return a 400 code on upload.
    """
    if exp is None:
        exp = timedelta(hours=1)
    credentials, project_id = auth.default()
    if credentials.token is None:
        # Perform a refresh request to populate the access token of the
        # current credentials.
        credentials.refresh(requests.Request())
    client = Client()
    bucket = client.get_bucket(bucket)
    blob = bucket.blob(blob)
    return blob.generate_signed_url(
        version="v4",
        expiration=exp,
        service_account_email=credentials.service_account_email,
        access_token=credentials.token,
        method="PUT",
        content_type=content_type,
        headers={"X-Goog-Content-Length-Range": f"{min_size},{max_size}"}
    )

地形


インフラを持たない雲をコードとして行う堅牢な方法はないTerraform あなたのクラウドリソースを管理するための最適なツールです.

このデザインを展開するための地形図です.
# Resources to handle big data files (>32 Mb)
# These files are uploaded to a special bucket with notifications

provider "google-beta" {
  project = <your GCP project name>
}

data "google_project" "default" {
  provider = google-beta
}

resource "google_storage_bucket" "bigframes_bucket" {
  project  = <your GCP project name>
  name     = "upload-big-files"
  location = "EU"

  cors {
    origin = ["*"]
    method = ["*"]
    response_header = [
      "Content-Type",
      "Access-Control-Allow-Origin",
      "X-Goog-Content-Length-Range"
    ]
    max_age_seconds = 3600
  }
}

resource "google_service_account" "default" {
  provider     = google-beta
  account_id   = "sa-webservice"
}

resource "google_storage_bucket_iam_member" "bigframes_admin" {
  bucket = google_storage_bucket.bigframes_bucket.name
  role   = "roles/storage.admin"
  member = "serviceAccount:${google_service_account.default.email}"
}

# required to generate a signed url
resource "google_service_account_iam_member" "tokencreator" {
  provider           = google-beta
  service_account_id = google_service_account.default.name
  role               = "roles/iam.serviceAccountTokenCreator"
  member             = "serviceAccount:${google_service_account.default.email}"
}

# upload topic for notifications
resource "google_pubsub_topic" "bigframes_topic" {
  provider = google-beta
  name     = "topic-bigframes"
}

# upload deadletter topic for failed notifications
resource "google_pubsub_topic" "bigframes_topic_deadletter" {
  provider = google-beta
  name     = "topic-bigframesdeadletter"
}

# add frame upload notifications on the bucket
resource "google_storage_notification" "bigframes_notification" {
  provider       = google-beta
  bucket         = google_storage_bucket.bigframes_bucket.name
  payload_format = "JSON_API_V1"
  topic          = google_pubsub_topic.bigframes_topic.id
  event_types    = ["OBJECT_FINALIZE"]
  depends_on = [google_pubsub_topic_iam_binding.bigframes_binding]
}

# required for storage notifications
# seriously, Google, this should be by default !
resource "google_pubsub_topic_iam_binding" "bigframes_binding" {
  topic   = google_pubsub_topic.bigframes_topic.id
  role    = "roles/pubsub.publisher"
  members = ["serviceAccount:service-${data.google_project.default.number}@gs-project-accounts.iam.gserviceaccount.com"]
}

# frame upload main sub
resource "google_pubsub_subscription" "bigframes_sub" {
  provider = google-beta
  name     = "sub-bigframes"
  topic    = google_pubsub_topic.bigframes_topic.id

  push_config {
    push_endpoint = <URL where pushed notification are POST-ed>
  }
  dead_letter_policy {
    dead_letter_topic = google_pubsub_topic.bigframes_topic_deadletter.id
  }
}

# frame upload deadletter subscription
resource "google_pubsub_subscription" "bigframes_sub_deadletter" {
  provider             = google-beta
  name                 = "sub-bigframesdeadletter"
  topic                = google_pubsub_topic.bigframes_topic_deadletter.id
  ack_deadline_seconds = 600

  push_config {
    push_endpoint = <URL where pushed notification are POST-ed>
  }
}

ジャストterraform deploy それ!

ハウツーとスタイル


つの最終的なgotcha:署名されたURLでクラウドストレージにアップロードするには、PUT リクエストX-Goog-Content-Length-Range: <min size>,<max size>どこmin size and max size マッチmin_size and max_sizemake_signed_upload_url() 上記のメソッド.

結論


このデザインを経験しましたか.どうやってそれを改善するの?コメントを教えてください.
デザインスキーマExcalidraw とGCPのアイコンライブラリ
カバー写真joel herzog on Unsplash