使用 Cloud Run 提供動態內容並託管微服務

將 Cloud Run 與 Firebase Hosting 配對,以產生和提供動態內容或將 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 還支援許多其他語言

  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))
    }
    

    此程式碼建立一個基本的 Web 伺服器,該伺服器偵聽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.18.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);
    });
    

    此程式碼建立一個基本的 Web 伺服器,該伺服器偵聽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)))
    

    此程式碼建立一個基本的 Web 伺服器,該伺服器偵聽PORT環境變數定義的連接埠。

您的應用程式已完成並準備好進行容器化並上傳到 Container Registry。

爪哇

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

    請注意,我們只需執行此操作即可在下一個步驟中建立新的 Web 專案。稍後將介紹的 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. 更新src/main/java/com/example/helloworld/HelloworldApplication.java中的SpringBootApplication類,加入@RestController來處理/映射,並新增@Value欄位來提供TARGET環境變數:

    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);
      }
    }
    

    此程式碼建立一個基本的 Web 伺服器,該伺服器偵聽PORT環境變數定義的連接埠。

您的應用程式已完成並準備好進行容器化並上傳到 Container Registry。

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

  1. 透過在與原始檔案相同的目錄中建立一個名為Dockerfile的新檔案來容器化範例應用程式。將以下內容複製到您的文件中。

    # 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
    

    爪哇

    # 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 中,並且可以根據需要重新使用。

請注意,您可以使用本機安裝的 Docker 版本來本機建置容器,而不是 Cloud Build。

步驟 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. 稍等片刻,讓部署完成。成功後,命令列將顯示服務 URL。例如: https://helloworld- RANDOM_HASH -us-central1.a.run.app

  2. 透過在 Web 瀏覽器中開啟服務 URL 來存取已部署的容器。

下一步將引導您了解如何從 Firebase 託管 URL存取此容器化應用,以便它可以為您的 Firebase 託管網站產生動態內容。

步驟 4:將託管請求定向到您的容器化應用程式

透過重寫規則,您可以將與特定模式相符的請求定向到單一目標。

以下範例顯示如何引導來自託管網站上的頁面/helloworld的所有請求以觸發helloworld容器執行個體的啟動和運行。

  1. 確保:

    有關安裝 CLI 和初始化 Hosting 的詳細說明,請參閱Hosting 入門指南

  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

現在可以透過以下 URL 存取您的容器:

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

  • 任何連接的自訂域
    CUSTOM_DOMAIN /

有關重寫規則的更多詳細信息,請訪問託管配置頁面。您也可以了解各種託管配置的回應優先順序

本地測試

在開發過程中,您可以在本地運行和測試容器鏡像。有關詳細說明,請造訪Cloud Run 文件

下一步