コンソールへ移動

Cloud Run を使用した動的コンテンツの配信とマイクロサービスのホスティング

Cloud Run と Firebase Hosting を組み合わせれば、動的コンテンツを生成して配信できるほか、REST API をマイクロサービスとしてビルドすることもできます。

Cloud Run を使用すると、コンテナ イメージにパッケージ化されたアプリケーションをデプロイできます。その後、Firebase Hosting を使用して HTTPS リクエストをリダイレクトすることで、リクエストに応じてコンテナ化アプリを実行できます。

  • Cloud Run は Go、Node.js、Python、Java などの複数の言語をサポートしているため、プログラミング言語とフレームワークを柔軟に選ぶことができます。
  • 受信リクエストに合わせて、コンテナ イメージが自動的に水平方向にスケールされます。需要が減少した場合はスケールダウンします。
  • 料金は、リクエスト処理中に使用した CPU、メモリ、ネットワークに対してのみ発生します。

Cloud Run と Firebase Hosting の統合のユースケースとサンプルについては、サーバーレスの概要をご覧ください。


このガイドでは、以下の方法について説明します。

  1. 単純な Hello World アプリケーションを記述する
  2. アプリをコンテナ化して Container Registry にアップロードする
  3. コンテナ イメージを Cloud Run にデプロイする
  4. コンテナ化されたアプリに Hosting リクエストをリダイレクトする

動的コンテンツの配信パフォーマンスを向上させるために、必要に応じてキャッシュ設定を調整できます。

始める前に

Cloud Run を使用する前に、請求先アカウントの設定、Cloud Run API の有効化、gcloud コマンドライン ツールのインストールなど、いくつかの準備を行う必要があります。

プロジェクトの請求先を設定する

Cloud Run では無料の使用量割り当てが提供されていますが、それでも Cloud Run を使用または試用するには Firebase プロジェクトに関連付けられた請求先アカウントが必要です。

API を有効にして SDK をインストールする

  1. Google API Console で Cloud Run API を有効にします。

    1. Google API Console で Cloud Run API ページを開きます。

    2. プロンプトが表示されたら、Firebase プロジェクトを選択します。

    3. Cloud Run API ページで [有効にする] をクリックします。

  2. Cloud SDK をインストールして初期化します。

gcloud ベータ版コンポーネントをインストールする

  1. 次のコマンドを実行して gcloud ベータ版コンポーネントをインストールします。

    gcloud components install beta
  2. コンポーネントを更新します。

    gcloud components update
  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("Hello world received a request.")
    	target := os.Getenv("TARGET")
    	if target == "" {
    		target = "World"
    	}
    	fmt.Fprintf(w, "Hello %s!\n", target)
    }
    
    func main() {
    	log.Print("Hello world sample started.")
    
    	http.HandleFunc("/", handler)
    
    	port := os.Getenv("PORT")
    	if port == "" {
    		port = "8080"
    	}
    
    	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.16.4"
      }
    }
    
  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}!`);
    });
    
    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 を使用して新しい空のウェブ プロジェクトを作成し、コマンドを解凍します。

    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 と、TARGET 環境変数を提供する @Value フィールドを追加します。

    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 message;
    
    	@RestController
    	class HelloworldController {
    		@GetMapping("/")
    		String hello() {
    			return "Hello " + message + "!";
    		}
    	}
    
    	public static void main(String[] args) {
    		SpringApplication.run(HelloworldApplication.class, args);
    	}
    }
    

    このコードは、PORT 環境変数で定義されたポートをリッスンする基本的なウェブサーバーを作成します。

これでアプリは完成しました。このアプリをコンテナ化し、Container Registry にアップロードします。

ステップ 2: アプリをコンテナ化して Container Registry にアップロードする

  1. ソースファイルと同じディレクトリに Dockerfile という名前の新しいファイルを作成することで、サンプルアプリをコンテナ化します。次の内容をファイルにコピーします。

    Go

    # Use the offical Golang image to create a build artifact.
    # This is based on Debian and sets the GOPATH to /go.
    # https://hub.docker.com/_/golang
    FROM golang as builder
    
    # Copy local code to the container image.
    WORKDIR /go/src/github.com/knative/docs/helloworld
    COPY . .
    
    # Build the outyet command inside the container.
    # (You may fetch or manage dependencies here,
    # either manually or with a tool like "godep".)
    RUN CGO_ENABLED=0 GOOS=linux go build -v -o helloworld
    
    # Use a Docker multi-stage build to create a lean production image.
    # https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
    FROM alpine
    
    # Copy the binary to the production image from the builder stage.
    COPY --from=builder /go/src/github.com/knative/docs/helloworld/helloworld /helloworld
    
    # Service must listen to $PORT environment variable.
    # This default value facilitates local development.
    ENV PORT 8080
    
    # Run the web service on container startup.
    CMD ["/helloworld"]
    

    Node.js

    # Use the official Node.js 10 image.
    # https://hub.docker.com/_/node
    FROM node:10
    
    # 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 . .
    
    # Service must listen to $PORT environment variable.
    # This default value facilitates local development.
    ENV PORT 8080
    
    # Run the web service on container startup.
    CMD [ "npm", "start" ]
    

    Python

    # Use the official Python image.
    # https://hub.docker.com/_/python
    FROM python
    
    # Copy local code to the container image.
    ENV APP_HOME /app
    WORKDIR $APP_HOME
    COPY . .
    
    # Install production dependencies.
    RUN pip install Flask gunicorn
    
    # Service must listen to $PORT environment variable.
    # This default value facilitates local development.
    ENV PORT 8080
    
    # 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 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
    
    # Service must listen to $PORT environment variable.
    # This default value facilitates local development.
    ENV PORT 8080
    
    # Run the web service on container startup.
    CMD ["java","-Djava.security.egd=file:/dev/./urandom","-Dserver.port=${PORT}","-jar","/helloworld.jar"]
    

  2. Dockerfile を含むディレクトリから次のコマンドを実行し、Cloud Build を使用してコンテナ イメージをビルドします。

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

    ビルドが成功すると、次のイメージ名を含む SUCCESS メッセージが表示されます。
    gcr.io/projectID/helloworld

これでコンテナ イメージが Container Registry に保存されました。このイメージは必要に応じて再利用できます。

Cloud Build の代わりに、Docker のローカルにインストールされたバージョンを使用してコンテナをローカルにビルドすることもできます。

ステップ 3: コンテナ イメージを Cloud Run にデプロイする

  1. 次のコマンドを使用してデプロイします。

    gcloud beta run deploy --image gcr.io/projectID/helloworld

  2. プロンプトが表示されたら、次の操作を行います。

    • リージョンを選択します(例: us-central1)。
    • サービス名を確認します(例: helloworld)。
    • 未認証の呼び出しを許可するかどうか尋ねられたら、「Y」と入力して許可します。
  3. デプロイが完了するまでしばらく待ちます。デプロイが成功すると、コマンドラインに次のようなサービス URL が表示されます。

    https://helloworld-random_hash-us-central1.a.run.app
  4. ウェブブラウザでこのサービス URL を開き、デプロイしたコンテナにアクセスします。

次のステップでは、このコンテナ化されたアプリに Firebase Hosting URL からアクセスする方法を示します。これにより、Firebase でホストされているサイトの動的コンテンツを生成できます。

ステップ 4: コンテナ化されたアプリに Hosting リクエストをリダイレクトする

rewrite ルールを使用して、特定のパターンに一致するリクエストを単一の宛先に向けることができます。

次の例は、Hosting サイトのページ /helloworld からのすべてのリクエストをリダイレクトし、リクエストに応じて helloworld コンテナ インスタンスを起動して実行する方法を示します。

  1. 以下のことを確認します。

    CLI のインストールと Hosting の初期化の詳細については、Hosting のスタートガイドをご覧ください。

  2. firebase.json ファイルを開きます。

  3. hosting セクションに次の rewrite 構成を追加します。

    "hosting": {
      // ...
    
      // Add the "rewrites" attribute within "hosting"
      "rewrites": [ {
        "source": "**",
        "run": {
          "serviceId": "helloworld",  // "service name" (from when you deployed the container image)
          "region": "us-central1"     // optional (if omitted, default is us-central1)
        }
      } ]
    }
    
  4. プロジェクト ディレクトリのルートから次のコマンドを実行して、ホスティング構成をサイトにデプロイします。

    firebase deploy

次の URL でコンテナに接続できるようになりました。

  • Firebase のサブドメイン: projectID.web.app/helloworldprojectID.firebaseapp.com/helloworld

  • 接続されたカスタム ドメイン: custom-domain/helloworld

rewrite ルールの詳細については、Hosting 構成ページをご覧ください。さまざまな Hosting 構成のレスポンスの優先順位についての説明もあります。

ローカルでテストする

開発中に、コンテナ イメージをローカルで実行してテストできます。詳しい手順については、Cloud Run のドキュメントをご覧ください。

次のステップ