クイックスタート: 言語に固有のアプリのデプロイ

このページでは、次の方法について説明します。

  1. Hello World アプリを作成する。
  2. Cloud Build を使用して、アプリをコンテナ イメージにパッケージ化する。
  3. Google Kubernetes Engine(GKE)でクラスタを作成する。
  4. コンテナ イメージをクラスタにデプロイする。

ここでは、複数の言語でサンプルを示しますが、それ以外の言語を使用することもできます。

始める前に

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

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

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

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

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

  4. Cloud Build and Google Kubernetes Engine API を有効にします。

    API を有効にする

  5. Cloud SDK をインストールし、初期化します
  6. kubectl は、GKE で使用されるクラスタ オーケストレーション システムである Kubernetes の管理に使用されます。kubectlgcloud でインストールできます。
    gcloud components install kubectl

サンプルアプリの作成

GKE で実行される Hello World アプリの作成手順については、ご使用の言語をクリックしてください。

Go

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

    mkdir helloworld-gke
        cd helloworld-gke
        
  2. example.com/helloworld という名前の新しいモジュールを作成します。

    go mod init example.com/helloworld
        
  3. helloworld.go という名前で新しいファイルを作成し、次のコードを貼り付けます。

    package main
    
        import (
        	"fmt"
        	"log"
        	"net/http"
        	"os"
        )
    
        func main() {
        	http.HandleFunc("/", handler)
    
        	port := os.Getenv("PORT")
        	if port == "" {
        		port = "8080"
        	}
    
        	log.Printf("Listening on localhost:%s", port)
        	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
        }
    
        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)
        }
        

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

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

Node.js

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

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

    {
          "name": "gke-helloworld",
          "version": "1.0.0",
          "description": "GKE 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 環境変数で定義されたポートをリッスンするウェブサーバーを作成します。

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

Python

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

    mkdir helloworld-gke
        cd helloworld-gke
        
  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)))
        

Java

Spring Boot アプリを作成します。

  1. Java SE 8 以降の JDKcURL をインストールします。Java SE と cURL は、次のステップで新しいウェブ プロジェクトを作成する場合にのみ必要になります。Dockerfile がすべての依存関係をコンテナに読み込みます(詳細はあとで説明します)。

  2. ターミナルで、新しい空のウェブ プロジェクトを作成します。

    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-gke \
            -o helloworld-gke.zip
        unzip helloworld-gke.zip
        cd helloworld-gke
        

    新しい Spring Boot プロジェクトが helloworld-gke に作成されました。

  3. src/main/java/com/example/helloworld/HelloworldApplication.java ファイルで、/ マッピングを処理する @RestController を追加して HelloworldApplication クラスを更新します。

    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 環境変数で定義されたポートをリッスンするウェブサーバーを作成します。

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

C#

  1. .NET Core SDK 2.2 をインストールします。.NET Core SDK は、次のステップで新しいウェブ プロジェクトを作成する場合にのみ必要です。Dockerfile がすべての依存関係をコンテナに読み込みます(詳細はあとで説明します)。

  2. ターミナルで、新しい空のウェブ プロジェクトを作成します。

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

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

    // Copyright (c) 2019 Google LLC.
        //
        // Licensed under the Apache License, Version 2.0 (the "License"); you may not
        // use this file except in compliance with the License. You may obtain a copy of
        // the License at
        //
        // http://www.apache.org/licenses/LICENSE-2.0
        //
        // Unless required by applicable law or agreed to in writing, software
        // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
        // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
        // License for the specific language governing permissions and limitations under
        // the License.
    
        using System;
        using Microsoft.AspNetCore;
        using Microsoft.AspNetCore.Hosting;
    
        namespace HelloGKE
        {
            public class Program
            {
                public static void Main(string[] args)
                {
                    CreateWebHostBuilder(args).Build().Run();
                }
    
                public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
                    WebHost.CreateDefaultBuilder(args)
                        .UseStartup<Startup>()
                        .UsePortEnvironmentVariable();
            }
    
            static class ProgramExtensions
            {
                // Google Cloud Run sets the PORT environment variable to tell this
                // process which port to listen to.
                public static IWebHostBuilder UsePortEnvironmentVariable(
                    this IWebHostBuilder builder)
                {
                    string port = Environment.GetEnvironmentVariable("PORT");
                    if (!string.IsNullOrEmpty(port))
                    {
                        builder.UseUrls($"http://0.0.0.0:{port}");
                    }
                    return builder;
                }
            }
        }
        

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

PHP

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

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

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

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

Ruby

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

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

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

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

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

    source 'https://rubygems.org'
    
        gem 'sinatra'
        gem 'rack', '>= 2.0.6'
        

Cloud Build によるアプリのコンテナ化

  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:1.12 as builder
    
        # Copy local code to the container image.
        WORKDIR /app
        COPY . .
    
        # Build the command inside the container.
        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
        RUN apk add --no-cache ca-certificates
    
        # Copy the binary to the production image from the builder stage.
        COPY --from=builder /app/helloworld /helloworld
    
        # Run the web service on container startup.
        CMD ["/helloworld"]
        

    Node.js

    # Use the official lightweight Node.js 10 image.
        # https://hub.docker.com/_/node
        FROM node:10-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

    # 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 ファイルを追加します。

    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 lightweight .NET images.
        # https://hub.docker.com/r/microsoft/dotnet
    
        FROM mcr.microsoft.com/dotnet/core/sdk:2.2-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 . ./
    
        # Build a release artifact.
        RUN dotnet publish -c Release -o out
    
        # Run the web service on container startup in a lean production image.
        FROM mcr.microsoft.com/dotnet/core/aspnet:2.2-alpine
        WORKDIR /app
    
        COPY --from=build /app/out ./
        CMD ["dotnet", "HelloGKE.dll"]
        

    ローカル ファイルがコンテナのビルドプロセスに影響しないように、.dockerignore ファイルを追加します。

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

    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 port 8080 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.5-slim
    
        # Install production dependencies.
        WORKDIR /usr/src/app
        COPY Gemfile ./
        RUN bundle install
    
        # Copy local code to the container image.
        COPY . ./
    
        # Run the web service on container startup.
        CMD ["ruby", "./app.rb"]
        

  2. Google Cloud プロジェクト ID を取得します。

    gcloud config get-value project
        

    Cloud Build を使用してコンテナ イメージを作成します。これは、docker builddocker push を実行する場合と似ていますが、処理は Google Cloud で実行されます。project-id を Google Cloud ID で置き換えます。

        gcloud builds submit --tag gcr.io/project-id/helloworld-gke .
        

    イメージが Container Registry に保存されます。

Kubernetes Engine クラスタの作成

GKE クラスタは、単一の GKE クラスタとして動作する Compute Engine 仮想マシンのマネージド セットです。このチュートリアルでは、単一ノードを使用します。

  1. クラスタを作成します。your-gcp-zone は、クラスタをホストする Google Cloud ゾーンで置き換えます。完全なリストについては、地域とリージョンをご覧ください。

        gcloud container clusters create helloworld-gke \
           --num-nodes 1 \
           --enable-basic-auth \
           --issue-client-certificate \
           --zone your-gcp-zone
        
  2. クラスタに対するアクセス権があることを確認します。次のコマンドは、コンテナ クラスタ内の稼働中のノードを一覧表示し、ユーザーにアクセス権があることを示します。

    kubectl get nodes
        

    エラーが発生した場合は、Kubernetes トラブルシューティング ガイドをご覧ください。

GKE へのデプロイ

作成した GKE クラスタにアプリをデプロイするには、2 つの Kubernetes オブジェクトが必要です。

  1. アプリを定義する Deployment
  2. アプリへのアクセス方法を定義する Service

アプリをデプロイする

アプリには、ウェブ リクエストを処理するフロントエンド サーバーがあります。deployment.yaml という新しいファイルに、フロントエンドの実行に必要なクラスタ リソースを定義します。これらのリソースは Deployment として記述されます。Deployment を使用して、ReplicaSet とそれに関連する Pod の作成と更新を行います。

  1. 他のファイルと同じディレクトリに deployment.yaml ファイルを作成し、次の内容をコピーします。$GCLOUD_PROJECT は、Google Cloud プロジェクト ID で置き換えます。

    # This file configures the hello-world app which serves public web traffic.
        apiVersion: extensions/v1beta1
        kind: Deployment
        metadata:
          name: helloworld-gke
        spec:
          replicas: 1
          selector:
            matchLabels:
              app: hello
          template:
            metadata:
              labels:
                app: hello
            spec:
              containers:
              - name: hello-app
                # Replace $GCLOUD_PROJECT with your project ID
                image: gcr.io/$GCLOUD_PROJECT/helloworld-gke:latest
                # This app listens on port 8080 for web traffic by default.
                ports:
                - containerPort: 8080
                env:
                  - name: PORT
                    value: "8080"
        
  2. リソースをクラスタにデプロイします。

    kubectl apply -f deployment.yaml
        
  3. Deployment のステータスを確認します。

    kubectl get deployments
        

    すべての AVAILABLE Deployment が READY の場合、デプロイが完了します。

    NAME               READY   UP-TO-DATE   AVAILABLE   AGE
        hello-deployment   1/1     1            1           20s
        

    Deployment に間違いがある場合は、kubectl apply -f deployment.yaml を再度実行し、必要な変更を行って Deployment を更新します。

  4. Deployment が完成すると、Deployment によって作成された Pod が表示されます。

    kubectl get pods
        

Service をデプロイする

Service は、一連の Pod に単一のアクセス ポイントを提供します。それぞれの Pod には個別にアクセスできますが、Pod は一時的なものであるため、確実にアクセスするには Service のアドレスを使う必要があります。Hello World アプリでは、hello Service にロードバランサが定義されています。これにより、単一の IP アドレスから hello-app Pod にアクセスできるようになります。この Service は、service.yaml ファイルに定義されています。

  1. 他のソースファイルと同じディレクトリに service.yaml ファイルを作成し、次の内容をコピーします。

    # The hello service provides a load-balancing proxy over the hello-app
        # pods. By specifying the type as a 'LoadBalancer', Kubernetes Engine will
        # create an external HTTP load balancer.
        apiVersion: v1
        kind: Service
        metadata:
          name: hello
        spec:
          type: LoadBalancer
          selector:
            app: hello
          ports:
          - port: 80
            targetPort: 8080
        

    Pod は、Pod を使用する Service とは別に定義されます。Kubernetes は、ラベルを使用して Service の Pod を選択します。ラベルを使用すると、1 つの Service で異なるレプリカセットにある複数の Pod に対応することも、複数の Service で 1 つずつの Pod に対応することもできます。

  2. Hello World Service を作成します。

    kubectl apply -f service.yaml
        
  3. Service の外部 IP アドレスを取得します。

    kubectl get services
        

    IP アドレスの割り当てに 60 秒ほどかかることがあります。外部 IP アドレスが、hello Service の EXTERNAL-IP 列に表示されます。

    NAME         TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE
        hello        LoadBalancer   10.22.222.222   35.111.111.11   80:32341/TCP   1m
        kubernetes   ClusterIP      10.22.222.1     <none>          443/TCP        20m
        

デプロイされたアプリを表示する

GKE で Hello World アプリを実行するために必要なすべてのリソースがデプロイされました。前の手順の外部 IP アドレスを使用して、ウェブブラウザでアプリを読み込み、実行中のアプリを表示します。

    # Example cURL call to your running application on GKE (external IP address of service)
    curl 35.111.111.11
    Hello World!
    

クリーンアップ

このクイックスタートで使用したリソースについて、Google Cloud アカウントに課金されないようにするには、次の手順に沿って操作します。

クラスタで実行される Compute Engine インスタンスContainer Registry のコンテナ イメージは課金対象です。

プロジェクトの削除

Cloud プロジェクトを削除すると、そのプロジェクト内で使用されているすべてのリソースに対する課金が停止します。

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

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

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

クラスタとコンテナの削除

プロジェクトを削除せず、このチュートリアルで使用したリソースのみを削除する場合は、クラスタとイメージを削除します。

gcloud コマンドライン ツールを使用してクラスタを削除するには、次のコマンドを実行します。

gcloud container clusters delete helloworld-gke

Container Registry リポジトリからイメージを削除するには、次のコマンドを実行します。

gcloud container images delete gcr.io/project-id/helloworld-gke

次のステップ

Kubernetes の詳細については、次をご覧ください。

GKE へのデプロイの詳細については、次をご覧ください。

Cloud Code を使用して IDE から直接 GKE にデプロイする方法については、次をご覧ください。