将工作负载迁移到不同的机器类型


本教程演示如何在不造成应用停机的情况下,将在 Google Kubernetes Engine (GKE) 集群上运行的工作负载迁移到同一集群内的一组新节点。如果您想要将工作负载迁移到使用不同机器类型的节点,则此迁移方法十分有用。

背景

节点池是指配置全部相同(包括机器类型(CPU 和内存)、授权范围)的一组机器。节点池代表集群中节点的子集;容器集群可包含一个或多个节点池。

如果您需要更改 Compute Engine 集群的机器配置文件,则可以创建新的节点池,然后将工作负载迁移到新节点池。

要在不造成停机的情况下迁移工作负载,您需要:

  • 将现有节点池标记为无法安排。
  • 排空在现有节点池上运行的工作负载。
  • 删除现有节点池。

Kubernetes 是 GKE 集群的集群编排系统,可在排空现有节点池时自动将逐出的 Pod 重新安排到新的节点池中。

目标

  • 创建 GKE 集群。
  • 将示例 Web 应用部署到集群。
  • 创建新的节点池。
  • 在不造成停机的情况下将 Pod 迁移到新节点池。

准备工作

请按照以下步骤启用 Kubernetes Engine API:
  1. 访问 Google Cloud Console 中的 Kubernetes Engine 页面
  2. 创建或选择项目。
  3. 稍作等待,让 API 和相关服务完成启用过程。 此过程可能耗时几分钟。
  4. 确保您的 Cloud 项目已启用结算功能。 了解如何确认您的项目是否已启用结算功能

安装本教程中使用的以下命令行工具:

  • gcloud 用于创建和删除 Kubernetes Engine 集群。gcloud 包含在 Google Cloud SDK 中。
  • kubectl 用于管理 Kubernetes(即 Kubernetes Engine 使用的集群编排系统)。您可以使用 gcloud 安装 kubectl
    gcloud components install kubectl

从 GitHub 克隆示例代码:

git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
cd kubernetes-engine-samples/migrating-node-pool

gcloud 命令行工具设置默认值

如需节省在 gcloud 命令行工具中输入项目 IDCompute Engine 地区选项的时间,您可以设置以下默认值:
gcloud config set project project-id
gcloud config set compute/zone compute-zone

创建 GKE 集群

第一步是创建用于运行应用工作负载的容器集群。以下命令将创建一个包含 5 个节点的新集群,这些节点使用默认的机器类型 (e2-medium):

gcloud container clusters create migration-tutorial --num-nodes=5

运行复制的应用部署

以下清单说明了示例 Web 应用容器映像的 6 个副本 Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 6
  selector:
    matchLabels:
      app: hello-app
  template:
    metadata:
      labels:
        app: hello-app
    spec:
      containers:
      - name: hello-app
        image: us-docker.pkg.dev/google-samples/containers/gke/hello-app:1.0

如需部署此清单,请运行以下命令:

kubectl apply -f node-pools-deployment.yaml

您可以运行以下命令来检索已启动 Pod 的列表:

kubectl get pods
输出:
NAME                   READY     STATUS    RESTARTS   AGE
web-2212180648-80q72   1/1       Running   0          10m
web-2212180648-jwj0j   1/1       Running   0          10m
web-2212180648-pf67q   1/1       Running   0          10m
web-2212180648-pqz73   1/1       Running   0          10m
web-2212180648-rrd3b   1/1       Running   0          10m
web-2212180648-v3b18   1/1       Running   0          10m

创建具有大型机器类型的节点池

默认情况下,GKE 会为每个新集群创建一个名为 default-pool 的节点池:

gcloud container node-pools list --cluster migration-tutorial
输出:
NAME          MACHINE_TYPE   DISK_SIZE_GB  NODE_VERSION
default-pool  e2-medium      100           1.16.13-gke.401

如需引入配置不同(如机器类型不同或身份验证范围不同)的实例,您需要创建新的节点池

以下命令会创建一个名为 larger-pool 的新节点池,其包含 e2-highmem-2 机器类型的五个高内存实例:

gcloud container node-pools create larger-pool \
  --cluster=migration-tutorial \
  --machine-type=e2-highmem-2 \
  --num-nodes=5

现在,您的容器集群应该具有两个节点池:

gcloud container node-pools list --cluster migration-tutorial
输出:
NAME          MACHINE_TYPE   DISK_SIZE_GB  NODE_VERSION
default-pool  e2-medium      100           v1.16.13-gke.401
larger-pool   e2-highmem-2   100           v1.16.13-gke.401

您可以运行以下命令查看添加到 GKE 集群的新节点池的实例:

kubectl get nodes
输出:
NAME                                                STATUS    AGE       VERSION
gke-migration-tutorial-default-pool-56e3af9a-059q   Ready     40m       v1.16.13-gke.401
gke-migration-tutorial-default-pool-56e3af9a-0ng4   Ready     40m       v1.16.13-gke.401
gke-migration-tutorial-default-pool-56e3af9a-k6jm   Ready     40m       v1.16.13-gke.401
gke-migration-tutorial-default-pool-56e3af9a-lkrv   Ready     40m       v1.16.13-gke.401
gke-migration-tutorial-default-pool-56e3af9a-p9j4   Ready     40m       v1.16.13-gke.401
gke-migration-tutorial-larger-pool-b8ec62a6-2rhk    Ready     4m        v1.16.13-gke.401
gke-migration-tutorial-larger-pool-b8ec62a6-4bb2    Ready     4m        v1.16.13-gke.401
gke-migration-tutorial-larger-pool-b8ec62a6-7fl0    Ready     4m        v1.16.13-gke.401
gke-migration-tutorial-larger-pool-b8ec62a6-cx9q    Ready     4m        v1.16.13-gke.401
gke-migration-tutorial-larger-pool-b8ec62a6-hs6p    Ready     4m        v1.16.13-gke.401

迁移工作负载

创建了新的节点池后,您的工作负载仍在 default-pool 上运行。只要 Pod 正在运行且可用,Kubernetes 就不会重新安排 Pod。

运行以下命令以查看 Pod 正在哪个节点上运行(请参阅 NODE 列):

kubectl get pods -o=wide
输出:
NAME                          READY     STATUS    IP         NODE
web-2212180648-80q72          1/1       Running   10.8.3.4   gke-migration-tutorial-default-pool-56e3af9a-k6jm
web-2212180648-jwj0j          1/1       Running   10.8.2.5   gke-migration-tutorial-default-pool-56e3af9a-0ng4
web-2212180648-pf67q          1/1       Running   10.8.4.4   gke-migration-tutorial-default-pool-56e3af9a-lkrv
web-2212180648-pqz73          1/1       Running   10.8.2.6   gke-migration-tutorial-default-pool-56e3af9a-0ng4
web-2212180648-rrd3b          1/1       Running   10.8.4.3   gke-migration-tutorial-default-pool-56e3af9a-lkrv
web-2212180648-v3b18          1/1       Running   10.8.1.4   gke-migration-tutorial-default-pool-56e3af9a-p9j4

如需将这些 Pod 迁移到新节点池,您必须执行以下步骤:

  1. 封锁现有节点池:此操作会将现有节点池 (default-pool) 中的节点标记为无法安排。将它们标记为无法安排后,Kubernetes 会停止将新 Pod 安排到这些节点。

  2. 排空现有节点池:此操作会正常逐出正在现有节点池 (default-pool) 的节点上运行的工作负载。

执行前述步骤,可以正常终止正在现有节点池中运行的 Pod,随后 Kubernetes 会将这些 Pod 重新安排到其他可用的节点上。在本例中,仅有的可用节点是在 larger-pool 节点池中。

为了确保 Kubernetes 正常终止您的应用,容器应该处理 SIGTERM 信号。此信号可用于关闭与客户端的活动连接,并干净利落地提交或取消数据库事务。在 Pod 清单中,您可以使用 spec.terminationGracePeriodSeconds 字段指定 Kubernetes 必须等待多长时间才能停止 Pod 中的容器。默认值为 30 秒。您可以在 Kubernetes 文档中详细了解终止 Pod

首先,封锁 default-pool 中的节点。您可以运行以下命令,获取此节点池中节点的列表:

kubectl get nodes -l cloud.google.com/gke-nodepool=default-pool

然后,通过运行 kubectl cordon NODE 命令(将 NODE 替换为上一个命令中的名称),封锁每个节点。以下命令将遍历每个节点,并将它们标记为无法安排:

for node in $(kubectl get nodes -l cloud.google.com/gke-nodepool=default-pool -o=name); do
  kubectl cordon "$node";
done
输出:
node "gke-migration-tutorial-default-pool-56e3af9a-059q" cordoned
node "gke-migration-tutorial-default-pool-56e3af9a-0ng4" cordoned
node "gke-migration-tutorial-default-pool-56e3af9a-k6jm" cordoned
node "gke-migration-tutorial-default-pool-56e3af9a-lkrv" cordoned
node "gke-migration-tutorial-default-pool-56e3af9a-p9j4" cordoned

现在,您应该会在节点列表中看到 default-pool 节点的状态为 SchedulingDisabled

kubectl get nodes
输出:
NAME                                                STATUS                     AGE       VERSION
gke-migration-tutorial-default-pool-56e3af9a-059q   Ready,SchedulingDisabled   1h        v1.16.13-gke.401
gke-migration-tutorial-default-pool-56e3af9a-0ng4   Ready,SchedulingDisabled   1h        v1.16.13-gke.401
gke-migration-tutorial-default-pool-56e3af9a-k6jm   Ready,SchedulingDisabled   1h        v1.16.13-gke.401
gke-migration-tutorial-default-pool-56e3af9a-lkrv   Ready,SchedulingDisabled   1h        v1.16.13-gke.401
gke-migration-tutorial-default-pool-56e3af9a-p9j4   Ready,SchedulingDisabled   1h        v1.16.13-gke.401
gke-migration-tutorial-larger-pool-b8ec62a6-2rhk    Ready                      1h        v1.16.13-gke.401
gke-migration-tutorial-larger-pool-b8ec62a6-4bb2    Ready                      1h        v1.16.13-gke.401
gke-migration-tutorial-larger-pool-b8ec62a6-7fl0    Ready                      1h        v1.16.13-gke.401
gke-migration-tutorial-larger-pool-b8ec62a6-cx9q    Ready                      1h        v1.16.13-gke.401
gke-migration-tutorial-larger-pool-b8ec62a6-hs6p    Ready                      1h        v1.16.13-gke.401

接下来,正常排空每个节点上的 Pod。要执行排空操作,请使用 kubectl drain 命令,该命令会逐出每个节点上的 Pod。

要运行 kubectl drain --force NODE,您可以将 NODE 替换为传递给 kubectl cordon 命令的同一名称列表。

以下 shell 命令将遍历 default-pool 中的每个节点,并在分配的正常终止期限(10 秒)内逐出 Pod,从而将节点排空。

for node in $(kubectl get nodes -l cloud.google.com/gke-nodepool=default-pool -o=name); do
  kubectl drain --force --ignore-daemonsets --delete-emptydir-data --grace-period=10 "$node";
done

完成此命令后,您应该会看到 Pod 现正在 larger-pool 节点上运行:

kubectl get pods -o=wide
输出:
NAME                   READY     STATUS    IP         NODE
web-2212180648-3n9hz   1/1       Running   10.8.9.4   gke-migration-tutorial-larger-pool-b8ec62a6-cx9q
web-2212180648-88q1c   1/1       Running   10.8.7.4   gke-migration-tutorial-larger-pool-b8ec62a6-2rhk
web-2212180648-dlmjc   1/1       Running   10.8.9.3   gke-migration-tutorial-larger-pool-b8ec62a6-cx9q
web-2212180648-hcv46   1/1       Running   10.8.5.4   gke-migration-tutorial-larger-pool-b8ec62a6-hs6p
web-2212180648-n0nht   1/1       Running   10.8.6.4   gke-migration-tutorial-larger-pool-b8ec62a6-7fl0
web-2212180648-s51jb   1/1       Running   10.8.8.4   gke-migration-tutorial-larger-pool-b8ec62a6-4bb2

删除旧节点池

Kubernetes 将 web Deployment 中的所有 Pod 重新安排到 larger-pool 后,您便可安全地删除已不再需要的 default-pool。运行以下命令以删除 default-pool

gcloud container node-pools delete default-pool --cluster migration-tutorial

此操作完成后,您的容器集群应该就只剩一个节点池,即 larger-pool

gcloud container node-pools list --cluster migration-tutorial
输出:
NAME          MACHINE_TYPE   DISK_SIZE_GB  NODE_VERSION
larger-pool   e2-highmem-2   100           1.16.13-gke.401

清除数据

为避免因本教程中使用的资源导致您的 Google Cloud 帐号产生费用,请删除包含这些资源的项目,或者保留项目但删除各个资源。

  • 删除容器集群:此步骤会删除构成容器集群的资源,如计算实例、磁盘和网络资源。

    gcloud container clusters delete migration-tutorial

后续步骤