Docker搭建Redis分布式集群

  • 需求:

    • 搭建单机版的redis服务,在应对高并发、稳定性等需求难以胜任。而本次由于项目数据量大,要求稳定性强,能应对高可用,高并发等情况。故搭建redis分布式集群很有必要。考虑到不对物理机造成任何影响,方便管理,本次搭建选择在docker里搭建。
  • 简介

    • redis分布式集群,由于已经集成了redis哨兵,故具有:

      监控(Monitoring): 哨兵(sentinel) 会不断地检查你的Master和Slave是否运作正常。

      提醒(Notification):当被监控的某个 Redis出现问题时, 哨兵(sentinel) 可以通过 API 向管理员或者其他应用程序发送通知。

      自动故障迁移(Automatic failover):当一个Master不能正常工作时,哨兵(sentinel) 会开始一次自动故障迁移操作,它会将失效Master的其中一个Slave升级为新的Master, 并让失效Master的其他Slave改为复制新的Master; 当客户端试图连接失效的Master时,集群也会向客户端返回新Master的地址,使得集群可以使用Master代替失效Master。

      从而提高了redis服务的稳定性,高可用性等。

  • 工具

    • redis镜像
    • Docker Compose
  • 图解

    • 本次搭建是在两台服务器上,最终集群是三主三从,当使用python客户端连接集群后,数据是通过CRC16算法,之后得到结果,将结果与分配的哈希槽值对比,如果在某一个哈希槽范围内,则将该条数据写入到对应的Master上,同时对应的Slave自动备份;当其中一个Master宕机,在指定的时间内没有恢复,则对应的Slave经过哨兵中自动故障迁移(Automatic failover)功能,提升为Master。之后宕机的服务器恢复后自动降级为Slave。
  • 安装

    • Docker Compose (可以一条命令启动或删除集群)

      # 简介
      Compose 是用于定义和运行多容器 Docker 应用程序的工具。通过 Compose,您可以使用 YML 文件来配置应用程序需要的所有服务。然后,使用一个命令,就可以从 YML 文件配置中创建并启动所有服务。
      
      # 安装(Linux系统)
      # 常规安装:
      1、下载安装包
      sudo curl -L "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
      2、更改权限
      sudo chmod +x /usr/local/bin/docker-compose
      3、创建软链接
      sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
      4、测试
      docker-compose --version
      
      # 本次在Linux中下载安装包时,发现慢,掉包严重,下载失败。而直接执行docker-compose时会提示apt install docker-compose。这条命令提示简单,但是安装后发现无法运行YML文件
      

      可以看到,通过apt install docker-compose安装的docker-compose运行命令docker-compose up -d报错。

      # 解决
      https://github.com/docker/compose/releases/download/1.24.1/docker-compose-Linux-x86_64
      复制该url到浏览器中下载二进制文件包,并且重命名为docker-compose(也可以不重命名),将文件上传到Linux服务端中。
      
      # 修改权限
      chome +x docker-compose
      
      # 复制-->此时任何目录下都可以执行docker-compose命令
      cp docker-compose /usr/bin/
      
      # 启动命令(在YML文件目录下执行)
      docker-compose up -d  //-d参数指的是后台启动
      
      # 删除集群
      docker-compose down
      

    Redis Cluster配置文件

    # 分别在两台服务器上执行以下操作
    
    # 创建目录
    mkdir -p /home/redis/redis-cluster/
    
    # 编写redis-cluster.tmpl文件
    cd /home/redis/redis-cluster/
    vim redis-cluster.tmpl
    

    redis_cluster.tmpl文件内容如下:

    # 第一台服务器(192.168.112.133)
    port ${PORT}
    requirepass 1234agrass
    masterauth 1234agrass
    protected-mode no
    daemonize no
    appendonly no
    cluster-enabled yes
    cluster-config-file nodes.conf
    cluster-node-timeout 15000
    cluster-announce-ip 192.168.112.133
    cluster-announce-port ${PORT}
    cluster-announce-bus-port 1${PORT}
    
    # 第二台服务器(192.168.112.134)
    port ${PORT}
    requirepass 1234agrass
    masterauth 1234agrass
    protected-mode no
    daemonize no
    appendonly no
    cluster-enabled yes
    cluster-config-file nodes.conf
    cluster-node-timeout 15000
    cluster-announce-ip 192.168.112.134
    cluster-announce-port ${PORT}
    cluster-announce-bus-port 1${PORT}
    
    # 参数说明:
    port:节点端口;
    requirepass:添加访问认证;
    masterauth:如果主节点开启了访问认证,从节点访问主节点需要认证;
    protected-mode:保护模式,默认值 yes,即开启。开启保护模式以后,需配置 bind ip 或者设置访问密码;关闭保护模式,外部网络可以直接访问;
    daemonize:是否以守护线程的方式启动(后台启动),默认 no;
    appendonly:是否开启 AOF 持久化模式,默认 no;
    cluster-enabled:是否开启集群模式,默认 no;
    cluster-config-file:集群节点信息文件;
    cluster-node-timeout:集群节点连接超时时间;
    cluster-announce-ip:集群节点 IP,填写宿主机的 IP;
    cluster-announce-port:集群节点映射端口;
    cluster-announce-bus-port:集群节点总线端口。
    

    注意:

      每个 Redis 集群节点都需要打开两个 TCP 连接。一个用于为客户端提供服务的正常 Redis TCP 端口,例如 6379。还有一个基于 6379 端口加 10000 的端口,比如 16379。

      第二个端口用于集群总线,这是一个使用二进制协议的节点到节点通信通道。节点使用集群总线进行故障检测、配置更新、故障转移授权等等。客户端永远不要尝试与集群总线端口通信,与正常的 Redis 命令端口通信即可,但是请确保防火墙中的这两个端口都已经打开,否则 Redis 集群节点将无法通信

    在当前目录下执行以下命令

    # 第一台服务器(192.168.112.133)
    for port in `seq 6371 6373`; do \
      mkdir -p ${port}/conf \
      && PORT=${port} envsubst < redis-cluster.tmpl > ${port}/conf/redis.conf \
      && mkdir -p ${port}/data;\
    done
    
    # 第二台服务器(192.168.112.134)
    for port in `seq 6374 6376`; do \
      mkdir -p ${port}/conf \
      && PORT=${port} envsubst < redis-cluster.tmpl > ${port}/conf/redis.conf \
      && mkdir -p ${port}/data;\
    done
    

    编写Docker Compose模板文件

    # 切换目录
    cd /home/redis
    
    # 编写docker-compose.yml文件
    vim docker-compose.yml
    

    docker-compose.yml文件内容:

    第一台服务器添加以下信息:
    
    # 描述 Compose 文件的版本信息
    version: "3.8"
    
    # 定义服务,可以多个
    services:
      redis-6371: # 服务名称
        image: redis # 创建容器时所需的镜像
        container_name: redis-6371 # 容器名称
        restart: always # 容器总是重新启动
        network_mode: "host" # host 网络模式
        volumes: # 数据卷,目录挂载
          - /home/redis/redis-cluster/6371/conf/redis.conf:/usr/local/etc/redis/redis.conf
          - /home/redis/redis-cluster/6371/data:/data
        command: redis-server /usr/local/etc/redis/redis.conf # 覆盖容器启动后默认执行的命令
    
      redis-6372:
        image: redis
        container_name: redis-6372
        network_mode: "host"
        volumes:
          - /home/redis/redis-cluster/6372/conf/redis.conf:/usr/local/etc/redis/redis.conf
          - /home/redis/redis-cluster/6372/data:/data
        command: redis-server /usr/local/etc/redis/redis.conf
    
      redis-6373:
        image: redis
        container_name: redis-6373
        network_mode: "host"
        volumes:
          - /home/redis/redis-cluster/6373/conf/redis.conf:/usr/local/etc/redis/redis.conf
          - /home/redis/redis-cluster/6373/data:/data
        command: redis-server /usr/local/etc/redis/redis.conf
    
    第二台服务器添加以下信息:
    
    # 描述 Compose 文件的版本信息
    version: "3.8"
    
    # 定义服务,可以多个
    services:
      redis-6374: # 服务名称
        image: redis # 创建容器时所需的镜像
        container_name: redis-6374 # 容器名称
        restart: always # 容器总是重新启动
        network_mode: "host" # host 网络模式
        volumes: # 数据卷,目录挂载
          - /home/redis/redis-cluster/6374/conf/redis.conf:/usr/local/etc/redis/redis.conf
          - /home/redis/redis-cluster/6374/data:/data
        command: redis-server /usr/local/etc/redis/redis.conf # 覆盖容器启动后默认执行的命令
    
      redis-6375:
        image: redis
        container_name: redis-6375
        network_mode: "host"
        volumes:
          - /home/redis/redis-cluster/6375/conf/redis.conf:/usr/local/etc/redis/redis.conf
          - /home/redis/redis-cluster/6375/data:/data
        command: redis-server /usr/local/etc/redis/redis.conf
    
      redis-6376:
        image: redis
        container_name: redis-6376
        network_mode: "host"
        volumes:
          - /home/redis/redis-cluster/6376/conf/redis.conf:/usr/local/etc/redis/redis.conf
          - /home/redis/redis-cluster/6376/data:/data
        command: redis-server /usr/local/etc/redis/redis.conf
    
    • 启动Redis Cluster

      # 进入到docker-compose.yml文件目录下,并执行
      docker-compose up -d
      
      # 说明:
      启动redis容器中的网络模式设置为host,此时redis容器与宿主机是同一个网络,解决主从链接问题
      

    • 创建Redis Cluster

      # 进入其中一台容器中
      docker exec -it redis-6371 bash
      
      # 切换至指定目录
      cd /usr/local/bin/
      
      # redis cluster 主从链接(-a 1234agrass 认证密码)
      redis-cli -a 1234agrass --cluster create \
      192.168.112.133:6371 \
      192.168.112.133:6372 \
      192.168.112.133:6373 \
      192.168.112.134:6374 \
      192.168.112.134:6375 \
      192.168.112.134:6376 --cluster-replicas 1
      

      以下信息表示成功

      # 链接集群
      1、进入到docker容器中链接  (-c 参数表示集群)
      docker exec -it redis-6371 /usr/local/bin/redis-cli -c -a 1234agrass -h 192.168.112.134 -p 6375
      
      2、在windows中链接
      redis-cli -c -h 192.168.112.133 -p 6371
      > auth 1234agrass
      
      # 查看主从
      > info replication
      
      # 查看集群节点信息
      > cluster nodes
      

  • python链接示例

    # 安装依赖包(此时会安装redis和redis-py-cluster)
    pip install redis-py-cluster
    
    # 链接示例
    
    from rediscluster import RedisCluster
    
    
    def redis_cluster(startup_nodes: list):
        """链接redis_cluster"""
        return RedisCluster(startup_nodes=startup_nodes, password='1234agrass', decode_responses=True)
    
    
    def redis_handle(redis_obj):
        """redis_cluster操作"""
        result = redis_obj.set('world', 'helloredis')
        print(result)
        res = redis_obj.get('world')
        print(res)
    
    
    if __name__ == '__main__':
        startup_nodes = [
            {"host": "192.168.112.133", "port": "6371"},
            {"host": "192.168.112.133", "port": "6372"},
            {"host": "192.168.112.133", "port": "6373"}
        ]
        try:
            redis_obj = redis_cluster(startup_nodes=startup_nodes)
            redis_handle(redis_obj=redis_obj)
        except Exception as e:
            print(e)
    
  • 总结

    此次搭建使用了redis-cluster.tmpl文件快速建立了要配置的文件和目录,运用docker compose工具快速启动和删除集群,redis分布式集群的优点:可以将数据分散到不同的服务器上存储,提高了高并发性能,缓解一台服务器的压力,集成redis哨兵机制,使得集群更加的高可用,但是由于是分布式,故redis事务不可用,redis批量操作也不可用(mset、hmset等)。由于redis本身在内存中操作数据,故速度非常快,因此redis cluster中的Slave作用仅仅是备份和故障升级为Master。不用像Mysql做读写分离。

    经测试,当两台服务都重启后,分别打开redis集群容器,此时集群会自动链接,之后重新组成三主三从,并且在前面配置中redis容器中的快照文件已经做了目录映射,保证了容器删除再启动,数据依然存在,本次考虑到作AOF持久化没必要。

  • 文献

    https://my.oschina.net/u/4126211/blog/4555576
    https://juejin.cn/post/6868814738751488008
    https://www.runoob.com/docker/docker-compose.html