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

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

始める前に

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

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

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

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

  3. Google Cloud Platform プロジェクトに対して課金が有効になっていることを確認します。 詳しくは、課金を有効にする方法をご覧ください。

  4. Cloud Run API を有効にします。
  5. Cloud SDK をインストールし、初期化します
  6. gcloud ベータ版コンポーネントをインストールします。
    gcloud components install beta
  7. コンポーネントを更新します。
    gcloud components update

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

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

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

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

  1. Java SE 8 以降の JDKCURL をインストールします。これで、新しいウェブ プロジェクトを作成できます。後述する Dockerfile により、すべての依存関係がコンテナに読み込まれます。

  2. コンソールから 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 プロジェクトが作成されます。

  3. src/main/java/com/example/helloworld/HelloworldApplication.javaHelloworldApplication クラスを更新します。@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 環境変数で定義されたポートをリッスンする基本的なウェブサーバーを作成します。

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

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

C#

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

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

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

  4. Program.csCreateWebHostBuilder 定義を更新します。.UseUrls() にポートの URL を指定して、処理中のポートを定義します。このサンプルでは 8080 を使用していますが、他のポートを使用することもできます。サーバーがリッスンするポートを指定する必要があります。

    using System;
    using Microsoft.AspNetCore;
    using Microsoft.AspNetCore.Hosting;
    
    namespace helloworld_csharp
    {
        public class Program
        {
            public static void Main(string[] args)
            {
                CreateWebHostBuilder(args).Build().Run();
            }
    
            public static IWebHostBuilder CreateWebHostBuilder(string[] args)
            {
                string port = Environment.GetEnvironmentVariable("PORT") ?? "8080";
                string url = String.Concat("http://0.0.0.0:", port);
    
                return WebHost.CreateDefaultBuilder(args)
                    .UseStartup<Startup>().UseUrls(url);
            }
        }
    }
    
  5. TARGET 環境変数を読み込み、結果を返すように、app.Run(...) ステートメント(Startup.cs 内)を更新します。

    using System;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.AspNetCore.Http;
    using Microsoft.Extensions.DependencyInjection;
    
    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, IHostingEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
    
                app.Run(async (context) =>
                {
                    var target = Environment.GetEnvironmentVariable("TARGET") ?? "World";
                    await context.Response.WriteAsync($"Hello {target}\n");
                });
            }
        }
    }
    

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

これでアプリは完成しました。このアプリをコンテナ化し、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);
    

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

これでアプリは完成しました。このアプリをコンテナ化し、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. Gemfile.lock という名前のファイルを作成し、そのファイルに次の内容をコピーします。

    GEM
      remote: https://rubygems.org/
      specs:
        mustermann (1.0.2)
        rack (2.0.6)
        rack-protection (2.0.3)
          rack
        sinatra (2.0.3)
          mustermann (~> 1.0)
          rack (~> 2.0)
          rack-protection (= 2.0.3)
          tilt (~> 2.0)
        tilt (2.0.8)
    
    PLATFORMS
      ruby
    
    DEPENDENCIES
      rack (>= 2.0.6)
      sinatra
    
    BUNDLED WITH
       1.17.1
    

これでアプリは完成しました。このアプリをコンテナ化し、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 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
COPY . .

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

# 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 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 Python image.
# https://hub.docker.com/_/python
FROM python:3.7

# 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

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 .NET image.
# https://hub.docker.com/r/microsoft/dotnet
FROM microsoft/dotnet:2.2-sdk

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

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

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

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

PHP

# Use the official PHP 7.2 image.
# https://hub.docker.com/_/php
FROM php:7.2-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"

Ruby

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

# 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

FROM golang:1.11

WORKDIR /go/src/invoke

COPY invoke.go .
RUN go install -v

COPY . .

CMD ["invoke"]

その他

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

ただし、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 beta run deploy --image gcr.io/[PROJECT-ID]/helloworld
    

    Java

    gcloud beta run deploy --image gcr.io/[PROJECT-ID]/helloworld \
          --set-env-vars="JAVA_TOOL_OPTIONS=-XX:MaxRAM=256m"
    

    環境変数の割り当て JAVA_TOOL_OPTIONS=-XX:MaxRAM=256m で、JVM メモリの既知の問題が解決されます。

    [PROJECT-ID] は実際の GCP プロジェクト ID です。gcloud config get-value project で取得できます。

    プロンプトが表示されたら、リージョン(us-central1 など)を選択して、サービス名を確認します。[未認証の呼び出しを許可] に y と応答します。

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

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

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

クリーンアップ

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

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

  1. GCP Console で [プロジェクト] ページに移動します。

    プロジェクト ページに移動

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

次のステップ

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

このページは役立ちましたか?評価をお願いいたします。

フィードバックを送信...