在 GKE 上部署 Redis 集群


本教程介绍了有关创建有状态应用以及升级运行应用的 Google Kubernetes Engine (GKE) 集群的推荐做法。本教程使用 Redis 作为部署有状态应用的示例,但相同的概念也适用于 GKE 上部署的其他类型的有状态应用。

目标

本教程介绍以下步骤:

  1. 创建在发布渠道中注册的 GKE 集群。
  2. 在 GKE 上创建 Redis 集群
  3. 将 Redis 客户端应用部署到 GKE。
  4. 遵循以下最佳实践来升级节点池:
    1. 设置 Pod 中断预算 (PDB)
    2. 设置维护期和排除项
    3. 节点升级策略设置为超额配置升级蓝绿升级
  5. 测试应用。
  6. 升级集群。
  7. 测试工作负载中断。

下图简要展示了本教程的集群架构:

架构图

费用

在本文档中,您将使用 Google Cloud 的以下收费组件:

您可使用价格计算器根据您的预计使用情况来估算费用。 Google Cloud 新用户可能有资格申请免费试用

完成本文档中描述的任务后,您可以通过删除所创建的资源来避免继续计费。如需了解详情,请参阅清理

准备工作

设置项目

  1. 登录您的 Google Cloud 账号。如果您是 Google Cloud 新手,请创建一个账号来评估我们的产品在实际场景中的表现。新客户还可获享 $300 赠金,用于运行、测试和部署工作负载。
  2. In the Google Cloud console, on the project selector page, click Create project to begin creating a new Google Cloud project.

    Go to project selector

  3. 确保您的 Google Cloud 项目已启用结算功能

  4. 启用 GKE API。

    启用 API

  5. In the Google Cloud console, on the project selector page, click Create project to begin creating a new Google Cloud project.

    Go to project selector

  6. 确保您的 Google Cloud 项目已启用结算功能

  7. 启用 GKE API。

    启用 API

设置 Google Cloud CLI 的默认值

  1. 在 Google Cloud 控制台中,启动 Cloud Shell 实例:
    打开 Cloud Shell

  2. 下载此示例应用的源代码:

     git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
     cd kubernetes-engine-samples/quickstarts/hello-app-redis/manifests
    
  3. 设置默认环境变量:

     gcloud config set project PROJECT-ID
     gcloud config set compute/zone COMPUTE-ZONE
    

    替换以下值:

创建在发布渠道中注册的 GKE 集群

如需创建 GKE 集群,请完成以下步骤:

  1. 创建一个名为 redis-test 且具有三个节点的集群:

    gcloud container clusters create redis-test \
        --num-nodes=3 \
        --release-channel regular
    

    创建该集群后,您应该会看到类似于以下示例的输出:

      NAME: redis-test
      LOCATION: us-central1-c
      MASTER_VERSION: 1.22.10-gke.600
      MASTER_IP: 34.69.67.7
      MACHINE_TYPE: e2-medium
      NODE_VERSION: 1.22.10-gke.600
      NUM_NODES: 3
      STATUS: RUNNING
    
  2. 配置 kubectl 以与集群通信:

    gcloud container clusters get-credentials redis-test
    

在 GKE 上创建 Redis 集群

在本部分中,您将通过部署 ConfigMapStatefulSet无头 Service,在先前创建的 GKE 集群之上添加一个 Redis 集群。

如需创建 Redis 集群,请完成以下步骤:

  1. 请查阅存储 Redis 配置的 ConfigMap 文件 (redis-configmap.yaml)。以下代码段展示了就绪性探测和活跃性探测脚本。

    readiness.sh: |-
      #!/bin/sh
    
      pingResponse="$(redis-cli -h localhost ping)"
      if [ "$?" -eq "124" ]; then
        echo "PING timed out"
        exit 1
      fi
    
      if [ "$pingResponse" != "PONG"]; then
        echo "$pingResponse"
        exit 1
      fi
    liveness.sh: |-
      #!/bin/sh
    
      pingResponse="$(redis-cli -h localhost ping | head -n1 | awk '{print $1;}')"
      if [ "$?" -eq "124" ]; then
        echo "PING timed out"
        exit 1
      fi
    
      if [ "$pingResponse" != "PONG"] && [ "$pingResponse" != "LOADING" ] && [ "$pingResponse" != "MASTERDOWN" ]; then
        echo "$pingResponse"
        exit 1
      fi

    readiness.shliveness.sh 脚本使用 redis-cli ping 来检查 redis 服务器是否正在运行。如果它返回 PONG,则表示 Redis 服务器已启动并正在运行。这些脚本将用在 redis-cluster.yaml 中。

    如需详细了解此 ConfigMap 中的 Redis 参数,请参阅 Redis 集群教程中的 Redis 集群配置参数部分。

  2. 部署 ConfigMap:

    kubectl apply -f redis-configmap.yaml
    
  3. 请参阅下面的 StatefulSet (redis-cluster.yaml) 代码段,其中使用了就绪性探测和活跃性探测。

    如需了解如何在 Kubernetes 中配置探测,请参阅配置探测

    startupProbe:
      periodSeconds: 5
      timeoutSeconds: 5
      successThreshold: 1
      failureThreshold: 20
      tcpSocket:
        port: redis
    livenessProbe:
      periodSeconds: 5
      timeoutSeconds: 5
      successThreshold: 1
      failureThreshold: 5
      exec:
        command: ["sh", "-c", "/probes/liveness.sh"]
    readinessProbe:
      periodSeconds: 5
      timeoutSeconds: 1
      successThreshold: 1
      failureThreshold: 5
      exec:
        command: ["sh", "-c", "/probes/readiness.sh"]

    我们强烈建议您在升级节点池时使用就绪性探测和活跃性探测,这样可以确保您的 Pod 在升级期间处于准备就绪状态。

  4. 部署 StatefulSet:

    kubectl apply -f redis-cluster.yaml
    
  5. 名为 redis-service.yaml 的无头 Service 用于 Redis 节点的连接。若要创建无头 Service,需将 clusterIP 字段设置为 None

    部署 Service:

    kubectl apply -f redis-service.yaml
    
  6. 等待大约两分钟,然后使用以下命令验证所有 Pod 是否正在运行:

    kubectl get pods
    

    您应该会看到类似于以下示例的输出:

    NAME      READY   STATUS              RESTARTS   AGE
    redis-0   1/1     Running             0          2m29s
    redis-1   1/1     Running             0          2m8s
    redis-2   1/1     Running             0          107s
    redis-3   1/1     Running             0          85s
    redis-4   1/1     Running             0          54s
    redis-5   1/1     Running             0          23s
    
  7. 通过运行以下命令来验证是否已创建永久性卷:

    kubectl get pv
    

    您应该会看到类似于以下示例的输出:

    NAME       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                  STORAGECLASS   REASON   AGE
    pvc-HASH   1Gi        RWO            Delete           Bound    default/data-redis-5   standard                75s
    pvc-HASH   1Gi        RWO            Delete           Bound    default/data-redis-1   standard                2m59s
    pvc-HASH   1Gi        RWO            Delete           Bound    default/data-redis-3   standard                2m16s
    pvc-HASH   1Gi        RWO            Delete           Bound    default/data-redis-2   standard                2m38s
    pvc-HASH   1Gi        RWO            Delete           Bound    default/data-redis-0   standard                3m20s
    pvc-HASH   1Gi        RWO            Delete           Bound    default/data-redis-4   standard                104s
    

    在此输出中,HASH 表示附加到每个永久性卷名称的哈希值。

向 Redis 集群分配角色

完成配置后,向 Redis 集群分配角色。

以下脚本会获取 Pod IP 地址,然后通过将每个 Pod IP 地址传递给命令来分配主角色和跟随角色:

#!/bin/bash
# Usage: ./roles.sh

urls=$(kubectl get pods -l app=redis -o jsonpath='{range.items[*]}{.status.podIP} ')
command="kubectl exec -it redis-0 -- redis-cli --cluster create --cluster-replicas 1 "

for url in $urls
do
    command+=$url":6379 "
done

echo "Executing command: " $command
$command

如需向 Redis 集群分配角色,请完成以下步骤:

  1. 运行脚本:

    chmod +x ./roles.sh
    ./roles.sh
    
  2. 出现提示时,请输入 yes

  3. 登录 Redis 节点以检查其角色。例如,如需验证 redis-0 是否具有主角色,请运行以下命令:

    kubectl exec -it redis-0 -- redis-cli role
    

    您应该会看到类似于以下示例的输出:

    1) "master"
    2) (integer) 574
    3) 1) 1) "10.28.2.3"
           2) "6379"
           3) "574"
    

部署 Redis 客户端应用

如需将应用部署到您创建的 GKE 集群,您需为应用定义 Deployment。名为 app-deployment.yaml 的文件包含应用的部署定义。

如需详细了解此 Deployment 中使用的探测和 Pod 亲和性规则,请参阅 GKE 最佳做法:设计和构建高可用性集群

如需创建 Deployment,请完成以下步骤:

  1. 应用 Deployment:

    kubectl apply -f app-deployment.yaml
    
  2. 通过负载均衡器公开应用:

    kubectl expose deployment hello-web \
        --type=LoadBalancer \
        --port 80 \
        --target-port 8080
    
  3. 等待大约一分钟,然后通过运行以下命令来检索应用的外部 IP 地址:

    kubectl get service
    

    在输出中,复制 hello-web's EXTERNAL-IP 列中列出的值:

    NAME             TYPE           CLUSTER-IP    EXTERNAL-IP    PORT(S)              AGE
    hello-web        LoadBalancer   10.13.10.55   EXTERNAL_IP   80:30703/TCP         166m
    
  4. 通过将 EXTERNAL_IP 粘贴到您的网络浏览器中来验证应用是否正常运行。您应该会看到类似于以下示例的输出:

    I have been hit [1] times since deployment!
    

    记下访问次数。在测试应用的中断部分中您需要使用该访问次数。

  5. 为刚刚复制的 EXTERNAL_IP 设置变量。在下一部分中创建脚本来测试应用时,您将使用此值:

    export IP=EXTERNAL_IP
    

根据最佳实践配置节点池升级

遵循以下最佳实践优化有状态应用,以便在节点池升级期间提高可用性。

设置 Pod 中断预算 (PDB)

创建 Pod 中断预算以限制在主动中断期间同时关停的 Pod 副本的数量。这对于需要仲裁来确定在升级期间可用的副本数量的有状态应用而言非常有用。

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: redis-pdb
spec:
  minAvailable: 3
  selector:
    matchLabels:
      app: redis

在 PDB 定义中:

  • app 指定此 PDB 适用的应用。
  • minAvailable 设置在中断期间可用的 Pod 数下限。该参数可以是数值或百分比(例如 30%)。
  • maxUnavailable 设置在中断期间不可用的 Pod 数上限。该参数也可以是数值或百分比。

如需设置 PDB,请完成以下步骤:

  1. 部署 PDB:

    kubectl apply -f pdb-minavailable.yaml
    
  2. 验证 PDB 是否已创建:

    kubectl get pdb
    

设置维护期和排除项

采用节点自动升级可简化升级过程,同时在系统代表您升级控制层面时,可确保集群中的节点保持最新状态。默认情况下,此功能处于启用状态。 如需了解详情,请参阅自动升级节点

使用维护期和维护排除项设置时间段来控制 GKE 集群上可以进行维护的时段以及不允许进行维护的时段:

  1. 设置从 2022 年 8 月 19 日凌晨 2:00 开始(采用世界协调时间)并持续 4 小时的维护期。此维护期每天运行一次。在此期间,允许自动维护。

    gcloud container clusters update redis-test \
       --maintenance-window-start 2022-08-19T02:00:00Z \
       --maintenance-window-end 2022-08-19T06:00:00Z \
       --maintenance-window-recurrence FREQ=DAILY
    
  2. 设置一个排除期,不允许在新年假期期间进行维护。此维护排除项使用 no_upgrades 范围。在此期间,不允许进行任何类型的自动维护。如需了解详情,请参阅要排除的维护范围

    gcloud container clusters update redis-test \
       --add-maintenance-exclusion-name new-year \
       --add-maintenance-exclusion-start 2022-12-26T00:00:00Z \
       --add-maintenance-exclusion-end 2023-01-02T02:00:00Z \
       --add-maintenance-exclusion-scope no_upgrades
    
  3. 验证维护期和排除项是否均已应用。查看 maintenancePolicy: 下的内容

    gcloud container clusters describe redis-test
    

如需了解详情,请参阅配置维护期和排除项

配置节点升级策略

目前有两种节点升级策略可用于 GKE 集群中的节点池:蓝绿升级超额配置升级。如需了解详情,请参阅节点升级策略

蓝绿升级

如果工作负载对中断的容忍度较低,并且能够接受因资源用量增加而导致的临时费用增加,则可选择蓝绿升级。

运行以下命令,将当前节点池更改为蓝绿升级策略。

gcloud container node-pools update default-pool \
--cluster=redis-test \
--enable-blue-green-upgrade \
--zone COMPUTE-ZONE \
--node-pool-soak-duration=120s

在本教程中,节点池过渡时长设置为两分钟,以节省节点池过渡阶段的时间。过渡阶段用于在蓝色池节点排空后验证工作负载的健康状况。我们建议将节点池过渡时段设置为一小时(即 3600 秒)或最适合应用的其他时段。

如需详细了解如何管理 Pod 分配,请参阅将 Pod 部署到特定节点池将 Service 部署到特定节点池

如需详细了解如何配置蓝绿升级,请参阅配置蓝绿升级

超额配置升级

如果费用优化非常重要,并且工作负载可以容忍 60 分钟以内的安全关停(GKE 遵循最长 60 分钟的 PDB),则可以选择超额配置升级。

运行以下命令,将当前节点池更改为超额配置升级策略。

gcloud container node-pools update default-pool \
--max-surge-upgrade=1 \
--max-unavailable-upgrade=0 \
--cluster=redis-test

使用此配置(maxSurge=1maxUnavailable=0)时,在升级期间只能将一个超额配置节点添加到节点池,因此一次只能升级一个节点。此设置可以在升级期间加快 Pod 重启的速度,同时以保守的方式进行升级。

如需详细了解如何配置超额配置升级,请参阅配置超额配置升级

检查当前节点池的配置:

   gcloud container node-pools describe default-pool \
   --cluster redis-test \
   --zone COMPUTE-ZONE

如需详细了解如何查看节点池,请参阅查看集群中的节点池

测试应用

在本部分中,您将使用两个脚本,一个用于向应用发送请求,一个用于衡量请求的成功率。您可以使用这些脚本来衡量升级集群时会发生什么情况。

如需创建脚本,请执行以下操作:

  1. 切换到包含脚本的目录:

    cd
    cd kubernetes-engine-samples/quickstarts/hello-app-redis/scripts
    
  2. 请查阅名为 generate_load.sh 的脚本,该脚本会向应用发送每秒查询次数 (QPS) 请求。该脚本会将 HTTP 响应代码保存到当前目录内名为 output 的文件中。output 的值用于您在下一步中创建的脚本。

    #!/bin/bash
    # Usage: ./generate_load.sh <IP> <QPS>
    
    IP=$1
    QPS=$2
    
    while true
      do for N in $(seq 1 $QPS)
        do curl -I -m 5 -s -w "%{http_code}\n" -o /dev/null http://${IP}/ >> output &
        done
      sleep 1
    done
  3. 请查阅名为 print_error_rate.sh 的脚本,该脚本会根据 generate_load.sh 生成的输出计算成功率。

    #!/bin/bash
    # Usage: watch ./print_error_rate.sh
    
    TOTAL=$(cat output | wc -l);
    SUCCESS=$(grep "200" output |  wc -l);
    ERROR1=$(grep "000" output |  wc -l)
    ERROR2=$(grep "503" output |  wc -l)
    ERROR3=$(grep "500" output |  wc -l)
    SUCCESS_RATE=$(($SUCCESS * 100 / TOTAL))
    ERROR_RATE=$(($ERROR1 * 100 / TOTAL))
    ERROR_RATE_2=$(($ERROR2 * 100 / TOTAL))
    ERROR_RATE_3=$(($ERROR3 * 100 / TOTAL))
    echo "Success rate: $SUCCESS/$TOTAL (${SUCCESS_RATE}%)"
    echo "App network Error rate: $ERROR1/$TOTAL (${ERROR_RATE}%)"
    echo "Resource Error rate: $ERROR2/$TOTAL (${ERROR_RATE_2}%)"
    echo "Redis Error rate: $ERROR3/$TOTAL (${ERROR_RATE_3}%)"
  4. 授权自己运行脚本的权限:

    chmod u+x generate_load.sh print_error_rate.sh
    
  5. 为 QPS 次数设置变量。与您为 EXTERNAL_IP 设置的变量一样,该值用于 generate_load.sh 脚本。我们建议您将值设置为 40。

    export QPS=40
    
  6. 运行 generate_load.sh 脚本以开始发送 QPS:

    ./generate_load.sh $IP $QPS 2>&1
    
  7. generate_load.sh 脚本保持运行状态,并打开一个新终端。在新终端中,运行 print_error_rate.sh 脚本以检查错误率:

    cd
    cd kubernetes-engine-samples/quickstarts/hello-app-redis/scripts
    watch ./print_error_rate.sh
    

    发出 QPS 时,您应该会看到 100% 的成功率和 0% 的错误率。

  8. 让这两个脚本保持运行状态,并打开第三个终端,为下一部分做好准备。

升级集群

如需升级集群,请完成以下步骤:

  1. 确定 redis-test 集群正在使用的 GKE 版本:

    V=$(gcloud container clusters describe redis-test | grep "version:" | sed "s/version: //")
    echo $V
    

    您应该会看到类似于以下示例的输出:1.22.9-gke.2000

  2. 检索可用 Kubernetes 版本的列表:

    gcloud container get-server-config
    
  3. 在该版本列表中,找到 validMasterVersions: 部分,并查找您在上一步中检索到的 redis-cluster 版本。为了避免版本倾斜,请从紧跟 redis-cluster 版本的列表中复制该版本。

  4. 将集群的控制层面升级到您选择的版本,并在出现提示时输入 y

    gcloud container clusters upgrade redis-test \
        --master \
        --cluster-version VERSION
    

    VERSION 替换为您在上一步从列表中选择的版本。

    控制层面升级需要几分钟时间。

  5. 将集群的节点升级到您选择的版本,并在出现提示时输入 y

    gcloud container clusters upgrade redis-test \
        --cluster-version=VERSION \
        --node-pool=default-pool
    

    VERSION 替换为您从列表中选择的版本。

测试工作负载中断

在本部分中,您将测试应用的状态并观察工作负载的中断情况。

  1. 返回到运行 ./print_error_rate.sh 的终端窗口,并观察升级期间成功率的变化情况。在节点关停以进行升级时,您应该会发现成功率略有下降,应用网络错误率略有增长。

    Success rate 字段中,您会看到成功访问网站的次数。请记下该值。

  2. 通过在相关终端中输入 CTRL+C 停止运行这两个脚本。

  3. 通过在浏览器中输入应用的 IP 地址(即您在部署 Redis 客户端应用期间复制的 EXTERNAL_IP),返回到应用的网站。

  4. 注意应用的访问次数。您看到的数字应等于:

    ORIGINAL_VISIT_NUMBER + SUCCESSFUL_VISIT_NUMBER

    其中,ORIGINAL_VISIT_NUMBER 是您在部署 Redis 客户端应用的最后一步中记录的数字,SUCCESSFUL_VISIT_NUMBER 是您在本部分的第一步中记录的值。

清理

完成本教程后,您可以清理您创建的资源,让它们停止使用配额,以免产生费用。以下部分介绍如何删除或关闭这些资源。

删除项目

为了避免产生费用,最简单的方法是删除您为本教程创建的项目。

如需删除项目,请执行以下操作:

  1. 在 Google Cloud 控制台中,进入管理资源页面。

    转到“管理资源”

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

删除集群

如需删除为本教程创建的集群,请运行以下命令:

gcloud container clusters delete redis-test

后续步骤