搭建高可用redis集群

1 常见redis集群

常见redis集群有RedisCluster、Codis、Twemproxy,其中Codis、Twemproxy是有中心节点的,而RedisCluster是没中心节点,而且是redis内置的集群方案,推荐使用redisCluster集群。

redisCluster特点:

  • 无中心节点,客户端与 redis节点直连,不需要中间代理层。
  • 数据可以被分片存储,集群数据加起来就是全量数据。
  • 可以通过任意一个节点,读写不属于本节点的数据。
  • 管理方便,后续可自行增加或删除节点。


由于RedisCluster是分片存储,当集群中一个节点故障时,会损失该节点数据,为了实现高可用,需要对每个节点各添加一个从节点,形成主从同步,当主redis(集群节点)出现故障时,从节点替换故障的主节点。

  • Redist集群中的数据库复制是通过主从同步来实现的。
  • 主节点( Master)把数据分发给从节点(Slave)。
  • 主从同步的好处在于高可用, Redis节点有冗余设计。
  • 集群节点个数最好是奇数并且3个以上, 奇数个的好处是,有一个节点出现故障了,集群数量过半数,集群可以继续正常使用,数量少于半数则认为集群瘫痪了。

redisCluster

如果客户端本身带有负载均衡功能,可以省去使用haproxy搭建负载均衡,也可以在程序简单的实现负载均衡功能。


2 单机版redis集群

在单机上搭建3个节点的集群,3个主节点分别有各自的从节点,如图所示:

redisCluster


2.1 修改redis配置文件

因为官方镜像中没有redis配置,也没有启动redis,需要去官方下载对应版本的redis源码获取redis.conf和redis-trib.rb文件,下载官方源码地址:http://download.redis.io/releases/

mkdir redis-cluster
cd redis-cluster
wget http://download.redis.io/releases/redis-4.0.11.tar.gz
tar zxvf redis-4.0.11.tar.gz

# 从源码中复制redis.conf和redis-trib.rb文件到当前目录
cp redis-4.0.11/redis.conf .
cp redis-4.0.11/src/redis-trib.rb .

# 删除redis源码文件
rm -rf redis-4.0.11 redis-4.0.11.tar.gz


因为redis默认没有开启集群功能,需要修改redis配置文件redis.conf,主要修改下面几个参数

bind 0.0.0.0 # 允许外部登录
cluster-enabled yes   # 开启集群
cluster-config-file nodes-6379.conf   # 集群配置文件
cluster-node-timeout 15000  # 超时时间
appendonly yes    # 并开启AOF模式


2.2 重新打包redis镜像

下载指定版本redis官方镜像

docker pull redis:4.0.11

因为官方redis镜像不能满足搭建redis集群要求,需要在官方镜像基础上添加ruby和启动redis命令功能,在当前目录创建Dockerfile文件,文件内容如下:

FROM redis

# 安装ruby
RUN apt-get -y update
RUN apt-get -y install ruby
RUN apt-get -y install rubygems
# ruby 安装redis接口
RUN gem install redis
# 安装vim
RUN apt-get -y install vim

# 启动redis
CMD redis-server /usr/local/etc/redis/redis.conf


2.3 编辑docker-compose.yml文件

在当前目录创建docker-compose.yml文件来管理容器,共6个容器,文件内容如下:

version: "3"

services:

  redis-node1:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - 20001:6379
    volumes:
      - ./redis.conf:/usr/local/etc/redis/redis.conf
      # 创建集群的配置文件,只需创建一次,其他节点不需要映射此文件
      - ./redis-trib.rb:/usr/local/etc/redis/redis-trib.rb
    networks:
      - redis-net

  redis-node2:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - 20002:6379
    volumes:
      - ./redis.conf:/usr/local/etc/redis/redis.conf
    networks:
      - redis-net

  redis-node3:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - 20003:6379
    volumes:
      - ./redis.conf:/usr/local/etc/redis/redis.conf
    networks:
      - redis-net

  redis-node4:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - 20004:6379
    volumes:
      - ./redis.conf:/usr/local/etc/redis/redis.conf
    networks:
      - redis-net

  redis-node5:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - 20005:6379
    volumes:
      - ./redis.conf:/usr/local/etc/redis/redis.conf
    networks:
      - redis-net

  redis-node6:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - 20006:6379
    volumes:
      - ./redis.conf:/usr/local/etc/redis/redis.conf
    networks:
      - redis-net

networks:
  redis-net:
    driver: bridge


2.4 创建redis集群

当前目录下完整文件列表如下所示:

.
├── docker-compose.yml
├── Dockerfile
├── redis.conf
└── redis-trib.rb


准备好文件后,开始创建redis集群:

# 先打包好新的redis镜像
docker-compose build

# 启动容器
docker-compose up -d

# 获取所有启动redis容器ip地址,获取ip地址是用来创建集群准备
docker inspect -f '{{.Name}} - {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(docker ps -aq) | grep redis

    # 执行命令的结果如下:
    # /redis-cluster_redis-node2_1 - 172.20.0.6
    # /redis-cluster_redis-node4_1 - 172.20.0.7
    # /redis-cluster_redis-node6_1 - 172.20.0.4
    # /redis-cluster_redis-node1_1 - 172.20.0.5
    # /redis-cluster_redis-node5_1 - 172.20.0.3
    # /redis-cluster_redis-node3_1 - 172.20.0.2

# 进入redis-node1容器,因为只有redis-node1容器映射了redis-trib.rb文件
docker-compose exec redis-node1 bash

# 创建集群
/usr/local/etc/redis/redis-trib.rb create --replicas 1 172.20.0.2:6379 172.20.0.3:6379 172.20.0.4:6379 172.20.0.5:6379 172.20.0.6:6379 172.20.0.7:6379

  # 其中参数--replicas 1表示每个主节点有一个从节点。
  # 在创建集群过程会提示Can I set the above configuration? 在终端输入yes即可,如果不出现错误,很快就完成集群的创建。

# 创建完集群后,在任意一个redis节点都可以进入redis集群,-c表示连接的是集群
redis-cli -c

# 查看集群节点
127.0.0.1:6379> cluster nodes

  # afb979236e96b6e9aac972af7508e4cd9505676d 172.20.0.6:6379@16379 slave 9715f836310b872961a7defc64bdb4a319f9848d 0 1537636386000 5 connected
  # 1ffa00413a5f75465e1bb5aaef551c38fb3db1f3 172.20.0.5:6379@16379 myself,master - 0 1537636387000 7 connected 10923-16383
  # 9715f836310b872961a7defc64bdb4a319f9848d 172.20.0.2:6379@16379 master - 0 1537636387811 1 connected 0-5460
  # d59a0ad710f35fe349f62eda4279e02972983fd3 172.20.0.3:6379@16379 master - 0 1537636386000 2 connected 5461-10922
  # c9371c4936d12b9f764e5bf5a7257928aa36cbc2 172.20.0.7:6379@16379 slave d59a0ad710f35fe349f62eda4279e02972983fd3 0 1537636385000 6 connected
  # b39a684ad281f03c1befcd90048c503d95526645 172.20.0.4:6379@16379 slave 1ffa00413a5f75465e1bb5aaef551c38fb3db1f3 0 1537636386807 7 connected

# 测试集群的主从复制
127.0.0.1:6379> set testkey 100
  # -> Redirected to slot [4757] located at 172.20.0.2:6379
  # OK

# 根据172.20.0.2查到对应节点为redis-node3,暂停该容器
docker-compose pause redis-node3

# 然后再次读看是否能够读取到数据
172.20.0.2:6379> get testkey
# "100"

# 此时可以查看到集群节点状态,显示172.20.0.2和集群连接失败状态
127.0.0.1:6379> cluster nodes


2.5 创建带有密码的redis集群

实际使用中一般连接redis是需要密码授权的,添加密码很简单,创建集群前修改redis.conf配置和gems的redis客户端client.rb配置。

删除没有密码的集群容器,重新建一个集群,然后在原来基础上修改配置文件。

docker-compose down


修改redis.conf配置

masterauth 123456
requirepass 123456


进入redis-node1容器,修改redis客户端client.rb配置

# 找出client.rb配置文件位置
find / -name client.rb

# 在配置中添加密码
# 把 :password => nil 改为 :password => 123456

# 创建集群
/usr/local/etc/redis/redis-trib.rb create --replicas 1 172.20.0.2:6379 172.20.0.3:6379 172.20.0.4:6379 172.20.0.5:6379 172.20.0.6:6379 172.20.0.7:6379

# 带密码登录集群
redis-cli -a 123456 -c

2.5 创建集群中遇到的问题

redis-trib.rb create –replicas 1 redis-node1:6379 redis-node2:6379 redis-node3:6379 redis-node4:6379 redis-node5:6379 redis-node6:6379

问题1:使用容器名创建去创建集群报错:”ERR Invalid node address specified”

答:redis-trib.rb 不支持域名或主机名,必须使用ip:port的方式


问题2:创建集群时报错:err slot 0 is already busy (redis::commanderror)

答:由于第一次创建集群没有成功,但还在待创建状态,需要将nodes.conf和dir里面的文件全部删除,如果是在docker创建的话,删除容器重新创建。



专题「数据库」的其它文章 »