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

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

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

בסקירה הכללית על Serverless תוכלו למצוא דוגמאות לתרחישי שימוש ולדוגמיות של Cloud Run בשילוב עם Firebase Hosting.


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

  1. כתיבת אפליקציה פשוטה של Hello World
  2. העברת אפליקציה לקונטיינר והעלאה שלה אל Artifact 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 Console:

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

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

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

  2. מתקינים ומפעילים את Cloud SDK.

  3. מוודאים שהכלי gcloud מוגדר לפרויקט הנכון:

    gcloud config list

שלב 1: כתיבת האפליקציה לדוגמה

שימו לב ש-Cloud Run תומך בשפות רבות אחרות בנוסף לשפות שמוצגות בדוגמה הבאה.

המשך

  1. יוצרים ספרייה חדשה בשם helloworld-go ועוברים אליה:

    mkdir helloworld-go
    cd helloworld-go
    1. יוצרים קובץ חדש בשם 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.

האפליקציה מוכנה ואפשר להכיל אותה בקונטיינר ולהעלות אותה אל Artifact 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.22.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.

האפליקציה מוכנה ואפשר להכיל אותה בקונטיינר ולהעלות אותה אל Artifact 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.

האפליקציה מוכנה ואפשר להכיל אותה בקונטיינר ולהעלות אותה אל Artifact Registry.

Java

  1. מתקינים את Java SE 8 או גרסה מתקדמת יותר של JDK ואת CURL.

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

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

    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.

האפליקציה מוכנה ואפשר להכיל אותה בקונטיינר ולהעלות אותה אל Artifact Registry.

שלב 2: הפיכת אפליקציה לקובץ קונטיינר והעלאה שלה אל Artifact 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
    
    # 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

    אם הפעולה בוצעה בהצלחה, תופיע הודעת SUCCESS עם שם קובץ האימג'
    (gcr.io/PROJECT_ID/helloworld).

קובץ האימג' של הקונטיינר מאוחסן עכשיו ב-Artifact Registry ואפשר להשתמש בו שוב אם רוצים.

שימו לב שבמקום Cloud Build, אתם יכולים להשתמש בגרסה של Docker שמותקנת באופן מקומי כדי לבנות את הקונטיינר באופן מקומי.

שלב 3: פריסת קובץ האימג' של הקונטיינר ב-Cloud Run

  1. פורסים באמצעות הפקודה הבאה:

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

  2. כשמוצגת בקשה:

  3. מחכים כמה רגעים עד שהפריסה תסתיים. אם הפעולה הצליחה, כתובת ה-URL של השירות תוצג בשורת הפקודה. לדוגמה: https://helloworld-RANDOM_HASH-us-central1.a.run.app

  4. כדי לבקר בקונטיינר שנפרס, פותחים את כתובת ה-URL של השירות בדפדפן אינטרנט.

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

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

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

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

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

    במדריך לתחילת השימוש ב-Hosting מוסבר בפירוט איך מתקינים את ה-CLI ומפעילים את 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. כדי לפרוס את הגדרות האירוח באתר, מריצים את הפקודה הבאה מהרמה הבסיסית (root) של ספריית הפרויקט:

    firebase deploy --only hosting

עכשיו אפשר לגשת למאגר התגים דרך כתובות ה-URL הבאות:

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

בדיקה מקומית

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

השלבים הבאים