基于docker模拟多台redis哨兵(sentinel)集群搭建以及细节讲解

版权声明:[email protected] https://blog.csdn.net/qq_36324113/article/details/88896154

一.docker入门

1.docker是什么

Docker 是一个开源的应用容器引擎,你可以将其理解为一个轻量级的虚拟机,开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上。

2.为什么要使用docker

作为一种新兴的虚拟化方式,Docker 跟传统的虚拟化方式相比具有众多的优势。

(1) 更高效的利用系统资源

由于容器不需要进行硬件虚拟以及运行完整操作系统等额外开销,Docker 对系统资源的利用率更高。无论是应用执行速度、内存损耗或者文件存储速度,都要比传统虚拟机技术更高效。因此,相比虚拟机技术,一个相同配置的主机,往往可以运行更多数量的应用。

(2) 更快速的启动时间

传统的虚拟机技术启动应用服务往往需要数分钟,而 Docker 容器应用,由于直接运行于宿主内核,无需启动完整的操作系统,因此可以做到秒级、甚至毫秒级的启动时间。大大的节约了开发、测试、部署的时间。

(3) 一致的运行环境

开发过程中一个常见的问题是环境一致性问题。由于开发环境、测试环境、生产环境不一致,导致有些 bug 并未在开发过程中被发现。而 Docker 的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性,从而不会再出现 「这段代码在我机器上没问题啊」 这类问题。

(4) 持续交付和部署

对开发和运维(DevOps)人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。
使用 Docker 可以通过定制应用镜像来实现持续集成、持续交付、部署。开发人员可以通过 Dockerfile 来进行镜像构建,并结合 持续集成(Continuous Integration) 系统进行集成测试,而运维人员则可以直接在生产环境中快速部署该镜像,甚至结合 持续部署(Continuous Delivery/Deployment) 系统进行自动部署。
而且使用 Dockerfile 使镜像构建透明化,不仅仅开发团队可以理解应用运行环境,也方便运维团队理解应用运行所需条件,帮助更好的生产环境中部署该镜像。

(5) 更轻松的迁移

由于 Docker 确保了执行环境的一致性,使得应用的迁移更加容易。Docker 可以在很多平台上运行,无论是物理机、虚拟机、公有云、私有云,甚至是笔记本,其运行结果是一致的。因此用户可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况。

(6) 更轻松的维护和扩展

Docker 使用的分层存储以及镜像的技术,使得应用重复部分的复用更为容易,也使得应用的维护更新更加简单,基于基础镜像进一步扩展镜像也变得非常简单。此外,Docker 团队同各个开源项目团队一起维护了一大批高质量的 官方镜像,既可以直接在生产环境使用,又可以作为基础进一步定制,大大的降低了应用服务的镜像制作成本。

3.docker主要用途

(1)提供一次性的环境。比如,本地测试他人的软件、持续集成的时候提供单元测试和构建的环境。
(2)提供弹性的云服务。因为 Docker 容器可以随开随关,很适合动态扩容和缩容。
(3)组建微服务架构。通过多个容器,一台机器可以跑多个服务,因此在本机就可以模拟出微服务架构。

扫描二维码关注公众号,回复: 5742668 查看本文章

4.docker安装

(1) docker简单安装

  //1、更新update到最新的版本
  yum  update 
  //2、卸载老版本docker
  yum  remove docker  docker-common docker-selinux  docker-engine
  // 3、安装需要的软件包
  yum install -y yum-utils  device-mapper-persistent-data lvm2
  //  4、设置yum源
  yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
  //  5、查看docker版本
  yum list docker-ce --showduplicates|sort -r  
  //6、安装docker
  yum  install  docker-ce-18.03.1.ce -y
  //7、启动docker
  systemctl start docker
  //8、加入开机自启
  systemctl enable docker
  //9、配置国内镜像
  vi /etc/docker/daemon.json 
  {
     "registry-mirrors": ["http://hub-mirror.c.163.com"]
  }

5.编写Dockerfile

(1) 编写Dockerfile

//Dockerfile
FROM centos:latest
RUN groupadd -r redis && useradd  -r -g redis redis
RUN yum -y update &&  yum -y install epel-release && yum -y install redis && yum -y install net-tools
EXPOSE 6379

6.容器网络简介

Docker安装后,默认会创建下面三种网络类型
docker network ls 查看默认的网络如下

NETWORK ID          NAME                DRIVER              SCOPE
f57d92c28b04        bridge              bridge              local
52936ea0f9e2        host                host                local
4de7a4846290        none                null                local

在启动容器时使用 --network bridge 指定网络类型

bridge:桥接网络

默认情况下启动的Docker容器,都是使用 bridge,Docker安装时创建的桥接网络,每次Docker容器重启时,会按照顺序获取对应的IP地址,这个就导致重启下,Docker的IP地址就变了

none:无指定网络

使用 --network=none ,docker 容器就不会分配局域网的IP

host: 主机网络

使用 --network=host,此时,Docker 容器的网络会附属在主机上,两者是互通的。
例如,在容器中运行一个Web服务,监听8080端口,则主机的8080端口就会自动映射到容器中

指定自定义网络

因为默认的网络不能制定固定的地址,所以我们将创建自定义网络,并指定网段:172.10.0.0/16 并命名为mynetwork,指令

如下:
docker network create --subnet=172.10.0.0/16 mynetwork

再次查看docker network ls

NETWORK ID          NAME                DRIVER              SCOPE
f57d92c28b04        bridge              bridge              local
52936ea0f9e2        host                host                local
2ba78f50ec5a        mynetwork           bridge              local
4de7a4846290        none                null                local

桥接网络已经加入

7.通过Dockerfile构建redis镜像

如果看到这里看不懂了推荐好好了解Docker在继续看下去哈
执行命令构建镜像(时间有点长,因为要下载centos基础镜像大约300M这样子)

docker build -t redis  .

列出镜像

docker images

效果如下

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
redis               latest              e4ff5dfb1541        36 seconds ago      337MB
centos              latest              9f38484d220f        2 weeks ago         202MB

8.通过镜像创建redis和哨兵容器

哨兵机制我就不介绍了 一些基础理论的东西自己百度google一下就行了

docker run -itd --name  redis-master  --net mynetwork  -p 6380:6379  --ip 172.10.0.2  redis
docker run -itd --name  redis-slave1  --net mynetwork  -p 6381:6379  --ip 172.10.0.3  redis
docker run -itd --name  redis-slave2  --net mynetwork  -p 6382:6379  --ip 172.10.0.4  redis
docker run -itd --name  redis-slave3  --net mynetwork  -p 6383:6379  --ip 172.10.0.5  redis
docker run -itd --name  redis-sentinel1 --net mynetwork  -p 22530:26379  --ip 172.10.0.6  redis
docker run -itd --name  redis-sentinel2  --net mynetwork  -p 22531:26379  --ip 172.10.0.7  redis
docker run -itd --name  redis-sentinel3  --net mynetwork  -p 22532:26379  --ip 172.10.0.8  redis

执行后容器列表如下

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                NAMES
f0e87238bc45        redis               "docker-entrypoint.s…"   5 seconds ago       Up 4 seconds        6379/tcp, 0.0.0.0:22532->26379/tcp   redis-sentinel3
8dee5bdf51d3        redis               "docker-entrypoint.s…"   14 seconds ago      Up 13 seconds       6379/tcp, 0.0.0.0:22531->26379/tcp   redis-sentinel2
7987b4dc0e52        redis               "docker-entrypoint.s…"   23 seconds ago      Up 22 seconds       6379/tcp, 0.0.0.0:22530->26379/tcp   redis-sentinel1
6b81c726aad9        redis               "docker-entrypoint.s…"   15 minutes ago      Up 15 minutes       0.0.0.0:6383->6379/tcp               redis-slave3
059a0b033ec4        redis               "docker-entrypoint.s…"   15 minutes ago      Up 15 minutes       0.0.0.0:6382->6379/tcp               redis-slave2
78d3fb5a8afe        redis               "docker-entrypoint.s…"   16 minutes ago      Up 16 minutes       0.0.0.0:6381->6379/tcp               redis-slave1
7e17f83e1577        redis               "docker-entrypoint.s…"   17 minutes ago      Up 17 minutes       0.0.0.0:6380->6379/tcp               redis-master


可以看到 我配置了1主节点3从节点3个哨兵

(1)配置主从复制

再从节点进入redis运行

SLAVEOF  172.10.0.2 6379 

在从主点查看信息

info replication

显示

# Replication
role:master
connected_slaves:3
slave0:ip=172.10.0.3,port=6379,state=online,offset=391,lag=0
slave1:ip=172.10.0.4,port=6379,state=online,offset=391,lag=0
slave2:ip=172.10.0.5,port=6379,state=online,offset=391,lag=0
master_replid:d86fa1d6f1eea9d9887b96b0a10c2f3a5a98954b
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:391
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:391


可以看到connected_slaves有三个 对应详细信息也有了
主节点 set一个数据 从节点全部能get到 ok

9.配置哨兵(sentinel)集群

1、 为什么使用sentinel

配置了Redis主从复制后,其实 Redis 复制有一个缺点,就是当主机 Master 宕机以后,我们需要人工解决切换,比如使用slaveof no one 。实际上主从复制并没有实现,高可用, 高可用侧重备份机器,所以, 利用集群中系统的冗余,当系统中某台机器发生损坏的时候,其他后备的机器可以迅速的接替它来启动服务,一旦主节点宕机,写服务无法使用,就需要手动去切换,重新选取主节点,手动设置主从关系。那么如何解决呢?如果我们有一个监控程序能够监控各个机器的状态及时作出调整,将手动的操作变成自动的。Sentinel的出现就是为了解决这个问题。

2、 sentinel的原理及实现

Redis Sentinel 是一个分布式架构,其中包含若干个 Sentinel 节点和 Redis 数据节点,每个 Sentinel 节点会对数据节点和其余 Sentinel 节点进行监控,当它发现节点不可达时,会对节点做下线标识。如果被标识的是主节点,它还会和其他 Sentinel 节点进行“协商”,当大多数 Sentinel 节点都认为主节点不可达时,它们会选举出一个 Sentinel 节点来完成自动故障转移的工作,同时会将这个变化实时通知给 Redis 应用方。整个过程完全是自动的,不需要人工来介入,所以这套方案很有效地解决了 Redis 的高可用问题。

3、 基本的故障转移流程:

1.主节点出现故障,此时两个从节点与主节点失去连接,主从复制失败。
2.每个 Sentinel 节点通过定期监控发现主节点出现了故障
3.多个 Sentinel 节点对主节点的故障达成一致会选举出其中一个节点作为领导者负责故障转移。
4.Sentinel 领导者节点执行了故障转移,整个过程基本是跟我们手动调整(slaveof no one)一致的,只不过是自动化完成的。
5.故障转移后整个 Redis Sentinel 的结构,重新选举了新的主节点,并且通知给客户端。

4、 Redis Sentinel的功能

·监控:Sentinel 节点会定期检测 Redis 数据节点、其余 Sentinel 节点是否可达。
·通知:Sentinel 节点会将故障转移的结果通知给应用方。
·主节点故障转移:实现从节点晋升为主节点并维护后续正确的主从关系。
·配置提供者:在 Redis Sentinel 结构中,客户端在初始化的时候连接的是 Sentinel 节点集合,从中获取主节点信息。

 同时Redis Sentinel 包含了若个 Sentinel 节点,这样做也带来了两个好处:

·1.对于节点的故障判断是由多个 Sentinel 节点共同完成,这样可以有效地防止误判。
·2.Sentinel 节点集合是由若干个 Sentinel 节点组成的,这样即使个别 Sentinel 节点不可用,整个 Sentinel 节点集合依然是健壮的。
但是 Sentinel 节点本身就是独立的 Redis 节点,只不过它们有一些特殊,它们不存储数据,只支持部分命令

5、 Redis Sentinel的配置

基本命令如下

[root@9091a5bb544e /]# [root@iZ2zeeqdyvolvobj0qhr70Z ~]# docker exec -it redis-sentinel2 bash
[root@bbc2684115c2 /]# vi /etc/redis-sentinel.conf 
[root@bbc2684115c2 /]# redis-sentinel /etc/redis-sentinel.conf &
[1] 26
[root@bbc2684115c2 /]# 

宿主机中停止主节点

[root@iZ2zeeqdyvolvobj0qhr70Z ~]# docker stop redis-master
redis-master
[root@iZ2zeeqdyvolvobj0qhr70Z ~]#

在子节点查看详细信息

127.0.0.1:6379> info replication
	# Replication
    role:master
    connected_slaves:2
    slave0:ip=172.10.0.4,port=6379,state=online,offset=5331,lag=1  /*这里已经变成主节点*/
    slave1:ip=172.10.0.3,port=6379,state=online,offset=5331,lag=1  /*这里已经变成主节点*/
    master_repl_offset:5331
    repl_backlog_active:1
    repl_backlog_size:1048576
    repl_backlog_first_byte_offset:2
    repl_backlog_histlen:5330
    127.0.0.1:6379> 

恢复redis-master主节点

[root@iZ2zeeqdyvolvobj0qhr70Z ~]# docker start redis-master
redis-master

在主节点中查看

[root@ed03878d3088 /]# redis-cli
127.0.0.1:6379> info replication
# Replication
role:slave
master_host:172.10.0.5    /*redis-master 已经变成从节点*/
master_port:6379
master_link_status:up
master_last_io_seconds_ago:0
master_sync_in_progress:0
slave_repl_offset:59057
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
127.0.0.1:6379> 

在Sentinel选举出的新主节点set 一个数据

127.0.0.1:6379> set hello 12345678
OK
127.0.0.1:6379> 

其他从节点查看

127.0.0.1:6379> get hello
"12345678"
127.0.0.1:6379> 

配置成功

6、 Sentinel 的核心配置简介

sentinel monitor mymaster 127.0.0.1 7000 2  

监控的主节点的名字、IP 和端口,最后一个2的意思是有几台 Sentinel 发现有问题,就会发生故障转移,例如 配置为2,代表至少有2个 Sentinel 节点认为主节点不可达,那么这个不可达的判定才是客观的。对于设置的越小,那么达到下线的条件越宽松,反之越严格。一般建议将其设置为 Sentinel 节点的一半加1。
注:最后的参数不得大于conut(sentinel)

sentinel down-after-millseconds mymaster 30000 

这个是超时的时间(单位为毫秒)。打个比方,当你去 ping 一个机器的时候,多长时间后仍 ping 不通,那么就认为它是有问题。

sentinel parallel-syncs mymaster 1 

当 Sentinel 节点集合对主节点故障判定达成一致时,Sentinel 领导者节点会做故障转移操作,选出新的主节点,原来的从节点会向新的主节点发起复制操作,parallel-syncs 就是用来限制在一次故障转移之后,每次向新的主节点发起复制操作的从节点个数,指出 Sentinel 属于并发还是串行。1代表每次只能复制一个,可以减轻 Master 的压力;

sentinel auth-pass <master-name> <password>

如果 Sentinel 监控的主节点配置了密码,sentinel auth-pass 配置通过添加主节点的密码,防止 Sentinel 节点对主节点无法监控。

sentinel failover-timeout mymaster 180000 

表示故障转移的时间。

7、 Sentinel命令

sentinel支持的合法命令如下:

SENTINEL masters 显示被监控的所有master以及它们的状态.

SENTINEL master <master name> 显示指定master的信息和状态;

SENTINEL slaves <master name> 显示指定master的所有slave以及它们的状态;

SENTINEL get-master-addr-by-name <master name> 返回指定master的ip和端口,如果正在进行failover或者failover已经完成,将会显示被提升为master的slave的ip和端口。

SENTINEL failover <master name> 强制sentinel执行failover,并且不需要得到其他sentinel的同意。但是failover后会将最新的配置发送给其他sentinel。

修改配置
sentinel monitor test 127.0.0.1 6379 2 添加新的监听

SENTINEL REMOVE test 放弃对某个master监听

SENTINEL set failover-timeout mymaster 180000 设置配置选项

8、 应用端调用

Master可能会因为某些情况宕机了,如果在客户端是固定一个地址去访问,肯定是不合理的,所以客户端请求是请求哨兵,从哨兵获取主机地址的信息,或者是从机的信息。可以实现一个例子
1、随机选择一个哨兵连接,获取主机、从机信息
2、模拟客户端定时访问,实现简单轮训效果,轮训从节点
3、连接失败重试访问

9、Sentinel 实现原理

讲完了 Sentinel 的代码实现,很多人对 Sentinel 还不懂其原理。那么接下来我们就来看下 Sentinel 的实现原理,主要分为以下三个步骤。

1.检测问题

主要会执行的是三个定时任务,这三个内部的执行任务可以保证出现问题马上让 Sentinel 知道。
每10秒每个 Sentinel 对 Master 和 Slave 执行一次 Info Replication。

每2秒每个 Sentinel 通过 Master 节点的 channel 交换信息(pub/sub)。

每1秒每个 Sentinel 对其他 Sentinel 和 Redis 执行 ping。

第一个定时任务,指的是 Redis Sentinel 可以对 Redis 节点做失败判断和故障转移,在 Redis 内部有三个定时任务作为基础,来 Info Replication 发现 Slave 节点,这个命令可以确定主从关系。

第两个定时任务,类似于发布订阅,Sentinel 会对主从关系进行判定,通过 sentinel:hello 频道交互。了解主从关系可以帮助更好的自动化操作 Redis。然后 Sentinel 会告知系统消息给其它 Sentinel 节点,最终达到共识,同时 Sentinel 节点能够互相感知到对方。

第三个定时任务,指的是对每个节点和其它 Sentinel 进行心跳检测,它是失败判定的依据。

2.发现问题

主观下线和客观下线。当有一台 Sentinel 机器发现问题时,它就会主观对它主观下线,但是当多个 Sentinel 都发现有问题的时候,才会出现客观下线。
我们先来回顾一下 Sentinel 的配置。
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000
Sentinel 会 ping 每个节点,如果超过30秒,依然没有回复的话,做下线的判断。
那么什么是主观下线呢?
每个 Sentinel 节点对 Redis 节点失败的“偏见”。之所以是偏见,只是因为某一台机器30秒内没有得到回复。
那么如何做到客观下线呢?
这个时候需要所有 Sentinel 节点都发现它30秒内无回复,才会达到共识。

3.找到解决问题的人

领导者选举,如何在 Sentinel 内部多台节点做领导者选举,选出一个领导者。
1.每个做主观下线的sentinel节点,会向其他的sentinel节点发送命令,要求将它设置成为领导者
2.收到命令sentinel节点,如果没有同意通过其它节点发送的命令,那么就会同意请求,否则就会拒绝
3.如果sentinel节点发现自己票数超过半数,同时也超过了sentinel monitor mymaster 127.0.0.1 6379 2 超过2个的时候,就会成为领导者
4.进行故障转移操作

4.解决问题

故障转移,即如何进行故障转移。
Redis 内部其实是有一个优先级配置的,在配置文件中 slave-priority,这个参数是 Salve 节点的优先级配置,如果存在则返回,如果不存在则继续。
当上面这个优先级不满足的时候,Redis 还会选择复制偏移量最大的 Slave节点,如果存在则返回,如果不存在则继续。之所以选择偏移量最大,这是因为偏移量越小,和 Master 的数据越不接近,现在 Master挂掉了,说明这个偏移量小的机器数据也可能存在问题,这就是为什么要选偏移量最大的 Slave 的原因。
如果发现偏移量都一样,这个时候 Redis 会默认选择 runid 最小的节点。

10、生产环境中部署技巧

1 .Sentinel 节点不应该部署在一台物理“机器”上。

这里特意强调物理机是因为一台物理机做成了若干虚拟机或者现今比较流行的容器,它们虽然有不同的 IP 地址,但实际上它们都是同一台物理机,同一台物理机意味着如果这台机器有什么硬件故障,所有的虚拟机都会受到影响,为了实现 Sentinel 节点集合真正的高可用,请勿将 Sentinel 节点部署在同一台物理机器上。
2 .部署至少三个且奇数个的 Sentinel 节点。3个以上是通过增加 Sentinel 节点的个数提高对于故障判定的准确性,因为领导者选举需要至少一半加1个节点,奇数个节点可以在满足该条件的基础上节省一个节点。

11、 哨兵常见问题

哨兵集群在发现master node挂掉后会进行故障转移,也就是启动其中一个slave node为master node。在这过程中,可能会导致数据丢失的情况。

1、异步复制导致数据丢失

因为master->slave的复制是异步,所以可能有部分还没来得及复制到slave就宕机了,此时这些部分数据就丢失了。

2、集群脑裂导致数据丢失

脑裂,也就是说,某个master所在机器突然脱离了正常的网络,跟其它slave机器不能连接,但是实际上master还运行着。

1、 造成的问题

此时哨兵可能就会认为master宕机了,然后开始选举,讲其它slave切换成master。这时候集群里就会有2个master,也就是所谓的脑裂。
此时虽然某个slave被切换成了master,但是可能client还没来得及切换成新的master,还继续写向旧的master的数据可能就丢失了。
因此旧master再次恢复的时候,会被作为一个slave挂到新的master上去,自己的数据会被清空,重新从新的master复制数据。

2、 怎么解决?
min-slaves-to-write 1
min-slaves-max-lag  10

要求至少有1个slave,数据复制和同步的延迟不能超过10秒
如果说一旦所有的slave,数据复制和同步的延迟都超过了10秒钟,那么这个时候,master就不会再接收任何请求了

上面两个配置可以减少异步复制和脑裂导致的数据丢失

3、异步复制导致的数据丢失

在异步复制的过程当中,通过min-slaves-max-lag这个配置,就可以确保的说,一旦slave复制数据和ack延迟时间太长,就认为可能master宕机后损失的数据太多了,那么就拒绝写请求,这样就可以把master宕机时由于部分数据未同步到slave导致的数据丢失降低到可控范围内

4、集群脑裂导致的数据丢失

集群脑裂因为client还没来得及切换成新的master,还继续写向旧的master的数据可能就丢失了通过min-slaves-to-write 确保必须是有多少个从节点连接,并且延迟时间小于min-slaves-max-lag多少秒。

3、客户端需要怎么做

对于client来讲,就需要做些处理,比如先将数据缓存到内存当中,然后过一段时间处理,或者连接失败,接收到错误切换新的master处理。

下一篇介绍 Redis-cluster集群伸缩

猜你喜欢

转载自blog.csdn.net/qq_36324113/article/details/88896154