在我们日常运维工作中,经常会碰到负载均衡器后端应用代码更新、临时剔除后端服务器、排查一主机应用故障等,往往我们会选择比较粗鲁的做法,直接停止或重 启应用服务,让负载均衡器探测服务不可用将其剔除。这样带来的坏处是用户与服务器已经建立的连接会被中止,开发人员无法对已经停止服务的主机进行调试。现 介绍一种较为温柔的做法,即通过禁用/启用成员的方式来达到目的。本文针对目前最为流行的负载均衡器逐一进行介绍。包括LVS、Haproxy、F5在命 令行模式下的实现(方便与其它管理平台对接,实现自动化维护)。当然,Haproxy与F5都提供了人性化管理界面,不过只依赖手工来进行操作。

一、LVS负载均衡器
原理
使用LVS自带的管理工具来实现。

环境说明
Disable VIP:192.168.100.11:80
Disable REAL SERVER:192.168.100.78

实施步骤
1、初始状态
[devuser@lvsserver ~]# ipvsadm -Ln


IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  192.168.100.11:80 rr persistent 60
  -> 192.168.100.74:80            Route   3      462        464        
  -> 192.168.100.75:80            Route   3      420        440      
  -> 192.168.100.76:80            Route   3      431        400
  -> 192.168.100.77:80            Route   3      430        432
  -> 192.168.100.78:80            Route   3      435        438


2、禁用成员
[devuser@lvsserver ~]# ipvsadm -d -t 192.168.100.11:80 -r 192.168.100.78

3、当前状态
[devuser@lvsserver ~]# ipvsadm -Ln


IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  192.168.100.11:80 rr persistent 60
  -> 192.168.100.74:80            Route   3      462        464        
  -> 192.168.100.75:80            Route   3      420        440      
  -> 192.168.100.76:80            Route   3      431        400
  -> 192.168.100.77:80            Route   3      430        432


4、启用成员
[devuser@lvsserver ~]#ipvsadm -a -t 192.168.100.11:80 -r 192.168.100.78

5、当前状态
[devuser@lvsserver ~]# ipvsadm -Ln


IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  192.168.100.11:80 rr persistent 60
  -> 192.168.100.74:80            Route   3      462        464        
  -> 192.168.100.75:80            Route   3      420        440      
  -> 192.168.100.76:80            Route   3      431        400
  -> 192.168.100.77:80            Route   3      430        432
  -> 192.168.100.78:80            Route   3      435        438



二、Haproxy负载均衡器
原理
使用Haproxy的socket admin通道来实现。

环境说明
Disable backend:test.tianya.cn
Disable REAL SERVER:192.168.100.78

实施步骤
1、修改haproxy.cfg配置
#vi /usr/local/haproxy/etc/haproxy.cfg
在global域添加socket admin支持并重启Haproxy服务


global
... ...
   stats socket /usr/local/haproxy/HaproxySocket level admin
... ...

#service haproxy restart

2、安装socat(在任意的两个socket管道之间建立一个通道,在该通道中交换两端的数据。)


wget http://www.dest-unreach.org/socat/download/socat-2.0.0-b3.tar.gz
./configure --disable-fips
make;make install


注:disable OpenSSL FIPS support  "--disable-fips",在没有安装fips包的情况下make时会提示:


FIPSLD_CC=gcc fipsld -O -D_GNU_SOURCE  -Wall -Wno-parentheses -DHAVE_CONFIG_H -I.  -I.   -c -o socat.o socat.c
/bin/sh: fipsld: command not found
make: *** [socat.o] Error 127



3、禁用成员
#echo "disable server test.tianya.cn/192.168.100.78" | socat stdio /usr/local/haproxy/HaproxySocket



4、启用成员
#echo "enable server test.tianya.cn/192.168.100.78" | socat stdio /usr/local/haproxy/HaproxySocket



三、F5-LTM负载均衡器


原理
使用F5-iControl开发包Pycontrol对F5设备进行管理。

环境说明
Disable POOL:test.tianya.cn
Disable SERVER:192.168.100.42:80 192.168.100.43:80 192.168.100.44:80

实施步骤
1、部署运行环境
#mkdir -p /home/install;cd /home/install
安装python环境(略),要求py2.5或以上。
1.1、安装SUDS模块


#wget https://fedorahosted.org/releases/s/u/suds/python-suds-0.3.8.tar.gz
#tar xvfz python-suds-0.3.8.tar.gz
#cd python-suds-0.3.8
#python setup.py install


1.2、安装PYCONTROL模块


#wget http://trungale.net/pycontrol.tar.gz
#tar xvfz pycontrol.tar.gz
#cd trunk
#python setup.py install


1.3、校验安装结果
#python

  1. >>> import suds  
  2. >>> import pycontrol.pycontrol as pc  
  3. >>> suds.__version__  
  4. '0.4'  
  5. >>> suds.__build__  
  6. 'GA R699-20100913'  
  7. >>> pc.__version__  
  8. '2.0.1'  
  9. >>> pc.__build__  
  10. 'r83'  
  11. >>>  


2、编写LB_member.py代码

  1. # -*- coding: utf-8 -*-  
  2. """ 
  3. ---------------------------------------------------------------------------- 
  4.  Disable/Enable F5-LTM POOL member  
  5.  Name:        LB_member.py 
  6.  Author:      Liu tian si  
  7.  Email:       liutiansi@gamil.com 
  8.  Created:     2011/05/08 
  9.  Version:     1.0 
  10.  Blog:        http://blog.liuts.com 
  11.  Copyright:   (c) 2011 
  12. ---------------------------------------------------------------------------- 
  13. """  
  14.   
  15. import sys  
  16. import time  
  17. import string  
  18. import pycontrol.pycontrol as pc  
  19.   
  20. """ 
  21. ---------------------------------------------------------------------------- 
  22.  F5-LTM Disable/Enable Pool member Class 
  23. ---------------------------------------------------------------------------- 
  24. __init__() -Initialization F5-BIG object 
  25. set_pool_member() -Initialization pool and member object 
  26. member_factory() -Create a pool member object (Common.IPPortDefinition) 
  27. session_state_factory() -Create a session state object (LocalLB.PoolMember.MemberSessionState) 
  28. disable_member() -Disable menber methods 
  29. enable_member() -Enable menber methods 
  30. ---------------------------------------------------------------------------- 
  31. """  
  32.   
  33. class F5_LB_menber():  
  34.   
  35.     def __init__(self,_hostname,_username,_password):  
  36.         self.b = pc.BIGIP(  
  37.         hostname = _hostname,  
  38.         username = _username,  
  39.         password = _password,  
  40.         fromurl = True,  
  41.         wsdls = ['LocalLB.PoolMember'])  
  42.         self.sstate_seq = self.b.LocalLB.PoolMember.typefactory.create('LocalLB.PoolMember.MemberSessionStateSequence')  
  43.           
  44.   
  45.     def set_pool_member(self,pool,members):  
  46.         self.POOL=pool  
  47.         self.members=members  
  48.         self.sstate_seq.item = self.session_state_factory()  
  49.   
  50.     def member_factory(self, member):  
  51.         ip,port = member.split(':')  
  52.         pmem = self.b.LocalLB.PoolMember.typefactory.create('Common.IPPortDefinition')  
  53.         pmem.address = ip  
  54.         pmem.port = int(port)  
  55.         return pmem  
  56.   
  57.   
  58.     def session_state_factory(self):  
  59.         session_states = []  
  60.         for x in self.members:  
  61.             sstate = self.b.LocalLB.PoolMember.typefactory.create('LocalLB.PoolMember.MemberSessionState')  
  62.             sstate.member = self.member_factory(x)  
  63.             session_states.append(sstate)  
  64.         return session_states  
  65.   
  66.     def disable_member(self):  
  67.         for x in self.sstate_seq.item:  
  68.             x.session_state = 'STATE_DISABLED'  
  69.         try:  
  70.             self.b.LocalLB.PoolMember.set_session_enabled_state(pool_names =  
  71.                 [self.POOL], session_states = [self.sstate_seq])  
  72.       
  73.         except Exception, e:  
  74.             print e  
  75.   
  76.     def enable_member(self):  
  77.         for x in self.sstate_seq.item:  
  78.             x.session_state = 'STATE_ENABLED'  
  79.         try:  
  80.             self.b.LocalLB.PoolMember.set_session_enabled_state(pool_names = [self.POOL],  
  81.                 session_states = [self.sstate_seq])  
  82.         except Exception, e:  
  83.             print e  
  84.   
  85. if __name__ == "__main__":  
  86.     if len(sys.argv) < 4:  
  87.         print "Usage %s POOL MEMBER:port[,member1:80,member2:80,member3:80] enable|disable" % sys.argv[0]  
  88.         print "Examples: python LB_member.py app.domain.com 192.168.0.10:80,192.168.0.11:80,192.168.0.12:80 disable"  
  89.         sys.exit()  
  90.   
  91.     #F5 administrator info  
  92.     hostname="192.168.100.2"  
  93.     username="adminuser"  
  94.     password="adminpass"  
  95.   
  96.     App=F5_LB_menber(hostname,username,password)  
  97.       
  98.     #init F5 pool and member  
  99.     CommandParameters = sys.argv[1:]  
  100.     pool=CommandParameters[0]  
  101.     members=string.split(CommandParameters[1],',')  
  102.       
  103.     App.set_pool_member(pool,members)  
  104.     if CommandParameters[2]=="enable":  
  105.         App.enable_member()  
  106.     elif CommandParameters[2]=="disable":  
  107.         App.disable_member()  
  108.     else:  
  109.         print "opt parameters error!"  
  110.  

  1.         sys.exit()  


3、源码分析:
3.1、创建一个池成员对象,将用户传入的成员列表转成规范的pool成员,见member_factory()方法;
3.2、创建一个会话状态对象,追加成员对象到会话状态当中,见session_state_factory()方法;
3.3、创建一个队列,将会话状态对象添加到队列子项中,同时修改所处状态,见__init__()、set_pool_member()、disable_member()/enable_member()方法。

4、禁用成员
#python LB_member.py test.tianya.cn 192.168.100.42:80,192.168.100.43:80,192.168.100.44:80 disable



5、启用成员
#python LB_member.py test.tianya.cn 192.168.100.42:80,192.168.100.43:80,192.168.100.44:80 enable