基于Codis 3.2.2的Redis集群方案介绍与安装配置方法

认识Codis

Codis架构简介

Codis是一个开源的、以Go语言写成的代理方式Redis集群框架,其架构图如下所示。

Codis有三大核心组件。

  • codis-server:即小改版的redis-server,代表一个Redis服务,主要加入了对slot的支持与原子性的数据迁移指令。多个codis-server即组成一个codis-group,group之间相互独立,并且由一个主节点与一个或多个从节点组成。
  • codis-proxy:供客户端连接的Redis代理,实现了Redis协议。用其官方文档中的话来说,对于上层的应用,连接到codis-proxy和连接原生的Redis server没有显著区别,上层应用可以像单机Redis一样使用,Codis底层会处理请求的转发、不停机的数据迁移等工作,这些对客户端来说都透明。
  • codis-dashboard:管理Codis集群的工具,支持codis-proxy、codis-server的添加、删除、数据迁移等操作。在集群状态发生改变时,codis-dashboard维护集群下所有codis-proxy状态的一致性。

其他组件包括:

  • codis-fe:codis-dashboard的图形化前端。
  • redis-sentinel:Redis官方的高可用组件,用于监控集群状态,并在主节点不可用时进行切换。
  • 外部存储:可选ZooKeeper/etcd,作为集群元数据以及客户端信息的注册中心。

Codis与Redis Cluster的不同

Codis与官方Redis Cluster实现的根本不同点在于,Codis是中心化的,借助内部组件(codis-dashboard)和外部组件(redis-sentinel、ZK/etcd)进行集群管理、请求路由、探活,通过Redis协议的代理(codis-proxy)对客户端屏蔽集群细节。而Redis Cluster则是去中心化的P2P架构,所有工作都在集群内部进行,每个节点都对整个集群的信息有感知,通过Gossip协议互相通信。下面这张图就非常形象。

Redis Cluster的客户端必须是“smart client”,就是说它必须额外支持Redis Cluster协议,这点是不及Codis的。另外,集群规模越大,gossip带来的额外开销就越多,因此Codis在水平扩展方面也优于Redis Cluster。Codis也支持Redis Cluster因自身局限而无法支持的一些特性,如pipeline。
相对而言,Redis Cluster的主要优点在于有官方保证,迭代完善很快,以及轻量级的实现使得部署很方便。

安装Codis

集群配置

我们现有5台硬件余量较大的服务器(双路E5 12C/24T,256G内存,500G SSD RAID10),hostname为es0~es4,在它们上面配置Codis。ZooKeeper是很久之前随着CDH集群配置好的,因此略去安装过程。

集群中的每个节点都会部署2个codis-server(6379/6380端口)及1个codis-proxy(19000端口),形成5主、5从、5代理的节点结构。另外codis-dashboard在es0节点(18080端口),codis-fe在es1节点(18070端口),es2~es4则部署redis-sentinel。

codis-server

首先在Codis项目的Releases页面下载最新的二进制包或源码,按照官方文档的步骤解压或者自行编译。

在codis目录下新建conf目录,并创建两个Redis配置文件redis_6379.conf、redis_6380.conf。在文件中写入如下配置(以6379为例),看官可以根据自己集群的情况稍加修改,注意配置中的data、log和pid文件夹也要预先创建。

daemonize yes 
bind 0.0.0.0
port 6379
protected-mode no
timeout 86400 
tcp-keepalive 60 
loglevel notice 
logfile /var/codis/log/redis_6379.log
pidfile /var/codis/pid/redis_6379.pid 
databases 16 

save 900 1 
save 300 10 
save 60 10000 
rdbcompression yes 
dbfilename dump_6379.rdb
dir /var/codis/data/6379
stop-writes-on-bgsave-error no 
slave-serve-stale-data yes  
slave-priority 100 
repl-disable-tcp-nodelay no
repl-backlog-size 32mb
 
maxmemory 24gb 
maxmemory-policy allkeys-lru 

appendonly no 
appendfsync everysec 
auto-aof-rewrite-percentage 100 
auto-aof-rewrite-min-size 64mb
aof-rewrite-incremental-fsync yes
no-appendfsync-on-rewrite yes 

slowlog-log-slower-than 10000 
slowlog-max-len 128 

activerehashing yes
hash-max-ziplist-entries 512 
hash-max-ziplist-value 64 
list-max-ziplist-entries 512 
list-max-ziplist-value 64 
set-max-intset-entries 512 
zset-max-ziplist-entries 128 
zset-max-ziplist-value 64

client-output-buffer-limit normal 0 0 0 
client-output-buffer-limit slave 256mb 128mb 60 
client-output-buffer-limit pubsub 64mb 32mb 60 

hz 10 
lua-time-limit 500

注意,不需要专门设置slaveof参数来指定主从关系,因为后面会用codis-fe来指定。关于redis.conf的各个参数含义,网络上有大把文章讲解,本文不再赘述。有需求的看官可以参考这里或者这里,都比较全面。

然后,在所有节点上分别启动两个codis-server。

./codis-server conf/redis_6379.conf
./codis-server conf/redis_6380.conf

codis-dashboard

在conf目录中创建默认的codis-dashboard配置文件。

./codis-dashboard --default-config | tee conf/dashboard.conf

修改配置如下。

# 注册中心类型,接受zookeeper/etcd
coordinator_name = "zookeeper"
# 注册中心地址
coordinator_addr = "10.10.99.130:2181,10.10.99.132:2181,10.10.99.133:2181,10.10.99.124:2181,10.10.99.125:2181"
# 集群名称,满足正则 \w[\w\.\-]*
product_name = "bd-redis"
# 集群密码,默认为空
product_auth = ""
# RESTful API端口
admin_addr = "0.0.0.0:18080"

然后启动codis-dashboard即可。ncpu指定其最多能使用多少个CPU核心,config和log则分别指定配置文件和日志的路径。

nohup ./codis-dashboard --ncpu=4 \
--config=/var/codis/conf/dashboard.conf \
--log=/var/codis/log/dashboard.log \
--log-level=WARN &

codis-proxy

在conf目录中创建默认的codis-dashboard配置文件。

./codis-proxy --default-config | tee conf/proxy.conf

修改配置如下。

# 集群名称
product_name = "bd-redis"
# 集群密码
product_auth = ""
# 客户端session密码
session_auth = ""
# RESTful API端口
admin_addr = "0.0.0.0:11080"
# proxy协议类型和端口地址
proto_type = "tcp4"
proxy_addr = "0.0.0.0:19000"
# Jodis客户端的配置中心、地址、密码、超时设置
jodis_name = "zookeeper"
jodis_addr = "10.10.99.130:2181,10.10.99.132:2181,10.10.99.133:2181,10.10.99.124:2181,10.10.99.125:2181"
jodis_auth = ""
jodis_timeout = "20s"
# 是否与旧的Codis 2.x版本Jodis注册路径兼容
jodis_compatible = false
# session接收/发送缓存大小和超时,如果并发高,要适当调大
session_recv_bufsize = "256kb"
session_recv_timeout = "0s"
session_send_bufsize = "128kb"
session_send_timeout = "30s"
# proxy的最大连接数,pipeline最大请求数
proxy_max_clients = 3000
session_max_pipeline = 20480

通过以下命令启动codis-proxy,参数含义与codis-dashboard相同。

nohup ./codis-proxy --ncpu=8 \
--config=/var/codis/conf/proxy.conf \
--log=/var/codis/log/proxy.log \
--log-level=WARN &

codis-fe

先用codis-admin管理工具(比较危险,不要轻易使用)从ZK中取得当前的集群名称与codis-dashboard地址的关系。

./codis-admin --dashboard-list --zookeeper=10.10.99.124:2181 | tee conf/fe.json

然后运行codis-fe即可,listen参数可以指定其端口。

nohup ./codis-fe --ncpu=4 \
--dashboard-list=/var/codis/conf/fe.json \
--log=/var/codis/log/fe.log \
--log-level=WARN \
--listen=0.0.0.0:18070 &

redis-sentinel

在conf目录下创建sentinel.conf文件,写入如下内容。

daemonize yes
bind 0.0.0.0
port 26379
protected-mode no
dir /var/codis/data
pidfile /var/codis/pid/sentinel_26379.pid
logfile /var/codis/log/sentinel.log

然后启动之。

./redis-sentinel conf/sentinel.conf --sentinel

通过codis-fe配置集群

用浏览器访问es1:18070即可进入codis-fe界面,接下来需要在这个界面完成剩余的配置工作。

加入proxy

在Proxy菜单栏的文本框中输入各个codis-proxy的地址及admin端口,并点击New Proxy按钮,就可以将其加入集群了。proxy在刚加入时是未同步(Pending)状态,稍等一会或者点击Sync按钮即可同步。

加入server,配置主从

找到Group菜单栏,首先输入group ID,并点击New Group按钮,就可以新建一个group。然后在下面输入redis-server的地址与端口,以及要加入哪个Group,点击Add Server按钮即可。

每个group加入的第一个server将成为主节点,其他server会作为从节点。为了防止出现单点问题,主从节点建议按照类似下图中的交错方式来分配。

server加入完毕后,点击上面绿色的Replica(s): Enable All按钮,就可以启用所有主从配置。

配置sentinel

很简单,直接上图。添加完之后会提示out of sync,点击Sync按钮同步就行了。

sentinel配置好后,在各个group中会出现高可用(HA)的提示。

废话两句,Codis在旧版本中其实有自己的高可用实现codis-ha,但是它有个致命的缺点:当主节点宕机并脱离自己所属的group后,如果再手动把它加回对应的group,就加不进去了。所以在当前版本中,已经改用sentinel来做高可用了。

初始化slot分配

Codis预置了1024个数据分片(即slot),通过crc32(key) % 1024的规则确定数据应该被放在哪个slot中。每个slot的数据都有且仅有一个codis-group来持有。

集群创建时,所有slot都处于Offline状态。我们可以手动指定哪些slot分配到哪些group上,但如果是第一次安装,最简单的方法就是直接点击下面的“Rebalance All Slots”按钮,等待所有slot从Migrating变成Default状态,Codis就自动将slot分配好了。

至此,整个基于Codis的Redis集群搭建完毕,可以正常投入生产。