運用 Cloud Run 提供動態內容和託管微服務

將 Cloud Run 與 Firebase 託管搭配使用,即可產生並提供動態內容,或是以微服務的形式建構 REST API。

使用 Cloud Run 即可部署封裝在容器映像檔中的應用程式。接著,使用 Firebase 代管,您就能引導 HTTPS 要求觸發容器化應用程式。

  • Cloud Run 支援多種語言 (包括 Go、Node.js、Python 和 Java),讓您能靈活使用您選擇的程式設計語言與架構。
  • Cloud Run 會自動及水平擴充容器映像檔來處理收到的要求,然後在需求減少時縮減規模。
  • 您只須為處理要求期間使用的 CPU、記憶體和網路付費

如需查看與 Firebase 託管整合的 Cloud Run 用途和範例,請參閱我們的無伺服器總覽


本指南說明如何:

  1. 編寫簡單的 Hello World 應用程式
  2. 將應用程式容器化並上傳至 Container Registry
  3. 將容器映像檔部署至 Cloud Run
  4. 將代管要求直接傳送至容器化應用程式

請注意,如要改善提供動態內容的效能,您可以選擇調整快取設定

事前準備

使用 Cloud Run 前,您必須先完成幾項初始工作,包括設定 Cloud Billing 帳戶、啟用 Cloud Run API,以及安裝 gcloud 指令列工具。

設定專案帳單

Cloud Run 提供免費用量配額,但您必須具備與 Firebase 專案相關聯的 Cloud Billing 帳戶,才能使用或試用 Cloud Run。

啟用 API 並安裝 SDK

  1. 在 Google API 控制台中啟用 Cloud Run API:

    1. 在 Google API 控制台中開啟 Cloud Run API 頁面

    2. 畫面出現提示時,請選取 Firebase 專案。

    3. 在 Cloud Run API 頁面按一下「啟用」

  2. 安裝並初始化 Cloud SDK。

  3. 確認 gcloud 工具已設為正確的專案:

    gcloud config list

步驟 1:編寫範例應用程式

請注意,除了下列範例中顯示的語言,Cloud Run 也支援許多其他語言

Go

  1. 建立名為 helloworld-go 的新目錄,然後將目錄變更為該目錄:

    mkdir helloworld-go
    cd helloworld-go
  2. 建立名為 helloworld.go 的新檔案,然後新增下列程式碼:

    package main
    
    import (
    	"fmt"
    	"log"
    	"net/http"
    	"os"
    )
    
    func handler(w http.ResponseWriter, r *http.Request) {
    	log.Print("helloworld: received a request")
    	target := os.Getenv("TARGET")
    	if target == "" {
    		target = "World"
    	}
    	fmt.Fprintf(w, "Hello %s!\n", target)
    }
    
    func main() {
    	log.Print("helloworld: starting server...")
    
    	http.HandleFunc("/", handler)
    
    	port := os.Getenv("PORT")
    	if port == "" {
    		port = "8080"
    	}
    
    	log.Printf("helloworld: listening on port %s", port)
    	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
    }
    

    這個程式碼會建立一個基本網路伺服器,用於監聽 PORT 環境變數定義的連接埠。

您的應用程式已經完成,您可以開始將這個應用程式容器化並上傳到 Container Registry。

Node.js

  1. 建立名為 helloworld-nodejs 的新目錄,然後將目錄變更為該目錄:

    mkdir helloworld-nodejs
    cd helloworld-nodejs
  2. 使用以下內容建立 package.json 檔案:

    {
      "name": "knative-serving-helloworld",
      "version": "1.0.0",
      "description": "Simple hello world sample in Node",
      "main": "index.js",
      "scripts": {
        "start": "node index.js"
      },
      "author": "",
      "license": "Apache-2.0",
      "dependencies": {
        "express": "^4.19.2"
      }
    }
    
  3. 建立名為 index.js 的新檔案,然後新增下列程式碼:

    const express = require('express');
    const app = express();
    
    app.get('/', (req, res) => {
      console.log('Hello world received a request.');
    
      const target = process.env.TARGET || 'World';
      res.send(`Hello ${target}!\n`);
    });
    
    const port = process.env.PORT || 8080;
    app.listen(port, () => {
      console.log('Hello world listening on port', port);
    });
    

    這個程式碼會建立一個基本網路伺服器,用於監聽 PORT 環境變數定義的連接埠。

您的應用程式已經完成,您可以開始將這個應用程式容器化並上傳到 Container Registry。

Python

  1. 建立名為 helloworld-python 的新目錄,然後將目錄變更為該目錄:

    mkdir helloworld-python
    cd helloworld-python
  2. 建立名為 app.py 的新檔案,然後新增下列程式碼:

    import os
    
    from flask import Flask
    
    app = Flask(__name__)
    
    @app.route('/')
    def hello_world():
        target = os.environ.get('TARGET', 'World')
        return 'Hello {}!\n'.format(target)
    
    if __name__ == "__main__":
        app.run(debug=True,host='0.0.0.0',port=int(os.environ.get('PORT', 8080)))
    

    這個程式碼會建立一個基本網路伺服器,用於監聽 PORT 環境變數定義的連接埠。

您的應用程式已經完成,您可以開始將這個應用程式容器化並上傳到 Container Registry。

Java

  1. 安裝 Java SE 8 或更新版本的 JDKCURL

    請注意,我們只需在下個步驟執行此操作,即可建立新的網路專案。Dockerfile 會將所有依附元件載入容器,稍後會說明。

  2. 在主控台中,使用 cURL,然後解壓縮指令來建立新的空白 Web 專案:

    curl https://start.spring.io/starter.zip \
        -d dependencies=web \
        -d name=helloworld \
        -d artifactId=helloworld \
        -o helloworld.zip
    unzip helloworld.zip

    這項操作會建立 SpringBoot 專案。

  3. 新增 @RestController 來處理 / 對應,並新增 @Value 欄位來提供 TARGET 環境變數,藉此更新 src/main/java/com/example/helloworld/HelloworldApplication.java 中的 SpringBootApplication 類別:

    package com.example.helloworld;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @SpringBootApplication
    public class HelloworldApplication {
    
      @Value("${TARGET:World}")
      String target;
    
      @RestController
      class HelloworldController {
        @GetMapping("/")
        String hello() {
          return "Hello " + target + "!";
        }
      }
    
      public static void main(String[] args) {
        SpringApplication.run(HelloworldApplication.class, args);
      }
    }
    

    這個程式碼會建立一個基本網路伺服器,用於監聽 PORT 環境變數定義的連接埠。

您的應用程式已經完成,您可以開始將這個應用程式容器化並上傳到 Container Registry。

步驟 2:將應用程式容器化並上傳至 Container Registry

  1. 在來源檔案所在的目錄中建立名為 Dockerfile 的新檔案,藉此將範例應用程式容器化。將下列內容複製到您的檔案中。

    Go

    # Use the official Golang image to create a build artifact.
    # This is based on Debian and sets the GOPATH to /go.
    FROM golang:latest as builder
    
    ARG TARGETOS
    ARG TARGETARCH
    
    # Create and change to the app directory.
    WORKDIR /app
    
    # Retrieve application dependencies using go modules.
    # Allows container builds to reuse downloaded dependencies.
    COPY go.* ./
    RUN go mod download
    
    # Copy local code to the container image.
    COPY . ./
    
    # Build the binary.
    # -mod=readonly ensures immutable go.mod and go.sum in container builds.
    RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -mod=readonly -v -o server
    
    # Use the official Alpine image for a lean production container.
    # https://hub.docker.com/_/alpine
    # https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
    FROM alpine:3
    RUN apk add --no-cache ca-certificates
    
    # Copy the binary to the production image from the builder stage.
    COPY --from=builder /app/server /server
    
    # Run the web service on container startup.
    CMD ["/server"]
    

    Node.js

    # Use the official lightweight Node.js 12 image.
    # https://hub.docker.com/_/node
    FROM node:12-slim
    
    # Create and change to the app directory.
    WORKDIR /usr/src/app
    
    # Copy application dependency manifests to the container image.
    # A wildcard is used to ensure both package.json AND package-lock.json are copied.
    # Copying this separately prevents re-running npm install on every code change.
    COPY package*.json ./
    
    # Install production dependencies.
    RUN npm install --only=production
    
    # Copy local code to the container image.
    COPY . ./
    
    # Run the web service on container startup.
    CMD [ "npm", "start" ]
    

    Python

    # Use the official lightweight Python image.
    # https://hub.docker.com/_/python
    FROM python:3.7-slim
    
    # Allow statements and log messages to immediately appear in the Knative logs
    ENV PYTHONUNBUFFERED True
    
    # Copy local code to the container image.
    ENV APP_HOME /app
    WORKDIR $APP_HOME
    COPY . ./
    
    # Install production dependencies.
    RUN pip install Flask gunicorn
    
    # Run the web service on container startup. Here we use the gunicorn
    # webserver, with one worker process and 8 threads.
    # For environments with multiple CPU cores, increase the number of workers
    # to be equal to the cores available.
    CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 app:app
    

    Java

    # Use the official maven/Java 8 image to create a build artifact: https://hub.docker.com/_/maven
    FROM maven:3.5-jdk-8-alpine as builder
    
    # Copy local code to the container image.
    WORKDIR /app
    COPY pom.xml .
    COPY src ./src
    
    # Build a release artifact.
    RUN mvn package -DskipTests
    
    # Use the Official OpenJDK image for a lean production stage of our multi-stage build.
    # https://hub.docker.com/_/openjdk
    # https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
    FROM openjdk:8-jre-alpine
    
    # Copy the jar to the production image from the builder stage.
    COPY --from=builder /app/target/helloworld-*.jar /helloworld.jar
    
    # Run the web service on container startup.
    CMD ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/helloworld.jar"]
    

  2. 從包含 Dockerfile 的目錄執行下列指令,透過 Cloud Build 建構容器映像檔:

    gcloud builds submit --tag gcr.io/PROJECT_ID/helloworld

    成功執行後,系統會顯示包含映像檔名稱的「成功」訊息
    (gcr.io/PROJECT_ID/helloworld)。

容器映像檔現已儲存在 Container Registry 中,如有需要,可以重複使用。

請注意,您不必使用 Cloud Build,也可使用安裝於本機的 Docker 版本在本機建構容器

步驟 3:將容器映像檔部署至 Cloud Run

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

    gcloud run deploy --image gcr.io/PROJECT_ID/helloworld

  2. 出現提示時:

為獲得最佳效能,請透過下列區域將 Cloud Run 服務與託管服務存放在相同位置:

  • us-west1
  • us-central1
  • us-east1
  • europe-west1
  • asia-east1

下列地區支援從託管重新寫入 Cloud Run 的功能:

  • asia-east1
  • asia-east2
  • asia-northeast1
  • asia-northeast2
  • asia-northeast3
  • asia-south1
  • asia-south2
  • asia-southeast1
  • asia-southeast2
  • australia-southeast1
  • australia-southeast2
  • europe-central2
  • europe-north1
  • europe-southwest1
  • europe-west1
  • europe-west12
  • europe-west2
  • europe-west3
  • europe-west4
  • europe-west6
  • europe-west8
  • europe-west9
  • me-central1
  • me-west1
  • northamerica-northeast1
  • northamerica-northeast2
  • southamerica-east1
  • southamerica-west1
  • us-central1
  • us-east1
  • us-east4
  • us-east5
  • us-south1
  • us-west1
  • us-west2
  • us-west3
  • us-west4
  • us-west1
  • us-central1
  • us-east1
  • europe-west1
  • asia-east1
  1. 稍候片刻,等待部署作業完成。部署成功之後,指令列會顯示服務網址。例如: https://helloworld-RANDOM_HASH-us-central1.a.run.app

  2. 在網路瀏覽器中開啟服務網址,以造訪您所部署的容器。

下一步將逐步說明如何透過 Firebase 託管網址存取這個容器化應用程式,使其能夠為 Firebase 託管的網站產生動態內容。

步驟 4:將要求直接託管至容器化應用程式

透過重新編寫規則,您可以將符合特定模式的要求導向至單一目的地。

以下範例說明如何引導託管網站上 /helloworld 頁面的所有要求,觸發啟動並執行 helloworld 容器執行個體。

  1. 請確認下列事項:

    如需安裝 CLI 和初始化主機的詳細操作說明,請參閱代管入門指南

  2. 開啟 firebase.json 檔案

  3. hosting 區段下方新增下列 rewrite 設定:

    "hosting": {
      // ...
    
      // Add the "rewrites" attribute within "hosting"
      "rewrites": [ {
        "source": "/helloworld",
        "run": {
          "serviceId": "helloworld",  // "service name" (from when you deployed the container image)
          "region": "us-central1",    // optional (if omitted, default is us-central1)
          "pinTag": true              // optional (see note below)
        }
      } ]
    }
    
  4. 在專案目錄的根目錄中執行下列指令,將託管設定部署至網站:

    firebase deploy --only hosting

您的容器現在可以透過下列網址連線:

  • 您的 Firebase 子網域:
    PROJECT_ID.web.app/PROJECT_ID.firebaseapp.com/

  • 任何已連結的自訂網域
    CUSTOM_DOMAIN/

如要進一步瞭解重寫規則,請前往「託管」設定頁面。您也可以瞭解各種託管設定的回應優先順序

在本機測試

在開發期間,您可以在本機執行及測試容器映像檔。如需詳細操作說明,請參閱 Cloud Run 說明文件

後續步驟