docker-compose搭建Redis高可用架构

之前,我们已经用 Docker安装Redis 单机环境,我们继续沿用 5.0.14 版本的镜像。

但是,由于“单点故障”,生产环境通常部署的是集群服务。Redis的高可用架构一般有这么几种:主从模式、哨兵模式、redis sharding 模式、redis cluster模式。

redis sharding 模式是“Redis客户端”集群模式,因此本文不做搭建。
本文默认你已经安装好Docker,且拉取了Redis镜像,因此这些步骤就不多赘述了。

准备工作

  1. 从 GitHub 上下载 redis.conf
  2. 从 GitHub 上下载 sentinel.conf

下载上述配置文件,可以帮助你了解配置参数
但是,我接下来没有准备使用配置文件来启动Redis进程,而是直接使用命令行参数
命令行参数的名称和含义相同

一、主从模式

1.1 docker-compose.yml

以下是我的 F:\DockerCluster\redis\master-slave\docker-compose.yml :

version: '3.8'
services:
  master:
    image: redis:5.0.14
    container_name: redis-master
    command: redis-server --requirepass abc123  --masterauth abc123
    ports:
      - 6380:6379
  slave1:
    image: redis:5.0.14
    container_name: redis-slave-1
    ports:
      - 6381:6379
    command:  redis-server --replicaof redis-master 6379 --requirepass abc123 --masterauth abc123
  slave2:
    image: redis:5.0.14
    container_name: redis-slave-2
    ports:
      - 6382:6379
    command: redis-server --replicaof redis-master 6379 --requirepass abc123 --masterauth abc123
  1. version 通过查阅 Compose file Reference 可以知道 Docker Engine 版本高于 19.03.0,应该对应 version '3.8'
  2. --requirepass abc123 是 redis-server 命令的参数。它表示客户端在执行其他命令前,需要通过 auth abc123 验证密码并获取授权;
  3. --masterauth abc123 也是 redis-server 命令的参数。它表示因为 master 节点有密码保护,所以 slave 节点在同步副本时,也需要通过密码校验;
  4. --replicaof redis-master 6379 也是 redis-server 命令的参数。如果不设置这句话,则启动的 redis 服务器就是 master 节点;相反,通过 replicaof <masterip> <masterport> 可以指定当前启动的 redis 服务器是 slave 服务器,且它对应的 master 服务器的ip为redis-master容器的ip,port为6379;

1.2 启动主从节点

docker-compose.yml 所在的目录下执行:

docker-compose up -d

如图所示:

1.3 验证

  1. 进入 redis-master 容器,用 redis-cli 查看的所有的键:

  2. 进入 redis-slave-1 容器,用 redis-cli 查看的所有的键:

  3. 进入 redis-slave-2 容器,用 redis-cli 查看的所有的键:

  4. redis-master 容器的 redis-cli 中,用 set hello world 将字符串值 "world" 关联到键 hello

  5. 检查 redis-slave-1 的同步情况:

  6. 检查 redis-slave-2 的同步情况:

master 节点允许读写,但是 slave 是只读的,如图所示,redis-slave-1 执行 set key value 命令时报错:

1.4 info replication

用 redis-cli 连接 master 节点,输入 info replication 命令,可以查看副本信息,主节点上保存了 slave 节点的信息。

1.5 主从切换

docker stop redis-master 停掉主节点之后,在连接 redis-slave-1 的 redis-cli 输入 info replication

可以看到 master_link_status:down,slave 节点失去了与 master 节点的连接。

输入 replicaof no one 命令,手动将从节点 redis-slave-1 设置成主节点,如下图所示:

但是,此时 redis-slave-2 不会直接认 redis-slave-1 为新的主节点,如下图所示:

我们可以继续在 redis-slave-1 上用 set whoishe geekziyu 保存一个新的字符串,再用 info replication 查看,发现 master_replid 没有改变,master_repl_offset 从 9564 增长到 9627 :

接着,用 docker start redis-master 重启原主节点:

1.6 恢复主节点

1.6.1 错误的恢复方法

我尝试在 redis-slave-1 直接输入命令 replicaof redis-master 6379

这样做,直接导致 redis-slave-1 上的数据丢失,且 redis-master 重启时没有数据,所以现在整个集群都没有数据了...(如果在生产环境,可以背大锅了)

1.6.2 正确的恢复方法

解决方案:当原来的主节点从宕机中进行恢复,则将临时主节点的数据进行保存,将AOF文件与RDB文件拷贝替换原主节点下的AOF文件与RDB文件。
为此,我首先用 docker-compose down 停止了容器:

接着,修改了 docker-compose.yml :

version: '3.8'
services:
  master:
    image: redis:5.0.14
    container_name: redis-master
    command: redis-server --requirepass abc123  --masterauth abc123 --appendonly yes 
    ports:
      - 6380:6379
    volumes:
      - F:\DockerCluster\redis\master-slave\data\master:/data
  slave1:
    image: redis:5.0.14
    container_name: redis-slave-1
    ports:
      - 6381:6379
    command:  redis-server --replicaof redis-master 6379 --requirepass abc123 --masterauth abc123 --appendonly yes
    volumes:
      - F:\DockerCluster\redis\master-slave\data\slave1:/data
  slave2:
    image: redis:5.0.14
    container_name: redis-slave-2
    ports:
      - 6382:6379
    command: redis-server --replicaof redis-master 6379 --requirepass abc123 --masterauth abc123 --appendonly yes
    volumes:
      - F:\DockerCluster\redis\master-slave\data\slave2:/data
  1. RDB 持久化功能现在是默认开启的,所以不需要配置;
  2. AOF 持久化,需要设置 appendonly yes
  3. 增加了 volumes 映射,把宿主机 Windows 的文件夹路径映射到 Redis 容器的 /data 目录;

重启输入 docker-compose up -d 启动集群:

redis-cli 访问 redis-master,设置了 5 组字符串:

直接做一次 docker stop redis-master 再进行 docker start redis-master,Redis 会自动从 rdb.dump 文件恢复数据。

如果,我们 docker stop redis-master,并重现一下 redis-slave1 切换成主节点的场景:

之后,我们

  • F:\DockerCluster\redis\master-slave\data\slave1\dump.rdb 拷贝并覆盖 F:\DockerCluster\redis\master-slave\data\master\dump.rdb
  • F:\DockerCluster\redis\master-slave\data\slave1\appendonly.aof 拷贝并覆盖 F:\DockerCluster\redis\master-slave\data\master\appendonly.aof

然后 docker start redis-master 启动容器:

参考文档:Redis 持久化之RDB和AOF
这篇文章中还模拟了这种场景————胡乱修改 appendonly.aof 中的内容,会导致redis-server重启失败。
针对这种情况,使用 redis-check-aof --fix appendonly.aof 可以校验并修复 appendonly.aof 文件。

二、哨兵模式

从上面的演示中,我们也看到了手动切换还是非常麻烦的,哨兵模式提供了 监控(Monitoring)、提醒(Notification)、自动故障迁移(Automatic failover)。

  1. 监控:Sentinel实例会不断检测主从节点是否正常运行。

  2. 提醒:当某个节点出现异常宕机时,Sentinel实例会向管理员或者其他应用发送提醒。

  3. 自动故障迁移:当主节点宕机时,Sentinel实例会将该主节点下的其中一个从节点升级为新的主节点,并且原先其他从节点重新发起socket请求成为新的主节点的从节点。

  4. 配置中心:向客户端返回新主节点的地址,就可以正常上使用新的主节点来处理请求了。

配置哨兵模式,需要部署额外的3台哨兵服务。

文件结构如下所示:

2.1 docker-compose

version: '3.8'
services:
  sentinel1:
    image: redis:5.0.14
    container_name: redis-sentinel-1
    ports:
      - 26379:26379
    command: redis-sentinel /usr/local/etc/redis/sentinel.conf
    volumes:
      - F:\DockerCluster\redis\sentinel\sentinel1.conf:/usr/local/etc/redis/sentinel.conf
  sentinel2:
    image: redis:5.0.14
    container_name: redis-sentinel-2
    ports:
    - 26380:26379
    command: redis-sentinel /usr/local/etc/redis/sentinel.conf
    volumes:
      - F:\DockerCluster\redis\sentinel\sentinel2.conf:/usr/local/etc/redis/sentinel.conf
  sentinel3:
    image: redis:5.0.14
    container_name: redis-sentinel-3
    ports:
      - 26381:26379
    command: redis-sentinel /usr/local/etc/redis/sentinel.conf
    volumes:
      - F:\DockerCluster\redis\sentinel\sentinel3.conf:/usr/local/etc/redis/sentinel.conf
networks:
  default:
    external:
      name: master-slave_default

最后这个 networks:default:external:name 要怎么填呢?

docker inspect redis-master

如下图所示,因此我填写的是 master-slave_default

关于 networks:default:external:name 参数的作用,如下图所示:

这里希望把新增的3台“哨兵”容器,加入到已经存在的主从集群的网络 master-slave_default 中去。

2.2 sentinel.conf

另外,图中红框中的 "IPAddress" 也可以用在 sentinel1.conf 中:

port 26379
dir /tmp
sentinel monitor mymaster 172.22.0.2 6379 2
sentinel auth-pass mymaster abc123
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
sentinel deny-scripts-reconfig yes

sentinel2.conf / sentinel3.confsentinel1.conf 的内容一样的!

  1. sentinel monitor <master-name> <ip> <port> <quorum>: 告诉哨兵监视这个 master 节点

    • master-name 是对某个master+slave组合的一个区分标识(一套sentinel是可以监听多套master+slave这样的组合的);
    • ipport 分别指master节点的 ip 和 端口号;
    • quorum 这个参数是进行客观下线的一个依据,意思是至少有 quorum 个sentinel主观的认为这个master有故障,才会对这个master进行下线以及故障转移;
  2. sentinel auth-pass <master-name> <password>: 设置sentinel连接的master和slave的密码,这个需要和redis.conf文件中设置的密码一样

  3. sentinel down-after-milliseconds <master-name> <milliseconds>: 这个配置其实就是进行主观下线的一个依据,如果这台sentinel超过 milliseconds 这个时间都无法连通master包括slave(slave不需要客观下线,因为不需要故障转移)的话,就会主观认为该master已经下线(实际下线需要客观下线的判断通过才会下线)

  4. sentinel parallel-syncs <master-name> <numreplicas>: 当在执行故障转移时,设置 numreplicas 个slave同时进行切换master,该值越大,则可能就有越多的slave在切换master时不可用,可以将该值设置为1,即一个一个来,这样在某个slave进行切换master同步数据时,其余的slave还能正常工作,以此保证每次只有一个从服务器处于不能处理命令请求的状态。

  5. sentinel failover-timeout <master-name> <milliseconds>: 执行故障迁移的时间超过 milliseconds,即在指定时间内没有大多数的sentinel 反馈master下线,该故障迁移计划则失效

  6. sentinel deny-scripts-reconfig yes: 默认情况下,SENTINEL SET将无法在运行时更改通知脚本和客户端重新配置脚本。这避免了一个微不足道的安全问题,客户机可以将脚本设置为任何值,并触发故障转移以执行程序。

  7. SENTINEL SET <name> <option> <value>: 这个命令很像Redis的CONFIG SET命令,用来改变指定master的配置。支持多个<option><value>。例如以下实例:SENTINEL SET objects-cache-master down-after-milliseconds 1000

参考文档:Redis哨兵模式(sentinel)学习总结及部署记录(主从复制、读写分离、主从切换)

2.3 启动哨兵集群

接着就是执行 docker-compose up -d 命令:

如果File Sharing 的弹框点慢了,还会有包括,再执行一次 docker-compose up -d 命令就好了。

启动成功后,sentinel1.conf / sentinel2.conf / sentinel3.conf 这三个文件会被追加一些内容。

接着,我们用 docker stop redis-master 停止了原来的 master 节点的运行,过了一会,redis-slave-2 被自动选为了新的 master 节点:

redis-cli -h localhost -p 26379 这个命令可以连接 redis-sentinel 哨兵服务。

三、redis cluster集群模式

首先需要对Redis Cluster模型有一个认识Redis Cluster master-replica model

  1. Redis Cluster中的每个节点负责哈希槽的子集,因此,例如,您可能有一个包含3个节点的集群,其中:
    • 节点A包含从0到5461的哈希槽。
    • 节点B包含从5462到10922的哈希槽。
    • 节点C包含从10923到16383的哈希槽。
  2. 为了在主节点子集出现故障或无法与大多数节点通信时保持可用,Redis Cluster使用 主副本模型,其中每个哈希槽具有1个(主节点本身)到N个副本(N-1个附加副本节点)。
  3. 在具有节点A、B、C的示例集群中,如果节点B出现故障,集群将无法继续,因为我们不再能够提供5462-10922范围内的哈希槽。
  4. 但是,在创建集群时(或稍后),我们会向每个主节点添加一个副本节点,这样最终的集群就由A、B、C组成,A、B、C是主节点,A1、B1、C1是副本节点。这样,如果节点B出现故障,系统就能够继续。
  5. 节点B1复制B,如果B失败,集群将提升节点B1作为新的主节点,并将继续正常运行。
  6. 但是,请注意,如果节点B和B1同时出现故障,Redis群集将无法继续运行。

接下来,就是模拟搭建这样一个集群。

3.1 docker-compose.yml

version: '3.8'
services:
  redis-node-master-1:
    image: redis:5.0.14
    container_name: cluster-redis-1
    ports:
      - 6381:6379
    command: "redis-server --requirepass abc123 --masterauth abc123 --appendonly yes \
      --cluster-enabled yes \
      --cluster-config-file nodes.conf \
      --cluster-node-timeout 15000"
    volumes:
      - F:\DockerCluster\redis\cluster\data\redis1:/data
  redis-node-master-2:
    image: redis:5.0.14
    container_name: cluster-redis-2
    ports:
      - 6382:6379
    command: "redis-server --requirepass abc123 --masterauth abc123 --appendonly yes \
      --cluster-enabled yes \
      --cluster-config-file nodes.conf \
      --cluster-node-timeout 15000"
    volumes:
      - F:\DockerCluster\redis\cluster\data\redis2:/data
  redis-node-master-3:
    image: redis:5.0.14
    container_name: cluster-redis-3
    ports:
      - 6383:6379
    command: "redis-server --requirepass abc123 --masterauth abc123 --appendonly yes \
      --cluster-enabled yes \
      --cluster-config-file nodes.conf \
      --cluster-node-timeout 15000"
    volumes:
      - F:\DockerCluster\redis\cluster\data\redis3:/data
  redis-node-slave-1:
    image: redis:5.0.14
    container_name: cluster-redis-1-slave
    ports:
      - 6384:6379
    command: "redis-server --requirepass abc123 --masterauth abc123 --appendonly yes \
      --cluster-enabled yes \
      --cluster-config-file nodes.conf \
      --cluster-node-timeout 15000"
    volumes:
      - F:\DockerCluster\redis\cluster\data\redis1-slave:/data
  redis-node-slave-2:
    image: redis:5.0.14
    container_name: cluster-redis-2-slave
    ports:
      - 6385:6379
    command: "redis-server --requirepass abc123 --masterauth abc123 --appendonly yes \
      --cluster-enabled yes \
      --cluster-config-file nodes.conf \
      --cluster-node-timeout 15000"
    volumes:
      - F:\DockerCluster\redis\cluster\data\redis2-slave:/data
  redis-node-slave-3:
    image: redis:5.0.14
    container_name: cluster-redis-3-slave
    ports:
      - 6386:6379
    command: "redis-server --requirepass abc123 --masterauth abc123 --appendonly yes \
      --cluster-enabled yes \
      --cluster-config-file nodes.conf \
      --cluster-node-timeout 15000"
    volumes:
      - F:\DockerCluster\redis\cluster\data\redis3-slave:/data
  1. 先来说一下 yml配置文件中换行问题,为了保持可读性,我选择让 command 中的内容换行,需要注意三点
    • yml中在每个需要换行的末尾 加上一个 \
    • 因为多个指令参数之间需要有空格分隔,所以除最后一行以外其他行是以[空格]\结尾的;
    • 把整个字符串用双引号 ("") 括起来
  2. 关于 volumes 的使用
    • 冒号(:)前,通常是你的宿主机,比如 Windows/Ubuntu/MacOS 的路径;
    • 冒号(:)后,则是你用docker创建的容器,也就是用 docker ps 能查看到的那些容器内的路径;
    • Windows系统的路径分隔符是 ****,而 Linux系统的路径分隔符则是 /
  3. cluster-enabled yes 该配置表示启用群集支持:开启后,将Redis服务实例作为cluster节点启动
    • 普通Redis服务实例不能是Redis Cluster的一部分,只有作为Redis Cluster节点启动的服务实例才可以。
  4. cluster-config-file nodes.conf 该配置用于指定集群配置文件名
    • 每个Redis Cluster节点都有一个集群配置文件,此文件不可手动编辑。它由Redis节点创建和更新。
    • 每个Redis Cluster节点都需要不同的集群配置文件。确保在同一系统中运行的实例没有重叠的集群配置文件名。
  5. cluster-node-timeout 15000 该配置单位为毫秒,超过 cluster-node-timeout 无法访问才能被视为处于故障状态
    • 大多数其他内部时间限制是该节点超时的倍数。

启动就不多说了,还是 docker-compose up -d :

3.2 cluster nodes

进入 cluster-redis-1 容器:

docker exec -it cluster-redis-1 /bin/bash

接着在容器内执行:

redis-cli
127.0.0.1:6379> auth abc123
127.0.0.1:6379> cluster nodes

结果如下图所示:

此时,当前节点并不知道其他 cluster 节点的存在。

3.3 节点握手 cluster meet

cluster meet <ip> <port>

注意,我试了一下,这里直接使用容器名是不行的:

所以,我又另外开了一个 cmd 来查看容器ip:

docker inspect cluster-redis-1 | findstr IPAddress
docker inspect cluster-redis-2 | findstr IPAddress
docker inspect cluster-redis-3 | findstr IPAddress
docker inspect cluster-redis-1-slave | findstr IPAddress
docker inspect cluster-redis-2-slave | findstr IPAddress
docker inspect cluster-redis-3-slave | findstr IPAddress

我查到,在我的Docker中,

  • cluster-redis-1 的IP地址为 172.28.0.4
  • cluster-redis-2 的IP地址为 172.28.0.6
  • cluster-redis-3 的IP地址为 172.28.0.3
  • cluster-redis-1-slave 的IP地址为 172.28.0.7
  • cluster-redis-2-slave 的IP地址为 172.28.0.5
  • cluster-redis-3-slave 的IP地址为 172.28.0.2

进入 cluster-redis-1 容器,执行 redis-cli,然后在Redis客户端中执行以下操作

127.0.0.1:6379> cluster meet 172.28.0.2 6379
127.0.0.1:6379> cluster meet 172.28.0.3 6379
127.0.0.1:6379> cluster meet 172.28.0.4 6379
127.0.0.1:6379> cluster meet 172.28.0.5 6379
127.0.0.1:6379> cluster meet 172.28.0.6 6379
127.0.0.1:6379> cluster meet 172.28.0.7 6379

温馨小贴士:这里 cluster meet 使用的是 Docker网络 ip,而不是宿主机 ip,会导致你在宿主机上的客户端代码无法连接到 Redis集群,
所以,如果你希望宿主机上的客户端可以访问到该 Redis 集群,那么 ip 用你的宿主机 ip,端口使用进程对应的宿主机映射端口
可以参考一下这篇文章中的配置

最后,再执行 cluster nodes 查看节点:

通过 cluster nodes 打印的信息可以得知 cluster-redis-1 和其他的 Redis Cluster 节点已经是 connected 状态。

但是问题是,所有集群节点都是 master,所以接下来要设置主从关系。

3.4 设置从节点 cluster replicate

我就在 cluster-redis-1 里面执行:
设置 cluster-redis-1-slavecluster-redis-1 的从节点

# redis-cli -h 172.28.0.7 -p 6379 -a abc123 cluster replicate 623bdaf73485a97e95e4c2f620c309fd308de677

设置 cluster-redis-2-slavecluster-redis-2 的从节点:

# redis-cli -h 172.28.0.5 -p 6379 -a abc123 cluster replicate 46a194cefaed112b59c5a492eb08926245174051

设置 cluster-redis-3-slavecluster-redis-3 的从节点:

# redis-cli -h 172.28.0.2 -p 6379 -a abc123 cluster replicate a646c788f7dc3ec7add05e8ecd78fb779de99a87

cluster-redis-1 容器中,再次查看 cluster nodes

此时,主从关系已经设置好了。

3.4 设置插槽cluster addslots

登入 cluster-redis-1 容器,添加插槽 redis-cli -a abc123 cluster addslots {0..5461}

登入 cluster-redis-2 容器,添加插槽 redis-cli -a abc123 cluster addslots {5462..10922}

登入 cluster-redis-3 容器,添加插槽 redis-cli -a abc123 cluster addslots {10923..16383}

参考文档:
为什么redis-cluster使用16384 slots?
redis 集群分配 slot (error) ERR Invalid or out of range slot 错误

3.5 cluster slots

登入 cluster-redis-1 容器,执行 redis-cli,完整密码验证后再输入 cluster slots 查看Redis Cluster插槽情况:

如图所示:

  1. 172.28.0.4(主)和172.28.0.7(副)负责插槽 0~5641
  2. 172.28.0.6(主)和172.28.0.5(副)负责插槽 5462~10922
  3. 172.28.0.3(主)和172.28.0.2(副)负责插槽 10923~16383

3.6 cluster info 查看集群信息

如果出现 cluster_state:fail,那么可能是0~16383插槽中还没有未分配负责的 master节点。
详情参考 redis cluster info显示cluster_state:fail解决方案

3.7 redis-cli -c

127.0.0.1:6379> set mynameis geekziyu
(error) MOVED 14286 172.28.0.3:6379  --> 提示该key需要存储到14286号槽中,负责这个槽的节点是172.28.0.3
127.0.0.1:6379> cluster keyslot mynameis
(integer) 14286
  • CLUSTER keyslot <key>:列出key被放置在哪个槽上。

打印 -> Redirected to slot [14286] located at 172.28.0.3:6379 之后,连接的Redis节点就已经改变了,因此需要再次验证密码

参考文档

  1. 使用Docker-Compose搭建高可用redis哨兵集群
  2. 基于Docker-compose搭建Redis高可用集群-哨兵模式(Redis-Sentinel)
  3. docker快速搭建redis高可用架构实战---主从模式
  4. 基于Docker的Redis集群搭建
  5. docker-compose 编排方式安装redis cluster集群(手动安装)
  6. Redis Cluster 命令官方手册
posted @ 2022-01-19 16:27  极客子羽  阅读(961)  评论(0编辑  收藏  举报