Показ динамического контента и размещение микросервисов с помощью Cloud Run

Используйте Cloud Run вместе с Firebase Hosting, чтобы генерировать и показывать динамический контент или создавать REST API в виде микросервисов.

С помощью Cloud Run можно развернуть приложение, упакованное в образ контейнера. Затем с помощью Firebase Hosting вы можете настроить запуск контейнеризированного приложения по HTTPS-запросам.

  • Cloud Run поддерживает несколько языков (включая Go, Node.js, Python и Java), что дает вам возможность использовать язык программирования и фреймворк по вашему выбору.
  • Cloud Run автоматически и горизонтально масштабирует образ контейнера для обработки полученных запросов, а затем уменьшает масштаб при снижении спроса.
  • Вы платите только за ЦП, память и сеть, которые используются при обработке запросов.

Примеры использования и образцы кода для 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 Run, вам все равно потребуется аккаунт Cloud Billing, связанный с вашим проектом Firebase.

Как включить API и установить SDK

  1. Включите Cloud Run API в Google API Console:

    1. Откройте страницу Cloud Run API в Google API Console.

    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
    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, а затем распакуйте его:

    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, выполнив следующую команду в каталоге, где находится Dockerfile:

    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 при получении запросов со страницы /helloworld сайта Hosting.

  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.

Дальнейшие действия