הצגת תוכן דינמי ואירוח מיקרו-שירותים באמצעות Cloud Run

אפשר להתאים בין Cloud Run ל-Firebase Hosting כדי ליצור תוכן דינמי ולהציג אותו, או כדי ליצור ממשקי API ל-REST כמיקרו-שירותים.

באמצעות Cloud Run אפשר לפרוס אפליקציה שארוזה בקובץ אימג' בקונטיינר. לאחר מכן, באמצעות Firebase Hosting, תוכלו להפנות בקשות HTTPS כדי להפעיל את האפליקציה הקונטיינרים.

לדוגמה, תרחישים לדוגמה ודוגמאות של Cloud Run המשולבים עם Firebase Hosting, כדאי לעיין בסקירה הכללית שלנו ללא שרת (serverless).


במדריך הזה נסביר איך:

  1. כתיבת אפליקציה פשוטה מסוג Hello World
  2. ארגון אפליקציה בקונטיינר והעלאה שלה אל Container Registry
  3. פורסים את קובץ האימג' של הקונטיינר ב-Cloud Run
  4. הפניית בקשות Hosting לאפליקציה בקונטיינרים

שימו לב שכדי לשפר את הביצועים של הצגת תוכן דינמי, אפשר לכוונן את הגדרות המטמון (אופציונלי).

לפני שמתחילים

לפני שמשתמשים ב-Cloud Run, צריך לבצע כמה משימות ראשוניות, כולל הגדרת חשבון Cloud Billing, הפעלת ה-API של Cloud Run והתקנה של כלי שורת הפקודה gcloud.

הגדרת חיוב לפרויקט

ב-Cloud Run יש מכסת שימוש בחינם, אבל עדיין צריך להיות לכם חשבון Cloud Billing שמשויך לפרויקט Firebase כדי להשתמש בו או לנסות את Cloud Run.

הפעלת ה-API והתקנת ה-SDK

  1. מפעילים את ה-API של Cloud Run במסוף Google APIs:

    1. פותחים את דף ה-API של Cloud Run במסוף Google APIs.

    2. כשמופיעה בקשה, בוחרים את הפרויקט ב-Firebase.

    3. לוחצים על Enable בדף ה-API של Cloud Run.

  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.21.1"
      }
    }
    
  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. מתקינים את JDK של Java SE 8 ואילך ואת CURL.

    חשוב לזכור שאנחנו צריכים לעשות זאת רק כדי ליצור את פרויקט האינטרנט החדש בשלב הבא. קובץ ה-Dockerfile, שמתואר בהמשך, יטמיע את כל יחסי התלות בקונטיינר.

  2. במסוף, יוצרים פרויקט אינטרנט ריק חדש באמצעות cURL ואז מבטלים את הדחיסה של פקודות:

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

    הפקודה הזו יוצרת פרויקט SpringBoot.

  3. מעדכנים את המחלקה SpringBootApplication ב-src/main/java/com/example/helloworld/HelloworldApplication.java על ידי הוספת @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);
      }
    }
    

    הקוד הזה יוצר שרת אינטרנט בסיסי שמקשיב ביציאה שמוגדרת על ידי משתנה הסביבה 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
    
    # Copy local code to the container image.
    COPY . ./
    
    # Install dependencies and tidy up the go.mod and go.sum files.
    RUN go mod tidy
    
    # 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. יוצרים את קובץ האימג' בקונטיינר באמצעות Cloud Build על ידי הרצת הפקודה הבאה מהספרייה שמכילה את קובץ ה-Docker:

    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 אל Hosting באמצעות האזורים הבאים:

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

אפשר לכתוב מחדש ב-Cloud Run מ-Hosting באזורים הבאים:

  • 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. כדי להיכנס למאגר התגים שנפרס, פותחים את כתובת ה-URL של השירות בדפדפן אינטרנט.

בשלב הבא נסביר איך לגשת לאפליקציה בקונטיינר הזה מכתובת URL של Firebase Hosting, כדי שהיא תוכל ליצור תוכן דינמי לאתר שלכם ב-Firebase.

שלב 4: הפניית בקשות אירוח לאפליקציה בקונטיינרים

בעזרת כללי כתיבה מחדש, אפשר להפנות בקשות שתואמות לדפוסים ספציפיים ליעד יחיד.

בדוגמה הבאה מוסבר איך להפנות את כל הבקשות מהדף /helloworld באתר Hosting כדי להפעיל את המכונה של הקונטיינר helloworld ולהריץ אותה.

  1. חשוב לוודא ש:

    הוראות מפורטות להתקנת ה-CLI ולהפעלת Hosting מפורטות במדריך למתחילים בנושא Hosting.

  2. פותחים את קובץ ה-firebase.json.

  3. מוסיפים את הגדרת rewrite הבאה בקטע hosting:

    "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 הבאות:

בדף ההגדרות של Hosting תוכלו לקרוא פרטים נוספים על כללי הכתיבה מחדש. תוכלו גם לקרוא על סדר העדיפויות של התשובות בהגדרות שונות של Hosting.

בדיקה מקומית

במהלך הפיתוח, תוכלו להריץ ולבדוק את קובץ האימג' של הקונטיינר באופן מקומי. הוראות מפורטות זמינות במסמכי העזרה של Cloud Run.

השלבים הבאים