使用 Spotahome 将 Redis 部署到 GKE


Redis 是一个开源内存中 NoSQL 数据库,主要用于缓存。它具有内置的复制功能、Lua 脚本、LRU 逐出、事务、磁盘持久性和高可用性。

本指南适用于有意在 Google Kubernetes Engine (GKE) 上部署 Redis 集群的平台管理员、云架构师和专业运营人员。

本指南介绍如何使用 Spotahome Redis 运算符部署 Redis 集群。

运算符根据 Apache License 2.0 获得授权

Spotahome 具有以下优势:

  • Kubernetes 原生 Redis 集群管理
  • Redis Sentinel 提供的高可用性
  • 无缝 Prometheus 集成,实现数据库可观测性
  • 支持设置自定义 Redis 配置

目标

  • 为 Redis 规划和部署 GKE 基础架构
  • 部署和配置 Spotahome Redis 运算符
  • 使用运算符配置 Redis,以确保可用性、安全性、可观测性和性能

部署架构

在本教程中,您将使用 Spotahome Redis Operator 将高可用性 Redis 集群部署和配置到一个具有主节点和两个读取副本的 GKE,以及由三个副本组成的 Redis Sentinel 集群。

Redis Sentinel 是开源 Redis 的高可用性和监控系统。它会持续监控 Redis 实例,包括主要副本及其关联的副本。如果主节点发生故障,Sentinel 可以自动将一个副本提升为新的主节点,确保始终有可用的主节点用于数据读写。当 Redis 集群中发生重大事件(例如领导者故障或故障切换事件)时,Sentinel 可以通过电子邮件或其他通知机制通知管理员或其他系统。

您还将部署 Redis 的高可用性区域级 GKE 集群,其中多个 Kubernetes 节点分布在多个可用区中。此设置有助于确保容错、可伸缩性和地理冗余。这样可支持滚动更新和维护,同时提供 SLA 以保证正常运行时间和可用性。如需了解详情,请参阅区域级集群

下图显示了 Redis 集群如何在 GKE 集群中的多个节点和可用区上运行:

在该图中,Redis StatefulSet 部署在三个不同可用区的三个节点上。您可以通过以下方式控制 GKE 如何将 StatefulSet 部署到节点和可用区:在 RedisFailover 自定义资源规范中设置 Pod 亲和性拓扑扩展规则。

如果一个可用区发生故障,则使用推荐的配置,GKE 会在新节点上重新调度 Pod。

下图显示了安排在三个不同可用区的三个节点上的 Sentinel Deployment:

费用

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

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

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

准备工作

  1. 登录您的 Google Cloud 账号。如果您是 Google Cloud 新手,请创建一个账号来评估我们的产品在实际场景中的表现。新客户还可获享 $300 赠金,用于运行、测试和部署工作负载。
  2. 安装 Google Cloud CLI。
  3. 如需初始化 gcloud CLI,请运行以下命令:

    gcloud init
  4. Create or select a Google Cloud project.

    • Create a Google Cloud project:

      gcloud projects create PROJECT_ID

      Replace PROJECT_ID with a name for the Google Cloud project you are creating.

    • Select the Google Cloud project that you created:

      gcloud config set project PROJECT_ID

      Replace PROJECT_ID with your Google Cloud project name.

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

  6. Enable the Compute Engine, IAM, GKE, Backup for GKE, and Resource Manager APIs:

    gcloud services enable compute.googleapis.com iam.googleapis.com container.googleapis.com gkebackup.googleapis.com cloudresourcemanager.googleapis.com
  7. 安装 Google Cloud CLI。
  8. 如需初始化 gcloud CLI,请运行以下命令:

    gcloud init
  9. Create or select a Google Cloud project.

    • Create a Google Cloud project:

      gcloud projects create PROJECT_ID

      Replace PROJECT_ID with a name for the Google Cloud project you are creating.

    • Select the Google Cloud project that you created:

      gcloud config set project PROJECT_ID

      Replace PROJECT_ID with your Google Cloud project name.

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

  11. Enable the Compute Engine, IAM, GKE, Backup for GKE, and Resource Manager APIs:

    gcloud services enable compute.googleapis.com iam.googleapis.com container.googleapis.com gkebackup.googleapis.com cloudresourcemanager.googleapis.com
  12. 向您的 Google 账号授予角色。对以下每个 IAM 角色运行以下命令一次: roles/storage.objectViewer, roles/container.admin, roles/iam.serviceAccountAdmin, roles/compute.admin, roles/gkebackup.admin, roles/monitoring.viewer

    gcloud projects add-iam-policy-binding PROJECT_ID --member="user:EMAIL_ADDRESS" --role=ROLE
    • PROJECT_ID 替换为您的项目 ID。
    • EMAIL_ADDRESS 替换为您的电子邮件地址。
    • ROLE 替换为每个角色。

设置您的环境

在本教程中,您将使用 Cloud Shell 来管理 Google Cloud 上托管的资源。Cloud Shell 预安装有本教程所需的软件,包括 kubectlgcloud CLIHelmTerraform

如需使用 Cloud Shell 设置您的环境,请按照以下步骤操作:

  1. 点击 Google Cloud 控制台中的 Cloud Shell 激活图标 激活 Cloud Shell,从 Google Cloud 控制台启动 Cloud Shell 会话。此操作会在 Google Cloud 控制台的底部窗格中启动会话。

  2. 设置环境变量:

    export PROJECT_ID=PROJECT_ID
    export KUBERNETES_CLUSTER_PREFIX=redis
    export REGION=us-central1
    

    替换 PROJECT_ID:您的 Google Cloud 项目 ID

  3. 克隆 GitHub 代码库:

    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
    
  4. 切换到工作目录:

    cd kubernetes-engine-samples/databases/redis-spotahome
    

创建集群基础架构

在本部分中,您将运行 Terraform 脚本以创建可用性高的专用区域级 GKE 集群。通过执行以下步骤,可以公开访问控制平面。

您可以使用 Standard 或 Autopilot 集群安装操作器。

Standard

下图显示了部署在三个不同可用区中的专用区域级 Standard GKE 集群:

若要部署此基础架构,请从 Cloud Shell 运行以下命令:

export GOOGLE_OAUTH_ACCESS_TOKEN=$(gcloud auth print-access-token)
terraform -chdir=terraform/gke-standard init
terraform -chdir=terraform/gke-standard apply -var project_id=${PROJECT_ID} \
  -var region=${REGION} \
  -var cluster_prefix=${KUBERNETES_CLUSTER_PREFIX}

出现提示时,请输入 yes。完成此命令并使集群显示就绪状态可能需要几分钟时间。

Terraform 会创建以下资源:

  • Kubernetes 节点的 VPC 网络和专用子网
  • 用于通过 NAT 访问互联网的路由器
  • 专用 GKE 集群(在 us-central1 区域中)
  • 两个启用了自动扩缩功能的节点池(每个可用区一个到两个节点,每个可用区最少一个节点)
  • 具有日志记录和监控权限的 ServiceAccount
  • 用于灾难恢复的 Backup for GKE
  • Google Cloud Managed Service for Prometheus,用于监控集群

输出类似于以下内容:

...
Apply complete! Resources: 14 added, 0 changed, 0 destroyed.
...

Autopilot

下图显示了专用区域级 Autopilot GKE 集群:

若要部署此基础架构,请从 Cloud Shell 运行以下命令:

export GOOGLE_OAUTH_ACCESS_TOKEN=$(gcloud auth print-access-token)
terraform -chdir=terraform/gke-autopilot init
terraform -chdir=terraform/gke-autopilot apply -var project_id=${PROJECT_ID} \
  -var region=${REGION} \
  -var cluster_prefix=${KUBERNETES_CLUSTER_PREFIX}

出现提示时,请输入 yes。完成此命令并使集群显示就绪状态可能需要几分钟时间。

Terraform 会创建以下资源:

  • Kubernetes 节点的 VPC 网络和专用子网
  • 用于通过 NAT 访问互联网的路由器
  • 专用 GKE 集群(在 us-central1 区域中)
  • 具有日志记录和监控权限的 ServiceAccount
  • Google Cloud Managed Service for Prometheus,用于监控集群

输出类似于以下内容:

...
Apply complete! Resources: 12 added, 0 changed, 0 destroyed.
...

连接到集群

使用 Cloud Shell,配置 kubectl 以与集群通信:

gcloud container clusters get-credentials ${KUBERNETES_CLUSTER_PREFIX}-cluster --region ${REGION}

将 Spotahome 操作器部署到您的集群

在本部分中,您将使用 Helm Chart 将 Spotahome 运算符部署到 Kubernetes 集群,然后部署 Redis 集群。

  1. 添加 Spotahome Redis 运算符 Helm 图表代码库:

    helm repo add redis-operator https://spotahome.github.io/redis-operator
    
  2. 为 Spotahome 运算符和 Redis 集群添加命名空间:

    kubectl create ns redis
    
  3. 使用 Helm 命令行工具部署 Spotahome 运算符:

    helm install redis-operator redis-operator/redis-operator --version 3.2.9 -n redis
    
  4. 使用 Helm 检查 Spotahome 运算符的部署状态:

    helm ls -n redis
    

    输出类似于以下内容:

    NAME             NAMESPACE    REVISION    UPDATED                                STATUS      CHART                   APP VERSION
    redis-operator    redis      1           2023-09-12 13:21:48.179503 +0200 CEST    deployed    redis-operator-3.2.9    1.2.4
    

部署 Redis

Redis 集群实例的基本配置包括以下组件:

  • Redis 节点的三个副本:一个主节点,两个读取副本。
  • Sentinel 节点的三个副本,共同形成仲裁。
  • 一个 CPU 请求和两个 CPU 限制的 CPU 资源分配,对于 Redis 为 4 GB 内存请求和限制,对于 Sentinel 为 100 m/500 m CPU 和 500 MB。
  • 为每个工作负载配置容忍设置、nodeAffinitiestopologySpreadConstraints,利用它们各自的节点池和不同的可用区来确保跨 Kubernetes 节点进行适当分布。

此配置表示创建可直接用于生产环境的 Redis 集群所需的最少设置。

创建基本 Redis 集群

  1. 创建包含用户凭据的 Secret:

    export PASSWORD=$(openssl rand -base64 12)
    kubectl create secret generic my-user -n redis \
        --from-literal=password="$PASSWORD"
    

    Operator 没有生成凭据的功能,并且应预先生成数据库密码。

  2. 使用基本配置创建新的 Redis 集群:

    kubectl apply -n redis -f manifests/01-basic-cluster/my-cluster.yaml
    

    此命令会创建一个 Spotahome 运算符的 RedisFailover 自定义资源,用于指定 CPU、内存请求和限制;以及在 Kubernetes 节点之间分发预配的 Pod 副本的污点及亲和性。

  3. 请等待几分钟,让 Kubernetes 启动所需的工作负载:

    kubectl wait pods -l redisfailovers.databases.spotahome.com/name=my-cluster --for condition=Ready --timeout=300s -n redis
    
  4. 验证是否已创建 Redis 工作负载:

    kubectl get pod,svc,statefulset,deploy,pdb -n redis
    

    输出类似于以下内容:

    NAME                                READY   STATUS  RESTARTS   AGE
    pod/redis-operator-5dc65cb7cc-krlcs   1/1   Running   0         49m
    pod/rfr-my-cluster-0                2/2     Running   0         60s
    pod/rfr-my-cluster-1                2/2     Running   0         60s
    pod/rfr-my-cluster-2                2/2     Running   0         60s
    pod/rfs-my-cluster-8475dfd96c-h5zvw   1/1   Running   0         60s
    pod/rfs-my-cluster-8475dfd96c-rmh6f   1/1   Running   0         60s
    pod/rfs-my-cluster-8475dfd96c-shzxh   1/1   Running   0         60s
    
    NAME                    TYPE        CLUSTER-IP  EXTERNAL-IP   PORT(S)   AGE
    service/redis-my-cluster ClusterIP   10.52.14.87   <none>       6389/TCP    55s
    service/redis-operator   ClusterIP   10.52.13.217   <none>      9710/TCP    49m
    service/rfr-my-cluster   ClusterIP   None           <none>      9121/TCP    61s
    service/rfs-my-cluster   ClusterIP   10.52.15.197   <none>      26379/TCP   61s
    
    NAME                            READY   AGE
    statefulset.apps/rfr-my-cluster   3/3   61s
    
    NAME                            READY   UP-TO-DATE   AVAILABLE   AGE
    deployment.apps/redis-operator   1/1    1           1           50m
    deployment.apps/rfs-my-cluster   3/3    3           3           62s
    
    NAME                                        MIN AVAILABLE   MAX UNAVAILABLE   ALLOWED DISRUPTIONS   AGE
    poddisruptionbudget.policy/rfr-my-cluster   2               N/A             1                   64s
    poddisruptionbudget.policy/rfs-my-cluster   2               N/A             1                   63s
    

操作器创建以下资源:

  • Redis StatefulSet 和 Sentinel Deployment
  • 三个适用于 Redis 的 Pod 副本
  • Sentinel 的三个 Pod 副本
  • 两个 PodDisruptionBudgets,确保至少有两个可用的副本以实现集群一致性
  • rfr-my-cluster Service,用于公开 Redis 指标
  • redis-my-cluster Service,用于定位 Redis 集群主节点
  • rfs-my-cluster Service,允许客户端通过 Sentinels 连接到集群。客户端库需要 Sentinel 支持。

共享 Redis 凭据

您可以使用 Spotahome 运算符旧版身份验证方法与客户端共享 Redis 凭据

您必须使用数据库密码通过 requirepass 设置。此密码随后会用于所有客户端。如需管理其他用户,请使用 Redis CLI 命令

apiVersion: databases.spotahome.com/v1
kind: RedisFailover
metadata:
  name: my-cluster
spec:
  ...
  auth:
    secretPath: my-user

使用此方法与客户端共享 Redis 凭据存在以下限制:

  • Spotahome 不为用户管理提供自定义资源。您可以将凭据存储在 Secret 中,并在 auth 规范中引用它们。
  • 无法使用自定义资源通过 TLS 加密保护连接。
  • 不支持实时更新凭据。

连接到 Redis

您可以部署 Redis 客户端,并使用存储在 Kubernetes Secret 中的密码进行身份验证。

  1. 运行客户端 pod 以与您的 Redis 集群进行交互:

    kubectl apply -n redis -f manifests/02-auth/client-pod.yaml
    

    PASS 环境变量从保险柜中获取 my-user Secret。

  2. 等待 Pod 准备就绪,然后连接到 Pod:

    kubectl wait pod redis-client --for=condition=Ready --timeout=300s -n redis
    kubectl exec -it redis-client -n redis -- /bin/bash
    
  3. 验证连接是否有效:

    redis-cli -h redis-my-cluster -a $PASS --no-auth-warning SET my-key "testvalue"
    

    输出类似于以下内容:

    OK
    
  4. 获取 my-key 值:

    redis-cli -h redis-my-cluster -a $PASS --no-auth-warning GET my-key
    

    输出类似于以下内容:

    "testvalue"
    
  5. 退出 Pod shell

    exit
    

了解 Prometheus 如何收集 Redis 集群的指标

下图展示了 Prometheus 指标收集的工作原理:

在该图中,GKE 专用集群包含:

  • Redis Pod,用于收集路径 / 和端口 9121 的指标
  • 基于 Prometheus 的收集器,用于处理 Redis Pod 中的指标
  • 将指标发送到 Cloud Monitoring 的 PodMonitoring 资源

Google Cloud Managed Service for Prometheus 支持 Prometheus 格式的指标收集。Cloud Monitoring 使用集成式信息中心来处理 Redis 指标。

Spotahome 运算符使用 redis_exporter 作为 Sidecar 公开采用 Prometheus 格式的集群指标。

  1. 创建 PodMonitoring 资源,以按 labelSelector 抓取指标:

    kubectl apply -n redis -f manifests/03-prometheus-metrics/pod-monitoring.yaml
    
  2. 在 Google Cloud 控制台中,转到 GKE 集群信息中心页面。

    转到 GKE 集群信息中心

    信息中心显示非零指标提取率。

  3. 在 Google Cloud 控制台中,转到信息中心页面。

    转到“信息中心”

  4. 打开 Redis Prometheus 概览信息中心。信息中心会显示连接数和密钥数。信息中心可能需要几分钟时间才能完成自动预配。

  5. 连接到客户端 Pod 并准备变量:

    kubectl exec -it redis-client -n redis -- /bin/bash
    
  6. 使用 redis-cli 工具创建新密钥:

    for i in {1..50}; do \
      redis-cli -h redis-my-cluster -a $PASS \
      --no-auth-warning SET mykey-$i "myvalue-$i"; \
    done
    
  7. 刷新页面,您会观察到每秒命令数图已更新为显示实际数据库状态。

  8. 退出 Pod shell

    exit
    

清理

删除项目

    删除 Google Cloud 项目:

    gcloud projects delete PROJECT_ID

删除各个资源

  1. 设置环境变量。

    export PROJECT_ID=${PROJECT_ID}
    export KUBERNETES_CLUSTER_PREFIX=redis
    export REGION=us-central1
    
  2. 运行 terraform destroy 命令:

    export GOOGLE_OAUTH_ACCESS_TOKEN=$(gcloud auth print-access-token)
    terraform -chdir=terraform/FOLDER destroy -var project_id=${PROJECT_ID} \
      -var region=${REGION} \
      -var cluster_prefix=${KUBERNETES_CLUSTER_PREFIX}
    

    FOLDER 替换为 gke-autopilotgke-standard

    出现提示时,请输入 yes

  3. 查找所有未挂接的磁盘:

    export disk_list=$(gcloud compute disks list --filter="-users:* AND labels.name=${KUBERNETES_CLUSTER_PREFIX}-cluster" --format "value[separator=|](name,zone)")
    
  4. 删除磁盘:

    for i in $disk_list; do
      disk_name=$(echo $i| cut -d'|' -f1)
      disk_zone=$(echo $i| cut -d'|' -f2|sed 's|.*/||')
      echo "Deleting $disk_name"
      gcloud compute disks delete $disk_name --zone $disk_zone --quiet
    done
    

后续步骤