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

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

  1. 创建一个 Hello World 应用。
  2. 使用 Cloud Build 将该应用封装到容器映像中。
  3. 在 Google Kubernetes Engine (GKE) 中创建一个集群。
  4. 将容器映像部署到该集群。

示例以多种语言提供,但是,除了所提供的语言以外,您也可以使用其他语言。


如需在 Cloud Shell Editor 中直接获取有关此任务的分步指导,请点击操作演示

操作演示


以下部分将引导您完成与点击操作演示相同的步骤。

准备工作

  1. 登录您的 Google Cloud 帐号。如果您是 Google Cloud 新手,请创建一个帐号来评估我们的产品在实际场景中的表现。新客户还可获享 $300 赠金,用于运行、测试和部署工作负载。
  2. 在 Google Cloud Console 的项目选择器页面上,选择或创建一个 Google Cloud 项目。

    转到“项目选择器”

  3. 确保您的 Cloud 项目已启用结算功能。 了解如何确认您的项目是否已启用结算功能

  4. 启用 Artifact Registry, 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 容器中,然后上传到 Artifact 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 容器中,然后上传到 Artifact 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 容器中,然后上传到 Artifact 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 容器中,然后上传到 Artifact 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 容器中,然后上传到 Artifact 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.4 image.
    # https://hub.docker.com/_/php
    FROM php:7.4-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
    
  3. 在本快速入门中,您需要将容器存储在 Artifact Registry 中,并从注册表中将其部署到集群。运行以下命令,在集群所在的区域中创建名为 hello-repo 的代码库:

    gcloud artifacts repositories create hello-repo \
        --project=PROJECT_ID \
        --repository-format=docker \
        --location=LOCATION \
        --description="Docker repository"
    

    替换以下值:

    • PROJECT_ID 是您的 Google Cloud 项目 ID
    • LOCATION 是代码库的位置,例如 us-west1。在下一部分中,您将在同一区域中为容器映像部署该集群。

      如需查看可用位置的列表,请运行以下命令:

      gcloud artifacts locations list
      
  4. 使用 Cloud Build 构建容器映像,此行为类似于运行 docker builddocker push,但构建是在 Google Cloud 上进行的:

     gcloud builds submit \
        --tag LOCATION-docker.pkg.dev/PROJECT_ID/hello-repo/helloworld-gke .
    

    该映像存储在 Artifact Registry 中。

创建 GKE 集群

GKE 集群是作为单个 GKE 集群运行的一组托管式 Compute Engine 虚拟机。根据您在 GKE 中选择使用的操作模式,在创建集群时,请指定默认的计算可用区或区域。如果您使用标准模式,您的集群是可用区级(对于本快速入门),因此请设置您的默认计算可用区。如果您使用 Autopilot 模式,您的集群是区域级,因此请设置您的默认计算区域

  1. 创建集群。

    标准

    COMPUTE_ZONE 替换为要托管集群的 Google Cloud 可用区,例如 us-west1-a。在 Artifact Registry 代码库所在的区域中选择一个地区。

    gcloud container clusters create helloworld-gke \
        --num-nodes 1 \
        --zone COMPUTE_ZONE
    

    Autopilot

    COMPUTE_REGION 替换为您要在其中托管集群的 Google Cloud 区域,例如 us-west1。选择 Artifact Registry 代码库所在的区域。

     gcloud container clusters create-auto helloworld-gke \
        --region COMPUTE_REGION
    
  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:
    • $LOCATION 是代码库位置,例如 us-west1
    # This file configures the hello-world app which serves public web traffic.
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: helloworld-gke
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: hello
      template:
        metadata:
          labels:
            app: hello
        spec:
          containers:
          - name: hello-app
            # Replace $LOCATION with your Artifact Registry location (e.g., us-west1).
            # Replace $GCLOUD_PROJECT with your project ID.
            image: $LOCATION-docker.pkg.dev/$GCLOUD_PROJECT/hello-repo/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 地址在网络浏览器中加载应用,并查看正在运行的应用:

 http://EXTERNAL_IP

或者,您可以对 Service 的外部 IP 地址进行 curl 调用:

curl EXTERNAL_IP

输出显示以下内容:

Hello World!

清除数据

为避免系统因本快速入门中使用的资源向您的 Google Cloud 帐号收取费用,请按照以下步骤操作。

您需要为集群中运行的 Compute Engine 实例Artifact Registry 中的容器映像付费。

删除项目

删除 Cloud 项目后,系统即会停止对该项目中使用的所有资源计费。

  1. 在 Cloud Console 中,转到管理资源页面。

    转到“管理资源”

  2. 在项目列表中,选择要删除的项目,然后点击删除
  3. 在对话框中输入项目 ID,然后点击关闭以删除项目。

删除您的集群和容器

如果您想保留项目,只删除本教程中使用的资源,请删除您的集群和映像。

要使用 gcloud 命令行工具删除集群,请根据您使用的模式运行以下命令:

标准

gcloud container clusters delete helloworld-gke  \
    --zone COMPUTE_ZONE

Autopilot

 gcloud container clusters delete helloworld-gke \
    --region COMPUTE_REGION

如需删除 Artifact Registry 代码库中的映像,请运行以下命令:

gcloud artifacts docker images delete \
    LOCATION-docker.pkg.dev/PROJECT_ID/hello-repo/helloworld-gke

后续步骤

如需详细了解 Kubernetes,请参阅以下内容:

如需详细了解如何部署到 GKE,请参阅以下内容:

如需详细了解如何使用 Cloud Code 直接从您的 IDE 在 GKE 上创建、开发和运行应用,请参阅以下内容: