使用 Envoy 为 Cloud Memorystore 创建跨区域副本

原作者为:Cloud Memorystore 产品经理:Matt Geerling 云架构师经理:Kundan Kumar

编译作者:MeshCloud脉时云公有云CSM邓军

引言

内存数据库是一个关键组件,它可以为您的用户提供尽可能低的延迟,这些用户可能会向在线购物车添加商品、获取个性化内容推荐或查看最新账户余额。Memorystore 使开发人员可以轻松地在 Google Cloud 上构建这些类型的应用程序,以利用最受欢迎的内存存储:Redis 的速度和强大功能。Memorystore for Redis 为其标准层级实例提供区域高可用性和 99.9% 的 SLA。在某些情况下,用户希望将他们的 Memorystore 足迹扩展到多个区域,以支持区域故障的灾难恢复场景,或为多区域应用程序部署提供尽可能低的延迟。使用 Cloud Memorystore 和 Envoy 扩展到新的高度。由于支持众多配置,Envoy 使得创建这样的架构既简单又可扩展。让从一个动手教程开始演示如何构建类似的解决方案。

架构概述

我们首先讨论谷歌云原生服务与开源软件相结合的架构,该架构支持多区域 Memorystore 架构。为此,我们将使用Envoy将流量镜像到我们将在不同区域创建的两个 Memorystore 实例。为简单起见,我们将使用 Memtier Benchmark(一种流行的 Redis 负载生成 CLI)作为示例应用程序来模拟最终用户流量。在实践中,您可以随意使用您现有的应用程序或编写您自己的应用程序。

由于 Envoy 的流量镜像配置,应用程序不需要知道存在的各种后端实例,只需要连接到代理。您将在下面找到一个示例架构,我们将简要介绍每个主要组件。

在开始之前,您还需要通过查看Envoy 当前支持的 Redis 命令列表来确保与您的应用程序的兼容性。详情参考:

https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/other_protocols/redis#supported-commands

先决条件

要按照本演练进行操作,您需要一个具有执行以下操作权限的 Google Cloud 项目:

  • 为 Redis 实例部署 Cloud Memorystore(需要权限)
  • 使用 SSH 访问部署 GCE 实例(所需权限)
  • 云监控查看者访问权限(所需权限)
  • 访问Cloud Shell或其他经过 gCloud 身份验证的环境

部署多区域 Memorystore 后端

首先将从为 Redis 缓存部署后端 Memorystore 开始,它将为所有应用程序流量提供服务。需要在不同的区域部署两个实例,以便可以保护业务环境免受区域中断的影响。本环境中选择了区域 US-West1 和 US-Central1,当然我们也可以自由选择测试环境最适合的区域。

在经过身份验证的云 Shell 环境中,这可以按如下方式完成:

$ gcloud redis instances create memorystore-primary –size=1 –region=us-west1 –tier=STANDARD –async

$ gcloud redis instances create memorystore-standby –size=1 –region=us-central1 –tier=STANDARD –async

如果尚未在项目中启用 Memorystore for Redis API,在执行上面该命令将之前建议先启用 API。在部署 Memorystore 实例时(通常需要几分钟),然后继续执行后续步骤。

创建客户端和代理 VM

接下来,需要创建两台 VM(为了防止区域故障,建议在每个区域创建一个 GCE 实例。)用于部署 Redis 客户端和 Envoy 代理。在每个实例上,您部署两个应用程序,Envoy 和 Memtier Benchmark,作为容器。这种类型的部署被称为“sidecar 架构”,这是一种常见的 Envoy 部署模型。以这种方式部署几乎消除了任何额外的网络延迟,因为没有发生额外的物理网络跃点。

主要区域 VM 开始: 在Cloud Shell中执行如下gcloud命令

$ gcloud compute instances create client-primary –zone=us-west1-a –machine-type=e2-highcpu-8 –image-family cos-stable –image-project cos-cloud

接下来,创建次要区域 VM:

$ gcloud compute instances create client-standby –zone=us-central1-a –machine-type=e2-highcpu-8 –image-family cos-stable –image-project cos-cloud

配置和部署 Envoy 代理

在部署代理之前,需要收集必要的信息以正确配置 Memorystore 端点。首先获取已创建的 Memorystore 实例的主机 IP 地址。您可以像这样收集这些:

gcloud redis instances describe memorystore-primary –region us-west1 –format=json | jq -r “.host”

gcloud redis instances describe memorystore-standby –region us-central1 –format=json | jq -r “.host”

将这些 IP 地址复制到文本工具中,后续会在 Envoy 配置中使用它们。另外也可以在“主端点”列下的 Memorystore 控制台页面中找到这些IP地址。

接下来,需要SSH连接到每个新创建的 VM 实例,开始部署 Envoy 代理。一般可以通过 Google Cloud Console 中的 SSH 连接VM实例。

成功连接到实例后,您将创建 Envoy 配置文件:envoy.yaml

首先使用文本编辑器创建一个以实例命名的新文件。使用以下 .yaml 文件,输入之前创建的主实例和辅助实例的 IP 地址:

static_resources:
 listeners:
 - name: primary_redis_listener
   address:
     socket_address:
       address: 0.0.0.0
       port_value: 1999
   filter_chains:
   - filters:
     - name: envoy.filters.network.redis_proxy
       typed_config:
         "@type": type.googleapis.com/envoy.extensions.filters.network.redis_proxy.v3.RedisProxy
         stat_prefix: primary_egress_redis
         settings:
           op_timeout: 5s
           enable_hashtagging: true
         prefix_routes:
           catch_all_route:
             cluster: primary_redis_instance
             request_mirror_policy:
               cluster: secondary_redis_instance
               exclude_read_commands: true
 - name: secondary_redis_listener
   address:
     socket_address:
       address: 0.0.0.0
       port_value: 2000
   filter_chains:
   - filters:
     - name: envoy.filters.network.redis_proxy
       typed_config:
         "@type": type.googleapis.com/envoy.extensions.filters.network.redis_proxy.v3.RedisProxy
         stat_prefix: secondary_egress_redis
         settings:
           op_timeout: 5s
           enable_hashtagging: true
         prefix_routes:
           catch_all_route:
             cluster: secondary_redis_instance
 clusters:
 - name: primary_redis_instance
   connect_timeout: 3s
   type: STRICT_DNS
   lb_policy: RING_HASH
   dns_lookup_family: V4_ONLY
   load_assignment:
     cluster_name: primary_redis_instance
     endpoints:
     - lb_endpoints:
       - endpoint:
           address:
             socket_address:
               address: <primary_region_memorystore_ip>
               port_value: 6379 
 - name: secondary_redis_instance
   connect_timeout: 3s
   type: STRICT_DNS 
   lb_policy: RING_HASH
   load_assignment:
     cluster_name: secondary_redis_instance
     endpoints:
     - lb_endpoints:
       - endpoint:
           address:
             socket_address:
               address: <secondary_region_memorystore_ip>
               port_value: 6379
   
admin:
 address:
   socket_address:
     address: 0.0.0.0
     port_value: 8001
  • 各种配置界面说明如下:
    • Admin:此接口是可选的,它允许您查看配置和统计信息等。还允许查询和修改 envoy 代理的不同方面。
    • Static_resources:这包含在 envoy 代理启动期间配置的项目。在此内部,我们定义了集群和侦听器接口。
    • 集群:此界面允许您定义我们为每个区域定义的集群。在集群配置中,您可以定义所有可用的主机以及如何在这些主机之间分配负载。我们定义了两个集群,一个在主要区域,另一个在次要区域。每个集群可以有一组不同的主机和不同的负载均衡器策略。由于每个集群中只有一台主机,您可以使用任何负载均衡器策略,因为所有请求都将转发到该主机。
    • Listeners:此接口允许您公开客户端连接的端口,并定义接收到的流量的行为。在这种情况下,我们定义了两个侦听器,一个用于每个区域 Memorystore 实例。

添加 Memorystore 实例 IP 地址后,将yaml 文件保存到容器操作系统虚拟机。

接下来使用 Docker 拉取官方 Envoy 代理镜像并使用您自己的配置运行它。

在主要区域客户端计算机上,运行此命令:

$ docker run –rm -d -p 8001:8001 -p 6379:1999 -v $(pwd)/envoy.yaml:/envoy.yaml envoyproxy/envoy:v1.21.0 -c /envoy.yaml

在备用区域客户端机器上,运行此命令:

$ docker run –rm -d -p 8001:8001 -p 6379:2000 -v $(pwd)/envoy.yaml:/envoy.yaml envoyproxy/envoy:v1.21.0 -c /envoy.yaml

备注:对于备用区域,我们已将绑定端口更改为端口 2000。这是为了确保在发生区域故障导致主实例不可用时,来自备用客户端的流量将路由到备用实例。

在本测试环境中,我们手动部署 envoy 代理。但实际生产环境中建议部署一个 CI/CD 管道,该管道将部署 envoy 代理并根据您基于区域的配置绑定端口。

  • 现在已经部署了 Envoy,您可以通过从容器 VM 访问管理界面来测试它:
  • $ curl -v localhost:8001/stats

如果成功,应该会在终端中看到打印出的各种 Envoy 管理统计信息。如果还没有任何流量,这些信息不会特别有用,但可以确认容器正在运行并在网络上可用。如果此命令不成功,建议检查 Envoy 容器是否正在运行。常见问题包括 envoy.yaml 中的语法错误,可以通过交互式运行 Envoy 容器并读取终端输出来发现。

部署并运行 Memtier 基准测试

通过 SSH 重新连接到 us-west1 中的主客户端实例后,需要部署 Memtier Benchmark 实用程序,使用它来生成人工 Redis 流量。使用 Memtier Benchmark无需提供自己的数据集。它可以使用一系列设置命令来填充缓存。

$ for i in {1..5}; do docker run --network="host" --rm -d redislabs/memtier_benchmark:1.3.0 -s 127.0.0.1 -p 6379 —threads 2 –clients 10 --test-time=300 --key-maximum=100000 --ratio=1:1 --key-prefix="memtier-$RANDOM-"; done

验证缓存内容

现在我们已经从主要区域的客户端生成了一些数据,验证数据是否已写入我们的两个区域 Memorystore 实例。我们可以使用GCP 云监控 metrics-explorer来查看。通过在云监控的资源管理器窗格顶部选择的“MQL”配置图表。为方便起见,我们创建了一个查询,您只需将其粘贴到您的控制台中即可填充您的图表:

fetch redis_instance | metric ‘redis.googleapis.com/keyspace/keys’ | filter (resource.instance_id =~ ‘.*memorystore.*’) && (metric.role == ‘primary’) | group_by 1m, [value_keys_mean: mean(value.keys)] | every 1m

如果有使用不同的命名约定创建了 Memorystore 实例,或者在同一项目中有其他 Memorystore 实例,则可以修改 resource.instance_id 过滤器。完成后,注意图表显示的是适当的时间范围,然后应该会看到类似以下内容:

在此图中,可以看到两条相似的线,它们在两个 Memorystore 实例中显示相同数量的键。如果想查看单个实例的指标,可以使用默认监控图来执行此操作,这些图表在选择特定实例后 可从Memorystore 控制台获得。

模拟区域故障

区域故障是罕见的事件。我们通过删除主要 Memorystore 实例和主要客户端 VM 来模拟这一点。

首先删除我们的主要 Memorystore 实例,gcloud命令如下:

$ gcloud redis instances delete memorystore-primary –region=us-west1

然后删除的客户端 VM :

$ gcloud compute instances delete client-primary

接下来,我们需要从用作备用应用程序的次要区域客户端 VM 生成流量。

为了方便验证,我们通过上述手动执行故障转移并生成流量来节省时间。但实际生产环境中需要设计一种故障转移策略,以便在主要区域变得不可用时自动将流量转移到备用区域,通常可以在Cloud Load Balancer负载均衡等服务来实现。接下来,从控制台通过 ssh 进入次要区域客户端 VM,并运行上一节中提到的 Memtier 基准测试应用程序。可以通过再次查看控制台的监控图来验证读取和写入是否正确路由到我们的备用实例。

一旦原始主 Memorystore 实例再次可用,它将根据我们的 Envoy 配置成为新的备用实例。它也将与我们的新主实例不同步,因为它在不可用期间错过了写入。本篇文章中就不详细介绍这种问题解决方案,但根据大多数用户的方式,可以选择在密钥上设置的TTL来确定缓存最终何时同步。

结论

虽然 Cloud Memorystore 标准层提供高可用性,但某些用例需要更高的可用性保证。Envoy 及其 Redis 过滤器使创建多区域部署变得简单且可扩展。上面提供的方案是一个很好的指引。这些指令可以轻松扩展以支持自动区域故障转移甚至双区域主动-主动部署。另外也可以通过有关 Cloud Memorystore 的GCP官方文档了解更多信息:
https://cloud.google.com/memorystore

发表评论

您的电子邮箱地址不会被公开。