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

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

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

始める前に

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

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

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

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

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

  4. Cloud Build and Cloud Run API を有効にします。

    API を有効にする

  5. Cloud SDK をインストールし、初期化します
  6. コンポーネントを更新します。
    gcloud components update

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

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

Go

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

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

    go mod init helloworld-go
        
  3. 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)))
        

    このコードは、「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) {
        	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() {
        	http.HandleFunc("/", handler)
    
        	port := os.Getenv("PORT")
        	if port == "" {
        		port = "8080"
        	}
    
        	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.
    # This allows the container build to reuse cached dependencies.
    COPY go.* ./
    RUN go mod download

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

    # Build the binary.
    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 ファイルを追加してコンテナ イメージからファイルを除外します。ファイルを Cloud Build にアップロードしないようにするには、.gcloudignore ファイルを追加します。

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 app:app
    

.dockerignore ファイルを追加してコンテナ イメージからファイルを除外します。ファイルを Cloud Build にアップロードしないようにするには、.gcloudignore ファイルを追加します。

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","-Dserver.port=${PORT}","-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"]
    

.dockerignore ファイルを追加してコンテナ イメージからファイルを除外します。ファイルを Cloud Build にアップロードしないようにするには、.gcloudignore ファイルを追加します。

Dockerfile
    README.md
    **/obj/
    **/bin/

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 ファイルを追加してコンテナ イメージからファイルを除外します。ファイルを Cloud Build にアップロードしないようにするには、.gcloudignore ファイルを追加します。

Dockerfile
    README.md
    vendor
    

Ruby

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

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

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

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

Shell

# 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:1.12 as builder

    # Copy local code to the container image.
    WORKDIR /go/src/github.com/knative/docs/helloworld-shell
    COPY invoke.go .

    # Build the 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 invoke

    # The official Alpine base image
    # https://hub.docker.com/_/alpine
    # 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:3.10

    # Copy Go binary
    COPY --from=builder /go/src/github.com/knative/docs/helloworld-shell/invoke /invoke
    COPY script.sh .

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

その他

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 は、次のリージョンで利用できます。

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

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 する方法については、以下をご覧ください。