Cloud Run 服務教學課程中的 gcloud 指令列

在本教學課程中,您將使用 Cloud Run 服務內的 Google Cloud CLI,建立 Cloud Run 服務的清單。您可以將本教學課程所學內容套用至現有的 Cloud 運算作業指令碼,或在透過用戶端程式庫建構更穩固的服務之前,先建構概念驗證。

您可以在網路服務中使用 gcloud CLI,就像使用任何殼層指令碼一樣,例如殼層快速入門導覽課程所示。在 Cloud Run 上,這兩項工具會與服務搭配運作,自動透過 Cloud Run 服務身分進行驗證。 Google Cloudgcloud CLI 可以使用授予服務身分的任何權限。

gcloud CLI 能夠廣泛收集資訊,並管理 Google Cloud 資源,因此在 Web 服務中使用時,必須盡量降低呼叫端誤用這些功能的風險。如果沒有安全控管措施,您可能會允許意外或蓄意的惡意活動,進而對在同一專案中執行的其他服務或資源造成風險。這類風險的例子包括:

  • 啟用私人虛擬機器的 IP 位址探索功能
  • 允許存取同一專案中資料庫的私人資料
  • 啟用刪除其他執行中服務

本教學課程的幾個步驟會說明如何強制執行控管措施,盡量降低風險,例如指定要在程式碼中執行的 gcloud 指令,而不是將其開放為使用者輸入內容。

在 Cloud Run 服務中使用指令列工具編寫指令碼,與在本機使用指令列類似。主要差異在於您應在主要指令碼邏輯周圍新增額外限制。

設定 gcloud 預設值

如要針對 Cloud Run 服務設定 gcloud 的預設值:

  1. 設定您的預設專案:

    gcloud config set project PROJECT_ID

    PROJECT_ID 改為您為本教學課程建立的專案名稱。

  2. 為所選區域設定 gcloud:

    gcloud config set run/region REGION

    REGION 改為您所選擇的支援 Cloud Run 地區

Cloud Run 位置

Cloud Run 具有「地區性」,這表示執行 Cloud Run 服務的基礎架構位於特定地區,並由 Google 代管,可為該地區內所有區域提供備援功能。

選擇 Cloud Run 服務的執行地區時,請將延遲時間、可用性或耐用性需求做為主要考量。一般而言,您可以選擇最靠近使用者的地區,但您應考量 Cloud Run 服務所使用的其他 Google Cloud 產品位置。使用分散在不同位置的 Google Cloud 產品,可能會影響服務的延遲時間和費用。

Cloud Run 可在下列地區使用:

採用級別 1 定價

採用級別 2 定價

  • africa-south1 (約翰尼斯堡)
  • asia-east2 (香港)
  • asia-northeast3 (韓國首爾)
  • asia-southeast1 (新加坡)
  • asia-southeast2 (雅加達)
  • asia-south2 (印度德里)
  • australia-southeast1 (雪梨)
  • australia-southeast2 (墨爾本)
  • europe-central2 (波蘭華沙)
  • europe-west10 (柏林)
  • europe-west12 (杜林)
  • europe-west2 (英國倫敦) 節能綠葉圖示 二氧化碳排放量低
  • europe-west3 (德國法蘭克福)
  • europe-west6 (瑞士蘇黎世) 節能綠葉圖示 二氧化碳排放量低
  • me-central1 (杜哈)
  • me-central2 (達曼)
  • northamerica-northeast1 (蒙特婁) 節能綠葉圖示 二氧化碳排放量低
  • northamerica-northeast2 (多倫多) 節能綠葉圖示 二氧化碳排放量低
  • southamerica-east1 (巴西聖保羅) 節能綠葉圖示 二氧化碳排放量低
  • southamerica-west1 (智利聖地牙哥) 節能綠葉圖示 二氧化碳排放量低
  • us-west2 (洛杉磯)
  • us-west3 (鹽湖城)
  • us-west4 (拉斯維加斯)

如果您已建立 Cloud Run 服務,即可在 Google Cloud 控制台的 Cloud Run 資訊主頁中查看地區。

擷取程式碼範例

如要擷取要使用的程式碼範例:

  1. 將應用程式存放區範例複製到本機電腦中:

    git clone https://github.com/GoogleCloudPlatform/cloud-run-samples.git

    您也可以 下載 zip 格式的範例,然後解壓縮該檔案。

  2. 變更為包含 Cloud Run 範例程式碼的目錄:

    cd cloud-run-samples/gcloud-report/

檢查程式碼

這個部分包含您擷取的程式碼範例相關資訊。

產生報表並上傳至 Cloud Storage

這個 Shell 指令碼會產生目前專案和區域中 Cloud Run 服務的報表,並將結果上傳至 Cloud Storage。列出名稱包含所提供字串 search 引數的服務。

這個指令碼會使用 gcloud run services list 指令、gcloud 進階格式選項,以及gcloud串流傳輸複製模式。

set -eo pipefail

# Check for required environment variables.
requireEnv() {
  test "${!1}" || (echo "gcloud-report: '$1' not found" >&2 && exit 1)
}
requireEnv GCLOUD_REPORT_BUCKET

# Prepare formatting: Default search term to include all services.
search=${1:-'.'}
limits='spec.template.spec.containers.resources.limits.flatten("", "", " ")'
format='table[box, title="Cloud Run Services"](name,status.url,metadata.annotations.[serving.knative.dev/creator],'${limits}')'

# Create a specific object name that will not be overridden in the future.
obj="gs://${GCLOUD_REPORT_BUCKET}/report-${search}-$(date +%s).txt"

# Write a report containing the service name, service URL, service account or user that
# deployed it, and any explicitly configured service "limits" such as CPU or Memory.
gcloud run services list \
  --format "${format}" \
  --filter "metadata.name~${search}" | gsutil -q cp -J - "${obj}"

# /dev/stderr is sent to Cloud Logging.
echo "gcloud-report: wrote to ${obj}" >&2
echo "Wrote report to ${obj}"

這個指令碼可安全地以服務形式執行,因為重複叫用指令碼會更新報表,不會造成進一步的昂貴流失。如果重複叫用使用 gcloud CLI 的其他指令碼 (例如建立新的雲端資源或執行耗費資源的工作),可能會產生更多費用。冪等指令碼 (重複叫用時會產生相同結果) 以服務形式執行時較安全。

在 HTTP 要求中叫用指令碼

這段 Go 程式碼會設定網路服務,執行 Shell 指令碼來產生報表。 由於搜尋查詢是使用者輸入內容,程式碼會驗證查詢,確保查詢只包含英文字母、數字或連字號,避免輸入惡意指令。這組字元範圍夠窄,可防範指令注入攻擊

網路服務會將搜尋參數做為引數傳遞至 Shell 指令碼。


// Service gcloud-report is a Cloud Run shell-script-as-a-service.
package main

import (
	"log"
	"net/http"
	"os"
	"os/exec"
	"regexp"
)

func main() {
	http.HandleFunc("/", scriptHandler)

	// Determine port for HTTP service.
	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
		log.Printf("defaulting to port %s", port)
	}

	// Start HTTP server.
	log.Printf("listening on port %s", port)
	if err := http.ListenAndServe(":"+port, nil); err != nil {
		log.Fatal(err)
	}
}

func scriptHandler(w http.ResponseWriter, r *http.Request) {
	search := r.URL.Query().Get("search")
	re := regexp.MustCompile(`^[a-z]+[a-z0-9\-]*$`)
	if !re.MatchString(search) {
		log.Printf("invalid search criteria %q, using default", search)
		search = "."
	}

	cmd := exec.CommandContext(r.Context(), "/bin/bash", "script.sh", search)
	cmd.Stderr = os.Stderr
	out, err := cmd.Output()
	if err != nil {
		log.Printf("Command.Output: %v", err)
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}
	w.Write(out)
}

go.mod 檔案會在 Go 模組中宣告應用程式依附元件:

module github.com/GoogleCloudPlatform/cloud-run-samples/gcloud-report

go 1.19

定義容器環境

Dockerfile 會定義服務的環境組合方式。 這與helloworld-shell 快速入門導覽課程中的 Dockerfile 類似,但最終容器映像檔是以 gcloud Google Cloud CLI 映像檔為基礎。這樣一來,您的服務就能使用 gcloud,而不必自訂 Google Cloud CLI 的安裝和設定步驟。


# Use the official golang image to create a binary.
# This is based on Debian and sets the GOPATH to /go.
# https://hub.docker.com/_/golang
FROM golang:1.20-buster as builder

# Create and change to the app directory.
WORKDIR /app

# Retrieve application dependencies.
# This allows the container build to reuse cached dependencies.
# Expecting to copy go.mod and if present go.sum.
COPY go.* ./
RUN go mod download

# Copy local code to the container image.
COPY invoke.go ./

# Build the binary.
RUN go build -mod=readonly -v -o server

# Use a gcloud image based on debian:buster-slim for a lean production container.
# https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
FROM gcr.io/google.com/cloudsdktool/cloud-sdk:slim

WORKDIR /app

# Copy the binary to the production image from the builder stage.
COPY --from=builder /app/server /app/server
COPY *.sh /app/
RUN chmod +x /app/*.sh

# Run the web service on container startup.
CMD ["/app/server"]

建立 Artifact Registry 標準存放區

建立 Artifact Registry 標準存放區,用於儲存容器映像檔:

gcloud artifacts repositories create REPOSITORY \
    --repository-format=docker \
    --location=REGION

取代:

  • REPOSITORY,並為存放區指定專屬名稱。
  • REGION,其中 Google Cloud 是 Artifact Registry 的區域。

設定 Cloud Storage bucket

建立 Cloud Storage bucket,用於上傳報表:

gcloud storage buckets create gs://REPORT_ARCHIVE_BUCKET

REPORT_ARCHIVE_BUCKET 替換成全域不重複的值區名稱。

設定服務身分

為限制服務對其他基礎架構的權限,您可以建立服務身分,並自訂執行工作所需的特定 IAM 權限。

在本例中,必要權限為讀取 Cloud Run 服務的權限,以及從 Cloud Storage bucket 讀取和寫入資料的權限。

  1. 建立服務帳戶:

    gcloud iam service-accounts create gcloud-report-identity

  2. 授予服務帳戶讀取 Cloud Run 服務的權限:

    gcloud projects add-iam-policy-binding PROJECT_ID \
      --member=serviceAccount:gcloud-report-identity@PROJECT_ID.iam.gserviceaccount.com \
      --role roles/run.viewer
  3. 授予服務帳戶讀取及寫入 Cloud Storage bucket 的權限:

    gcloud storage buckets add-iam-policy-binding gs://REPORT_ARCHIVE_BUCKET \
      --member=serviceAccount:gcloud-report-identity@PROJECT_ID.iam.gserviceaccount.com \
      --role=roles/storage.objectUser

這個自訂服務身分具有有限的存取權,因此服務無法存取其他 Google Cloud 資源。

運送服務

運送代碼的產生流程包含三個步驟:

  • 使用 Cloud Build 建構容器映像檔
  • 將容器映像檔上傳至 Artifact Registry
  • 將容器映像檔部署至 Cloud Run。

如要推送程式碼:

  1. 建構容器並發布至 Artifact Registry:

    gcloud builds submit --tag REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/gcloud-report

    取代:

    • PROJECT_ID 替換為您的 Google Cloud 專案 ID
    • REPOSITORY 替換為 Artifact Registry 存放區的名稱。
    • REGION,其中 Google Cloud 是 Artifact Registry 的區域。

    gcloud-report 是服務名稱。

    若成功執行,系統會顯示「SUCCESS」(成功) 訊息,當中包含 ID、建立時間和映像檔名稱。映像檔會儲存在 Artifact Registry 中,日後如有需要,可以重複使用。

  2. 執行下列指令來部署服務:

    gcloud run deploy gcloud-report \
       --image REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/gcloud-report \
       --update-env-vars GCLOUD_REPORT_BUCKET=REPORT_ARCHIVE_BUCKET \
       --service-account gcloud-report-identity \
       --no-allow-unauthenticated

    取代:

    • PROJECT_ID 改成您的 Google Cloud 專案 ID。
    • REPOSITORY 替換為 Artifact Registry 存放區的名稱。
    • REGION 替換為服務的 Google Cloud 區域。

    gcloud-report 是容器名稱和服務名稱的一部分。 容器映像檔會部署到您先前在「設定 gcloud」中設定的服務和地區 (Cloud Run)。

    --no-allow-unauthenticated 旗標會限制服務的公開存取權。不公開服務可讓您依靠 Cloud Run 的內建驗證功能,封鎖未經授權的要求。如要進一步瞭解以身分與存取權管理 (IAM) 為基礎的驗證,請參閱「使用 IAM 管理存取權」。

    等待部署作業完成。這項作業大約需要半分鐘。 成功完成後,指令列會顯示服務網址。

  3. 如果您要將程式碼更新部署到服務,請重複上述步驟。每次部署到服務都會建立一個新的修訂版本,並會在就緒時自動開始處理流量。

如要授予 Google Cloud 使用者呼叫這項服務的權限,請參閱使用 IAM 管理存取權。專案編輯者和擁有者會自動取得這項存取權。

產生報表

如要產生 Cloud Run 服務的報表,請按照下列步驟操作:

  1. 使用 curl 傳送已驗證的要求:

    curl -H "Authorization: Bearer $(gcloud auth print-identity-token)" SERVICE_URL

    SERVICE_URL 替換為 Cloud Run 在部署完成後提供的網址。

    如果您建立新專案並按照本教學課程操作,輸出內容會類似於:

    Wrote report to gs://REPORT_ARCHIVE_BUCKET/report-.-DATE.txt

    檔案名稱中的 . 是預設搜尋引數,如原始碼中所述。

    如要使用搜尋功能,請在要求中加入 search 引數:

    curl -H "Authorization: Bearer $(gcloud auth print-identity-token)" SERVICE_URL?search=gcloud

    這項查詢會傳回類似下列內容的輸出內容:

    Wrote report to gs://REPORT_ARCHIVE_BUCKET/report-gcloud-DATE.txt
  2. 在本機使用 gcloud CLI 擷取檔案:

    gcloud storage cp gs://REPORT_FILE_NAME .

    指令中的 . 代表目前的工作目錄。

    REPORT_FILE_NAME 替換為上一步輸出的 Cloud Storage 物件名稱。

開啟檔案即可查看報表。內容應該會類似這樣:

螢幕截圖:專案中的 Cloud Run 服務清單,包含四個服務屬性的資料欄。
這四個資料欄會從服務說明中擷取。包括服務「名稱」、首次部署時指派的「網址」、服務的初始「建立者」,以及 CPU 和記憶體上限的服務「限制」

提升未來穩定性

如果您打算進一步開發這項服務,建議以更穩健的程式設計語言重新編寫,並使用 Cloud Run Admin APICloud Storage 用戶端程式庫

您可以在 gcloud CLI 指令中加入 --log-http,檢查發出的 API 呼叫 (並查看部分驗證詳細資料)。

自動執行這項作業

現在,HTTP 要求可以觸發 Cloud Run 服務的報表,因此您可以在需要時使用自動化功能產生報表: