使用系統套件教學課程

本教學課程說明如何建構自訂的 Cloud Run 服務,這項服務可將圖表說明輸入參數轉換成 PNG 圖片格式的圖表。它使用 Graphviz,並且會安裝為服務容器環境中的系統套件。您可透過指令列公用程式使用 Graphviz 提供要求。

您可以搭配 Cloud Run 或 Cloud Run on GKE 使用本教學課程。

目標

  • 使用 Dockerfile 撰寫並建構自訂容器
  • 撰寫、建構及部署 Cloud Run 服務
  • 使用 Graphviz dot 公用程式產生圖表
  • 從收集內容或您自己的創作張貼 DOT 語法圖表來測試服務

費用

本教學課程使用的 Cloud Platform 計費元件包括:

初次使用 Cloud Platform 的使用者可能符合申請免費試用的資格。

事前準備

  1. 登入您的 Google 帳戶。

    如果您沒有帳戶,請申請新帳戶

  2. 在 GCP Console 的專案選擇器頁面中,選取或建立 GCP 專案。

    前往專案選取器頁面

  3. 請確認您已啟用 Google Cloud Platform 專案的計費功能。瞭解如何確認您已啟用專案的計費功能

  4. 啟用 Cloud Run API
  5. 安裝並初始化 Cloud SDK
  6. 安裝 gcloud Beta 版元件:
    gcloud components install beta
  7. 針對 Cloud Run on GKE,安裝 gcloud kubectl 元件:
    gcloud components install kubectl
  8. 更新元件:
    gcloud components update
  9. 安裝 curl 以便試用服務
  10. 如果您使用 Cloud Run on GKE,請使用設定 Cloud Run on GKE 中的操作說明建立新叢集。

設定 gcloud 預設值

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

  1. 設定您的預設專案:

    gcloud config set project [PROJECT-ID]

    將 [PROJECT-ID] 改為您為本教學課程建立的專案名稱。

  2. 如果您使用 Cloud Run,請為您選取的地區設定 gcloud:

     gcloud config set run/region us-central1
    

    us-central1 改為您所選擇的支援 Cloud Run 地區

  3. 如果您使用 Cloud Run on GKE,請為您的叢集設定 gcloud:

    gcloud config set run/cluster [CLUSTER_NAME]
    gcloud config set run/cluster_location us-central1-a

    將 [CLUSTER-NAME] 改為您使用的叢集名稱,然後視需要將 us-central1-a 改為您所選擇的支援叢集位置。

Cloud Run 位置

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

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

Cloud Run 可在下列地區使用:

  • us-central1 (愛荷華州)

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

擷取程式碼範例

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

  1. 下載範例服務程式碼:

  2. 將封存解壓縮。例如,使用這些標準 Linux 和 macOS 指令列公用程式:

    Tarball

    tar -xzvf [FILE].tar.gz

    ZIP 檔

    unzip [FILE].zip

    其中 [FILE] 是下載的封存檔案名稱 (不含副檔名)。

將架構視覺化

基本架構如下所示:

顯示從使用者到網路服務,再到 graphviz dot 公用程式的要求流程圖表。
如需圖表來源,請參閱 DOT 說明

使用者將 HTTP 要求傳送至 Cloud Run 服務,這個服務會執行 Graphviz 公用程式,將要求轉換成圖片,再將該圖片做為 HTTP 回應傳送給使用者。

瞭解程式碼

使用 Dockerfile 定義您的環境設定

您的 Dockerfile 僅適用於您的服務使用的語言和基本作業環境 (例如 Ubuntu)。

《建構及部署》快速入門導覽課程提供各種不同的 Dockerfiles,您可據此建構用於其他服務的 Dockerfile

這項服務需要一或多個額外系統套件 (預設不提供)。

  1. 在編輯器內開啟 Dockerfile

  2. 尋找 Dockerfile RUN 陳述式。這個陳述式允許執行任意殼層指令來修改環境。如果 Dockerfile 有多個階段,可藉由尋找多個 FROM 陳述式加以識別,您會在最後的階段中找到它。

    需要的特定套件和安裝機制,會因容器內部宣告的作業系統而異。

    如要取得作業系統或基本映像檔適用的操作說明,請按一下相關的分頁標籤。

    如果要判斷容器映像檔的作業系統,請查看 FROM 陳述式中的名稱或與基本映像檔相關聯的 README。例如,如果您從 node 進行擴充,可以在 Docker Hub 尋找說明文件和父項 Dockerfile

  3. 使用 docker build (本機)Cloud Build 建構映像檔,來測試您的自訂項目。

處理傳入要求

範例服務使用傳入 HTTP 要求中的參數來叫用系統呼叫,以執行適當的 dot 公用程式指令。

在下面的 HTTP 處理常式中,圖表說明輸入參數是從 dot 查詢字串變數中擷取出的。

圖表說明所包含的字元必須使用網址編碼,以便在查詢字串中使用。

Node.js

app.get('/diagram.png', (req, res) => {
  try {
    const image = createDiagram(req.query.dot);
    res.setHeader('Content-Type', 'image/png');
    res.setHeader('Content-Length', image.length);
    res.setHeader('Cache-Control', 'public, max-age=86400');
    res.send(image);
  } catch (err) {
    console.error(`error: ${err.message}`);
    const errDetails = (err.stderr || err.message).toString();
    if (errDetails.includes('syntax')) {
      res.status(400).send(`Bad Request: ${err.message}`);
    } else {
      res.status(500).send('Internal Server Error');
    }
  }
});

Go

// diagramHandler renders a diagram using HTTP request parameters and the dot command.
func diagramHandler(w http.ResponseWriter, r *http.Request) {
	var input io.Reader
	if r.Method == http.MethodGet {
		q := r.URL.Query()
		dot := q.Get("dot")
		if dot == "" {
			log.Print("no graphviz definition provided")
			http.Error(w, "Bad Request", http.StatusBadRequest)
			return
		}
		// Cache header must be set before writing a response.
		w.Header().Set("Cache-Control", "public, max-age=86400")
		input = strings.NewReader(dot)
	} else {
		log.Printf("method not allowed: %s", r.Method)
		http.Error(w, fmt.Sprintf("HTTP Method %s Not Allowed", r.Method), http.StatusMethodNotAllowed)
		return
	}

	if err := createDiagram(w, input); err != nil {
		log.Printf("createDiagram: %v", err)
		// Do not cache error responses.
		w.Header().Del("Cache-Control")
		if strings.Contains(err.Error(), "syntax") {
			http.Error(w, "Bad Request: DOT syntax error", http.StatusBadRequest)
		} else {
			http.Error(w, "Internal Server Error", http.StatusInternalServerError)
		}
	}
}

您需要分清楚內部伺服器錯誤與無效的使用者輸入。除非錯誤訊息含有 syntax 字串來表示使用者輸入問題,否則對於所有 dot 指令列錯誤,這個範例服務都會傳回內部伺服器錯誤。

產生圖表

產生圖表的核心邏輯使用 dot 指令列工具,將圖表說明輸入參數處理為 PNG 圖片格式的圖表。

Node.js

// Generate a diagram based on a graphviz DOT diagram description.
const createDiagram = (dot) => {
  if (!dot || (dot.constructor === Object && Object.keys(dot).length === 0)) {
    throw new Error('syntax: no graphviz definition provided');
  }

  const watermark = ` \
    -Glabel="Made on Cloud Run" \
    -Gfontsize=10 \
    -Glabeljust=right \
    -Glabelloc=bottom \
    -Gfontcolor=gray \
  `;
  const image = execSync(`/usr/bin/dot ${watermark} -Tpng`, {
    input: dot,
  });
  return image;
};

Go

// createDiagram generates a diagram image from the provided io.Reader written to the io.Writer.
func createDiagram(w io.Writer, r io.Reader) error {
	stderr := new(bytes.Buffer)
	args := []string{
		"-Glabel=Made on Cloud Run",
		"-Gfontsize=10",
		"-Glabeljust=right",
		"-Glabelloc=bottom",
		"-Gfontcolor=gray",
		"-Tpng",
	}
	cmd := exec.Command("/usr/bin/dot", args...)
	cmd.Stdin = r
	cmd.Stdout = w
	cmd.Stderr = stderr

	if err := cmd.Run(); err != nil {
		return fmt.Errorf("exec(%s) failed (%v): %s", cmd.Path, err, stderr.String())
	}

	return nil
}

設計安全的服務

dot 工具的任何安全漏洞都是網路服務的潛在安全漏洞。您可以透過定期重新建構容器映像檔,使用最新版 graphviz 套件來減緩這種情況。

如果您將目前的範例擴充為接受使用者輸入做為指令列參數,則應防範指令植入 (command-injection) 攻擊。防範植入攻擊的幾個方法包括:

  • 將輸入內容對應至支援參數的字典
  • 驗證輸入內容是否與範圍廣泛的已知安全值相符 (可使用規則運算式)
  • 逸出輸入內容以確保不會評估殼層語法

推送程式碼

如要推送您的程式碼,您可使用 Cloud Build 建構程式碼,並上傳至 Container Registry,然後部署到 Cloud Run 或 Cloud Run on GKE:

  1. 執行下列指令以建構您的容器,然後發布到 Container Registry。

    gcloud builds submit --tag gcr.io/[PROJECT-ID]/graphviz

    其中 [PROJECT-ID] 是您的 GCP 專案 ID,graphviz 則是您要給予服務的名稱。

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

  2. 使用下列指令進行部署:

    gcloud beta run deploy graphviz-web --image gcr.io/[PROJECT-ID]/graphviz

    其中 [PROJECT-ID] 是您的 GCP 專案 ID,graphviz 是上述的容器名稱,graphviz-web 則是服務名稱。

    如果是部署到 Cloud Run,出現「allow unauthenticated」(允許未經驗證) 提示時請回覆 Y。如要進一步瞭解以 IAM 為基礎的驗證,請參閱管理存取權一文。

    請等待部署完成,這可能需要半分鐘的時間。成功完成後,指令列會顯示服務網址。

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

立即體驗

如要開始試用您的服務,請在要求酬載中搭配 DOT 語法說明來傳送 HTTP POST 要求。

  1. 將 HTTP 要求傳送到您的服務。

    將網址複製到瀏覽器網址列,並更新 [SERVICE_DOMAIN]

    https://[SERVICE_DOMAIN]/diagram.png?dot=digraph Run { rankdir=LR Code -> Build -> Deploy -> Run }

    您可以在網頁中嵌入圖表:

    <img src="https://[SERVICE_DOMAIN]/diagram.png?dot=digraph Run { rankdir=LR Code -> Build -> Deploy -> Run }" />

    若部署到 Cloud Run on GKE 的服務沒有自訂網域,則需要修改這個指令。

    1. 如果您還沒有叢集的輸入閘道,請先建立一個。

      export GATEWAY_IP="$(kubectl get svc istio-ingressgateway \
         --namespace istio-system \
         --output 'jsonpath={.status.loadBalancer.ingress[0].ip}')"
    2. 使用網址中的 GATEWAY_IP 位址來執行 curl 指令。

      curl -G -H "Host: [SERVICE_DOMAIN]" https://$GATEWAY_IP/diagram.png \
         --data-urlencode "dot=digraph Run { rankdir=LR Code -> Build -> Deploy -> Run }" \
         > diagram.png
  2. 使用任何支援 PNG 檔案的應用程式 (例如 Chrome) 開啟產生的 diagram.png 檔案。

    它看起來應該像這樣:

    顯示程式碼從建構、部署到「執行」的階段流程圖表。
    來源:DOT 說明

清除所用資源

刪除專案

如要避免付費,最簡單的方法就是刪除您為了本教學課程所建立的專案。

刪除專案:

  1. 前往 GCP Console 中的「Manage resources」(管理資源) 頁面。

    前往「Manage resources」(管理資源) 頁面

  2. 在專案清單中選取您要刪除的專案,然後按一下「Delete」(刪除) 圖示
  3. 在對話方塊中輸入專案 ID,然後按一下 [Shut down] (關閉) 以刪除專案。

刪除 Cloud Run 服務

請注意,刪除 Cloud Run 服務不會移除儲存在 Cloud Storage 中的任何資源。您必須個別刪除這些資源。

如要刪除您在本教學課程中部署的服務:

gcloud beta run services delete [SERVICE_NAME]

其中 [SERVICE_NAME] 是您選擇的服務名稱。

您也可以從 Google Cloud Platform 主控台刪除 Cloud Run 或 Cloud Run on GKE 服務。

後續步驟

  • 利用您的 graphviz 應用程式進行實驗:
    • 針對可將不同演算法套用至圖表產生作業的其他 graphviz 公用程式,新增對這類公用程式的支援。
    • 將圖表儲存到 Cloud Storage。是否要儲存圖片或 DOT 語法?
    • 利用 Cloud Natural Language 實作內容濫用防護。
  • 歡迎自行試用其他 Google Cloud Platform 功能。請參考我們的教學課程