使用 Google Kubernetes Engine (GKE) 部署、扩缩和更新您的网站

1. 简介

运行网站和应用并非易事。

即使一切运行正常,也可能出现意外故障。服务器可能崩溃,业务需求的增长会消耗更多资源,而要在不停机的情况下进行更改,更是复杂且压力重重。

试想一下,如果有一款工具可以帮助您完成所有这些工作,甚至可以实现自动化!借助 GKE,不仅可以实现上述所有功能,而且非常简单!在此 Codelab 中,您将扮演一家虚构公司 Fancy Store 的开发者,该公司运营着一个电子商务网站。由于网站存在扩缩和服务中断的问题,您的任务是将应用部署到 GKE!

这些练习旨在帮助您熟悉常见的云开发者体验:

  1. 创建 GKE 集群。
  2. 创建 Docker 容器。
  3. 将容器部署到 GKE。
  4. 通过服务公开容器。
  5. 将容器扩缩到多个副本。
  6. 修改网站。
  7. 在不停机的情况下发布新版本。

架构图

ddba666bd2b02d0d.png

学习内容

  • 如何创建 GKE 集群
  • 如何创建 Docker 映像
  • 如何将 Docker 映像部署到 Kubernetes
  • 如何在 Kubernetes 上扩缩应用
  • 如何在 Kubernetes 上执行滚动更新

前提条件

  • 拥有创建项目的管理员权限的 Google 账号,或具有项目所有者角色的项目
  • 对 Docker 和 Kubernetes 有基本的了解(如果您不了解这些基础知识,请立即查看 DockerKubernetes)。

2. 环境设置

自定进度的环境设置

如果您还没有 Google 账号,则必须先创建一个。登录 Google Cloud 控制台,然后创建一个新项目。

53dad2cefdae71da.png

2016-02-10 12:45:26 的屏幕截图.png

请注意,项目 ID 在所有 Google Cloud 项目中都是唯一的名称(上述名称已被占用,您无法使用,抱歉!)。稍后将称为 PROJECT_ID

接下来,您需要在 Cloud 控制台中启用结算功能,才能使用 Google Cloud 资源。Google Cloud 的新用户有资格获享$300 免费试用。如果您不是新用户,请放心,此 Codelab 的费用不会超过几美元。不过,如果您使用更多资源或继续让它们运行,此 Codelab 的费用可能会更高(请参阅末尾的“清理”部分)。如需了解详情,请参阅价格

Cloud Shell

虽然您可以使用笔记本电脑远程操作 Google Cloud 和 GKE,但在此 Codelab 中,您将使用 Cloud Shell,这是一个在云端运行的命令行环境。

基于 Debian 的这个虚拟机已加载了您需要的所有开发工具。它提供了一个持久的 5GB 主目录,并且在 Google Cloud 中运行,大大增强了网络性能和身份验证。这意味着在本 Codelab 中,您只需要一个浏览器(没错,它适用于 Chromebook)。

  1. 如需从 Cloud Console 激活 Cloud Shell,只需点击激活 Cloud ShellfEbHefbRynwXpq1vj2wJw6Dr17O0np8l-WOekxAZYlZQIORsWQE_xJl-cNhogjATLn-YxLVz8CgLvIW1Ncc0yXKJsfzJGMYgUeLsVB7zSwz7p6ItNgx4tXqQjag7BfWPcZN5kP-X3Q(预配和连接到环境仅需花费一些时间)。

I5aEsuNurCxHoDFjZRZrKBdarPPKPoKuExYpdagmdaOLKe7eig3DAKJitIKyuOpuwmrMAyZhp5AXpmD_k66cBuc1aUnWlJeSfo_aTKPY9aNMurhfegg1CYaE11jdpSTYNNIYARe01A

Screen Shot 2017-06-14 at 10.13.43 PM.png

在连接到 Cloud Shell 后,您应该会看到自己已通过身份验证,并且相关项目已设置为您的 PROJECT_ID

gcloud auth list

命令输出

Credentialed accounts:
 - <myaccount>@<mydomain>.com (active)
gcloud config list project

命令输出

[core]
project = <PROJECT_ID>

如果出于某种原因未设置项目,只需发出以下命令即可:

gcloud config set project <PROJECT_ID>

正在查找您的 PROJECT_ID?检查您在设置步骤中使用的 ID,或在 Cloud Console 信息中心查找该 ID:

R7chO4PKQfLC3bvFBNZJALLTUiCgyLEq_67ECX7ohs_0ZnSjC7GxDNxWrJJUaoM53LnqABYamrBJhCuXF-J9XBzuUgaz7VvaxNrkP2TAn93Drxccyj2-5zz4AxL-G3hzxZ4PsM5HHQ

默认情况下,Cloud Shell 还会设置一些环境变量,这对您日后运行命令可能会很有用。

echo $GOOGLE_CLOUD_PROJECT

命令输出

<PROJECT_ID>
  1. 最后,设置默认可用区和项目配置。
gcloud config set compute/zone us-central1-f

您可以选择各种不同的可用区。如需了解详情,请参阅地区和可用区

3. 创建 GKE 集群

现在您已经有了可用的开发者环境,接下来需要一个 GKE 集群来部署网站!在创建集群之前,您需要确保已启用正确的 API。运行以下命令以启用 Containers API:

gcloud services enable container.googleapis.com

现在,您可以创建集群了!按照以下步骤创建一个名为 fancy-cluster 且包含 3 个节点的集群:

gcloud container clusters create fancy-cluster --num-nodes 3

集群可能需要几分钟才能完成创建。然后,运行以下命令,查看集群中的三个工作器虚拟机 (VM) 实例:

gcloud compute instances list

输出:

NAME                                          ZONE        MACHINE_TYPE   PREEMPTIBLE  INTERNAL_IP  EXTERNAL_IP    STATUS
gke-fancy-cluster-default-pool-ad92506d-1ng3  us-east4-a  n1-standard-1               10.150.0.7   XX.XX.XX.XX    RUNNING
gke-fancy-cluster-default-pool-ad92506d-4fvq  us-east4-a  n1-standard-1               10.150.0.5   XX.XX.XX.XX    RUNNING
gke-fancy-cluster-default-pool-ad92506d-4zs3  us-east4-a  n1-standard-1               10.150.0.6   XX.XX.XX.XX    RUNNING

您还可以在 Cloud 控制台中查看集群和相关信息。点击左上角的菜单按钮,向下滚动到 Kubernetes Engine,然后点击“集群”。此时应该会看到名为 fancy-cluster 的集群。

795c794b03c5d2b0.png

6b394dfb8a6031f2.png

恭喜!您已创建了第一个集群!

4. 克隆源代码库

由于这是一个现有网站,您只需从代码库中克隆源代码,即可专注于创建 Docker 映像并部署到 GKE。

运行以下命令,将源代码库克隆到 Cloud Shell 实例,并将其更改为相应的目录。您还将安装 Node.js 依赖项,以便在部署应用之前对其进行测试。

cd ~
git clone https://github.com/googlecodelabs/monolith-to-microservices.git
cd ~/monolith-to-microservices
./setup.sh

这会克隆代码库、更改目录,并安装在本地运行应用所需的依赖项。该脚本可能需要几分钟才能运行完毕。

尽职尽责地测试您的应用。运行以下命令以启动 Web 服务器:

cd ~/monolith-to-microservices/monolith
npm start

输出:

Monolith listening on port 8080!

点击 Cloud Shell 菜单中的“网页预览”图标,然后选择“在端口 8080 上预览”,即可预览应用。

5869738f0e9ec386.png

系统应该会打开一个新窗口,您可以在其中看到 Fancy Store 的实际效果!

9ed25c3f0cbe62fa.png

查看完网站后,您可以关闭该窗口。在终端窗口中按 Control+C(Windows 或 Mac)以停止 Web 服务器进程。

5. 使用 Cloud Build 创建 Docker 容器

现在您已准备好源文件,接下来使用 Docker 容器化您的应用!

通常,您需要分两步完成该操作。先构建 Docker 容器,然后将其推送到注册表,以便存储映像供 GKE 拉取。不过,您可以使用 Cloud Build,只需一个命令即可创建 Docker 容器并将映像放入 Container Registry,从而简化操作!(如需查看创建 Docker 文件和推送文件的手动过程,请参阅 Container Registry 快速入门。)

Cloud Build 会压缩目录中的文件,并将它们移至 Cloud Storage 存储分区。然后,构建流程会从存储分区中提取文件,并使用 Dockerfile 运行 Docker 构建流程。由于您为 Docker 映像指定了 --tag 标志,并将主机设置为 gcr.io,因此生成的 Docker 映像将推送到 Container Registry。

首先,您需要运行以下命令来启用 Cloud Build API:

gcloud services enable cloudbuild.googleapis.com

启用 API 后,在 Cloud Shell 中运行以下命令以启动构建流程:

cd ~/monolith-to-microservices/monolith
gcloud builds submit --tag gcr.io/${GOOGLE_CLOUD_PROJECT}/monolith:1.0.0 .

此过程需要几分钟时间,完成后,您可以在终端中看到以下输出:

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ID                                    CREATE_TIME                DURATION  SOURCE                                                                                  IMAGES                              STATUS
1ae295d9-63cb-482c-959b-bc52e9644d53  2019-08-29T01:56:35+00:00  33S       gs://<PROJECT_ID>_cloudbuild/source/1567043793.94-abfd382011724422bf49af1558b894aa.tgz  gcr.io/<PROJECT_ID>/monolith:1.0.0  SUCCESS

如需查看构建历史记录或实时监控构建过程,您可以前往 Cloud 控制台。点击左上角的菜单按钮,向下滚动到“CI/CD”,然后依次点击“Cloud Build”和“历史记录”。在该页面上,您可以看到之前所有 build 的列表,但其中应该只有您创建的 build。

4c753ede203255f6.png

点击 Build ID,即可查看相应 build 的所有详细信息,包括日志输出。

在“build 详情”页面中,您可以点击“build 信息”部分中的映像名称,查看创建的容器映像。

6e88ed1643dfe629.png

6. 将容器部署到 GKE

现在,您已经将网站容器化并将容器推送到了 Container Registry,接下来就可以部署到 Kubernetes 了!

如需在 GKE 集群上部署和管理应用,您必须与 Kubernetes 集群管理系统进行通信。您通常使用 kubectl 命令行工具执行该操作。

Kubernetes 将应用表示为 Pod,这是表示一个容器(或一组紧密耦合的容器)的单元。Pod 是 Kubernetes 中最小的可部署单元。在此处,每个 Pod 仅包含您的单体式应用容器。

如需部署应用,您需要创建 Deployment。Deployment 管理应用的多个运行实例(称为副本),并安排这些副本在集群中的各个节点上运行。在本例中,Deployment 将只运行应用的一个 Pod。Deployment 通过创建 ReplicaSet 来确保这一点。ReplicaSet 负责确保指定数量的副本始终处于运行状态。

kubectl create deployment 命令会让 Kubernetes 在您的集群上创建一个名为 monolith 的 Deployment,其中包含 1 个副本。

运行以下命令以部署应用:

kubectl create deployment monolith --image=gcr.io/${GOOGLE_CLOUD_PROJECT}/monolith:1.0.0

验证部署

如需验证 Deployment 是否已成功创建,请运行以下命令(Pod 状态可能需要过一会儿才会变为“Running”):

kubectl get all

输出:

NAME                            READY   STATUS    RESTARTS   AGE
pod/monolith-7d8bc7bf68-htm7z   1/1     Running   0          6m21s

NAME                 TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.27.240.1   <none>        443/TCP   24h

NAME                       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/monolith   1         1         1            1           20m

NAME                                  DESIRED   CURRENT   READY   AGE
replicaset.apps/monolith-7d8bc7bf68   1         1         1       20m

该输出显示了以下几项内容:您可以看到 Deployment 已处于最新状态、ReplicaSet 设定的期望 Pod 数量为 1,并且 Pod 正在运行。看来您已成功创建所有内容!

如需单独查看资源,您可以运行以下命令:

# Show pods
kubectl get pods

# Show deployments
kubectl get deployments

# Show replica sets
kubectl get rs

#You can also combine them
kubectl get pods,deployments

为充分了解 Kubernetes 的优势,您可以模拟一次服务器崩溃,删除 Pod,看看会发生什么!

从上一个命令中复制您的 pod 名称,然后运行以下命令以删除该 pod:

kubectl delete pod/<POD_NAME>

如果您操作速度够快,可以再次运行上一个命令来查看所有内容,此时您会看到两个 Pod:一个正在终止,另一个正在创建或运行:

kubectl get all

输出:

NAME                            READY   STATUS        RESTARTS   AGE
pod/monolith-7d8bc7bf68-2bxts   1/1     Running       0          4s
pod/monolith-7d8bc7bf68-htm7z   1/1     Terminating   0          9m35s

NAME                 TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.27.240.1   <none>        443/TCP   24h

NAME                       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/monolith   1         1         1            1           24m

NAME                                  DESIRED   CURRENT   READY   AGE
replicaset.apps/monolith-7d8bc7bf68   1         1         1       24m

为什么会发生这种情况?这是因为 ReplicaSet 检测到 Pod 正在终止,并触发了一个新 Pod 来保持期望副本数。稍后,您将了解如何扩容以确保有多个实例运行,这样即使某个实例出现故障,用户也不会经历服务中断!

7. 公开 GKE Deployment

您已将应用部署到 GKE,但无法从集群外部访问该应用。默认情况下,您在 GKE 上运行的容器无法通过互联网访问,因为这些容器没有外部 IP 地址。您必须通过 Service 资源明确公开应用,以便允许来自互联网的流量访问。Service 为应用的 Pod 提供网络和 IP 支持。GKE 会为您的应用创建外部 IP 地址和负载平衡器(需要付费)。

运行以下命令,将您的网站公开到互联网:

kubectl expose deployment monolith --type=LoadBalancer --port 80 --target-port 8080

输出:

service/monolith exposed

访问服务

GKE 会将外部 IP 地址分配给 Service 资源,而不是 Deployment。如果您想查找 GKE 为应用预配的外部 IP,可以使用 kubectl get service 命令检查服务:

kubectl get service

输出:

NAME         CLUSTER-IP      EXTERNAL-IP     PORT(S)          AGE
monolith     10.3.251.122    203.0.113.0     80:30877/TCP     3d

确定应用的外部 IP 地址后,请复制该地址。将浏览器指向该网址(例如 http://203.0.113.0),以检查您的应用是否可访问。

9ed25c3f0cbe62fa.png

您应该会看到之前测试过的网站!恭喜!您的网站已完全在 Kubernetes 上运行!

8. 扩缩 GKE Deployment

现在,您已在 GKE 中运行应用实例并将其向互联网公开,您的网站已变得非常热门!您需要一种方法将应用扩缩到多个实例,以便处理流量。了解如何将应用扩缩到最多 3 个副本。

运行以下命令,将部署扩缩到最多 3 个副本:

kubectl scale deployment monolith --replicas=3

输出:

deployment.apps/monolith scaled

验证扩缩后的部署

如需验证 Deployment 是否已成功扩缩,请运行以下命令:

kubectl get all

输出:

NAME                            READY   STATUS    RESTARTS   AGE
pod/monolith-7d8bc7bf68-2bxts   1/1     Running   0          36m
pod/monolith-7d8bc7bf68-7ds7q   1/1     Running   0          45s
pod/monolith-7d8bc7bf68-c5kxk   1/1     Running   0          45s

NAME                 TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)        AGE
service/kubernetes   ClusterIP      10.27.240.1    <none>         443/TCP        25h
service/monolith     LoadBalancer   10.27.253.64   XX.XX.XX.XX   80:32050/TCP   6m7s

NAME                       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/monolith   3         3         3            3           61m

NAME                                  DESIRED   CURRENT   READY   AGE
replicaset.apps/monolith-7d8bc7bf68   3         3         3       61m

您应该会看到 3 个 Pod 实例正在运行。另请注意,您的 Deployment 和 ReplicaSet 现在的期望副本数均为 3。

9. 更改网站内容

您的营销团队要求您更改网站的首页。他们认为,该页面应提供更多信息,说明您的公司是做什么的以及您实际销售的产品/服务。在本部分中,您将根据营销团队的要求,在首页上添加一些文字。已经有一名开发者使用名为 index.js.new 的文件创建了更改。您可以将该文件复制到 index.js,首页上就会反映所做更改。按照以下说明进行适当更改。

运行以下命令,为更新后的文件设置正确的文件名,并输出其内容以验证所做更改是否已经应用:

cd ~/monolith-to-microservices/react-app/src/pages/Home
mv index.js.new index.js
cat ~/monolith-to-microservices/react-app/src/pages/Home/index.js

最终代码应如下所示:

/*
Copyright 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

    https://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.
*/

import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import Paper from "@material-ui/core/Paper";
import Typography from "@material-ui/core/Typography";
const useStyles = makeStyles(theme => ({
  root: {
    flexGrow: 1
  },
  paper: {
    width: "800px",
    margin: "0 auto",
    padding: theme.spacing(3, 2)
  }
}));
export default function Home() {
  const classes = useStyles();
  return (
    <div className={classes.root}>
      <Paper className={classes.paper}>
        <Typography variant="h5">
          Fancy Fashion &amp; Style Online
        </Typography>
        <br />
        <Typography variant="body1">
          Tired of mainstream fashion ideas, popular trends and societal norms?
          This line of lifestyle products will help you catch up with the Fancy trend and express your personal style.
          Start shopping Fancy items now!
        </Typography>
      </Paper>
    </div>
  );
}

您已经更新了 React 组件,但还需要构建 React 应用来生成静态文件。运行下面的命令,构建 React 应用并将其复制到单体式应用公共目录:

cd ~/monolith-to-microservices/react-app
npm run build:monolith

现在代码已更新,您需要重新构建 Docker 容器并将其发布到 Container Registry。您可以使用与之前相同的命令,但这次您将更新版本标签!

运行以下命令,使用更新后的映像版本 2.0.0 触发新的 Cloud Build:

cd ~/monolith-to-microservices/monolith

#Feel free to test your application
npm start

gcloud builds submit --tag gcr.io/${GOOGLE_CLOUD_PROJECT}/monolith:2.0.0 .

在终端窗口中按 Control+C(Windows 或 Mac)以停止 Web 服务器进程。

在下一部分中,您将在不停机的情况下使用该映像更新应用。

10. 在不停机的情况下更新网站

更改已完成,营销团队对您的更新非常满意!现在需要更新网站,但不能中断用户访问。请按照以下说明更新您的网站。

GKE 的滚动更新可确保您的应用保持正常运行,即使系统将所有正在运行的副本中的旧容器映像实例替换为新容器映像,应用也依然可用。

在命令行中,您可以使用以下命令告知 Kubernetes,您要将 Deployment 的映像更新为新版本:

kubectl set image deployment/monolith monolith=gcr.io/${GOOGLE_CLOUD_PROJECT}/monolith:2.0.0

输出:

deployment.apps/monolith image updated

验证部署

您可以运行以下命令来验证 Deployment 是否成功更新:

kubectl get pods

输出:

NAME                        READY   STATUS              RESTARTS   AGE
monolith-584fbc994b-4hj68   1/1     Terminating         0          60m
monolith-584fbc994b-fpwdw   1/1     Running             0          60m
monolith-584fbc994b-xsk8s   1/1     Terminating         0          60m
monolith-75f4cf58d5-24cq8   1/1     Running             0          3s
monolith-75f4cf58d5-rfj8r   1/1     Running             0          5s
monolith-75f4cf58d5-xm44v   0/1     ContainerCreating   0          1s

您会看到 3 个新 Pod 被创建,旧 Pod 被终止。可以通过 AGE 字段判断哪些是新 Pod,哪些是旧 Pod。最终,您将再次看到 3 个 Pod,这 3 个 Pod 即为更新后的 Pod。

如需验证更改,请再次前往负载平衡器的外部 IP 地址,并确认应用已更新。

运行以下命令以列出服务并查看 IP 地址(如果您忘记了该地址):

kubectl get svc

您的网站应该会显示您添加到首页组件中的文本!

8006c9938dbd5aa5.png

11. 清理

删除 Git 代码库

cd ~
rm -rf monolith-to-microservices

删除 Container Registry 映像

注意:如果您创建了其他版本,也可以使用相同的语法来删除这些图片。此 Codelab 假定您只有两个代码。

# Delete the container image for version 1.0.0 of our monolith
gcloud container images delete gcr.io/${GOOGLE_CLOUD_PROJECT}/monolith:1.0.0 --quiet

# Delete the container image for version 2.0.0 of our monolith
gcloud container images delete gcr.io/${GOOGLE_CLOUD_PROJECT}/monolith:2.0.0 --quiet

从 Cloud Storage 中删除 Cloud Build 制品

注意:如果您将 Cloud Build 用于此 Codelab 以外的制品,则必须手动从 Cloud Storage 存储分区 gs://<PROJECT_ID>_cloudbuild/source 中删除您的来源。

# The following command will take all source archives from all builds and delete them from cloud storage

# Run this command to print all sources:
# gcloud builds list | awk 'NR > 1 {print $4}'

gcloud builds list | awk 'NR > 1 {print $4}' | while read line; do gsutil rm $line; done

删除 GKE 服务

kubectl delete service monolith
kubectl delete deployment monolith

删除 GKE 集群

gcloud container clusters delete fancy-cluster

注意:此命令可能需要一点时间才能完成。

12. 恭喜!

您已使用 GKE 部署网站,并完成了扩缩和更新。现在,您已经具备使用 Docker 和 Kubernetes 的实践经验!

其他资源