孙宇的技术专栏 大数据/机器学习

高可用架构

2013-08-06

阅读:


高可用架构

高可用HA(High Availability)是分布式系统架构设计中必须考虑的因素之一,它通常是指通过设计减少系统不能提供服务的时间。

假设系统一直能够提供服务,我们说系统的可用性是100%。

如果系统每运行100个时间单位,会有1个时间单位无法提供服务,我们说系统的可用性是99%。

很多公司的高可用目标是4个9,也就是99.99%。这就意味着,系统的年停机时间为8.76个小时。

在小型公司或者项目初期,通常都是单点设计,一旦出现故障就直接停止服务了,应该尽量在系统设计的过程中避免单点。高可用保证的原则是“集群化”,或者叫“冗余”。挂了还有其他备机能够顶上。

有了冗余之后,还不够,每次出现故障需要人工介入恢复势必会增加系统的不可服务实践。所以,又往往是通过“自动故障转移”来实现系统的高可用。

于是我们就得出结果:通过 冗余 + 自动故障转移 实现高可用

常见架构

常见的互联网服务架构是分层的:负载均衡、WEB服务、缓存、数据库。在各层,我们都可以进行各自的高可用架构。

负载均衡

负载均衡通常通过 LVS 或者 nginx 实现。如果我们要实现冗余,我们则需要多个负载均衡的服务器,并且需要一个虚拟IP。而自动故障切换则借助 keepalived 来实现。

WEB 服务

服务层通常是用集群的方式,在负载均衡这一层进行存活的检测。在反向代理的过程中,通过负载的策略,将请求转移到对应的机器上。对于挂掉的服务器,负载均衡这一层将它从集群中移出。

缓存

常用缓存有 memcached, redis。

memcached 支持分布式。采用的是一致性哈希的方式,我们需要自己实现冗余和请求的转发。

redis则有主/从的机制,同时也支持分布式集群。redis官方也有sentinel哨兵机制,来做redis的存活性检测。当redis主挂了的时候,sentinel能够探测到,会通知调用方访问新的redis,整个过程由sentinel和redis集群配合完成。

数据库

数据库这一核心层通常是读写分离、主从结构以及在这基础上更复杂的多主从同步及中继中从等方式。

故障转移也可以借助 keepalived。

核心概念

keepalived

Keepalived 有一个服务器列表,当它检查到有机器有故障时,就将它从列表中去除,正常后再加到列表中。这些都是全自动的,人工只需要修复坏的机器即可。

虚拟 IP

现在已经拥有一个公网 IP. 我们要访问一台服务器上的资源,只需要将该 IP 绑定到该台服务器上,然后进行资源的映射。

虚拟 IP 技术是指在 N 台机器上都绑定该 IP ,然后通过其它软件去控制将请求导到哪台机器上。不管到哪台机器上,因为它已经绑定了该IP的,请求都能正常响应。

由于 TCP/IP 数据传输时,数据包里包含了 IP, MAC, 内容等数据。而 MAC 是一致的,所以要实现请求的分组(即 虚拟 IP 动态转移), 需要对 TCP/IP 包的内容进行修改。

Keepalived 即可实现该功能。

对用户来说是和同一个IP打交道,实际上背后是N台机器。

Session 同步

session 是由应用服务器维持的一个服务器端的存储空间,用户在连接服务器时,服务器会生成一个唯一的 SessionId, 做为一个 key, 它对应的 value 存在服务器上。

SessionId 是存在用户浏览器的 Cookie 中的,每次请求页面,都会将该值传到服务器。服务器去 session 存储空间查找该值对应的值,来进行一些身份验证,数据读取。所以你在浏览器上登录后,若清空 Cookie, 就要重新登录了。因为这时候没有 SessionId 传过去,服务器认为你是新用户在访问。

当我们用集群来解决访问压力时,可能会出现如下情况:

将整体服务划分成小块服务,然后放在二级域名中。(新浪新闻,新浪星座等都是单独的二级域名)部署到多台服务器上。多个频道共享一组服务器。

而在 PHP 中,session 默认是以文件的形式保存在本地服务器的磁盘上的(当然也可以选择存在 DB 中).这样的结果就是,SessionId 在不同机器上不一致,用户可能会访问的时候不停的登录。因为服务器不能去访问其它机器上的 Session.为了解决这种情况方法就产生了:

  1. 将 Session 的数据加密后存在 Cookie 中. 这样很省事,但由于每次请求都会提交 Cookie 中的内容,所以这样会占用一定的带宽,而且由于 http 请求头长度的限制,Cookie 中存放不了太多的东西。另外还需要进行加密解密,开销也是不小的.

  2. 基于数据库的 Session 共享 将 Session 存在 DB 中,各服务器就可共享了。通常选用内存表 Heap,以提高 Session 操作的读写效率。缺点是数据库的性能可能会成为站点的瓶颈。同时,Session 的变更频繁,要选用行级锁的引擎。并且要注意删除超时的 Session

  3. 基于 Memcache 的 Session 共享 Session 通过 SessionId 来关联存储的形式和 Memcache 完全吻合,而且 Memcache 就有数据过期机制,而且这货效率高。

总结: Memcache 和 MySQL 虽然都可以解决,但实际上,它们的效率也会成为整个系统的瓶颈。所以,最后不要出现需要 Session 共享的情况,也就是,和用户通信的,尽量保持在一台机器上。

会话保持

在典型的电子商务系统,或需要进行用户身份认证的在线系统中。一个客户要与服务器连着几次交互才能完成一笔交易或一个请求。对于这样的几个请求,负载均衡的时候就不能把它分散到不同的服务器上了。这种会话保持机制的实现:

Nginx 的实现:

upstream backend{
ip_hash;
server 192.168.1.10:80 weight=4 max_fails=2 fail_timeout=30s;
server 192.168.1.11:80 weight=2 max_fails=2 fail_timeout=30s;
}
upstream 模块的 ip_hash 机制能够将某个IP的请求定向到同一台服务器上。

location /
{
# 如果后端服务器返回 502,504,超时等.将自动把请求转发到 upstream 池中的另一台服务器中实现故障转移
proxy_next_upstream http_502 http_504 error timeout invalid_header
proxy_pass http://backend;

proxy_set_header Host www.mydomain.jp
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
}

如果不加 proxy_set_header, 直接服务器上接收到请求的日志中会发现IP全是代理机器的IP。加上后会是用户实际的IP.

可参照: http://blog.sina.com.cn/s/blog_5f54f0be0100zvf5.html

VRRP(虚拟路由冗余协议)

对多台机器的默认网关进行冗余备份,当其中一台路由设备挂了时,备份路由接管转发工作。

原理: TPC/IP 协议中,设备之间不直接通信,它们通过自己所在网络的路由器,将数据包不停的转向下一个路由器,直到找到目的设备所在网络的路由器。而下一个路由是谁,有两种方法来知道:

  1. 动态学习;但需要每个路由器都支持这种形式,不现实。
  2. 静态配置;每个路由器上都存一份静态路由表,数据传输时,会从表中查出该IP对应的路由器地址,然后把包传过去。如果没有对应的,就会传到默认网关(默认的下一个路由器地址).

用静态方式的问题:如果有一个路由器坏了,那么所有将该路由器作为下一站或者作为默认网关的通信就中断了。即使自己配置了多个默认网关,如果不重启路由器也是无法生效的。

虚拟路由冗余协议就是用来避免静态指定网关的缺陷的。

VRRP 中有两组重要概念: VRRP 路由器(实体)和虚拟路由器; 主路由器和备份路由器。

一组VRRP路由器实体协同工作,就形成了虚拟路由器。该虚拟路由器对外来看是一个固定IP,一个固定MAC。

VRRP 组中有一个主路由器和一个或多个备份路由器。VRRP 会选择一台作为主的,负责响应和转发IP数据包。其它备份路由器处在等待状态。

如果主路由器发生故障,备份路由器会接管主路由器功能,不用改变IP和MAC。

一个VRRP路由器有唯一的标识,VRID(虚拟路由ID),范围是 0-255(不含 255), 所以,该组中最多 255 台主备。

该路由器对外表现的是唯一的虚拟MAC地址,格式是: 00-00-5E-00-01-{VRID} 不管谁当主路由器,该MAC都不再变。

VRRP 组之间通过 VRRP 控制报文进行通信。采用 IP 多播数据包,且只有主路由才能周期性的发送 VRRP 通告报文。

备份路由器连续三个通告周期收不到 VRRP 通告或收到的通告是优先级为 0 ,就会在所有的备份路由器中选一个优先级最高的作为主的。

优先级为 0 的情况是该路由器被设置成在VRRP组之外。所以实际上 VRID 是 1 - 254.

虚拟服务器: VS / NAT

实体 IP: 在网络的世界里,为了要辨识每一部计算机的位置,因此有了计算机IP 位址的定义。一个IP 就好似一个门牌!

例如,你要去微软的网站的话,就要去『207.46.197.101 』这个IP 位置!这些可以直接在网际网络上沟通的IP 就被称为『实体IP 』了。

虚拟 IP

不过,众所皆知的,IP位址仅为 xxx.xxx.xxx.xxx 的资料型态,其中, xxx为 1-255间的整数,

由于近来计算机的成长速度太快,实体的IP 已经有点不足了,好在早在规划IP 时就已经预留了三个网段的IP 做为内部网域的虚拟IP 之用。这三个预留的IP 分别为:

A级: 10.0.0.0 - 10.255.255.255 B级: 172.16.0.0 - 172.31.255.255 C级: 192.168.0.0 - 192.168.255.255

上述中最常用的是192.168.0.0这一组。

不过,由于是虚拟 IP,所以当您使用这些地址的时候,当然是有所限制的,限制如下:

  1. 私有位址的路由信息不能对外散播
  2. 使用私有位址作为来源或目的地址的封包,不能透过Internet来转送

当内部网络要访问外部的时候,就需要将内部地址转换成外网可用的地址,即:网络地址转换 Network Address Translation, NAT.

每次请求的报文头(目标地址,源地址,端口)都被正确改写。外部用户访问该网络时, 请求会被转到内部的某个 IP 上,然后报文的数据被改写。

目标机器处理完后把报文传给进行网络地址转换的设备(通常是路由器),这时候请求目标地址是该设备,然后返回的报文的报文头被改写成当前发起请求的地址.

这次交互完成.所以,每次请求都要在地址转换设备上中转一次。它的性能就会成为瓶颈。 我们的路由器就是用来做这个的。

我们的路由器的 IP 就是我们这个内部网络所有机器的外部 IP。

我们可以做如下设置: 将该路由器上所有的 80 端口的请求都转到内网的 192.168.2.222 (虚拟 IP)上。也可以转到其他任何一台连接到路由器上的机器上.

我们可以进行如下设置:

  1. 路由器将请求轮流解析到内部机器 A, B的内部 IP 上。[四层轮询的 LVS]
  2. 路由器将请求解析到 A 上,A, B 互相监视, 如果 A 挂了, B 将自己的 IP 设置成 A 之前的 IP。 这样可继续服务。[主 / 备]

这种办法的好处是用IP协议的,所以只要是支持TCP/IP的操作系统,只要一个公网IP放在调度机器上就成了,服务器组用私有IP。

不足的是,调度机器会承受很大的压力,因为每次请求和响应都会经过它。

虚拟服务器: VS / TUN

IP 隧道

大多数请求都有这种规律:请求报文较短,而响应很短。如平日我们看网站:提交的数据很少,但服务器返回的数据很多。

这时候想到的就是:将请求和响应分开。负载均衡的设备只负责响应请求,转发。而响应则直接返回给客户,不经过它。

调度器根据各个服务器的负载情况,动态地选择一台服务器,将请求报文封装在另一个IP报文中,再将封装后的IP报文转发给选出的服务器;

服务器收到报文后,先将报文解封获得原来目标地址为VIP的报文,服务器发现VIP地址被配置在本地的IP隧道设备上,所以就处理这个请求,然后根据路由表将响应报文直接返回给客户。

在这里,请求报文的目标地址为VIP,响应报文的源地址也为VIP,所以响应报文不需要作任何修改,可以直接返回给客户,客户认为得到正常的服务,而不会知道是哪一台服务器处理的。

这种方式解放了调度器,它只负责选择真实服务器,然后进行IP封装,转发。同样,它的问题就在于,使用它就必须支持 IP 隧道协议。

虚拟服务器: VS / DR

调度器和服务器组都必须在物理上有一个网卡通过不分段的局域网相连,即通过交换机或者高速的HUB相连,中间没有隔有路由器。

VIP地址为调度器和服务器组共享,调度器配置的VIP地址是对外可见的,用于接收虚拟服务的请求报文; 所有的服务器把VIP地址配置在各自的Non-ARP网络设备上,它对外面是不可见的,只是用于处理目标地址为VIP的网络请求。

调度器根据各个服务器的负载情况,动态地选择一台服务器,不修改也不封装IP报文,而是将数据帧的MAC地址改为选出服务器的MAC地址,再将修改后的数据帧在与服务器组的局域网上发送。

因为数据帧的MAC地址是选出的服务器,所以服务器肯定可以收到这个数据帧,从中可以获得该IP报文。 当服务器发现报文的目标地址VIP是在本地的网络设备上,服务器处理这个报文,然后根据路由表将响应报文直接返回给客户。

「Non-ARP 说明」arp (address resolution protocol),主要是确认网卡的物理地址用的,在三层是 ip,到了二层要通过 arp 协议确认哪个ip和哪个物理地址的对应关系,

如果一个网卡没有mac地址那么,这个网卡配置的ip就不会被外界知道,一般这样的ip只用于内部交流用我们称之为 Non-ARP,没有mac地址的网卡一般也只有本地的loopback 口,

在 lvs 的 Non-ARP 就是这个含义—- 没有 MAC 地址的网卡与 IP 只能在本机内做回环地址,不被外界所见。

和上面 VS/TUN 相比它不用 IP 隧道,通用性更强.但是要求真实服务器和调度器都有一块网卡,且连在同一物理网段上。

真实服务器的网卡不做 ARP 响应. 这种方式目前是最流行的。

那么,如果我们没有在同一个机房,连在一起的这么多机器,就可以选择 VS/TUN 或 VS/NAT,如果机器少,几台,或者不超过二十台,带宽足够的情况下,可以用 VS/NAT, 如果机器多,那就得用 VS / TUN 了。

案例

Nginx + Keepalived

两台 Nginx 通过 Keepalived 管理,作为代理。当然,也可以用 LVS + Keepalivd 但 Nginx 对高并发的处理及分发已经很优秀了,而且配置简单。

Nginx 中采用 ip_hash 进行会话保持。省去了存入 MySQL 或 Memcached 的开销。

Nginx_Master: 192.168.1.103 提供负载均衡 Nginx_BackUp: 192.168.1.104 负载均衡备机

Nginx_VIP_TP: 192.168.1.108 网站的 VIP 地址(虚拟 IP)

Real_Server1: 192.168.1.106 提供 WEB 服务 Real_Server2: 192.168.1.107 提供 WEB 服务

安装 Nginx (省略) http://blog.sina.com.cn/s/blog_5f54f0be0100yqm7.html

安装 Keepalived

地址: http://www.keepalived.org/download.html

wget http://www.keepalived.org/software/keepalived-1.2.7.tar.gz
tar -zxf keepalived-1.2.7.tar.gz
cd keepalived-1.2.7
./configure --sysconf=/etc --prefix=/usr/local/keepalived --with-kernel-dir=/usr/src/kernels/2.6.32-358.2.1.el6.x86_64/

./configure --sysconf=/etc --prefix=/usr/local/keepalived --with-kernel-dir=/usr/src/kernels/2.6.32-358.6.2.el6.i686/

–sysconf 指定了配置文件的地址.即:/etc/keepalived/keepalived.conf

–prefix 指定了安装目录

–with-kernel-dir 指定使用内核源码中的头文件,即 include 目录.只有使用 LVS 时才需要这个参数,其它的时候不需要。

报错:

configure: error: Popt libraries is required

解决:

yum install popt-devel

再 configue .成功后提示:

Keepalived configuration
------------------------
Keepalived version       : 1.2.7
Compiler                 : gcc
Compiler flags           : -g -O2
Extra Lib                : -lpopt -lssl -lcrypto
Use IPVS Framework       : Yes
IPVS sync daemon support : Yes
IPVS use libnl           : No
Use VRRP Framework       : Yes
Use VRRP VMAC            : Yes
SNMP support             : No
Use Debug flags          : No

然后安装:

make
make install

设置成为服务并开机自动启动:

cp /usr/local/keepalived/sbin/keepalived /usr/sbin/

/etc/rc.d/init.d/keepalived status
chkconfig --add keepalived
chkconfig keepalived on

设置主机上的配置文件内容:

vi /etc/keepalived/keepalived.conf

! Configuration File for keepalived

global_defs {
   notification_email {
     sunyu@easymobi.cn
     wuxuegang.123@163.com
   }
   notification_email_from pub@easymobi.cn
   smtp_server 127.0.0.1
   smtp_connect_timeout 30
   router_id LVS_DEVEL
}

vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 51
    # 此处是主 Nginx 的 IP 地址.
    mcast_src_ip 192.168.1.103
    # 该机的 priority(优先) 为 100
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111222
    }
    virtual_ipaddress {
        192.168.1.108
    }
}

前面的结构那里已经规定好了 VIP 和 主备机的 IP, 所以这里按上面的填。

备机的配置文件:

! Configuration File for keepalived

global_defs {
   notification_email {
     sunyu@easymobi.cn
     wuxuegang.123@163.com
   }
   notification_email_from pub@easymobi.cn
   smtp_server 127.0.0.1
   smtp_connect_timeout 30
   router_id LVS_DEVEL
}

vrrp_instance VI_1 {
    state SLAVER
    interface eth0
    virtual_router_id 51
    # 此处是备 Nginx 的 IP 地址.
    mcast_src_ip 192.168.1.104
    # 该机的 priority(优先) 为 99
    priority 99
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111222
    }
    virtual_ipaddress {
        192.168.1.108
    }
}

这时候 ping 192.168.1.108 是不通的.

然后在两台机器上分别启动 keepalived 服务。这时候再 ping 192.168.1.108 .通了.

实际上这时候 108 是被绑到主机上的。在主机上: 查看系统日志:

tailf /var/log/messages

May 29 18:32:16 localhost Keepalived_vrrp[27731]: Opening file '/etc/keepalived/keepalived.conf'.
May 29 18:32:16 localhost Keepalived_vrrp[27731]: Configuration is using : 62906 Bytes
May 29 18:32:16 localhost Keepalived_vrrp[27731]: Using LinkWatch kernel netlink reflector...
May 29 18:32:16 localhost Keepalived_healthcheckers[27729]: Using LinkWatch kernel netlink reflector...
May 29 18:32:16 localhost Keepalived_vrrp[27731]: VRRP sockpool: [ifindex(2), proto(112), fd(11,12)]
May 29 18:32:17 localhost Keepalived_vrrp[27731]: VRRP_Instance(VI_1) Transition to MASTER STATE
May 29 18:32:18 localhost Keepalived_vrrp[27731]: VRRP_Instance(VI_1) Entering MASTER STATE
May 29 18:32:18 localhost Keepalived_vrrp[27731]: VRRP_Instance(VI_1) setting protocol VIPs.
May 29 18:32:18 localhost Keepalived_vrrp[27731]: VRRP_Instance(VI_1) Sending gratuitous ARPs on eth0 for 192.168.1.108
May 29 18:32:18 localhost Keepalived_healthcheckers[27729]: Netlink reflector reports IP 192.168.1.108 added

可以看到.VRRP(虚拟路由冗余协议)已经启动.我们可以通过命令 ip addr 来检查主 Nginx 上的 IP 分配情况.

[root@localhost ~]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000
    link/ether 00:15:c5:ef:53:8c brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.103/25 brd 192.168.1.255 scope global eth0
    inet 192.168.1.108/32 scope global eth0
    inet6 fe80::215:c5ff:feef:538c/64 scope link
       valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
    link/ether 00:15:c5:ef:53:8e brd ff:ff:ff:ff:ff:ff

可以看到 VIP 地址已经绑定到主 Nginx 机器上: inet 192.168.1.108/32 scope global eth0

我们通过 tcpdump 抓包:

[root@localhost ~]# tcpdump vrrp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
13:38:27.797982 IP htuidc.bgp.ip > vrrp.mcast.net: VRRPv2, Advertisement, vrid 51, prio 100, authtype simple, intvl 1s, length 20
13:38:28.794693 IP htuidc.bgp.ip > vrrp.mcast.net: VRRPv2, Advertisement, vrid 51, prio 100, authtype simple, intvl 1s, length 20
13:38:29.794518 IP htuidc.bgp.ip > vrrp.mcast.net: VRRPv2, Advertisement, vrid 51, prio 100, authtype simple, intvl 1s, length 20
13:38:30.798581 IP htuidc.bgp.ip > vrrp.mcast.net: VRRPv2, Advertisement, vrid 51, prio 100, authtype simple, intvl 1s, length 20
13:38:31.795902 IP htuidc.bgp.ip > vrrp.mcast.net: VRRPv2, Advertisement, vrid 51, prio 100, authtype simple, intvl 1s, length 20
13:38:32.804050 IP htuidc.bgp.ip > vrrp.mcast.net: VRRPv2, Advertisement, vrid 51, prio 100, authtype simple, intvl 1s, length 20
13:38:33.801191 IP htuidc.bgp.ip > vrrp.mcast.net: VRRPv2, Advertisement, vrid 51, prio 100, authtype simple, intvl 1s, length 20
13:38:34.798793 IP htuidc.bgp.ip > vrrp.mcast.net: VRRPv2, Advertisement, vrid 51, prio 100, authtype simple, intvl 1s, length 20

发现:优先级高的一方(prio 100) 通过 VRRPv2 获得 VIP 地址。该 VRRPv2 包的发送极有规律,一秒发送一次(可配置)。

这样,一个 Nginx + Keepalived 的架构就完成了。接下来可以完善一下,加上实时监控,如果发现负载均衡的 Nginx 出现问题,就将该机器上的 Keepalived 服务停掉。

nginx_check.sh:

#!/bin/bash
while :
do
nginxpid = 'ps -C nginx --no-header | wc -l'
if[ $nginxpid -eq 0 ];then
	service nginx start
	sleep 3
	nginxpid = 'ps -C nginx --no-header | wc -l'
	echo $nginxpid
	if[ $nginxpid -eq 0 ];then
		service keepalived stop
	fi
fi
sleep 3
done

然后让该脚本一直在后台运行:

nohup /etc/nginx_check.sh

或者将它添加成服务,让它开机自启动: http://blog.sina.com.cn/s/blog_5f54f0be0101b3bs.html

测试:

在两台机器的 web 服务器上分别放一个 index.html, 里面内容分别是自己机器的IP. 通过VIP访问:

http://192.168.1.108/index.html 发现显示的是主机的IP.

此时,关掉主机的 nginx, 这时候由于上面的监控脚本。主机的 keepalived 也会关闭。这时候再访问上面地址,发现显示的是备机的IP。可见,切换成功。

LVS + Keepalived

LVS 是通过 IPVS 模块来实现的。IPVS是LVS集群的核心,主要用于完成用户的请求到达负载调度器后,如果将请求发送到每个真实服务器节点上的,服务器如何返回数据给用户。

要验证是否支持,用如下命令:

[root@localhost keepalived]# modprobe -l | grep ipvs
kernel/net/netfilter/ipvs/ip_vs.ko
kernel/net/netfilter/ipvs/ip_vs_rr.ko
kernel/net/netfilter/ipvs/ip_vs_wrr.ko
kernel/net/netfilter/ipvs/ip_vs_lc.ko
kernel/net/netfilter/ipvs/ip_vs_wlc.ko
kernel/net/netfilter/ipvs/ip_vs_lblc.ko
kernel/net/netfilter/ipvs/ip_vs_lblcr.ko
kernel/net/netfilter/ipvs/ip_vs_dh.ko
kernel/net/netfilter/ipvs/ip_vs_sh.ko
kernel/net/netfilter/ipvs/ip_vs_sed.ko
kernel/net/netfilter/ipvs/ip_vs_nq.ko
kernel/net/netfilter/ipvs/ip_vs_ftp.ko

如果类似上面,表示支持。 上面列出的是LVS支持的算法。如:

  • rr 表示分发的时候是轮询;Round Robin
  • wrr 表示加权轮询;Weight Round Robin
  • lc 表示最少连接,least connection
  • wlc 加权最少连接,Weighted Least-Connection
  • dh 目标地址哈希
  • sh 源地址哈希
  • sed 最少希望延迟
  • nq 永不排队
  • lblc 局部性最少连接,Locality-Based Least Connections

CentOS 安装:

yum install ipvsadm

源码安装:

下载地址: http://www.linuxvirtualserver.org/software/ipvs.html

不同 kernel 版本要下载的版本不一样。查看kernel 版本:

uname -a

我这边版本是 2.6.32 所以就下载 1.26 版的:

wget http://www.linuxvirtualserver.org/software/kernel-2.6/ipvsadm-1.26.tar.gz
tar -zxf ipvsadm-1.26.tar.gz
cd ipvsadm-1.26
make

报错:

make[1]: *** [libipvs.o] Error 1
make[1]: Leaving directory `/usr/src/ipvsadm-1.26/libipvs'
make: *** [libs] Error 2

安装完以下这些软件

[root@host2 ipvsadm-1.26]# rpm -qa | grep popt
popt-1.13-7.el6.x86_64
popt-devel-1.13-7.el6.x86_64
[root@host2 ipvsadm-1.26]# rpm -qa | grep libnl
libnl-1.1-14.el6.x86_64
libnl-devel-1.1-14.el6.x86_64

分别用 yum 安装

yum install libnl-devel-1.1-14.el6.x86_64

再 make, 又报:

ipvsadm.o: In function `parse_options':
/usr/src/ipvsadm-1.26/ipvsadm.c:432: undefined reference to `poptGetContext'
/usr/src/ipvsadm-1.26/ipvsadm.c:435: undefined reference to `poptGetNextOpt'
/usr/src/ipvsadm-1.26/ipvsadm.c:660: undefined reference to `poptBadOption'
/usr/src/ipvsadm-1.26/ipvsadm.c:502: undefined reference to `poptGetNextOpt'
/usr/src/ipvsadm-1.26/ipvsadm.c:667: undefined reference to `poptStrerror'
/usr/src/ipvsadm-1.26/ipvsadm.c:667: undefined reference to `poptBadOption'
/usr/src/ipvsadm-1.26/ipvsadm.c:670: undefined reference to `poptFreeContext'
/usr/src/ipvsadm-1.26/ipvsadm.c:677: undefined reference to `poptGetArg'
/usr/src/ipvsadm-1.26/ipvsadm.c:678: undefined reference to `poptGetArg'
/usr/src/ipvsadm-1.26/ipvsadm.c:679: undefined reference to `poptGetArg'
/usr/src/ipvsadm-1.26/ipvsadm.c:690: undefined reference to `poptGetArg'
/usr/src/ipvsadm-1.26/ipvsadm.c:693: undefined reference to `poptFreeContext'
collect2: ld returned 1 exit status
make: *** [ipvsadm] Error 1

解决:

wget http://mirror.centos.org/centos/6/os/x86_64/Packages/popt-static-1.13-7.el6.x86_64.rpm
rpm -ivh popt-static-1.13-7.el6.x86_64.rpm

然后再:

make
make install
ipvsadm --help

如果有对应的帮助信息表示安装成功了

配置 LVS 集群

配置 LVS 集群可以通过 ipvsadm 命令进行

这里写一个 shell 脚本去配置。在代理服务器主服务器上运行脚本 lvs.sh。绑定 VIP,设定 LVS 工作模式.

要注意,这里绑定 IP 是绑定在 eth0 上的,是要对外公开的.

#!/bin/bash
SNS_VIP=192.168.2.118
SNS_RIP1=192.168.2.119
SNS_RIP2=192.168.2.35

/etc/rc.d/init.d/functions

logger $0 called with $1
case "$1" in

	start)
	/sbin/ipvsadm -set 30 5 60
	/sbin/ifconfig eth0:0 $SNS_VIP broadcast $SNS_VIP netmask 255.255.255.0 broadcast $SNS_VIP up
	/sbin/route add -host $SNS_VIP dev eth0:0
	/sbin/ipvsadm -A -t $SNS_VIP:80 -s wlc -p 120
	/sbin/ipvsadm -a -t $SNS_VIP:80 -r $SNS_RIP1:80 -g -w 1
	/sbin/ipvsadm -a -t $SNS_VIP:80 -r $SNS_RIP2:80 -g -w 1
	touch /var/lock/subsys/ipvsadm > /dev/null 2>&1

	;;

	stop)
	/sbin/ipvsadm -C
	/sbin/ipvsadm -Z
	ifconfig eth0:0 down
	route del $SNS_VIP
	rm -rf /var/lock/subsys/ipvsadm > /dev/null 2>&1
	echo "ipvsadm stopped!"

	;;

	status)
	if [ ! -e /var/lock/subsys/ipvsadm ]
	then
		echo "ipvsadm stopped!"
		exit 1
	else
		echo "ipvsadm started!"
	fi

	;;
*)
	echo "Usage: $0 {start | stop | status}"
	exit 1

esac
exit 0

该脚本会去绑定 VIP到当前网卡 eth0 上,并把请求转发到其它两个IP上。 绑定后,给该脚本加上执行权限:

chmod +x lvs.sh

然后执行:

./lvs.sh start

运行完后,ifconfig 查看当前IP:

会发现多了一个 eth0:0

注意:当前脚本中是通过:

/sbin/ifconfig eth0:0 $SNS_VIP broadcast $SNS_VIP netmask 255.255.255.0 broadcast $SNS_VIP up 

去绑定 eth0:0。但根据网络环境不一样,绑定到的位置可能不同。比如我测试的时候发现 ifconfig 的值里面没有 eth0, 有一个 eth3, 这时就要绑定到 eth3:0 上。

同时,netmask 的值也要根据自己网络里的实际值去设置。

真实服务器上运行的脚本: realserver.sh。绑定 VIP, 配置 Non-ARP

要注意,这里绑定 IP 是绑定在 lo 上的,是不对外公开的,只在内部使用。

#!/bin/bash
SNS_VIP=192.168.2.118

/etc/rc.d/init.d/functions

case "$1" in

	start)
	ifconfig lo:0 $SNS_VIP netmask 255.255.255.0 broadcast $SNS_VIP
	/sbin/route add -host $SNS_VIP dev lo:0
	echo "1" > /proc/sys/net/ipv4/conf/lo/arp_ignore
	echo "2" > /proc/sys/net/ipv4/conf/lo/arp_announce
	echo "1" > /proc/sys/net/ipv4/conf/all/arp_ignore
	echo "2" > /proc/sys/net/ipv4/conf/all/arp_announce
	sysctl -p > /dev/null 2>&1
	echo "RealServer started!"

	;;

	stop)
	ifconfig lo:0 down
	/sbin/route del $LVS_VIP > /dev/null 2>&1
	echo "0" > /proc/sys/net/ipv4/conf/lo/arp_ignore
	echo "0" > /proc/sys/net/ipv4/conf/lo/arp_announce
	echo "0" > /proc/sys/net/ipv4/conf/all/arp_ignore
	echo "0" > /proc/sys/net/ipv4/conf/all/arp_announce
	echo "RealServer stopped!"

	;;
*)
	echo "Usage: $0 {start | stop}"
	exit 1

esac
exit 0

上面的脚本的功能就是用 ifconfig 来绑定 VIP,并防止 arp 功能。主代理上还进行请求的转发。

LVS + Keepalived 配置

/etc/keepalived/keepalived.conf 内容:

! Configuration File for keepalived

global_defs {
   notification_email {
     sunyu@easymobi.cn
     wuxuegang.123@163.com
   }
   notification_email_from pub@easymobi.cn
   smtp_server 127.0.0.1
   smtp_connect_timeout 30
   router_id LVS_DEVEL
}

vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 51
    # 此处是主 Nginx 的 IP 地址.
    mcast_src_ip 192.168.2.127
    # 该机的 priority(优先) 为 100
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111222
    }
    virtual_ipaddress {
        192.168.2.118
    }
}
virtual_server 192.168.2.118{
	delay_loop 6
	lb_algo wrr
	lb_kind DR
	persistence_timeout 60
	protocol TCP
	real_server 192.168.2.119 80{
		weight 3
		TCP_CHECK{
			connection_timeout 10
			nb_get_retry 3
			delay_before_retry 3
			connect_port 80
		}
	}

	real_server 192.168.2.35 80{
		weight 3
		TCP_CHECK{
			connection_timeout 10
			nb_get_retry 3
			delay_before_retry 3
			connect_port 80
		}
	}
}

备机上的配置文件和这个一样,要把 MASTER 改成其它的,如 SLAVER 或 BACKUP, 然后将 priority 设置成小于 100

将这些文件设置成自启动.添加成服务也可以,或直接加到 /etc/rc.local 里:

ulimit -SHn 65535
/usr/local/sbin/realserver start

通过观察得知:当客户机 192.168.1.100 发起第一次请求时。LVS 负载均衡器将其分配到后面的真实物理服务器 192.168.1.103(负载均衡机) 上,

完成三次握手(http://blog.sina.com.cn/s/blog_5f54f0be0101c8r9.html) 后,连接状态是 ESTABLISHED, 然后终止TCP连接,

在终止TCL连接后相当长的时间内,192.168.1.100 再次发起新链接,都会一直连接到 192.168.1.107(真实服务器中的一个) 上。


上一篇 MySQL 锁

下一篇 BloomFilter

评论

文章