クイックスタート: ビルドとデプロイ

このページでは、単純な Hello World アプリケーションを作成してコンテナ イメージにパッケージ化し、コンテナ イメージを Container Registry にアップロードしてから Cloud Run にデプロイする方法について説明します。ここでは、複数の言語でサンプルを示しますが、それ以外の言語を使用することもできます。

Qwiklabs のデモアカウントでもこのクイックスタートの手順に従うことができます。

始める前に

  1. Google アカウントにログインします。

    Google アカウントをまだお持ちでない場合は、新しいアカウントを登録します。

  2. Cloud Console のプロジェクト セレクタページで、Cloud プロジェクトを選択または作成します。

    プロジェクト セレクタのページに移動

  3. Google Cloud プロジェクトに対して課金が有効になっていることを確認します。プロジェクトに対して課金が有効になっていることを確認する方法を学習する

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

サンプル アプリケーションを作成する

Cloud Run で動作する Hello World サンプル アプリケーションを作成する手順については、ご使用の言語のタブをクリックしてください。

Go

  1. helloworld-go という名前の新しいディレクトリを作成し、そのディレクトリに移動します。

    mkdir helloworld-go
    cd helloworld-go
    
  2. go.mod ファイルを初期化して go モジュールを宣言します。

    module github.com/knative/docs/docs/serving/samples/hello-world/helloworld-go
    
    go 1.13
    
  3. 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.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)))
    

    このコードは、「Hello World」という応答メッセージでリクエストに応答します。HTTP 処理は、コンテナ内の Gunicorn ウェブサーバーによって行われます。ローカルで使用するために直接呼び出された場合、このコードは PORT 環境変数で定義されたポートをリッスンする基本的なウェブサーバーを作成します。

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

Java

Spring Boot アプリケーションを作成します。

  1. コンソールから cURL コマンドと unzip コマンドを使用して、新しい空のウェブ プロジェクトを作成します。

    curl https://start.spring.io/starter.zip \
        -d dependencies=web \
        -d javaVersion=1.8 \
        -d bootVersion=2.1.3.RELEASE \
        -d name=helloworld \
        -d artifactId=helloworld \
        -d baseDir=helloworld \
        -o helloworld.zip
    unzip helloworld.zip
    cd helloworld
    

    これで Spring Boot プロジェクトが作成されます。

  2. / マッピングを処理する @RestController と、TARGET 環境変数を指定する @Value フィールドを追加して、src/main/java/com/example/helloworld/HelloworldApplication.javaHelloworldApplication クラスを更新します。

    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 にアップロードします。

他のフレームワークと一緒に Java を Cloud Run にデプロイする場合は、SparkVert.x の Knative サンプルを確認します。

C#

  1. .NET Core SDK 3.1 をインストールします。これは、次のステップで新しいウェブ プロジェクトを作成するためだけに行います。後述する Dockerfile により、すべての依存関係がコンテナに読み込まれます。

  2. コンソールから dotnet コマンドを使用して、新しい空のウェブ プロジェクトを作成します。

    dotnet new web -o helloworld-csharp
    
  3. helloworld-csharp ディレクトリに移動します。

  4. Program.csCreateHostBuilder 定義を更新して、PORT 環境変数で定義されたポートをリッスンするようにします。

    using System;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.Hosting;
    
    namespace helloworld_csharp
    {
        public class Program
        {
            public static void Main(string[] args)
            {
                CreateHostBuilder(args).Build().Run();
            }
    
            public static IHostBuilder CreateHostBuilder(string[] args)
            {
                string port = Environment.GetEnvironmentVariable("PORT") ?? "8080";
                string url = String.Concat("http://0.0.0.0:", port);
    
                return Host.CreateDefaultBuilder(args)
                    .ConfigureWebHostDefaults(webBuilder =>
                    {
                        webBuilder.UseStartup<Startup>().UseUrls(url);
                    });
            }
        }
    }

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

  5. Startup.cs という名前のファイルを作成し、次のコードを貼り付けます。

    using System;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.AspNetCore.Http;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    
    namespace helloworld_csharp
    {
        public class Startup
        {
            // This method gets called by the runtime. Use this method to add services to the container.
            // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
            public void ConfigureServices(IServiceCollection services)
            {
            }
    
            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
    
                app.UseRouting();
    
                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapGet("/", async context =>
                    {
                        var target = Environment.GetEnvironmentVariable("TARGET") ?? "World";
                        await context.Response.WriteAsync($"Hello {target}!\n");
                    });
                });
            }
        }
    }

    このコードは、「Hello World」という応答メッセージでリクエストに応答します。

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

PHP

  1. helloworld-php という名前の新しいディレクトリを作成し、そのディレクトリに移動します。

    mkdir helloworld-php
    cd helloworld-php
    
  2. index.php という名前のファイルを作成し、次のコードを貼り付けます。

    <?php
    $target = getenv('TARGET', true) ?: 'World';
    echo sprintf("Hello %s!", $target);
    

    このコードは、「Hello World」という応答メッセージでリクエストに応答します。HTTP 処理は、コンテナ内の Apache ウェブサーバーによって行われます。

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

Ruby

  1. helloworld-ruby という名前の新しいディレクトリを作成し、そのディレクトリに移動します。

    mkdir helloworld-ruby
    cd helloworld-ruby
    
  2. app.rb という名前のファイルを作成し、次のコードを貼り付けます。

    require 'sinatra'
    
    set :bind, '0.0.0.0'
    
    get '/' do
      target = ENV['TARGET'] || 'World'
      "Hello #{target}!\n"
    end
    

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

  3. Gemfile という名前のファイルを作成し、次の内容をコピーします。

    source 'https://rubygems.org'
    
    gem 'sinatra'
    gem 'rack', '>= 2.0.6'
    
  4. Bundler 2.0 以降がインストールされていない場合は、Bundler をインストールします。

  5. 次のコマンドを実行して Gemfile.lock ファイルを生成します。

    bundle install

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

Shell

  1. helloworld-shell という名前の新しいディレクトリを作成し、そのディレクトリに移動します。

    mkdir helloworld-shell
    cd helloworld-shell
    
  2. script.sh ファイルを作成し、次の内容を追加します。

    #!/bin/sh
    echo Hello ${TARGET:=World}!
    
    

    このシェル スクリプトをすべての受信リクエストに実行するために、このサンプルでは小規模な Go プログラムを使用して基本ウェブサーバーを起動し、PORT 環境変数で定義されたポートをリッスンします。

  3. invoke.go ファイルを作成し、次の内容を追加します。

    package main
    
    import (
    	"fmt"
    	"log"
    	"net/http"
    	"os"
    	"os/exec"
    )
    
    func handler(w http.ResponseWriter, r *http.Request) {
    	log.Print("helloworld: received a request")
    
    	cmd := exec.CommandContext(r.Context(), "/bin/sh", "script.sh")
    	cmd.Stderr = os.Stderr
    	out, err := cmd.Output()
    	if err != nil {
    		w.WriteHeader(500)
    	}
    	w.Write(out)
    }
    
    func main() {
    	log.Print("helloworld: starting server...")
    
    	http.HandleFunc("/", handler)
    
    	port := os.Getenv("PORT")
    	if port == "" {
    		port = "8080"
    	}
    
    	log.Printf("helloworld: listening on %s", port)
    	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
    }
    

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

その他

Cloud Run はほとんどの言語をサポートしています。この表にない言語のサンプルについては、以下をご覧ください。

ただし、Cloud Run ではこれらのサンプルを使用しないため、service.yaml と Docker Hub に関する資料はすべて無視してください。

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

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

Go

# Use the official 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:1.13 as builder

# 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=linux 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" ]

.dockerignore ファイルを追加してコンテナ イメージからファイルを除外します。

Dockerfile
README.md
node_modules
npm-debug.log

Python

Python Dockerfile は、PORT 環境変数で定義されたポートをリッスンする Gunicorn ウェブサーバーを起動します。

# Use the official lightweight Python image.
# https://hub.docker.com/_/python
FROM python:3.7-slim

# 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

.dockerignore ファイルを追加してコンテナ イメージからファイルを除外します。

Dockerfile
README.md
*.pyc
*.pyo
*.pyd
__pycache__

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 AdoptOpenJDK for base image.
# It's important to use OpenJDK 8u191 or above that has container support enabled.
# https://hub.docker.com/r/adoptopenjdk/openjdk8
# https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
FROM adoptopenjdk/openjdk8:jdk8u202-b08-alpine-slim

# 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"]

C#

# Use Microsoft's official build .NET image.
# https://hub.docker.com/_/microsoft-dotnet-core-sdk/
FROM mcr.microsoft.com/dotnet/core/sdk:3.1-alpine AS build
WORKDIR /app

# Install production dependencies.
# Copy csproj and restore as distinct layers.
COPY *.csproj ./
RUN dotnet restore

# Copy local code to the container image.
COPY . ./
WORKDIR /app

# Build a release artifact.
RUN dotnet publish -c Release -o out

# Use Microsoft's official runtime .NET image.
# https://hub.docker.com/_/microsoft-dotnet-core-aspnet/
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-alpine AS runtime
WORKDIR /app
COPY --from=build /app/out ./

# Run the web service on container startup.
ENTRYPOINT ["dotnet", "helloworld-csharp.dll"]

ローカルの dotnet ビルド オペレーションで生成されたファイルを Cloud Build へのアップロードから除外するには、サンプルアプリのソースファイルと同じディレクトリに .gcloudignore ファイルを追加します。

**/obj/
**/bin/

これらの行が .gitignore ファイルにある場合は、.gitignore.gcloudignore 構成のデフォルトのソースであるため、この手順をスキップできます。

docker CLI を使用して、これらの行をローカル コンテナ ビルド用の .dockerignore ファイルにコピーします。

PHP

PHP Dockerfile は、PORT 環境変数で定義されたポートをリッスンする Apache ウェブサーバーを起動します。

# Use the official PHP 7.3 image.
# https://hub.docker.com/_/php
FROM php:7.3-apache

# Copy local code to the container image.
COPY index.php /var/www/html/

# Use the PORT environment variable in Apache configuration files.
RUN sed -i 's/80/${PORT}/g' /etc/apache2/sites-available/000-default.conf /etc/apache2/ports.conf

# Configure PHP for development.
# Switch to the production php.ini for production operations.
# RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
# https://hub.docker.com/_/php#configuration
RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini"

.dockerignore ファイルを追加してコンテナ イメージからファイルを除外します。

Dockerfile
README.md
vendor

Ruby

# Use the official lightweight Ruby image.
# https://hub.docker.com/_/ruby
FROM ruby:2.6-slim

# Install production dependencies.
WORKDIR /usr/src/app
COPY Gemfile Gemfile.lock ./
ENV BUNDLE_FROZEN=true
RUN gem install bundler && bundle install

# Copy local code to the container image.
COPY . ./

# Run the web service on container startup.
CMD ["ruby", "./app.rb"]

Shell

# Use the official 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:1.13 as builder

# 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 invoke.go ./

# Build the binary.
# -mod=readonly ensures immutable go.mod and go.sum in container builds.
RUN CGO_ENABLED=0 GOOS=linux 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
COPY script.sh ./

# Run the web service on container startup.
CMD ["/server"]

その他

Cloud Run はほとんどの言語をサポートしています。この表にない言語の Dockerfile のサンプルについては、以下をご覧ください。

ただし、Cloud Run では service.yaml と Docker Hub のサンプルを使用しないため、これらに関する資料は無視してください。

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

gcloud builds submit --tag gcr.io/PROJECT-ID/helloworld

PROJECT-ID は、GCP のプロジェクト ID です。gcloud config get-value project を実行すると取得できます。

成功すると、イメージ名(gcr.io/PROJECT-ID/helloworld)を含む SUCCESS メッセージが表示されます。イメージは Container Registry に保存され、必要に応じて再利用できます。

Cloud Run へのデプロイ

コンテナ イメージをデプロイするには:

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

    gcloud run deploy --image gcr.io/PROJECT-ID/helloworld --platform managed

    PROJECT-ID は、実際の GCP プロジェクト ID に置き換えてください。プロジェクト ID を表示するには、gcloud config get-value project コマンドを実行します。

    1. サービス名の入力を求められます。Enter キーを押してデフォルト名の helloworld を受け入れます。
    2. リージョンを指定するよう求められます。選択したリージョンus-central1 など)を選択します。
    3. 未認証の呼び出しを許可するように求められます。[y] と返信します。

    デプロイが完了するまで少しお待ちください。成功すると、コマンドラインにサービス URL が表示されます。

  2. ウェブブラウザでこのサービス URL を開き、デプロイしたコンテナにアクセスします。

Cloud Run のロケーション

Cloud Run はリージョナルです。つまり、Cloud Run サービスを実行するインフラストラクチャは特定のリージョンに配置され、そのリージョン内のすべてのゾーンで冗長的に利用できるように Google によって管理されます。

レイテンシ、可用性、耐久性の要件を満たしていることが、Cloud Run サービスを実行するリージョンを選択する際の主な判断材料になります。一般的には、ユーザーに最も近いリージョンを選択できますが、Cloud Run サービスで使用されている他の Google Cloud サービスのロケーションも考慮する必要があります。使用する Google Cloud サービスが複数のロケーションにまたがっていると、サービスの料金だけでなくレイテンシにも影響します。

Cloud Run は、次のリージョンで利用できます。

Tier 1 料金を適用

  • asia-east1(台湾)
  • asia-northeast1(東京)
  • asia-northeast2(大阪)
  • europe-north1(フィンランド)
  • europe-west1(ベルギー)
  • europe-west4(オランダ)
  • us-central1(アイオワ)
  • us-east1(サウスカロライナ)
  • us-east4(北バージニア)
  • us-west1(オレゴン)

Tier 2 料金を適用

  • australia-southeast1(シドニー)
  • northamerica-northeast1(モントリオール)

asia-northeast2australia-southeast1northamerica-northeast1 では、カスタム ドメインを Cloud Run(フルマネージド)サービスにマッピングできません。

Cloud Run サービスをすでに作成している場合は、Cloud Console の Cloud Run ダッシュボードにリージョンを表示できます。

お疲れさまでした。コンテナ イメージにパッケージ化されたアプリケーションが Cloud Run にデプロイされました。Cloud Run は、受信したリクエストを処理するためにコンテナ イメージを自動的に水平方向にスケーリングします。リクエスト数が減少すると、インスタンス数が少なくなります。料金は、リクエストの処理中に使用した CPU、メモリ、ネットワークに対してのみ発生します。

クリーンアップ

テスト プロジェクトの削除

サービスが使用されていない場合、Cloud Run の料金は発生しませんが、コンテナ イメージを Container Registry に保存することで課金される場合があります。イメージを削除するか、Cloud プロジェクトを削除してこのような料金が発生しないようにできます。Cloud プロジェクトを削除すると、そのプロジェクト内で使用されているすべてのリソースに対する課金が停止します。

  1. Cloud Console で [リソースの管理] ページに移動します。

    [リソースの管理] ページに移動

  2. プロジェクト リストで、削除するプロジェクトを選択し、[削除] をクリックします。
  3. ダイアログでプロジェクト ID を入力し、[シャットダウン] をクリックしてプロジェクトを削除します。

次のステップ

コードソースからコンテナをビルドし、Container Registry に push する方法については、以下をご覧ください。