关键字
Amazon ElastiCache,数据迁移,Redis 异构迁移,非集群模式,Redis 集群访问代理,自动扩展
背景
在一些客户的迁移项目中,我们会发现客户可能在自建或者托管 Redis 集群并非采用 Redis 原生集群模式(尤其是 Redis 5.0 以下版本),比如采用了 Codis 或者 Twemproxy 的代理集群模式方式。而客户对于迁移至 Amazon ElastiCache for Redis 后希望采用原生集群方案,并需要保证迁移后的原生集群尽量规避修改客户应用侧代码进行访问。而目前现有的亚马逊云科技原生 Redis 迁移工具并没有可以针对非亚马逊云科技原生 Redis 实例迁移至 Amazon ElastiCache for Redis(AWS ElastiCache 迁移先决条件),所以针对这种异构的迁移方案,我们采用了解决方案:Amazon ElastiCache for Redis 集群代理方案。
Amazon ElastiCache for Redis 集群代理方案设计
架构设计
- Redis 数据迁移
- 在源端(IDC/公有云)针对每一个 Redis 实例部署一个专属的 redis-port 代理用于迁移源端 Redis 数据。
- 源端和亚马逊云科技网络之间通过 Amazon Direct Connect 专用网络连接确保网络的稳定和低延迟。
- 使用网络负载均衡器进行负载均衡对于源端的写入请求进行分配至一个 Auto Scaling Group 中,其中每一个 EC2 部署 Redis 集群代理实例用于写入请求命令转换。
- 每一个 Redis 集群代理均是指向不同的 Amazon ElastiCache for Redis 节点平衡 Redis 集群中节点的写入流量,同时也是无状态存在并只负责进行协议层的命令转发。
- 应用程序访问
- 保持应用侧客户端单实例 Redis 访问方式不变,无需客户针对原生 Redis 集群改变客户端访问方式。
- 将客户端 Redis 连接目标地址设置为负责均衡的网络负载均衡器开放的接口,利用网络负载均衡器 4 层协议转发保证 Redis 请求命令至 Redis 集群代理。而由于 Redis 单节点和集群模式使用不同的请求命令,所以使用 Redis 集群代理完成命令转换。
- Redis 集群代理转换单节点 Redis 请求命令后转发至相应的 Amazon ElastiCache for Redis 集群相应节点保证结果正确并按原访问链路返回。
注意,该图示只是构建了 3 个 Redis 集群代理实例用于展示架构,真实迁移中的实例数量根据 Redis-Port Proxy 数量和数据量综合考虑。

Redis 集群代理部署详解
Redis 集群代理单节点部署测试
- 参考官方安装教程进行安装。注意,在安装过程中根据不同的操作系统可能缺少某些必要的依赖,请根据错误提示安装相应依赖。
- 安装好以后,进入 redis-cluster-proxy/src, 使用./redis-cluster-proxy 进行启动。注意,不同的 Redis 节点使用”,”进行分割,同时必须带上端口号。
./redis-cluster-proxy test-0002-001.pe3b6y.0001.apse1.cache.amazonaws.com:6379,test-0003-001.pe3b6y.0001.apse1.cache.amazonaws.com:6379 &
- 使用 redis-cli 连接 redis-cluster-proxy 实例 7777 端口,并发送 set 命令进行测试。并通过 redis-cli 连接 Redis 集群测试通过 Redis 集群代理转发的命令是否执行成功。

-
- 首先连接 redis-cluster-proxy 7777 端口,并添加 key “123”和”1234″。
- 连接 AWS ElastiCache 集群,检查添加的 key 是否成功。
- 在测试成功以后,我们对该 EC2 实例创建 AMI 用于集群其他的实例部署。具体步骤如下:


-
- 首先选择需要创建 AMI 的 EC2 实例,通过菜单选择创建出相应的镜像。
- 在 AMIs 中选择我们创建出来的镜像,并通过该镜像启动相应数量的 EC2 实例。在该实例中 Redis 集群代理已经安装完毕可以直接启动。
至此,我们已经安装并且验证 Redis 集群代理可以成功帮助我们转发面向单节点 Redis 写入命令的转发至集群版 Redis。
Redis 集群代理高可用部署测试
- 手动 Redis 集群代理高可用设置
基于之前对于 Redis 集群代理高可用的架构,我们通过设置网络负载均衡器对于 Redis 协议层的命令进行转发。
-
- Target Group 创建:
- 进入 AWS 控制台中 EC2 页面,并在侧边栏选中 Target Group。
- 选择创建 Target Group,并进行所需要的配置。注意我们需要转发的是 TCP 且端口为 7777 的请求,并且只选择部署有 Redis 集群代理的实例。
- 创建 Target Group 结束后等待数分钟,然后检查注册的实例是处于 healthy 状态。
- 网络负载均衡器创建:
- 在 Target Group 中选择我们刚创建的名为“redis-cluster-proxy”的 Target Group。

- 创建成功后我们可以看见网络负载均衡器 DNS 地址,通过该地址我们可以进行测试。
- 首先通过 redis-cli 连接网络负载均衡器的 7777 端口,并添加”789″和”7890″两个 key。
- 然后通过 redis-cli 连接到 Redis 集群,通过 key ”789“和”7890“成功获取两个 key 的值。
- CloudFormation 自动化集群部署方案
由于手动部署耗时且容易出现疏忽和错误,于是我们提出了基于 CloudFormation 的自动化部署方案,通过 IaC(Infra as Code)的思想使用 AWS CloudFormation 服务的能力将我们之前设计的基于 Redis 集群代理构建的异构 Redis 集群适配架构固化成模板。使用改模板输入针对于相适应场景的参数构建出相应的服务架构。
- CloudFormation 方案前期准备
EC2 AMI:需要建立一个 EC2 的 AMI,在该 AMI 中要将 Redis 集群代理建立为 Linux 服务保证即便重启还要确保服务存活(具体制作步骤详见附录)。
- CloudFormation Stack 模板
将 CloudFormation 模板代码保存成 yaml 文件,并通过 AWS CloudFormation 控制台中上传生成 Stack 构建相应的资源架构。
AWSTemplateFormatVersion: 2010-09-09
Resources:
RedisProxyAutoScalingGroupConfiguration:
Type: 'AWS::AutoScaling::LaunchConfiguration'
Properties:
KeyName: !Ref RedisProxyKey
ImageId: !Ref RedisClusterProxyAMI
SecurityGroups:
- !Ref RedisProxySecurityGroup
InstanceType: !Ref RedisClusterProxyInstanceType
UserData:
Fn::Base64:
!Sub |
#!/bin/bash -xe
sed -i 's/REDIS-ENDPOINT/${RedisClusterConfigurationEndPoint}/g' /opt/systemd-sh/redis-cluster-proxy.sh
sed -i 's/THREAD-COUNT/${RedisClusterProxyThreadCount}/g' /opt/systemd-sh/redis-cluster-proxy.sh
systemctl enable redis-cluster-proxy
systemctl start redis-cluster-proxy
RedisProxyAutoScalingPolicyCPUUtilization:
Type: AWS::AutoScaling::ScalingPolicy
Properties:
AutoScalingGroupName: !Ref RedisProxyAutoScalingGroup
PolicyType: TargetTrackingScaling
TargetTrackingConfiguration:
PredefinedMetricSpecification:
PredefinedMetricType: ASGAverageCPUUtilization
TargetValue: !Ref RedisClusterProxyAutoScallingCPUThreshold
RedisProxyTargetGroup:
Type: 'AWS::ElasticLoadBalancingV2::TargetGroup'
Properties:
HealthCheckEnabled: true
HealthCheckIntervalSeconds: 10
VpcId: !Ref RedisProxyVPC
Protocol: TCP
Port: 6379
RedisProxyNLB:
Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer'
Properties:
Name: !Ref RedisProxyClusterName
Type: network
Scheme: internal
Subnets: !Split [',', !Join [',', !Ref RedisProxySubnets]]
RedisProxyELBListener:
Type: 'AWS::ElasticLoadBalancingV2::Listener'
DependsOn:
- RedisProxyNLB
- RedisProxyTargetGroup
Properties:
DefaultActions:
- Type: forward
TargetGroupArn: !Ref RedisProxyTargetGroup
Port: 6379
Protocol: TCP
LoadBalancerArn: !Ref RedisProxyNLB
RedisProxyAutoScalingGroup:
Type: 'AWS::AutoScaling::AutoScalingGroup'
DependsOn:
- RedisProxyAutoScalingGroupConfiguration
- RedisProxyTargetGroup
Properties:
TargetGroupARNs:
- !Ref RedisProxyTargetGroup
VPCZoneIdentifier: !Split [',', !Join [',', !Ref RedisProxySubnets]]
DesiredCapacity: !Ref RedisClusterProxyDefaultInstanceCount
HealthCheckGracePeriod: 100
LaunchConfigurationName: !Ref RedisProxyAutoScalingGroupConfiguration
MinSize: !Ref RedisClusterProxyMinimalInstanceCount
MaxSize: !Ref RedisClusterProxyMaximumInstanceCount
Tags:
- Key: Name
Value: !Ref RedisProxyClusterName
PropagateAtLaunch: true
Parameters:
RedisClusterProxyThreadCount:
Type: Number
Description: Set Redis Cluster Proxy thread count
Default: 100
RedisClusterProxyAutoScallingCPUThreshold:
Type: Number
Description: Set CPU threshold for redis proxy cluster scale up
Default: 60
RedisClusterProxyDefaultInstanceCount:
Type: Number
Description: Set initial size of EC2 instances for redis proxy cluster
Default: 8
RedisClusterProxyMinimalInstanceCount:
Type: Number
Description: Set minimize size of EC2 instances for redis proxy cluster
Default: 4
RedisClusterProxyMaximumInstanceCount:
Type: Number
Description: Set maximum size of EC2 instances for redis proxy cluster
Default: 16
RedisClusterProxyAMI:
Type: String
Description: Set Redis Cluster Proxy AMI ID
RedisClusterProxyInstanceType:
Type: String
Default: c5.large
AllowedValues:
- c5.2xlarge
- c5.xlarge
- c5.large
Description: Enter EC2 type for redis cluster proxy.
RedisProxyClusterName:
Type: String
Description: Specific Redis Proxy Name
RedisClusterConfigurationEndPoint:
Type: String
Description: Specific corresponding Redis Cluster Configuration Endpoint
RedisProxyVPC:
Type: 'AWS::EC2::VPC::Id'
Description: Choose one valid VPC for Redis Proxy
RedisProxySubnets:
Type: 'List<AWS::EC2::Subnet::Id>'
Description: Choose one or more valid subnet for Redis Proxy
RedisProxyKey:
Type: 'AWS::EC2::KeyPair::KeyName'
Description: Select the key pair for those EC2 instance
RedisProxySecurityGroup:
Type: 'AWS::EC2::SecurityGroup::Id'
Description: Choose Security Group for this cloudformation
Outputs:
RedisProxyNLBDNSName:
Description: The DNSName of the Redis Proxy NLB load balancer
Value: !GetAtt RedisProxyNLB.DNSName
-
- CloudFormation 参数配置
| 参数名称 |
参数描述 |
参数建议 |
| RedisClusterConfigurationEndPoint |
需要填写相对应的 AWS Elasticache 集群的 Configuration Point |
|
| RedisClusterProxyAMI |
需要填写之前创建的包含 Redis 集群代理服务 AMI 的 ID。 |
|
| RedisClusterProxyAutoScallingCPUThreshold |
自动扩展组 CPU 使用率值。 |
建议使用 70% |
| RedisClusterProxyDefaultInstanceCount |
Redis 集群代理自动扩展组的初始 EC2 数量。 |
按照 QPS 3w/s 计算 |
| RedisClusterProxyMaximumInstanceCount |
Redis 集群代理自动扩展组的最大 EC2 数量。 |
按照 QPS 3w/s 计算 |
| RedisClusterProxyMinimalInstanceCount |
Redis 集群代理自动扩展组的最小 EC2 数量。 |
按照 QPS 3w/s 计算 |
| RedisClusterProxyInstanceType |
选择 Redis 集群代理 自动扩展组的 EC2 实例机型 |
建议使用 M5 机型 |
| RedisClusterProxyThreadCount |
每一个 Redis 集群代理进行 Redis 协议转发的线程数。 |
默认 8,建议不超过 20 |
| RedisProxyClusterName |
填写 Redis 集群代理高可用名称。 |
|
| RedisProxyKey |
选择 EC2 的 Key Pair。 |
|
| RedisProxySecurityGroup |
选择对应的 Security Group。 |
|
| RedisProxySubnets |
选择 Redis Proxy 部署的 Subnets。 |
|
| RedisProxyVPC |
选择 Redis Proxy 部署的 VPC。 |
|
- 确定网络负载均衡器健康检查
同手动创建网络负载均衡器一样,我们需要通过网络负载均衡器控制台确保所有 Redis 集群代理实例完成注册并健康检查通过。至此,通过 CloudFormation 我们完成整体架构的自动化部署。

总结
1. 针对部分客户的源 Redis 集群采用非标的集群方式如 Twemproxy,Codis 等,同时底层 Redis 物理节点无法访问的时候。可以采用 Redis 集群代理伪装为目标端的单节点 Redis 实例接收数据,并转发至真正的目标集群。
2. Redis 迁移完成后对于客户来讲应该尽可能规避掉对于应用客户端代码的修改来适配 Redis 原生集群,而本方案可以帮助客户达到这个目标即使目前 AWS 官方并没有提供相应的代理节点。
3. 采用网络负载均衡器的目的的提高整个 Proxy 集群的吞吐量,如源端输出吞吐量较小的情况,采用单节点 Redis 集群代理依然可以完成迁移的目的。
4. 有用 Redis 集群代理禁掉部分标准 Redis 命令如 info,则针对部分市面工具如 Tencent Cloud DTS 无法使用。Redis 集群代理支持命令
5. 对于超高吞吐量(Redis 集群请求超过 100W/TPS)的情况需要对于 Redis 集群代理配置进行压力测试和调整,如 Keepalive 参数尽可能调大避免大量 TCP 链接重建消耗系统资源。
附录:
1. Redis-Port:用于通过解析源 Redis 实例快照并根据复制缓冲区命令实现 Redis 全量及增量迁移的开源工具(查看详情)。
2. Redis 集群代理:用于解析单节点 Redis 请求命令并转换成集群 Redis 可接受命令的开源工具(查看详情)。
3. Redis 集群代理系统服务 AMI 创建:通过 AWS AMI 能力创建一个具有自动启动 Redis 集群代理作为底层服务的 Ubuntu 镜像。具体自启动服务构造如下:
- a. 进入建立好的 EC2 实例,根据如下步骤编译 Redis 集群代理
-
sudo su -
git clone https://github.com/RedisLabs/redis-cluster-proxy.git
cd redis-cluster-proxy
apt-get update
apt install gcc
cd deps
apt install make
make hiredis
cd ..
make install
- b.创建 Systemd Service 确保 Redis 集群代理高可用
- i. 创建 redis-cluster-proxy.service 文件
vi /lib/systemd/system/redis-cluster-proxy.service
-
- 文件内容如下:
[Unit]Description=create redis-cluster-proxy service
[Service]Type=simple
ExecStart=/opt/systemd-sh/redis-cluster-proxy.sh
[Install]WantedBy=multi-user.target
-
- ii. 创建 redis-cluster-proxy.sh 文件
mkdir /opt/systemd-sh
vi /opt/systemd-sh/redis-cluster-proxy.sh
chmod 777 /opt/systemd-sh/redis-cluster-proxy.sh
-
- 文件内容如下:
#!/bin/bash
/root/redis-cluster-proxy/src/redis-cluster-proxy --enable-cross-slot --threads THREAD-COUNT -p 6379 REDIS-ENDPOINT
- c. 在 AWS Console 选择该 EC2 实例并制作 AMI
本篇作者