本教學課程說明服務開發人員如何使用 Google Cloud Observability 工具進行探索,以及使用本機開發工作流程進行調查,排解 Cloud Run 服務中斷的問題。
這份逐步「案例研究」是疑難排解指南的輔助文件,會使用範例專案,說明如何排解部署時發生的執行階段錯誤,找出並修正問題。
目標
- 撰寫、建構服務,並將服務部署到 Cloud Run
- 使用 Error Reporting 和 Cloud Logging 找出錯誤
- 從 Container Registry 擷取容器映像檔,進行根本原因分析
- 修正「生產」服務,然後改善服務,以減少日後發生問題的機率
費用
在本文件中,您會使用 Google Cloud的下列計費元件:
如要根據預測用量估算費用,請使用 Pricing Calculator。
事前準備
- Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
-
Verify that billing is enabled for your Google Cloud project.
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
-
Verify that billing is enabled for your Google Cloud project.
- 啟用 Cloud Run Admin API
- 安裝並初始化 gcloud CLI。
- 更新元件:
gcloud components update
- 按照操作說明在本機安裝 Docker
-
Cloud Build 編輯器 (
roles/cloudbuild.builds.editor
) -
Cloud Run Admin (
roles/run.admin
) -
錯誤報告檢視器 (
roles/errorreporting.viewer
) -
記錄檔檢視存取者 (
roles/logging.viewAccessor
) -
專案 IAM 管理員 (
roles/resourcemanager.projectIamAdmin
) -
服務帳戶使用者 (
roles/iam.serviceAccountUser
) -
服務用量消費者 (
roles/serviceusage.serviceUsageConsumer
) -
儲存空間管理員 (
roles/storage.admin
)
必要的角色
如要取得完成本教學課程所需的權限,請要求管理員為您授予專案的下列 IAM 角色:
如要進一步瞭解如何授予角色,請參閱「管理專案、資料夾和機構的存取權」。
設定 gcloud 預設值
如要針對 Cloud Run 服務設定 gcloud 的預設值:
設定您的預設專案:
gcloud config set project PROJECT_ID
將 PROJECT_ID 改為您為本教學課程建立的專案名稱。
為所選區域設定 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 定價
asia-east1
(臺灣)asia-northeast1
(東京)asia-northeast2
(大阪)asia-south1
(印度孟買)europe-north1
(芬蘭)二氧化碳排放量低
europe-north2
(斯德哥爾摩)二氧化碳排放量低2
europe-southwest1
(馬德里)二氧化碳排放量低
europe-west1
(比利時)二氧化碳排放量低2
europe-west4
(荷蘭)二氧化碳排放量低
europe-west8
(米蘭)europe-west9
(巴黎)二氧化碳排放量低
me-west1
(特拉維夫)northamerica-south1
(墨西哥)us-central1
(愛荷華州)二氧化碳排放量低
us-east1
(南卡羅來納州)us-east4
(北維吉尼亞州)us-east5
(哥倫布)us-south1
(達拉斯)二氧化碳排放量低
us-west1
(奧勒岡州)二氧化碳排放量低
採用級別 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 資訊主頁中查看地區。
組裝程式碼
逐步建構新的 Cloud Run 問候服務。提醒您,這項服務會刻意建立執行階段錯誤,以供疑難排解練習使用。
建立新專案:
Node.js
定義服務套件、初始依附元件和一些常見作業,建立 Node.js 專案。建立新的
hello-service
目錄:mkdir hello-service cd hello-service
產生
package.json
檔案,建立新的 Node.js 專案:npm init --yes npm install express@4
在編輯器中開啟新的
package.json
檔案,並設定要執行node index.js
的start
指令碼。完成後,檔案會如下所示:
如果您在本教學課程結束後,仍持續開發這項服務,請考慮填寫說明、作者,並評估授權。詳情請參閱 package.json 說明文件。
Python
建立新的
hello-service
目錄:mkdir hello-service cd hello-service
建立 requirements.txt 檔案,然後將依附元件複製到該檔案:
Go
建立新的
hello-service
目錄:mkdir hello-service cd hello-service
初始化新的 Go 模組,建立 Go 專案:
go mod init example.com/hello-service
您可以視需要更新特定名稱:如果程式碼發布至可透過網路存取的程式碼存放區,就應該更新名稱。
Java
建立新的 Maven 專案:
mvn archetype:generate \ -DgroupId=com.example.cloudrun \ -DartifactId=hello-service \ -DarchetypeArtifactId=maven-archetype-quickstart \ -DinteractiveMode=false
將依附元件複製到
pom.xml
依附元件清單 (介於<dependencies>
元素之間):將建構設定複製到
pom.xml
(位於<dependencies>
元素下方):
建立 HTTP 服務來處理連入要求:
Node.js
Python
Go
Java
建立
Dockerfile
,定義用於部署服務的容器映像檔:Node.js
Python
Go
Java
本範例使用 Jib,透過常見的 Java 工具建構 Docker 映像檔。Jib 可最佳化容器建構作業,不需要 Dockerfile,也不必安裝 Docker。進一步瞭解如何使用 Jib 建構 Java 容器。
推送程式碼
推送程式碼包含三個步驟:使用 Cloud Build 建構容器映像檔、將容器映像檔上傳到 Container Registry,然後將容器映像檔部署到 Cloud Run。
如要推送程式碼:
建構容器並發布至 Container Registry:
Node.js
gcloud builds submit --tag gcr.io/PROJECT_ID/hello-service
其中 PROJECT_ID 是您的 Google Cloud 專案 ID。您可以使用
gcloud config get-value project
檢查目前的專案 ID。若成功執行,您應會看到包含 ID、建立時間和映像檔名稱的「SUCCESS」(成功) 訊息。映像檔儲存在 Container Registry 中,日後如有需要,可以重複使用。
Python
gcloud builds submit --tag gcr.io/PROJECT_ID/hello-service
其中 PROJECT_ID 是您的 Google Cloud 專案 ID。您可以使用
gcloud config get-value project
檢查目前的專案 ID。若成功執行,您應會看到包含 ID、建立時間和映像檔名稱的「SUCCESS」(成功) 訊息。映像檔儲存在 Container Registry 中,日後如有需要,可以重複使用。
Go
gcloud builds submit --tag gcr.io/PROJECT_ID/hello-service
其中 PROJECT_ID 是您的 Google Cloud 專案 ID。您可以使用
gcloud config get-value project
檢查目前的專案 ID。若成功執行,您應會看到包含 ID、建立時間和映像檔名稱的「SUCCESS」(成功) 訊息。映像檔儲存在 Container Registry 中,日後如有需要,可以重複使用。
Java
- 使用 gcloud 憑證輔助程式授權 Docker 推送至 Container Registry。
gcloud auth configure-docker
- 使用 Jib Maven 外掛程式建構容器,並推送至 Container Registry。
mvn compile jib:build -Dimage=gcr.io/PROJECT_ID/hello-service
其中 PROJECT_ID 是您的 Google Cloud 專案 ID。您可以使用
gcloud config get-value project
檢查目前的專案 ID。成功後,您應該會看到「BUILD SUCCESS」(建構成功) 訊息。映像檔會儲存在 Container Registry 中,日後如有需要,可以重複使用。
- 使用 gcloud 憑證輔助程式授權 Docker 推送至 Container Registry。
執行下列指令來部署您的應用程式:
gcloud run deploy hello-service --image gcr.io/PROJECT_ID/hello-service
將 PROJECT_ID 替換為您的 Google Cloud 專案 ID。
hello-service
是容器映像檔名稱,也是 Cloud Run 服務的名稱。請注意,容器映像檔是部署到您之前在設定 gcloud 中設定的服務和地區。在出現「allow unauthenticated」(允許未經驗證) 提示時,回覆
y
(代表「是」)。如要進一步瞭解以 IAM 為基礎的驗證,請參閱「管理存取權」一文。請等待部署完成,這可能需要半分鐘的時間。 成功完成後,指令列會顯示服務網址。
立即體驗
試用服務,確認您已成功部署。要求應會失敗,並顯示 HTTP 500 或 503 錯誤 (5xx 伺服器錯誤類別的成員)。本教學課程將逐步說明如何排解這項錯誤回應。
系統會自動為服務指派可導覽的網址。
使用網路瀏覽器前往以下網址:
開啟網路瀏覽器
找出先前部署指令輸出的服務網址。
如果部署指令未提供網址,表示發生錯誤。 查看錯誤訊息並採取相應行動:如果沒有可執行的指引,請參閱疑難排解指南,並可能重試部署指令。
將這個網址複製到瀏覽器的網址列中,然後按下 ENTER 鍵,即可前往該網址。
查看 HTTP 500 或 HTTP 503 錯誤。
如果收到 HTTP 403 錯誤,可能是因為您在部署提示中選取「拒絕」。
allow unauthenticated invocations
如要修正這個問題,請授予服務未經驗證的存取權:gcloud run services add-iam-policy-binding hello-service \ --member="allUsers" \ --role="roles/run.invoker"
詳情請參閱「允許公開 (未經驗證) 存取」。
調查問題
請注意,上述「試用」中遇到的 HTTP 5xx 錯誤,是正式環境執行階段錯誤。本教學課程將逐步說明處理這類問題的正式程序。雖然解決製作錯誤的程序差異很大,但本教學課程會介紹特定步驟順序,說明如何運用實用工具和技術。
如要調查這個問題,請完成下列階段:
- 收集回報錯誤的詳細資料,以利進一步調查並制定緩解策略。
- 決定繼續修正或復原至已知正常版本,減輕對使用者的影響。
- 重現錯誤,確認已收集正確的詳細資料,且錯誤並非一次性故障
- 對錯誤執行根本原因分析,找出造成錯誤的程式碼、設定或程序
調查開始時,您會看到網址、時間戳記和「Internal Server Error」訊息。
收集更多詳細資料
收集更多問題相關資訊,瞭解實際情況並決定後續步驟。
使用可用的 Google Cloud Observability 工具收集更多詳細資料:
使用 Error Reporting 控制台,這個控制台會提供詳細資料和資訊主頁,並追蹤含有可辨識堆疊追蹤的錯誤是否重複發生。
記錄的錯誤清單。系統會依據修訂版本、服務和平台,將錯誤依訊息分組。 按一下錯誤即可查看堆疊追蹤詳細資料,並注意錯誤發生前進行的函式呼叫。
錯誤詳細資料頁面中的「堆疊追蹤範例」會顯示單一錯誤例項。您可以查看每個個別執行個體。 使用 Cloud Logging 檢查導致問題的一連串作業,包括因缺少可辨識的錯誤堆疊追蹤,而未顯示在 Error Reporting 控制台中的錯誤訊息:
從第一個下拉式方塊中選取「Cloud Run 修訂版本」>「hello-service」。這樣一來,系統就會篩選出服務產生的記錄項目。
進一步瞭解如何在 Cloud Run 中查看記錄
復原至正常版本
如果這是已建立且已知可運作的服務,Cloud Run 上會有該服務的先前修訂版本。本教學課程使用沒有舊版的新服務,因此無法復原。
不過,如果服務有先前的版本可供復原,請按照「查看修訂版本詳細資料」一文的說明,擷取建立服務新工作部署作業所需的容器名稱和設定詳細資料。
重現錯誤
使用先前取得的詳細資料,確認問題是否持續在測試條件下發生。
再次試用並傳送相同的 HTTP 要求,查看是否回報相同的錯誤和詳細資料。錯誤詳細資料可能需要一段時間才會顯示。
由於本教學課程中的範例服務是唯讀服務,不會觸發任何複雜的副作用,因此在實際工作環境中重現錯誤是安全的。不過,許多實際服務並非如此:您可能需要在測試環境中重現錯誤,或將這個步驟限制在本地調查。
重現錯誤可為後續工作建立背景資訊。舉例來說,如果開發人員無法重現錯誤,可能需要對服務進行額外檢測,才能進一步調查。
執行根本原因分析
根本原因分析是有效疑難排解的重要步驟,可確保您修正問題本身,而非只是解決症狀。
在本教學課程中,您先前已在 Cloud Run 上重現問題,確認服務託管於 Cloud Run 時,問題確實會發生。現在請在本機重現問題,判斷問題是否與程式碼無關,或只會在實際工作環境主機中發生。
如果您尚未在本機使用 Docker CLI 搭配 Container Registry,請使用 gcloud 驗證:
gcloud auth configure-docker
如需替代方法,請參閱「Container Registry 驗證方法」。
如果無法取得最近使用的容器映像檔名稱,服務說明會提供最近部署的容器映像檔資訊:
gcloud run services describe hello-service
在
spec
物件中找出容器映像檔名稱。更精確的指令可以直接擷取該值:gcloud run services describe hello-service \ --format="value(spec.template.spec.containers.image)"
這個指令會顯示容器映像檔名稱,例如
gcr.io/PROJECT_ID/hello-service
。從 Container Registry 將容器映像檔提取至環境,下載容器映像檔可能需要幾分鐘:
docker pull gcr.io/PROJECT_ID/hello-service
日後更新容器映像檔時,只要沿用這個名稱,就能使用相同指令擷取映像檔。如果略過這個步驟,下方的
docker run
指令會提取容器映像檔 (如果本機電腦上沒有的話)。在本機執行,確認問題並非 Cloud Run 獨有:
PORT=8080 && docker run --rm -e PORT=$PORT -p 9000:$PORT \ gcr.io/PROJECT_ID/hello-service
上述指令的元素可細分為:
- 服務會使用
PORT
環境變數,判斷要在容器內監聽的通訊埠。 run
指令會啟動容器,預設為 Dockerfile 或父項容器映像檔中定義的進入點指令。--rm
旗標會在結束時刪除容器執行個體。-e
旗標會為環境變數指派值。-e PORT=$PORT
正在將PORT
變數從本機系統傳播到容器中, 且變數名稱相同。-p
標記會將容器發布為服務,並在通訊埠 9000 的本機主機上提供。系統會將對 localhost:9000 的要求,轉送至容器的 8080 通訊埠。這表示服務輸出的使用中連接埠號碼,與服務的存取方式不符。- 最後一個引數
gcr.io/PROJECT_ID/hello-service
是容器映像檔tag
,也就是容器映像檔 sha256 雜湊 ID 的易讀標籤。如果本機沒有,Docker 會嘗試從遠端登錄檔擷取映像檔。
在瀏覽器中開啟 http://localhost:9000。檢查終端機輸出內容,找出與 {ops_name} 上的錯誤訊息相符的訊息。
如果問題無法在本機重現,則可能是 Cloud Run 環境特有的問題。請參閱 Cloud Run 疑難排解指南,瞭解要調查的特定領域。
在這種情況下,錯誤會在本地重現。
- 服務會使用
現在已確認錯誤持續發生,且是由服務程式碼而非代管平台所致,因此接下來要更仔細地檢查程式碼。
在本教學課程中,您可以放心地假設容器內的程式碼與本機系統中的程式碼相同。
重新查看錯誤報告的堆疊追蹤,並與程式碼交叉參照,找出有問題的特定行。
Node.js
在index.js
檔案中,找出記錄檔中堆疊追蹤記錄所指出行號附近的錯誤訊息來源:
Python
在main.py
檔案中,找出記錄檔中堆疊追蹤記錄所指出行號附近的錯誤訊息來源:
Go
在 main.go
檔案中,找出記錄中顯示的堆疊追蹤記錄所指出行號附近的錯誤訊息來源:
Java
在記錄中顯示的堆疊追蹤記錄中,找出 App.java
附近行號的錯誤訊息來源:
檢查這段程式碼後,會發現如果未設定 NAME
環境變數,系統會採取下列行動:
- 錯誤會記錄到 Google Cloud Observability
- 傳送 HTTP 錯誤回應
問題是由於缺少變數所致,但根本原因更具體:程式碼變更會新增環境變數的硬性依附元件,但未包含部署指令碼和執行階段需求文件中的相關變更。
修正根本原因
收集程式碼並找出潛在根本原因後,我們就能採取步驟修正問題。
檢查服務是否在本機運作,並提供
NAME
環境:在本機執行容器,並新增環境變數:
PORT=8080 && docker run --rm -e PORT=$PORT -p 9000:$PORT \ -e NAME="Local World!" \ gcr.io/PROJECT_ID/hello-service
在瀏覽器中前往 http://localhost:9000
看到頁面顯示「Hello Local World!」
修改執行中的 Cloud Run 服務環境,加入這個變數:
執行服務更新指令,新增環境變數:
gcloud run services update hello-service \ --set-env-vars NAME=Override
等待幾秒鐘,Cloud Run 會根據先前的修訂版本建立新的修訂版本,並新增環境變數。
確認服務已修復:
- 在瀏覽器中前往 Cloud Run 服務網址。
- 頁面上會顯示「Hello Override!」。
- 確認 Cloud Logging 或 Error Reporting 中未顯示任何非預期的訊息或錯誤。
提升日後的疑難排解速度
在這個生產問題範例中,錯誤與作業設定有關。我們將進行程式碼變更,盡量減少日後發生這個問題的影響。
- 改善錯誤記錄,加入更具體的詳細資料。
- 服務應改為回溯至安全預設值,而非傳回錯誤。如果使用預設值會導致正常功能發生變化,請使用警告訊息進行監控。
讓我們逐步移除 NAME
環境變數,做為硬性依附元件。
移除現有的
NAME
處理程式碼:Node.js
Python
Go
Java
新增設定備用值的新程式碼:
Node.js
Python
Go
Java
透過受影響的設定案例,重新建構及執行容器,在本機進行測試:
Node.js
docker build --tag gcr.io/PROJECT_ID/hello-service .
Python
docker build --tag gcr.io/PROJECT_ID/hello-service .
Go
docker build --tag gcr.io/PROJECT_ID/hello-service .
Java
mvn compile jib:build
確認
NAME
環境變數仍可運作:PORT=8080 && docker run --rm -e PORT=$PORT -p 9000:$PORT \ -e NAME="Robust World" \ gcr.io/PROJECT_ID/hello-service
確認服務在沒有
NAME
變數的情況下也能運作:PORT=8080 && docker run --rm -e PORT=$PORT -p 9000:$PORT \ gcr.io/PROJECT_ID/hello-service
如果服務未傳回結果,請確認移除第一個步驟中的程式碼時,是否也移除了額外行數,例如用於撰寫回應的行數。
-
每次部署到服務都會建立一個新的修訂版本,並會在就緒時自動開始處理流量。
如要清除先前設定的環境變數,請執行下列操作:
gcloud run services update hello-service --clear-env-vars
將預設值的新功能新增至服務的自動化測試涵蓋範圍。
在記錄中尋找其他問題
您可能會在這項服務的記錄檢視器中看到其他問題。舉例來說,記錄檔會將不支援的系統呼叫顯示為「Container Sandbox Limitation」。
舉例來說,Node.js 服務有時會產生下列記錄訊息:
Container Sandbox Limitation: Unsupported syscall statx(0xffffff9c,0x3e1ba8e86d88,0x0,0xfff,0x3e1ba8e86970,0x3e1ba8e86a90). Please, refer to https://gvisor.dev/c/linux/amd64/statx for more information.
在本例中,缺少支援不會影響 hello-service 服務範例。
Terraform 疑難排解
如需 Terraform 相關的疑難排解或問題,請參閱「Terraform 政策驗證疑難排解」一文,或洽詢 Terraform 支援團隊。
清除所用資源
如果您是為了這個教學課程建立新專案,請刪除專案。如果您使用現有專案,並想保留專案,但不要在本教學課程中新增的變更,請刪除為本教學課程建立的資源。
刪除專案
如要避免付費,最簡單的方法就是刪除您為了本教學課程所建立的專案。
如要刪除專案:
- In the Google Cloud console, go to the Manage resources page.
- In the project list, select the project that you want to delete, and then click Delete.
- In the dialog, type the project ID, and then click Shut down to delete the project.
刪除教學課程資源
刪除您在本教學課程中部署的 Cloud Run 服務:
gcloud run services delete SERVICE-NAME
其中 SERVICE-NAME 是您選擇的服務名稱。
您也可以從Google Cloud 控制台刪除 Cloud Run 服務。
移除您在教學課程設定期間新增的 gcloud 預設區域設定:
gcloud config unset run/region
移除專案設定:
gcloud config unset project
刪除在本教學課程中建立的其他 Google Cloud 資源:
- 從 Container Registry 刪除名為
gcr.io/<var>PROJECT_ID</var>/hello-service
的容器映像檔。
- 從 Container Registry 刪除名為
後續步驟
- 進一步瞭解如何使用 Cloud Logging 和 Error Reporting 深入瞭解實際運作情況。
- 如要進一步瞭解如何排解 Cloud Run 問題,請參閱疑難排解指南。
- 探索 Google Cloud 的參考架構、圖表和最佳做法。 歡迎瀏覽我們的雲端架構中心。