nginx keepalive 高可用 原理和实操 (图解+秒懂+史上最全)

文章很长,而且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录 博客园版 为您奉上珍贵的学习资源 :

  • 免费赠送 :《尼恩Java面试宝典》持续更新+ 史上最全 + 面试必备 2000页+ 面试必备 + 大厂必备 +涨薪必备
  • 免费赠送 经典图书:《Java高并发核心编程(卷1)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
  • 免费赠送 经典图书:《Java高并发核心编程(卷2)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
  • 免费赠送 经典图书:《Java高并发核心编程(卷3)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
  • 免费赠送 经典图书:《尼恩Java面试宝典 V11》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
  • 免费赠送 资源宝库: Java 必备 百度网盘资源大合集 价值>10000元 加尼恩领取


背景:

下一个视频版本,从架构师视角,尼恩为大家打造高可用、高并发中间件的原理与实操。

目标:通过视频和博客的方式,为各位潜力架构师,彻底介绍清楚架构师必须掌握的高可用、高并发环境,包括但不限于:

  • 高可用、高并发nginx架构的原理与实操

  • 高可用、高并发mysql架构的原理与实操

  • 高可用、高并发nacos架构的原理与实操

  • 高可用、高并发rocketmq架构的原理与实操

  • 高可用、高并发es架构的原理与实操

  • 高可用、高并发minio架构的原理与实操

在这里插入图片描述

why 高可用、高并发中间件的原理与实操:

  • 实际的开发过程中,很多小伙伴聚焦crud开发,环境出了问题,都不能启动。

  • 作为架构师,或者未来想走向高端开发,或者做架构,必须掌握高可用、高并发中间件的原理,掌握其实操。

本系列博客的具体内容,请参见 Java 高并发 发烧友社群:疯狂创客圈

面试问题:Nginx 、haproxy如何实现高可用?

面试官:

既然使用nginx、haproxy 反向代理 HTTP服务,或者TCP服务,做到了上游服务的高可用

可问题是,如果 反向代理挂了,了怎么办?

答曰:

首先,其实反向代理的主机宕机概率很小,但是绝对不是没有,任何事都不是绝对

但是,为了减少 反向代理 的 宕机概念,在我们的 高可用架构 上做了优化,进行了 反向代理的 高可用

具体的技术方案是:我们使用 nginx-keepalived双机热备机制,vip主机可以进行漂移,这样主机挂掉了,还有备用机可以顶上

具体的vip漂移架构图,如下:

nginx keepalive 高可用 原理和实操 (图解+秒懂+史上最全)_第1张图片

Keepalived是什么?

Keepalived起初是为LVS设计的,专门用来监控集群系统中各个服务节点的状态,

它根据TCP/IP参考模型的第三、第四层、第五层交换机制检测每个服务节点的状态,如果某个服务器节点出现异常,或者工作出现故障,Keepalived将检测到,并将出现的故障的服务器节点从集群系统中剔除,这些工作全部是自动完成的,不需要人工干涉,需要人工完成的只是修复出现故障的服务节点。

后来Keepalived又加入了VRRP的功能,VRRP(VritrualRouterRedundancyProtocol,虚拟路由冗余协议)出现的目的是解决静态路由出现的单点故障问题,通过VRRP可以实现网络不间断稳定运行,因此Keepalvied一方面具有服务器状态检测和故障隔离功能,另外一方面也有HAcluster功能。

健康检查和失败切换是keepalived的两大核心功能。所谓的健康检查,就是采用tcp三次握手,icmp请求,http请求,udp echo请求等方式对负载均衡器后面的实际的服务器(通常是承载真实业务的服务器)进行保活;而失败切换主要是应用于配置了主备模式的负载均衡器,利用VRRP维持主备负载均衡器的心跳,当主负载均衡器出现问题时,由备负载均衡器承载对应的业务,从而在最大限度上减少流量损失,并提供服务的稳定性。

VRRP虚拟路由冗余协议

虚拟路由冗余协议(Virtual Router Redundancy Protocol,简称VRRP)是由IETF提出的解决局域网中配置静态网关出现单点失效现象的路由协议,1998年已推出正式的RFC2338协议标准。

VRRP广泛应用在边缘网络中,它的设计目标是支持特定情况下IP数据流量失败转移不会引起混乱,允许主机使用单路由器,以及即使在实际第一跳路由器使用失败的情形下仍能够维护路由器间的连通性。

VRRP协议的来源

在现实的网络环境中。主机之间的通信都是通过配置静态路由或者(默认网关)来完成的,而主机之间的路由器一旦发生故障,通信就会失效,因此这种通信模式当中,路由器就成了一个单点瓶颈,为了解决这个问题,就引入了VRRP协议。

VRRP的核心概念

VRRP是一种路由容错协议,也可以叫做备份路由协议。

在VRRP协议中,有两组重要的概念:

  • VRRP路由器和虚拟路由器
  • 主控路由器和备份路由器

备份路由器(BACKUP):虚拟路由器中的其他物理路由器不拥有对外的虚拟IP,也不对外提供网络功能,仅接受MASTER的VRRP状态通告信息,这些路由器被称为备份路由器。当主路由器失败时,处于BACKUP角色的备份路由器将重新进行选举,产生一个新的主路由器进入MASTER角色,继续提供对外服务,整个切换对用户来说是完全透明的。

虚拟路由器

VRRP路由器(物理实体)

什么是VRRP路由器?

VRRP路由器是指运行VRRP的路由器,是物理实体;

虚拟路由器

什么是虚拟路由器?

虚拟路由器是指VRRP协议创建的,是逻辑概念。一组VRRP路由器(物理实体)协同工作,共同构成一台虚拟路由器。该虚拟路由器对外表现为一个具有唯一固定的IP地址和MAC地址的逻辑路由器。

虚拟路由器是VRRP备份组中所有路由器的集合,它是一个逻辑概念,并不是正真存在的。从备份组外面看备份组中的路由器,感觉组中的所有路由器就像一个 一样,可以理解为在一个组中: 主路由器+所有备份路由器=虚拟路由器。

虚拟路由器有一个虚拟的IP地址和MAC地址。

主机将虚拟路由器当作默认网关。虚拟MAC地址的格式为00-00-5E-00-01-{VRID}。通常情况下,虚拟路由器回应ARP请求使用的是虚拟MAC地址,只有虚拟路由器做特殊配置的时候,才回应接口的真实MAC地址。

nginx keepalive 高可用 原理和实操 (图解+秒懂+史上最全)_第2张图片

主控路由器主路由器(MASTER):

什么是主控路由器?

处于同一个VRRP组中的路由器具有两种互斥的角色:主控路由器和备份路由器

一个VRRP组中有且只有一台处于主控角色的路由器,可以有一个或者多个处于备份角色的路由器VRRP协议从路由器组中选出一台作为主控路由器,负责ARP解析和转发IP数据包,组中的其他路由器作为备份的角色并处于待命状态。

虚拟路由器通过虚拟IP对外提供服务,而在虚拟路由器内部同一时间只有一台物理路由器对外提供服务,这台提供服务的物理路由器被称为主路由器。一般情况下Master是由选举算法产生,它拥有对外服务的虚拟IP,提供各种网络功能,如:ARP请求,ICMP数据转发等。

nginx keepalive 高可用 原理和实操 (图解+秒懂+史上最全)_第3张图片

当由于某种原因主控路由器发生故障时,其中的一台备份路由器能在瞬间的时延后升级为主控路由器,由于此切换非常迅速而且不用改变IP地址和MAC地址,故对终端使用者系统是透明的。

nginx keepalive 高可用 原理和实操 (图解+秒懂+史上最全)_第4张图片

一个局域网络内的所有主机都设置缺省路由,当网内主机发出的目的地址不在本网段时,报文将被通过缺省路由发往外部路由器,从而实现了主机与外部网络的通信。

当缺省路由器down掉(即端口关闭)之后,内部主机将无法与外部通信,如果路由器设置了VRRP时,那么这时,虚拟路由将启用备份路由器,从而实现全网通信。

题外话:ARP协议

地址解析协议,即ARP(Address Resolution Protocol),是根据IP地址获取物理地址的一个TCP/IP协议。

主机发送信息时将包含目标IP地址的ARP请求广播到局域网络上的所有主机,并接收返回消息,以此确定目标的物理地址。

nginx keepalive 高可用 原理和实操 (图解+秒懂+史上最全)_第5张图片

arp缓存:收到返回消息后将该IP地址和物理地址存入本机ARP缓存中并保留一定时间,下次请求时直接查询ARP缓存以节约资源。

地址解析协议是建立在网络中各个主机互相信任的基础上的,局域网络上的主机可以自主发送ARP应答消息,其他主机收到应答报文时不会检测该报文的真实性就会将其记入本机ARP缓存;由此攻击者就可以向某一主机发送伪ARP应答报文,使其发送的信息无法到达预期的主机或到达错误的主机,这就构成了一个ARP欺骗。ARP命令可用于查询本机ARP缓存中IP地址和MAC地址的对应关系、添加或删除静态对应关系等。相关协议有RARP、代理ARP。NDP用于在IPv6中代替地址解析协议。

ARP协议工作过程

主机A的IP地址为192.168.1.1,MAC地址为0A-11-22-33-44-01;

主机B的IP地址为192.168.1.2,MAC地址为0A-11-22-33-44-02;

当主机A要与主机B通信时,地址解析协议可以将主机B的IP地址(192.168.1.2)解析成主机B的MAC地址,以下为工作流程:

第1步:根据主机A上的路由表内容,IP确定用于访问主机B的转发IP地址是192.168.1.2。然后A主机在自己的本地ARP缓存中检查主机B的匹配MAC地址。

第2步:如果主机A在ARP缓存中没有找到映射,它将询问192.168.1.2的硬件地址,从而将ARP请求帧广播到本地网络上的所有主机。源主机A的IP地址和MAC地址都包括在ARP请求中。本地网络上的每台主机都接收到ARP请求并且检查是否与自己的IP地址匹配。如果主机发现请求的IP地址与自己的IP地址不匹配,它将丢弃ARP请求。

第3步:主机B确定ARP请求中的IP地址与自己的IP地址匹配,则将主机A的IP地址和MAC地址映射添加到本地ARP缓存中。

第4步:主机B将包含其MAC地址的ARP回复消息直接发送回主机A。

第5步:当主机A收到从主机B发来的ARP回复消息时,会用主机B的IP和MAC地址映射更新ARP缓存。本机缓存是有生存期的,生存期结束后,将再次重复上面的过程。主机B的MAC地址一旦确定,主机A就能向主机B发送IP通信了。

VRRP协议选举机制

VRRP路由器在运行过程中有三种状态:

  1. Initialize状态: 系统启动后就进入Initialize,此状态下路由器不对VRRP报文做任何处理;

  2. Master状态;

  3. Backup状态;

一般主路由器处于Master状态,备份路由器处于Backup状态。

VRRP使用选举机制来确定路由器的状态,优先级选举:
1.VRRP组中IP拥有者。如果虚拟IP地址与VRRP组中的某台VRRP路由器IP地址相同,则此路由器为IP地址拥有者,这台路由器将被定位主路由器。
2.比较优先级。如果没有IP地址拥有者,则比较路由器的优先级,优先级的范围是0~255,优先级大的作为主路由器
3.比较IP地址。在没有Ip地址拥有者和优先级相同的情况下,IP地址大的作为主路由器。

如下图所示,虚拟IP为10.1.1.254,在VRRP组中没有IP地址拥有者,则比较优先级,很明显RB和RA的优先级要大于RC,则比较RA和RB的IP地址,RB的IP地址大。

nginx keepalive 高可用 原理和实操 (图解+秒懂+史上最全)_第6张图片

所以RB为组中的主路由器。

选举流程

路由器使用VRRP 功能后,会根据优先级确定自己在备份组中的角色。

优先级高的路由器成为Master 路由器,优先级低的成为Backup 路由器。

Master 拥有对外服务的虚拟IP,提供各种网络功能,并定期发送VRRP 报文,通知备份组内的其他设备自己工作正常;

Backup 路由器只接收Master 发来的报文信息,用来监控Master 的运行状态。

当Master 失效时,Backup 路由器进行选举,优先级高的Backup 将成为新的Master 。

在抢占方式下,当Backup路由器收到VRRP 报文后,会将自己的优先级与报文中的优先级进行比较。如果大于通告报文中的优先级,则成为Master 路由器;否则将保持Backup状态;

在非抢占方式下,只要Master 路由器没有出现故障,备份组中的路由器始终保持 Master 或Backup 状态,Backup 路由器即使随后被配置了更高的优先级也不会成为Master 路由器;

如果Backup 路由器的定时器超时后仍未收到Master 路由器发送来的VRRP报文,则认为Master 路由器已经无法正常工作,此时Backup 路由器会认为自己是Master 路由器,并对外发送VRRP报文。

备份组内的路由器根据优先级选举出Master 路由器,承担报文的转发功能。

Keepalived体系结构

Keepalived起初是为LVS设计的,由于Keeplalived可以实现对集群节点的状态检测,

而IPVS可以实现负载均衡功能,因此,Keepalived借助于第三方模块IPVS就可以很方便地搭建一套负载均衡系统。

在Keepalived当中IPVS模块是可配置的,如果需要负载均衡功能,可以在编译Keepalived时开打负载均衡功能,也可以通过编译参数关闭。

nginx keepalive 高可用 原理和实操 (图解+秒懂+史上最全)_第7张图片

keepalived运行时,会启动3个进程,分别为:core(核心进程),check和vrrp
- core:负责主进程的启动,维护和全局配置文件的加载;
- check:负责健康检查
- vrrp:用来实现vrrp协议

SchedulerI/OMultiplexer是一个I/O复用分发调度器,它负载安排Keepalived所有内部的任务请求;

Memory Mngt是一个内存管理机制,这个框架提供了访问内存的一些通用方法;

Control Plane 是keepalived的控制版面,可以实现对配置文件编译和解析;

Core componets 这部分主要包含了5个部分;

  • Watchdog:是计算机可靠领域中极为简单又非常有效的检测工具,Keepalived正是通过它监控Checkers和VRRP进程的。
  • Checkers: 这是Keepalived最基础的功能,也是最主要的功能,可以实现对服务器运行状态检测和故障隔离。
  • VRRP Stack: 这是keepalived后来引用VRRP功能,可以实现HA集群中失败切换功能。负责负载均衡器之间的失败切换FailOver;
  • IPVS wrapper:这个是IPVS功能的一个实现,IPVSwarrper模块将可以设置好的IPVS规则发送的内核空间并且提供给IPVS模块,最终实现IPVS模块的负载功能。
  • Netlink Reflector:用来实现高可用集群Failover时虚拟IP(VIP)的设置和切换,

IPVS内核模块

此模块是此文的重点。

Linux 的 IPVS内核模块基本上是一种高效的Layer-4交换机,它提供负载平衡的功能。

ipvs (IP Virtual Server) 实现了传输层负载均衡,也就是我们常说的4层LAN交换,作为 Linux 内核的一部分。

ipvs运行在主机上,在真实服务器集群前充当负载均衡器。

ipvs可以将基于TCP和UDP的服务请求转发到真实服务器上,并使真实服务器的服务在单个 IP 地址上显示为虚拟服务。

当一个TCP连接的初始SYN报文到达时,IPVS就选择一台服务器,将报文转发给它。

此后通过查发报文的IP和TCP报文头地址,保证此连接的后继报文被转发到相同的服务器。这样,IPVS不用检查到请求的内容再选择服务器,这就要求后端的服务器组是提供相同的服务,不管请求被送到哪一台服务器,返回结果都应该是一样的。但是在有一些应用中后端的服务器可能功能不一,有的是提供HTML文档的Web服务器,有的是提供图片的Web服务器,有的是提供CGI的Web服务器。这时,就需要基于内容请求分发 (Content-Based Request Distribution),同时基于内容请求分发可以提高后端服务器上访问的局部性。

当一个TCP连接的初始SYN报文到达时,IPVS就选择一台服务器,将报文转发给它。此后通过查发报文的IP和TCP报文头地址,保证此连接的后继报文被转发到相同的服务器。这样,IPVS无法检查到请求的内容再选择服务器,这就要求后端的服务器组是提供相同的服务,不管请求被送到哪一台服务器,返回结果都应该是一样的。但是在有一些应用中后端>的服务器可能功能不一,有的是提供HTML文档的Web服务器,有的是提供图片的Web服务器,有的是提供CGI的Web服务器。这时,就需要基于内容请求分发 (Content-Based Request Distribution),同时基于内容请求分发可以提高后端服务器上访问的局部性。

Keepalived对服务器运行状态和故障隔离的工作原理

Keepalived工作在TCP/IP参考模型的网络层/传输层/应用层:

网络层:

Keepalived通过ICMP协议向服务器集群中的每一个节点发送一个ICMP数据包(有点类似与Ping的功能),如果某个节点没有返回响应数据包,那么认为该节点发生了故障,Keepalived将报告这个节点失效,并从服务器集群中剔除故障节点。

传输层:

Keepalived 在传输层里利用了TCP协议的端口连接和扫描技术来判断集群节点的端口是否正常,比如对于常见的WEB服务器80端口。或者SSH服务22端口,Keepalived一旦在传输层探测到这些端口号没有数据响应和数据返回,就认为这些端口发生异常,然后强制将这些端口所对应的节点从服务器集群中剔除掉。

应用层:

Keepalived 的运行方式也更加全面化和复杂化,用户可以通过自定义Keepalived工作方式,例如:

可以通过编写程序或者脚本来运行Keepalived,而Keepalived将根据用户的设定参数检测各种程序或者服务是否允许正常,如果Keepalived的检测结果和用户设定的不一致时,Keepalived将把对应的服务器从服务器集群中剔除。

离线环境导入镜像

两种方法制作镜像:

1 手工制作机芯

我们从下载镜像,启动容器,在容器中输入命令来运行程序,这些命令都是手工一条条往里输入的,无法重复利用,而且效率很低。

2 通过脚本制作镜像

所以就需要一 种文件或脚本,我们把想执行的操作以命令的方式写入其中,然后让docker读取并分析、执行,那么重复构建、更新将变得很方便,所以Dockerfile就此诞生了。

Dockerfile常用参数:

FROM命令。用法,``FROM ``:。FROM命令告诉docker我们构建的镜像是以哪个(发行版)镜像为基础的
RUN命令。用法RUN 。RUN 后面接要执行的命令,比如,我们想在镜像中安装vim,只需在Dockfile中写入RUN yum install -y vim
ENV命令。用法,ENV  。ENV命令主要用于设置容器运行时的环境变量
ADD命令。用法,ADD  。ADD主要用于将宿主机中的文件添加到镜像中

制作自己的镜像

1.从远程仓库拉取一个纯净的 centos 系统镜像

从有公网的环境拉取镜像,然后导出镜像

docker pull  docker pull nginx:latest

结果

[root@VM-4-17-centos ~]# docker pull  docker pull nginx:latest

1.13.5-alpine: Pulling from library/nginx
b1f00a6a160c: Pull complete
b5441325f46d: Pull complete
049763556f13: Pull complete
555a8317e22d: Pull complete
Digest: sha256:4a97b863a4386ba588cd4f264582d1f306bc9da46fe3e02540bd171709ce09d7
Status: Downloaded newer image for docker pull nginx:latest


[root@VM-4-17-centos ~]# docker image ls
REPOSITORY                                        TAG                     IMAGE ID            CREATED             SIZE
minio/minio                                       latest                  ecfbb387b46a        2 weeks ago         261MB
haproxy                                           latest                  d2164a4a948d        4 weeks ago         101MB
nginx                                             latest                  87a94228f133        6 weeks ago         133MB

 mkdir -p /root/ha-nginx
 cd /root/ha-nginx

把dockerfile 和sh脚本拖进去:

[root@VM-4-17-centos ha-nginx]# ll
total 8
-rw-r--r-- 1 root root 263 Oct 28 15:19 Dockerfile
-rw-r--r-- 1 root root 256 Oct 28 15:19 nginx_check.sh

构建镜像

docker build -t custom/ha-nginx:1.0 --rm=true .    

参数:

--rm :设置镜像成功后删除中间容器;

--shm-size :设置/dev/shm的大小,默认值是64M;

--ulimit :Ulimit配置。

--squash :将 Dockerfile 中所有的操作压缩为一层。

--tag, -t: 镜像的名字及标签,通常 name:tag 或者 name 格式;可以在一次构建中为一个镜像设置多个标签。

output:

[root@VM-4-17-centos ha-nginx]# docker build -t custom/ha-nginx:1.0 --rm=true .
Sending build context to Docker daemon  3.072kB
Step 1/6 : FROM nginx:1.13.5-alpine
 ---> ea7bef82810a
Step 2/6 : RUN apk update && apk upgrade
 ---> Running in cedbede9dda0
fetch http://dl-cdn.alpinelinux.org/alpine/v3.5/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.5/community/x86_64/APKINDEX.tar.gz
v3.5.3-40-g389d0b359a [http://dl-cdn.alpinelinux.org/alpine/v3.5/main]
v3.5.3-40-g389d0b359a [http://dl-cdn.alpinelinux.org/alpine/v3.5/community]
OK: 7973 distinct packages available
Upgrading critical system libraries and apk-tools:
(1/1) Upgrading apk-tools (2.6.9-r0 -> 2.6.10-r0)
Executing busybox-1.25.1-r0.trigger
Continuing the upgrade transaction with new apk-tools:
(1/8) Upgrading freetype (2.7-r1 -> 2.7-r2)
(2/8) Upgrading libjpeg-turbo (1.5.1-r0 -> 1.5.3-r2)
(3/8) Upgrading gd (2.2.4-r0 -> 2.2.5-r1)
(4/8) Upgrading libcrypto1.0 (1.0.2k-r0 -> 1.0.2q-r0)
(5/8) Upgrading libssl1.0 (1.0.2k-r0 -> 1.0.2q-r0)
(6/8) Upgrading libxml2 (2.9.4-r3 -> 2.9.8-r1)
(7/8) Upgrading libgcrypt (1.7.9-r0 -> 1.7.10-r0)
(8/8) Upgrading busybox (1.25.1-r0 -> 1.25.1-r2)
Executing busybox-1.25.1-r2.post-upgrade
Executing busybox-1.25.1-r2.trigger
OK: 13 MiB in 26 packages
Removing intermediate container cedbede9dda0
 ---> 18233f3baae5
Step 3/6 : RUN apk add --no-cache bash curl ipvsadm iproute2 openrc keepalived &&  rm -f /var/cache/apk/* /tmp/*
 ---> Running in 8ed3f3453400
fetch http://dl-cdn.alpinelinux.org/alpine/v3.5/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.5/community/x86_64/APKINDEX.tar.gz
(1/20) Installing ncurses-terminfo-base (6.0_p20171125-r1)
(2/20) Installing ncurses-terminfo (6.0_p20171125-r1)
(3/20) Installing ncurses-libs (6.0_p20171125-r1)
(4/20) Installing readline (6.3.008-r4)
(5/20) Installing bash (4.3.46-r5)
Executing bash-4.3.46-r5.post-install
(6/20) Installing ca-certificates (20161130-r1)
(7/20) Installing libssh2 (1.7.0-r2)
(8/20) Installing libcurl (7.61.1-r1)
(9/20) Installing curl (7.61.1-r1)
(10/20) Installing libelf (0.8.13-r2)
(11/20) Installing libmnl (1.0.4-r0)
(12/20) Installing libnftnl-libs (1.0.7-r0)
(13/20) Installing iptables (1.6.0-r0)
(14/20) Installing iproute2 (4.7.0-r0)
Executing iproute2-4.7.0-r0.post-install
(15/20) Installing libnl (1.1.4-r0)
(16/20) Installing popt (1.16-r6)
(17/20) Installing ipvsadm (1.28-r0)
(18/20) Installing keepalived-common (1.2.24-r0)
(19/20) Installing keepalived (1.2.24-r0)
(20/20) Installing openrc (0.21.7-r4)
Executing openrc-0.21.7-r4.post-install
Executing busybox-1.25.1-r2.trigger
Executing ca-certificates-20161130-r1.trigger
OK: 28 MiB in 46 packages
Removing intermediate container 8ed3f3453400
 ---> f11588d2c04c

Step 4/6 : COPY entrypoint.sh /entrypoint.sh
 ---> 3156f2333fc0
Step 5/6 : RUN chmod +x /entrypoint.sh
 ---> Running in a1b04490979b
Removing intermediate container a1b04490979b
 ---> 3d829725c7d4
Step 6/6 : CMD ["/entrypoint.sh"]
 ---> Running in beafbbeab51a
Removing intermediate container beafbbeab51a
 ---> c2a5a12324ab
Successfully built c2a5a12324ab
Successfully tagged custom/ha-nginx:1.0

查看镜像

docker images 可以查看下镜像是否构建成功

docker save tag名称 -o 镜像名(例如:/root/rocketmq.tar)

docker save custom/ha-nginx:1.0 -o /root/ha-nginx.tar

上传到内网并且导入

无公网的环境,上传到到内网环境, 上传镜像到目标虚拟机

然后导入docker,load到docker

docker load   -i  /root/ha-nginx.tar

导入后看到两个image 镜像

master的高可用配置文件

随后,撰写nginx配置文件,keepalived-master.conf 这里由于我们没有后端tornado服务,所以使用虚拟代理服务

vrrp_script chk_nginx {
    script "pidof nginx"
    interval 2
}

vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 33
    priority 200
    advert_int 1
    unicast_src_ip 172.20.128.2
    unicast_peer {
        172.20.128.3
    }
    
    authentication {
        auth_type PASS
        auth_pass 123456
    }
    
    virtual_ipaddress {
        172.20.128.4/24 dev eth0
    }

    track_script {
        chk_nginx
    }
}

slave的高可用配置文件

同理再复制一份从机的nginx配置keepalived-slave.conf

vrrp_script chk_nginx {
    script "pidof nginx"
    interval 2
}

vrrp_instance VI_1 {
    state BACKUP
    interface eth0
    virtual_router_id 33
    priority 100
    advert_int 1
    unicast_src_ip 172.20.128.3
    unicast_peer {
        172.20.128.2
    }
    
    authentication {
        auth_type PASS
        auth_pass letmein
    }
    
    virtual_ipaddress {
        172.20.128.4/24 dev eth0
    }
    
    track_script {
        chk_nginx
    }
}

pidof命令用于查找指定名称的进程的进程号id号。

语法

pidof(选项)(参数)

选项

-s:仅返回一个进程号;
-c:仅显示具有相同“root”目录的进程;
-x:显示由脚本开启的进程;
-o:指定不显示的进程ID。

一.体系架构

在Keepalived + Nginx高可用负载均衡架构中,keepalived负责实现High-availability (HA) 功能控制前端机VIP(虚拟网络地址),当有设备发生故障时,热备服务器可以瞬间将VIP自动切换过来,实际运行中体验只有2秒钟切换时间,DNS服务可以负责前端VIP的负载均衡。
nginx负责控制后端web服务器的负载均衡,将客户端的请求按照一定的算法转发给后端Real Server处理,而Real Server将响应直接返回给客户端。

二.简单原理

NGINX_MASTER、NGINX_BACKUP两台服务器均通过keepalived软件把ens32网卡绑上一个虚拟IP(VIP)地址192.168.2.242,此VIP当前由谁承载着服务就绑定在谁的ens32上,当NGINX_MASTER发生故障时,NGINX_BACKUP会通过/etc/keepalived/keepalived.conf文件中设置的心跳时间advert_int 1检查,无法获取NGINX_MASTER正常状态的话,NGINX_BACKUP会瞬间绑定VIP来接替nginx_master的工作,当NGINX_MASTER恢复后keepalived会通过priority参数判断优先权将虚拟VIP地址192.168.2.242重新绑定给NGINX_MASTER的ens32网卡。
使用此方案的优越性
1.实现了可弹性化的架构,在压力增大的时候可以临时添加web服务器添加到这个架构里面去;
2.upstream具有负载均衡能力,可以自动判断后端的机器,并且自动踢出不能正常提供服务的机器;
3.相对于lvs而言,正则分发和重定向更为灵活。而Keepalvied可保证单个nginx负载均衡器的有效性,避免单点故障;
4.用nginx做负载均衡,无需对后端的机器做任何改动。
5.nginx部署在docker容器里,即大量地节约开发、测试、部署的时间,又可以在出现故障时通过镜像快速恢复业务。

三、系统环境

两台负载机器安装:centos7.5+docker+nginx+keepalived,分别命名为:NGINX_MASTER,NGINX_BACKUP。
后端web服务器,可以是提供web服务的任何架构,分别命名为:WEB_1,WEB_2。
后端数据库机器可任意架构,只要能提供数据库服务即可。

服务器 操作系统 IP地址 安装软件
NGINX_MASTER Centos 7.5 64位 192.168.2.228 docker+nginx+keepalived
NGINX_BACKUP Centos 7.5 64位 192.168.2.229 docker+nginx+keepalived
WEB_1 Centos 7.5 64位 192.168.2.226 docker+springboot
WEB_2 Centos 7.5 64位 192.168.2.227 docker+springboot
数据库集群 Centos 7.5 64位 mysql集群

keepalive的配置文件

vim /etc/keepalived/keepalived.conf

vrrp_script chk_nginx {
    script "/etc/keepalived/nginx_pid.sh"   # 检查nginx状态的脚本
    interval 2
    weight 3
}

vrrp_instance VI_1 {
    state MASTER     #备份服务器上将MASTER改为BACKUP
    interface ens32
    virtual_router_id 51
    priority 100       #备份服务上将100改为小于100,可配置成90
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.2.242    #有多个vip可在下面继续增加
    }
    track_script {
        chk_nginx
    }
}

添加检查nginx状态的脚本

Keepalived的配置文件可以分为三块:

  • 全局定义块:对整个 Keepalive 配置生效的,不管是否使用 LVS;
  • VRRP 实例定义块:是 Keepalived 的核心;
  • 虚拟服务器(LVS)定义块:LVS 配置只在使用 Keepalived 来配置和管理 LVS 时才需要使用,如果仅仅使用 Keepalived做 HA,LVS 的配置完全是不需要的。

配置文件都是以块(block)形式组织的,每个块都在{和}包围的范围内。#和!开头的行都是注释。

[root@localhost ~]# cat /usr/local/keepalived/etc/keepalived/keepalived.conf
! Configuration File for keepalived
 
global_defs {					#全局配置
	notification_email {		#指定keepalived在发生切换时需要发送email到的对象,一行一个
		[email protected]	#指定收件人邮箱
		[email protected]
		[email protected]
	}
	notification_email_from [email protected] #指定发件人
	smtp_server 192.168.200.1	#指定smtp服务器地址
	smtp_connect_timeout 30		#指定smtp连接超时时间
	router_id LVS_DEVEL			#此处注意router_id为负载均衡标识,在局域网内应该是唯一的。
	vrrp_skip_check_adv_addr
	vrrp_strict
	vrrp_garp_interval 0
	vrrp_gna_interval 0
}
 
vrrp_sync_group VG_1{				#监控多个网段的实例
	group {
		inside_network				#实例名
		outside_network
	}
	notify_master /path/xx.sh		#指定当切换到master时,执行的脚本
	netify_backup /path/xx.sh		#指定当切换到backup时,执行的脚本
	notify_fault "path/xx.sh VG_1" 	#故障时执行的脚本
	notify /path/xx.sh
	smtp_alert 						#使用global_defs中提供的邮件地址和smtp服务器发送邮件通知
}
 
vrrp_instance inside_network {
	state BACKUP 			#指定那个为master,那个为backup,如果设置了nopreempt这个值不起作用,主备考priority决定
	interface eth0 			#设置实例绑定的网卡
	dont_track_primary 		#忽略vrrp的interface错误(默认不设置)
	track_interface{ 		#设置额外的监控,里面那个网卡出现问题都会切换
		eth0
		eth1
	}
	mcast_src_ip			#发送多播包的地址,如果不设置默认使用绑定网卡的primary ip
	garp_master_delay		#在切换到master状态后,延迟进行gratuitous ARP请求
	virtual_router_id 50	#VPID标记
	priority 99				#优先级,高优先级竞选为master
	advert_int 1			#检查间隔,默认1秒
	nopreempt				#设置为不抢占 注:这个配置只能设置在backup主机上,而且这个主机优先级要比另外一台高
	preempt_delay			#抢占延时,默认5分钟
	debug					#debug级别
	authentication {		#设置认证
		auth_type PASS		#认证方式,类型主要有PASS、AH 两种
		auth_pass 111111	#认证密码
	}
	virtual_ipaddress {		#设置vip
		192.168.36.200
	}
}
 
vrrp_instance VI_1 {		#虚拟路由的标识符
	state MASTER			#状态只有MASTER和BACKUP两种,并且要大写,MASTER为工作状态,BACKUP是备用状态
	interface eth0			#通信所使用的网络接口
    lvs_sync_daemon_inteface eth0  #这个默认没有,相当于心跳线接口,DR模式用的和上面的接口一样,也可以用机器上的其他网卡eth1,用来防止脑裂。
    virtual_router_id 51	#虚拟路由的ID号,是虚拟路由MAC的最后一位地址
    priority 100			#此节点的优先级,主节点的优先级需要比其他节点高
    advert_int 1			#通告的间隔时间
    nopreempt				#设置为不抢占 注:这个配置只能设置在backup主机上,而且这个主机优先级要比另外一台高
    preempt_delay			#抢占延时,默认5分钟
    authentication {		#认证配置
		auth_type PASS		#认证方式
        auth_pass 1111		#认证密码
    }
    virtual_ipaddress {		#虚拟ip地址,可以有多个地址,每个地址占一行,不需要子网掩码,同时这个ip 必须与我们在lvs 客户端设定的vip 相一致!
        192.168.200.16
        192.168.200.17
        192.168.200.18
    }
}
 

nginx 的参考配置

参考为:4核

user  nobody;
worker_processes  4;
worker_cpu_affinity 00000001 00000010 00000100 00001000;

error_log  /usr/local/nginx/logs/nginx_error.log  crit;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

pid        /usr/local/nginx/nginx.pid;
worker_rlimit_nofile 204800;

events {
    use epoll;
    worker_connections  204800;
}


http {
    include       mime.types;
    default_type  application/octet-stream;
    charset  utf-8;
    log_format  access  '$remote_addr - $remote_user [$time_local] "$request" '
              '$status $body_bytes_sent "$http_referer" '
              '"$http_user_agent" $http_x_forwarded_for';
    access_log  /usr/local/nginx/logs/access.log  access;

    server_names_hash_bucket_size 128;
	client_header_buffer_size 2k;
	large_client_header_buffers 4 4k;
	client_max_body_size 8m;

	sendfile on;
	tcp_nopush     on;

	keepalive_timeout 60;

	fastcgi_cache_path /usr/local/nginx/fastcgi_cache levels=1:2
	keys_zone=TEST:10m
	inactive=5m;
	fastcgi_connect_timeout 300;
	fastcgi_send_timeout 300;
	fastcgi_read_timeout 300;
	fastcgi_buffer_size 16k;
	fastcgi_buffers 16 16k;
	fastcgi_busy_buffers_size 16k;
	fastcgi_temp_file_write_size 16k;
	fastcgi_cache TEST;
	fastcgi_cache_key http://$host$request_uri;
	fastcgi_cache_valid 200 302 1h;
	fastcgi_cache_valid 301 1d;
	fastcgi_cache_valid any 1m;
	fastcgi_cache_min_uses 1;
	fastcgi_cache_use_stale error timeout invalid_header http_500;

	open_file_cache max=204800 inactive=20s;
	open_file_cache_min_uses 1;
	open_file_cache_valid 30s;



	tcp_nodelay on;

	gzip on;
	gzip_min_length  1k;
	gzip_buffers     4 16k;
	gzip_http_version 1.0;
	gzip_comp_level 2;
	gzip_types       text/plain application/x-javascript text/css application/xml;
	gzip_vary on;

    server {
        listen       8080;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            root   html;
            index  index.html index.htm;
        }

	root  /www/html/;

	location /status
	{
	     stub_status on;
	}


    location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|js|css)$
    {
      expires      30d;
    }

      
    }


    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}


    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

}
配置参考配置为8核CPU
  • worker_processes 8;

nginx进程数,建议按照cpu数目来指定,一般为它的倍数。

  • worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;

为每个进程分配cpu,上例中将8个进程分配到8个cpu,当然可以写多个,或者将一个进程分配到多个cpu。

  • worker_rlimit_nofile 102400;

这个指令是指当一个nginx进程打开的最多文件描述符数目,理论值应该是最多打开文件数(ulimit -n)与nginx进程数相除,但是nginx分配请求并不是那么均匀,所以最好与ulimit -n的值保持一致。

  • use epoll;

使用epoll的I/O模型,这个不用说了吧。

  • worker_connections 102400;

每个进程允许的最多连接数,理论上每台nginx服务器的最大连接数为worker_processes*worker_connections。

  • keepalive_timeout 60;

keepalive超时时间。

  • client_header_buffer_size 4k;

客户端请求头部的缓冲区大小,这个可以根据你的系统分页大小来设置,一般一个请求的头部大小不会超过1k,不过由于一般系统分页都要大于1k,所以这里设置为分页大小。分页大小可以用命令getconf PAGESIZE取得。

  • open_file_cache max=102400 inactive=20s;

这个将为打开文件指定缓存,默认是没有启用的,max指定缓存数量,建议和打开文件数一致,inactive是指经过多长时间文件没被请求后删除缓存。

  • open_file_cache_valid 30s;

这个是指多长时间检查一次缓存的有效信息。

  • open_file_cache_min_uses 1;

open_file_cache指令中的inactive参数时间内文件的最少使用次数,如果超过这个数字,文件描述符一直是在缓存中打开的,如上例,如果有一个文件在inactive时间内一次没被使用,它将被移除。

  • fastcgi_cache_path /usr/local/nginx/fastcgi_cache levels=1:2 keys_zone=TEST:10m inactive=5m;

这个指令为FastCGI缓存指定一个路径,目录结构等级,关键字区域存储时间和非活动删除时间。

  • fastcgi_connect_timeout 300;

指定连接到后端FastCGI的超时时间。

  • fastcgi_send_timeout 300;

向FastCGI传送请求的超时时间,这个值是指已经完成两次握手后向FastCGI传送请求的超时时间。

  • fastcgi_read_timeout 300;

接收FastCGI应答的超时时间,这个值是指已经完成两次握手后接收FastCGI应答的超时时间。

  • fastcgi_buffer_size 16k;

指定读取FastCGI应答第一部分需要用多大的缓冲区,这里可以设置为fastcgi_buffers指令指定的缓冲区大小,上面的指令指定它将使用1个16k的缓冲区去读取应答的第一部分,即应答头,其实这个应答头一般情况下都很小(不会超过1k),但是你如果在fastcgi_buffers指令中指定了缓冲区的大小,那么它也会分配一个fastcgi_buffers指定的缓冲区大小去缓存。

  • fastcgi_buffers 16 16k;

指定本地需要用多少和多大的缓冲区来缓冲FastCGI的应答,如上所示,如果一个php脚本所产生的页面大小为256k,则会为其分配16个16k的缓冲区来缓存,如果大于256k,增大于256k的部分会缓存到fastcgi_temp指定的路径中,当然这对服务器负载来说是不明智的方案,因为内存中处理数据速度要快于硬盘,通常这个值的设置应该选择一个你的站点中的php脚本所产生的页面大小的中间值,比如你的站点大部分脚本所产生的页面大小为256k就可以把这个值设置为16 16k,或者4 64k 或者64 4k,但很显然,后两种并不是好的设置方法,因为如果产生的页面只有32k,如果用4 64k它会分配1个64k的缓冲区去缓存,而如果使用64 4k它会分配8个4k的缓冲区去缓存,而如果使用16 16k则它会分配2个16k去缓存页面,这样看起来似乎更加合理。

  • fastcgi_busy_buffers_size 32k;

这个指令我也不知道是做什么用,只知道默认值是fastcgi_buffers的两倍。

  • fastcgi_temp_file_write_size 32k;

在写入fastcgi_temp_path时将用多大的数据块,默认值是fastcgi_buffers的两倍。

  • fastcgi_cache TEST

开启FastCGI缓存并且为其制定一个名称。个人感觉开启缓存非常有用,可以有效降低CPU负载,并且防止502错误。但是这个缓存会引起很多问题,因为它缓存的是动态页面。具体使用还需根据自己的需求。

  • fastcgi_cache_valid 200 302 1h;
  • fastcgi_cache_valid 301 1d;
  • fastcgi_cache_valid any 1m;

为指定的应答代码指定缓存时间,如上例中将200,302应答缓存一小时,301应答缓存1天,其他为1分钟。

  • fastcgi_cache_min_uses 1;

缓存在fastcgi_cache_path指令inactive参数值时间内的最少使用次数,如上例,如果在5分钟内某文件1次也没有被使用,那么这个文件将被移除。

参考文献

https://blog.csdn.net/zcxey2911/article/details/105593005/

你可能感兴趣的:(java,nginx,java,面试)