从今天开始学习Linux集群知识 !
集群 Cluster
什么是一个集群啊? 或者说为什么需要集群啊? 首先我们先来从一个普通的LAMP/LNMP看:
我们的httpd是一个前端服务器同时也是一个反向代理服务器, 也就是说所有的请求都要经过它. 当请求的数量就很大的时候, httpd就会有点吃不消了. 除此之外, 我们说说后面的动态资源– php. 动态资源一般只有20%的请求. 由于处理起来是需要时间的, 所以这个的最大并发量也是有一个限制的, 甚至有可能前端服务器还没有到达瓶颈, 后端服务已经承载不住了. 当超出了之后, 整体的性能也会急剧下降.
于是在这样的场景下, 我们有两种扩展的方式:
- scale up: 向上扩展. 也就是找个性能更好的主机, 但是性价比很不高, 往往会花大价钱, 不推荐.
- scale out: 向外扩展. 也就是多加机器, 分散请求 ,但是先要分散就需要一个接入点, 将服务进行分散.
比如这里的PHP服务, 我可以多来几台, 由httpd来调度, 这就需要httpd能够提供调度, 也就是将受到的请求发散到各个PHP服务器上, 其中httpd有个模块就是做这个事情的.
由于http协议是个无状态的协议, 因此他需要进行追踪. 一种可行的是cookie, 但是不是很安全. 而且考虑到以某宝为例, 并且假设其使用PHP做动态资源, 我们可以在浏览网页的时候, 加入购物车对么, 假设这个时候用户的请求被调度到了第一台PHP服务器, 于是这个操作被存储到了一个特殊的空间, 我们叫做session. 而后第二次访问的时候, 还是被调度到了第一台PHP服务器, 但是他携带的第一次访问服务端给的cookie, 就依然可以得到自己上一次访问的相关信息. 接着用户第三次访问, 但是这一次他被导向了第二台PHP服务器, 这个时候Cookie就没用了. 那么, session里的数据呢? 也没了, 自己的购物车就这样的清空了, 显然是不行的. 那怎么办?
使用数据库吧, 这样就解决了. 但是我的MySQL数据库也有很多啊. 怎么知道我的数据在哪一个数据库服务器里? 这个时候我们的所使用的一般是主从复制的架构. 也就是写操作都是写到一个固定的MySQL服务器上, 而其他的MySQL会自动进行同步和复制, 一般情况下, 我们的正常业务中都是读多写少的模型, 所以我们采取这样的操作, 只有主服务器可以进行写操作, 只有从服务器可以进行读操作, 而从服务器有多个.
但是这样又太麻烦了, 我们的程序怎么进行读操作啊? 多台MySQL, 虽然可以在程序中进行轮询或者不管怎么方式访问, 耦合度又太高了, 如果日后这些从服务器进行了改变, 难道还要修改程序的代码? 因此, 我们可以搞出来一个中间件. 所有的请求都发向这个中间件就好, 接着由这个middleware进行调度和分发, 最后返回结果就行了. 除了提供这样的功能, 中间件还可以提供消息队列的功能, 当流量很大的时候还是可以进行排队, 然后挨个处理.
这还只是后端就已经这么过分了, 如果我们把httpd分成两台, 那问题就更加严重了. 首先, 我需要两倍的静态页面? 还是其他的处理方法? 接着, 后端的服务器又怎么部署? 如果我想要进行程序的改版, 那岂不是n倍部署? 另外, 两台前端服务器? 怎么将用户均匀调度呢? 我们想到之前说过DNS的A记录, 我可以将一个域名绑定多个IP, 这样就可以轮询了. 事实上, 这种方法是一个会很不好的解决方案, 因为DNS存在缓存, 而大量用户使用运营商的DNS缓存, 所有我们不能达到期待的效果.
于是, 我们又在httpd前面加了一个负载均衡器. 但是这样的话, 负载均衡器又会变成性能瓶颈. 而且, 我们的负载均衡器作为网络入口, 还需要进行nat转换.另外, 既然是作为入口, 那么万一这台负载均衡器炸了怎么办, 整个系统就完了? 所以, 我们还需要对着这一台负载均衡器进行备份. 也就是冗余 而当活动主机出现了故障, 冗余主机怎么知道呢? 更严重的是, 如果负载均衡器是一个有状态的服务的话, 这个状态如何在冗余切换进行来的时候进行传递呢 ? 是很麻烦啊..
说到这里为止, 什么是个集群, 就差不多可以来说说了. 多台主机,为了满足某个特定目的,而组合起来使用. 这样一个计算机集合, 我们就叫做集群(cluster).
集群的类型
LB集群: 负载均衡集群, 通过scale out形式, 主要分摊负载.
HA集群: 高可用集群, 通过冗余, 能够做到服务一直可用, 一直在线.
为了衡量服务的可用性(availability), 我们需要考虑无故障时间和修复时间.
HP集群: 高性能集群…略
大规模并行处理集群: 分布式 map-reduce (hadoop等) …略
我们在这一部分先来说说负载集群. 还是先来考虑之前的数据库读写分离. 为什么我们说只有MySQL需要进行读写分离呢? 到底区别在哪里?
httpd不需要存储数据 正是因为这个, 所以我们才要对MySQL进行分离, 因为需要得到数据集. 读写不分离行吗? 如果每一个MySQL主机既可以读, 也可以写. 那么结果集是离散的. 所以我们 在这个情况下, 可以使用一个大的框架进行统筹, 将离散的数据集拼成一个完整的. 但是这还不够. 如果我们 需要进行大数据的排序结果集, 每一个MySQL服务器将排好序的部分结果集提交给集合器, 但是集合器这个时候拿到的只是三个结果集, 没有索引. 怎么排序?
所以一旦使用这种方式, 我们的访问模型就会发生改变, 其实仔细想想, 我们不会去访问2年前的微博, 也不会去查看你几个月之前的淘宝商品. 也就是说, 只有20%左右的商品是热点数据, 而这些当中可能也有20%是热点中的热点. 是最频繁访问的数据. 所以说业务模型决定数据的存储方式.
当我们在考虑一个系统的时候, 我们往往会考虑下面的几个:
- 可扩展性
- 可用性
- 容量
- 性能
作为一个系统运维: 我们在进行运维的时候, 首先要确保服务可用, 这是最重要的, 稳定大于一切, 这个达成之后, 我们就要进行标准化, 能够标准了, 就可以自动化了.
构建高可用扩展系统的重要原则:
- 在系统内部尽量避免串行化和交互
考虑负载均衡器成为了瓶颈, 这时候我们就要考虑使用多地部署, 多机房冗余部署. 多系统部署. 例如, 华北地区走华北机房的流量, 华南地区走华南机房的流量, 一旦华南机房挂掉了, 我们就需要一个大的, 全局的服务能够将华南地区的流量引到华北地区. 这里的全局服务, 我们把它叫做GSLB
, 也就是Global Service Load Balancing
比如基于View的DNS. 同时还有小型的SLB
, 也就是Service Load Balancing
.
另外, http静态资源还是可以进行缓存的, 所以我们就可以把对静态资源的请求结果缓存到离用户最近的服务器上. 这样有极大部分的请求都会直接命中缓存, 而最终到达负载均衡器的只有一部分的请求了.
LVS集群的实现(概述)
首先我们来说说负载均衡集群的实现, 这里主要分成软件和硬件两个层次来说.
硬件
硬件上比较有名的就是F5 BIG-IP了, 但是价格昂贵; 还例如Citrix Netscaler, A10, Array, Redware等等.
这个不是重点了,略过了
软件
主要是 lvs, haproxy, nginx, ats(Apache traffic server). 我们可以对这些软件(当然硬件也可以)按照工作的协议层次划分:
- 传输层: LVS, haproxy(mode tcp)
- 应用层: 基本上都是反向代理: nginx, haproxy, ats
于是, 我们开始正式介绍lvs.
LVS
LVS是一个工作在四层的负载均衡器. 我们之前在说iptables的时候说过, iptables在内核中工作的框架/组件叫做netfilter, 这个LVS其实也是工作在netfilter之上的. 由于工作在内核, 所以LVS必须理解来访的请求, 从而根据规则进行转发. 正常情况下, 我们的数据包是经过预先路由到入站接着进入用户空间, 但是如果LVS, 就不会将数据包发向用户空间而直接进行FORWARD.
LVS的全称是Linux Virtual Server. 为什么叫这个名字呢? 其实就和我们理解反向代理差不多.他不提供那个服务但是对外宣称自己提供.LVS基于请求的IP和端口进行转发, 而(三层)路由器基于目的IP进行转发, (二层)交换机基于MAC地址进行转发. 所以我们也可以把LVS称为是四层交换, 或者四层路由. 当然无非都是转发.
原先我们使用iptables进行nat转发的时候, 是在PREROUTING进行DNAT, 也就是在还没来得及路由的时候就把数据包的流向进行更改. 但是LVS并不是这样玩的, 他实际上工作在INPUT上. 在INPUT链上, LVS定义了集群规则, 从而强行改变正常流向. 直接扔到特定的网卡上. 所以如果需要使用LVS, 我们需要定义好哪些端口是需要进行LVS转发的, 以及扔到哪些real server去, 这些主机有多少个, 以及挑选方式是什么等等
和iptables一样, LVS也有两个部分构成, 一个工作在内核空间, 叫做ipvs
; 一个工作在用户空间, 叫做ipvsadm
.
所以如果想要使用LVS, 前提是我们需要这个用户空间的命令行工具, 管理集群服务. LVS依附在netfilter上工作, 所以如果想使用此功能当然你要确保netfilter时启用的, 当然了LVS在内核选项中也是启用的. 怎么判断呢? 在我们编译内核的时候, 还记得生成的.config
吗? 对了, 我可以从这里面获得相关的信息.
1 | [root@VM-node1 ~]# grep -i -A 5 "ipvs" /boot/config-3.10.0-693.2.2.el7.x86_64 |
也看到了 这里使用的是CentOS7做的实验. 可以很清楚的看出来, LVS功能是开启的, 支持诸多协议: TCP, UDP, AH_ESP, ESP~ 而且他的负载均衡算法都是编译成了模块存在的.
在使用LVS之前, 我们先来看一下整个LVS的工作架构, 有关一些名词等等:
LVS的类型
然后看看LVS的类型, 有四种:
- lvs-nat: 其实就是多目标的DNAT(iptables)
- lvs-dr(direct routing直接路由)
- lvs-tun(ip tunneling)
- lvs-fullnat(扩展功能)
lvs-nat
首先我们先来看一下lvs-nat, 其实这个过程和我们之前的iptables的nat转换没什么太大的不同呀~我们来过一遍过程. 首先, 客户端CIP像向VIP发出请求, 请求报文到达VIP, 由LVS强行将报文进行修改之后送到POSTROUTING. 此时报文的源地址还是CIP, 但是目的地址RIP. 后面的real server收到请求, 将响应结果报文封装. 注意啊, 此时的报文目的地址是CIP, 而源地址是RIP. 而且RS的网关一定要是前面的director才行(否则报文送不出去啊), 接着就是正常的过程了, director走正常流程经由FORWARD, 最后从外部接口发送到目的地址CIP. 一次通信结束.
不管怎么说, 我们要在director中, (准确的说是LVS专用的), 维护一张nat转换表.
如果基于nat的方式, 显然RS和DIP应该使用私网地址, 且RS的网关需要指向DIP. 另外请求和响应报文都要经由director转发, 这样极高的负载的场景中, director将会成为系统瓶颈. diretor还要支持端口映射才行.
lvs-dr
接着我们看一下lvs-dr, 这种模型是LVS默认使用的, 在大规模场景中经常使用. 而且也是一种较难理解的模型.
这个的原理是修改请求报文的目标MAC地址进行转发. 在说这个之前, 我们要先来看一下这个模型的架构图:
这里我们把director, real server都连接到一个交换机上(此时他们都只需要一个网卡), 首先先来回忆一下用户的请求是怎么到达我们的服务器的:
首先, 用户那边将包的目的地址设置成为我们的服务器的公网IP地址, 接着经过变换, 重重路由和网关, 请求到达图中的最后一个路由, 我们知道, 每一次经过一个路由, 包的源MAC地址都会变成那个路由的MAC地址对吗? 所以当最后一个路由想要将包发送过来的时候, 这个时候转换成为本地网络通信. 也就是说从图中右边那个接口出来的包头前需要再次附着一个MAC头(源MAC和目的MAC). 源MAC就是路由接口的那个MAC了BUT. 路由器怎么知道目的MAC是多少呢? 先来想下他知道什么. 对哦, 目的IP. 所以这个时候就可以做ARP广播了, 接着只有director发出回应, 路由器得到MAC地址, 完成通信.
那么, 接着过程是这样走的, 请求发到director之后, director从和都连接在一个交换机上的RS中挑选一台发出响应, 过程结束.
也就是说:
这样的一个过程, 明确的一点是, 响应报文不会经过director了, 由RS直接发出. 这样会存在一个致命的问题. 源IP地址不是VIP了. 怎么办? 处理方法是这样的: 每一个RS和Director的地址是一样的, 都是VIP.
问题就这样解决了? 当然不是, 这会引入一个新的问题. 当路由器做ARP广播的时候, 所有的主机都给了响应, 这怎么行?
其实, 我们的解决方法是这样的: 其实每一个主机都有两个IP, 对于director来说, 他有一个VIP和一个DIP, 而每一个RS也有一个VIP, 还有一个RIP. 这样当请求到达director的时候, 他会进行ARP广播获得挑选出来的那个RIP的主机的MAC地址, 接着IP包头依然是CIP->VIP, 封装MAC帧的时候, 将目的MAC封装成挑选出来的主机的那个MAC, 交换机是而二层设备, 只能转发帧, 所以就将请求报文发向挑选好的RS, RS收到报文之后解开MAC帧, 查看内部的IP包头, 源IP: CIP, 目的IP: VIP, VIP是不是自己的IP? 是的 !所以进入用户空间进行处理~.
可能还是有点问题, 来想一想RIP, DIP和VIP能够是同一网段吗? 其实这些倒不是十分重要的问题, 我的VIP, DIP, RIP全都是公网IP都是可行的, 而且这样还方便我们做远程管理. 实际上, 我们要确保VIP, DIP以及RIP必须要在一个物理网段中, 这样director的ARP广播才可以到达啊, 另外一般情况下, 我们会把DIP和RIP规划到一个子网段中 因为这样更好理解, 但是这并不绝对, 不在同一网段也无所谓.
既然RIP和VIP不在同一网段,那么显然网关, 出口路由就不能是进来的那个了, 所以我们需要再添加一个路由器, 充当出口路由了. 挨? 可是这样不对啊?我们要求出去的响应报文的源IP一定要是VIP才行啊? 我们的主机是Linux主机, 内核会遵从一个原则: 响应报文从哪一个网卡出去, 那么源地址就一定是那个网卡的IP地址.
对于每一个RS来说, 我们的RIP是配置在物理网卡上的, 而VIP是配置在lo这样的接口上的. 因此分发下来的报文是经由物理网卡转向lo, 最终发向上层用户空间的. 接着, 响应报文默认是从物理接口出去的, 我们强行将其规定成先走lo, 在转向物理网卡出去. 这是必须的一步.
最后我们稍微总结一下dr集群的几个特点:
- 保证前端路由器将目标IP为VIP的请求报文发送到director上
- 解决方法:
- 静态绑定
- arptables
- 修改内核参数
- RS的RIP可以使用私网地址, 也可以使用公网地址
- RS和directors一定要在同一个物理网络中
- 请求报文经过director调度, 但是响应报文一定不经过director
- 不支持端口映射
- RS的网关不能指向RIP
以上就是最常见的两种集群类型啦, 接着我们再来看看剩下的:
lvs-tun
先来想想之前的两种, 他们都有一个共同的缺点, director和RS之间的距离不可能很长, 对嘛? 甚至第二种连路由器都不能连接.
而tun就可以解决这样的问题, 我们不修改请求报文的IP首部, 而是通过在原有的ip首部之外, 再封装一个ip首部. 大体的原理就是这样. 其实只要理解了之前的 理解这个tun就不难了:
其实就有点像是加强版的 lvs-dr 了. 只不过由于是走IP隧道, 所以要求director和RS需要支持IP隧道协议. RS主机依然是物理网卡RIP, lo接口VIP的模式, 接着响应的时候就会有RS直接一口气发向User.
但是这样其实也会有一个问题, 就是MTU. 当然这是所有隧道的问题, 由于包头增大(多了一个IP包头[20字节]), 导致MTU相对来说不够就会进行IP分组分片操作.
lvs_fullnat
最后一个full-nat , 就是director同时修改源地址和目标IP. 怎么搞的呢? 其实就是在进去的时候把源IP换一下, 出去的时候把目的IP和源IP都换一下.
这样的话, 就可以跨机房(director和RS不在同一个机房), 而且支持端口映射机制.
session保持
接着我们聊聊之前说过的session保持. 一般有三种session保持的方式:
- session绑定
- 就是说将来自该用户的请求全部定向到某个特定的RS上, 追踪用户的方法就是维护一个session表, 通过定时器机制来维持. 这种机制也即:
ip_hash
对来源IP进行hash之后存表. 但是现在我们有很多连接互联网的方式是使用SNAT的呀, 也就是说一个IP可能后面是几百台机器. 这样一点都不均衡了, 力度还十分粗糙. - 所以还可以使用cookie_hash. (进程级别, 应用层)
- 就是说将来自该用户的请求全部定向到某个特定的RS上, 追踪用户的方法就是维护一个session表, 通过定时器机制来维持. 这种机制也即:
- session集群
- 上面的session绑定有个严重的问题, 就是一旦RS宕机, 整个session就全部都没了.所以需要进行session复制和同步就好了每一个主机都有每一个用户的session.但是这个模式会影响性能和网络拥塞.
- session服务器
- 上面的session集群虽然可行, 但是如果电源中断, 整个集群宕掉, 所有的session还是一样全体消失. 所以我们需要进行session的持久化. 具体说来就是找一台第三方的存储服务器. 这样每一台主机都去找这个存储服务器就行了.而且我们可以使用memcache或者redis这样的键值存储. 当然缺点也是有的: 一来引入了单点, 二来会带来延迟. 所以还是要慢慢的进行新的架构或者研发.
LVS 调度器(Scheduler)
其实所谓的调度器, 就是一些算法的程序实现罢了.我们之前也看到了LVS支持的一些调度算法. 当然那些并不是全部. 我们将调度算法分成静态的方法和动态的方法. 其中静态的方法就是说仅根据算法本身进行调度. 而动态方法能够根据算法和各个RS当前的负载状态来进行调度. 显然, 静态方法能够保证起点是公平的, 而动态方法能够尽可能使得结果更加公平.
静态方法有这些:
- RR(
round robin轮调
), - WRR(
weighted RR,加权的RR
), - SH(
source hash, 实现session保持的机制会损害负载均衡的效果
), - DH(
destination hash, 对同一个目标的请求是始终发向同一RS
).
对于动态方法, 其实就是一开始直接调用算法, 接着在后面就会开始讲各个RS负载状态加入考虑. 这就引入了Overhead, 通过这个来量化RS的负载情况. 经过计算Overhead最小的主机就会优先纳入考虑.
而动态方法有这些:
- LC(
Least connection, 最小连接, 如果每一个都是0, 就自上而下: Overhead=Active * 256+Inactive
), - WLC(
Weighted LC, 加权的LC: Overhead=(Active * 256 + Inactive)/weight, 但是这样的算法会有一个缺点, 那就是由于自上而下的策略, 会使得最开始的时候(也就是0连接), 会直接选择第一台主机, 然而万一这个第一台主机就是性能最差的那一台怎么办?于是就需要下面的SED来补救.
), - SED(
Shortest Expectation Delay, 也就是最小希望延迟: Overhead=(Active+1)*256/weight. 其实就是尽量保证权重最大的来响应请求.但是这样其实也有问题
), - NQ(
Never Queue.永不排队, 十分独特, 先把权重从大到小来一遍, 接着使用SED逐渐排除所有的RS. 接着循环就行了.
), - LBLC(
Locality-Base LC, 就是动态的DH算法
), - LBLCR(
LBLC with Replication, 带复制的LBLC.两台缓存服务器互相复制.
).
LVS的调度
接下来终于要来说说如何使用ipvsadm啦~由于我们已经会写iptables了, 所以ipvsadm其实是很简单的. 无非也就是一些规则的增删查改了. 而ipvsadm也无非就是管理集群服务, 和管理集群服务中的RS.
一个ipvs主机可以同时定义多个cluster service, 这主要依靠tcp和udp的端口决定. 另外一个cluster service上至少应该由一个real server. 定义时, 只要指明lvs-type 以及lvs-scheduler就行了.
1 | # 管理集群服务 |
基本上大写选项都是在管理集群服务的, 而小写选项都是在管理集群服务中的RS. 而且这个玩意也有一个save和一个restore.
构建LVS集群
终于开始了, 来亲手构造一个nat和dr模型的集群吧.
NAT模型
首先来看一下我们的实验拓扑图:
由于使用Vmware, 所以这里使用建立不同的虚拟网络来做隔离. 信息图中已经很明白了,
现在先来是构建环境, 先要把网络拓扑搭建完成.
接着就开启后端两个服务器的httpd服务, 并且配置默认页面(先设置成为不同的可区分的页面).
接着清空所有iptables规则. 接着使用director去访问后面的两个节点.
1 | [root@VM-node1 ~]# curl http://192.168.20.7 |
接着我们要确认核心转发功能是开启的:
1 | [root@VM-node1 ~]# cat /proc/sys/net/ipv4/ip_forward |
接下来就简单了, 请看:
1 | [root@VM-node1 ~]# ipvsadm -A -t 172.16.100.9:80 -s rr |
接下来访问试试: ( 因为我担心会有缓存的影响, 所以使用本机的shell来跑 )
1 | C:\Users\lenovo |
可以看到, 交替出现node2和node3, 这就是rr算法, 轮询. 接着我还用ab跑了一个简单的1000次访问:
1 | [root@VM-node1 ~]# ipvsadm -L -n |
可以看到基本上平摊了请求. 其实, 当我们进行ipvsadm的停止操作的时候, 默认service unit会执行保存操作, 而开启时会进行重读上次保存的配置的操作. 但是终究没有我们手动保存来的靠谱:
1 | [root@VM-node1 ~]# ipvsadm -S > ~/ipvsadm |
和iptables基本上是一样的, 但是这个地方, 建议在保存的时候 加上-n参数, 这样就不用进行反向解析了. 而Unit就是这样做的:
1 | [root@VM-node1 ~]# cat /usr/lib/systemd/system/ipvsadm.service |
接下来我们试试进行更改功能, 这里就使用另一种调度算法(SH)来试试:
1 | [root@VM-node1 ~]# ipvsadm -E -t 172.16.100.9:80 -s sh |
接着访问就会起到效果了.
1 | C:\Users\lenovo |
更改集群服务中的rs也很简单, 就不做演示了.
接着我们把他们删除, 顺便过一遍删除操作:
1 | [root@VM-node1 ~]# ipvsadm -d -t 172.16.100.9:80 -r 192.168.20.7:80 |
除了-n参数是不进行解析之外, 还有这些选项:
1 | [root@VM-node1 ~]# ipvsadm -L --stats -n |
以及显示连接:
1 | [root@VM-node1 ~]# ipvsadm -L -n -c |
还可以使用–rates显示速率:
1 | [root@VM-node1 ~]# ipvsadm -L -n --rate |
这里补充一个https负载均衡的小提示:
如果在这个情况下使用https的话, 要确保后面的Web server的证书是同一个, 私钥也是同一个.
DR模型
下面就来看一下DR模型的实现吧. 会比我们的LB模型复杂一些, 在展示我们的实验拓扑图之前, 我们先来看一下, 之前提到的内核参数:
- arp_announce: 该选项改变内核对于arp分组的请求, 通告
- arp_ignore: 该选项改变内核对于arp分组的接受
arp_announce这个选项有三个值: 0, 1, 2. 默认值是0, 表示将自己所有的IP进行通告. 显然我们不能使用这个值. 1表示尽量避免不把本机除了接口网段以外的网络进行通告, 但是这是尽量. 所以也不启用. 最后是2这个值, 意味, 始终使用最佳本地地址. 这才是我们所使用的, 不将自己的lo上的IP通告出去.
arp_ignore有很多值, 我们来看看. 首先还是默认值0, 就是如果接收到自己本地的IP的查询就提供. 接着是1, 只有询问到的地址正是自己配置在这个接口上的地址的时候, 才进行回应. 这正是我们所需要的. 还有2-8, 不过既然都找到了合适的值, 就略了.
最终, 我们需要: arp_ignore = 1, arp_announce = 2.
还是惯例, 我现在先去配置环境啦~ 这里关于虚拟接口的设定在之前的网络配置中也是说过的.
但是这里要插一句, 我们是应该先配置real server的lo接口的IP地址呢? 还是先进行参数配置呢? 当然是先配置参数啦. 原因很简单的.
1 | [root@VM-node2 ~]# echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore |
配置完成了之后, 找一台位于同于网络的主机( 如果你桥接的话, 就使用物理机就行了. ), 分别去ping一下director的vip和dip. 不出意外的话, 查看arp表的时候, MAC应该是一样的:
接下来就可以开启Web服务了, 并且这里要使用rip才行.
1 | [root@VM-node1 ~]# curl http://192.168.206.21 |
接下来就简单了, 和之前差不多了, 但是部分参数改一下就好了:
1 | [root@VM-node1 ~]# ipvsadm -A -t 192.168.206.10:80 -s rr |
接着就可以测试了:
接着…你就会发现, 奇怪, 怎么会连接不到服务器?
原因很简单, 因为响应报文没有经过lo接口啊~所以要加上路由:
1 | [root@VM-node2 ~]# route add -host 192.168.206.10/32 dev lo:0 |
接着就可以了: ( 注意iptables )
LVS的防火墙标记和Session绑定
我们之前是不是漏了一个-f参数呀? 当时说这个是叫做防火墙标记的东西. 其实在我们的数据包到达LVS之前, 他还是经过了PREROUTING的. 而且我们还说过, LVS是工作在INPUT上的, 所以也就是说, LVS发生在PREROUTING之后, 这样我们还可以通过在PREROUTING上对某个特定的数据包进行mangle, 也就是拆开之后打上标记再安装回去, 这个标记就是防火墙标记. 在iptables的扩展TARGET中, 有一个就叫做MARK
该扩展能用来实现对报文加上标记从而实现诸多辨识功能.
1 | MARK |
这样的话, 我们在定义规则的时候就可以更加灵活, 而且有的时候还可以进行规则的合并, 比如:
1 | PREROUTING: |
当我们把http(80)和https(443)都打上10的标记的时候, 我们就可以一起来写规则, 而不需要一次是vip:80, 一次是vip:443了.
做一下这个小实验吧~ 接着在观察结果时会出现新的问题:
Session保持的问题, 怎么会说到这个? 因为我们
使用SH调度算法的时候, 你会发现在访问http和https的时候还是不会绑定在一个real server的.
那么现在就来解决这个问题, 我们可以使用LVS的持久连接功能来解决, 对于多个共享同一组RS的服务器, 我们进行Session绑定.
这个持久连接功能, 可以确保在一段特定的时间内, 不论使用什么调度算法, 都可以使得同一个客户端访问到第一次被定向的RS上. 与iptables类似, 这个功能也需要一个持久连接模板, 该模板独立于算法本身 ,像这样:
1 | srcIP rs timer |
如果该模板中没有记录就直接带入算法. 如果能够查看到记录存在, 就会直接调度至该RS.
声明一个持久连接很简单, 直接加上一个-p参数就行了: [ 后面指定时长, 如果不指定就是默认的360s ]
1 | [root@VM-node1 ~]# ipvsadm -A -t 192.168.206.10:80 -s rr -p 30 |
实施之后的效果:
1 | [root@node2 ~]# curl 192.168.206.10 |
对于这个持久连接, 我们有多种实现方式:
- 每端口持久, PPC, 单服务持久调度
- 每FWM持久, PFWMC, 防火墙标记持久: 只要你的标记一样 那么就是统一调度的
- 每客户端持久: PCC, 单客户端持久调度
- director会将用户的任何请求都识别成集群服务, 并且向RS中进行调度.
最后一个其实最好实现, 只要把一开始的端口设置成0就行了, 也就是所有服务之意. 每端口持久已经在之前演示过了, 那么现在还剩一个FWM持久. 其实就是在定义规则的时候指定标记,并且加上-p参数指明要进行持久化就行了.
LVS的HA解决方案
接下来我们再来聊聊LVS集群的高可用解决方案.
事实上, 通过之前的几次实验, 我们也可以看出来, LVS的隐患之一就是我们的director是SPOF, 也就是 单点故障所在(Single Point Of Failure). 当然了, 现在的所谓解决方案只是简单的说说一说, 以后会多次聊这个话题. 毕竟现在也快要万字了, 再扯下去也不太合适了. 所以就简单的介绍下:
首先从我们的director来看, 他是整个系统的关键. 首先我们为了做高可用, 说白了也就是做冗余. 首先就想到双份, 当然这是要花价钱的. 除了这个, 还有一种就是缩短平均修复时间, 但是这样需要一个技能过硬的运维攻城狮才行, 而且使用人来做这样的事情其实是不明智的, 我们应该选择自动化的方式进行. 这是前端主机.
另外, 如果我们的后端real server出现问题, 首先还是做双份, 但是这样就会变成整个的双份. 还可以再开一只主机, 同来充当冗余, 只要有主机宕掉, 就用它来顶替. 但是这样是需要考虑数据的存储问题. 还可以考虑director进行后端主机的健康状态检查, 只要发现有主机出现问题, 告警并且将该主机从调度列表中剔除, 完成自动的上下线. 那么, 如何进行健康状态检查呢? 首先我们想到了ping, 但是这样不能说明服务是OK的, 那么就考虑进行端口扫描, 比如使用nmap这样的来扫描, 但是端口响应了, 也不能说资源是OK的, 所以干脆直接对资源进行请求好了, 由此, 我们可以从IP层, 传输层, 应用层进行检查. 检查层次越高, 消耗越大, 但是精度也越高, 这个就要根据具体的业务来说了.
除了检查的方式 我们还需要考虑检查的频度以及状态判断. 多少时间检查一次呢? 如何才会判定一个主机是宕机了呢? 会出现误判断呢?
就先这样啦.