快速入门:部署特定语言的应用

本页面介绍如何执行以下操作:

  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 用于管理 Kubernetes(即 GKE 使用的集群编排系统)。您可以使用 gcloud 安装 kubectl
    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)
    }
    

    此代码会创建一个 Web 服务器以侦听由 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);
    });
    

    此代码会创建一个 Web 服务器以侦听由 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 只在下一步中创建新的 Web 项目时需要。Dockerfile(将在稍后介绍)会将所有依赖项加载到容器中。

  2. 在您的终端新建一个空 Web 项目:

    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
    

    您现在已经有了一个位于 helloworld-gke 中的新 Spring Boot 项目。

  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);
    	}
    }
    

    此代码会创建一个 Web 服务器以侦听由 PORT 环境变量定义的端口。

您的应用已编写完毕,接下来可以将其封装到一个 Docker 容器中,然后上传到 Container Registry。

C#

  1. 安装 .NET Core SDK 2.2。 .NET Core SDK 只在下一步中创建新的 Web 项目时需要。Dockerfile(将在稍后介绍)会将所有依赖项加载到容器中。

  2. 在您的终端新建一个空 Web 项目:

    dotnet new web -o helloworld-gke
    
  3. 转到 helloworld-gke 目录。

  4. 更新 Program.cs 中的 CreateWebHostBuilder 定义,指定 .UseUrls() 的端口网址来定义服务端口。示例中显示的端口是 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
    

    此代码会创建一个 Web 服务器以侦听由 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 集群就是一组托管式 Compute Engine 虚拟机,这些虚拟机作为单个 GKE 集群运行。本教程使用单个节点。

  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 集群,您需要用到两个 Kubernetes 对象。

  1. 一个 Deployment 对象,用来定义您的应用。
  2. 一个 Service 对象,用来定义如何访问您的应用。

部署应用

该应用具有一个前端服务器,用于处理 Web 请求。您可以在名为 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 部署都为 READY,则表示 Deployment 已完成。

    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。利用标签,您既可以让一个 Service 指向来自不同副本集的多个 Pod,也可以让多个 Service 指向同一个 Pod。

  2. 创建 Hello World Service:

    kubectl apply -f service.yaml
    
  3. 获取该 Service 的外部 IP 地址:

    kubectl get services
    

    分配 IP 地址最多可能需要 60 秒的时间。外部 IP 地址列在 hello Serivce 的 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,请参阅以下内容: