Cloud Run의 32MB 요청 제한을 극복하는 방법
21366 단어 terraformpythongooglecloud
예를 들어 다음과 같은 디자인의 Python Flask Rest API를 호스트하기 위해 이 제품을 확실히 선택할 것입니다.
1- HTTP POST로 REST 끝점에 데이터 파일 업로드
2- 파일 처리
3- 클라이언트 라이브러리를 사용하여 BigQuery에 데이터 삽입
완벽하게 괜찮습니다 ... 32MB보다 큰 데이터 파일을 처리하고 싶지 않다면!
실제로 Cloud Run에서는 그렇게 큰 파일을 업로드할 수 없습니다. 대신 다음과 같은 오류 메시지가 표시됩니다.
413: Request entity too large
축하합니다. 열심히 하셨습니다size limit of Cloud Run inbound requests .
하지만 걱정하지 마세요. 아래의 개선된 디자인을 적용하면 서비스에 Cloud Run을 계속 사용할 수 있습니다.
Cloud Storage, 서명된 URL 및 PubSub 알림으로 디자인 개선
제한 사항을 해결하기 위해 Cloud Storage signed urls을 기반으로 솔루션을 설계할 수 있습니다.
이번에는 파일이 REST 끝점에 직접 업로드되지 않고 대신 클라우드 저장소에 업로드되어 32MB 제한을 우회합니다.
이 프로세스의 단점은 클라이언트가 하나가 아닌 두 개의 요청을 해야 한다는 것입니다. 따라서 완전히 새로운 순서는 다음과 같습니다.
1- 클라이언트가 업로드할 서명된 URL을 요청합니다.
2- Cloud Storage 클라이언트를 사용하는 웹 서비스는 서명된 URL을 생성하여 클라이언트에 반환합니다.
3- 클라이언트가 파일을 Cloud Storage 버킷에 직접 업로드합니다(서명된 URL에 HTTP PUT).
4- 파일 업로드가 끝나면 알림
OBJECT_FINALIZE
이 PubSub로 전송됩니다.5- 알림이 구독을 통해 Cloud Run의 웹 서비스로 다시 푸시됩니다.
6- 웹 서비스는 파일을 다운로드하여 알림에 반응합니다.
7- 그런 다음 웹 서비스는 원래 디자인에서와 똑같은 방식으로 파일을 처리할 수 있습니다.
8- 마찬가지로 데이터가 BigQuery에 삽입됩니다.
이 디자인은 완전한 서버리스이며 단일 장애 지점 없이 깔끔하게 확장됩니다. 이제 구현 방법을 자세히 살펴 보겠습니다.
Cloud Run에서 서명된 URL 만들기
잡았다! 서명된 URL을 생성하려면 Cloud Run 서비스에 역할
roles/iam.serviceAccountTokenCreator
이 있어야 합니다. 실제로 문서화되지 않았으며 승인하지 않으면 더 많은 정보 없이 HTTP 오류 403이 발생합니다.this blog post by Evan Peterson에서 제공하는 이 Python 코드는 로컬에서 비공개 키 파일을 요구하지 않고 Cloud Run 웹 서비스의 기본 서비스 계정을 사용하여 서명된 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은 클라우드 리소스를 관리하는 완벽한 도구입니다.
다음은 이 설계를 배포하기 위한 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
그것!업로드 방법
마지막 문제: 서명된 URL을 사용하여 Cloud Storage에 업로드하려면
PUT
요청에 추가 헤더를 설정해야 합니다.X-Goog-Content-Length-Range: <min size>,<max size>
여기서 min size
및 max size
는 위의 min_size
메서드의 max_size
및 make_signed_upload_url()
와 일치합니다.결론
이 디자인을 경험해 보셨습니까? 어떻게 개선하시겠습니까? 댓글로 알려주세요.
Excalidraw 및 GCP 아이콘 라이브러리로 만든 설계 스키마
joel herzog의 Unsplash님의 표지 사진
Reference
이 문제에 관하여(Cloud Run의 32MB 요청 제한을 극복하는 방법), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/stack-labs/how-to-overcome-cloud-runs-32mb-request-limit-190j텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)