【Hadoop】YARN详解与安装指南

一 背景介绍

  自从Hadoop推出以来,在大数据计算上得到广泛使用。其他分布式计算框架也出现,比如spark等。随着Hadoop的使用和研究越来越透彻,它暴漏出来的问题也越来越明显。比如NameNode的单点故障,任务调度器JobTracker的单点故障和性能瓶颈。各个公司对这几个问题都做出了对源代码的改变,比如阿里对NameNode做出修改使Hadoop的集群可以跨机房,而腾讯也做出了改变让Hadoop可以管理更多的节点
  相对于各个企业对Hadoop做出改变以适应应用需求,Apache基金也对Hadoop做出了升级,从Hadoop 0.2.2.0推出了Hadoop二代,即YARN。YARN对原有的Hadoop做出了多个地方的升级,对资源的管理与对任务的调度更加精准。下面就对YARN从集群本身到集群的安装做一个详细的介绍。

  二 YARN介绍

  在介绍YARN之前,Hadoop内容请查看相关附注中的连接。

  2.1 框架基本介绍

  相对于第一代Hadoop,YARN的升级主要体现在两个方面,一个是代码的重构上,另外一个是功能上。通过代码的重构不在像当初一代Hadoop中一个类的源码几千行,使源代码的阅读与维护都不在让人望而却步。除了代码上的重构之外,最主要的就是功能上的升级。
  功能上的升级主要解决的一代Hadoop中的如下几个问题:
  1:JobTracker的升级。这个其中有两个方面,一个是代码的庞大,导致难以维护和阅读;另外一个是功能的庞大,导致的单点故障和消耗问题。这也是YARN对原有Hadoop改善最大的一个方面。
  2:资源的调度粗粒度。在第一代Hadoop中,资源调度是对map和reduce以slot为单位,而且map中的slot与reduce的slot不能相互更换使用。即就算执行map任务没有多余的slot,但是reduce有很多空余slot也不能分配给map任务使用。
  3:对计算节点中的任务管理粒度太大。
  针对上述相应问题,YARN对Hadoop做了细致的升级。YARN已经不再是一个单纯的计算平台,而是一个资源的监管平台。在YARN框架之上可以使用MapReduce计算框架,也可以使用其他的计算或者数据处理,通过YARN框架对计算资源管理。升级后的YARN框架与原有的Hadoop框架之间的区别可以用下图解释:
 
 
 细节内容待补充。

  2.2 框架架构解读

   相对于一代Hadoop中计算框架的JobTracker与TaskTracker两个主要通信模块,YARN的模块变的更加丰富。在一代Hadoop中JobTracker负责资源调度与任务分配,而在YARN中则把这两个功能拆分由两个不同组件完成,这不仅减少了单个类的代码量(单个类不到1000行),也让每个类的功能更加专一。原有的JobTracker分为了如今的ResourceManager与ApplicationMaster 两个功能组件,一个负责任务的管理一个负责任务的管理。有人会问那任务的调度与计算节点的谁来负责。任务的调度有可插拔的调
度器ResourceScheduler,计算节点有NodeManager来完成。这在下面会细说。
  YARN的架构设计如下图所示:
  
  相对于第一代Hadoop,YARN把Hadoop中的资源控制、任务调度和具体任务计算的JobTracker/TaskTracker架构,变为下述的四个功能组件,让资源调度和任务调度更加细粒化。
  • 集群唯一的ResourceManager
  • 每个任务对应的ApplicationMaster
  • 每个机器节点上的NodeManager
  • 运行在每个NodeManager上针对某个任务的Container
通过上述四个功能组件的合作,解决了第一代Hadoop中JobTracker负责所有资源的调度和任务的调度的重任。除此之外还解决了,资源分配简单粗暴的问题。

  2.3 功能组件细讲

    ResourceManager
    ResourceManager是这一代Hadoop升级中最主要的一个功能组件。
    NodeManager
    ResourceScheduler

  三 安装

  相对于Hadoop的安装,YARN的安装稍微繁琐一点,因为它的组件更多。对于一代Hadoop,它大的组件就可以分为HDFS与MapReduce,HDFS主要是NameNode与Datanode,而MapReduce就是JobTracker与TaskTracker。YARN框架中HDFS部分虽然和一代Hadoop相同,但是启动方式则和一代完全不同;而MapReduce部分则更加复杂。所以安装起来稍微繁琐一点。下面我们就参考官方文档,开始慢慢讲解整个安装过程。在安装过程中遇到的问题都记录在附录中给出的连接中。
  YARN安装可以通过下载tar包或者通过源代码两种方式安装,我们集群是采用的下载tar包然后进行解压的安装方式,下载地址在附录中给出。为了方便安装,我们是解压到根目录/yarn。解压tar包后的YARN目录结构有一点需要注意,即配置文件conf所在的正确位置应该是/yarn/conf,但是解压后/yarn目录中是没有conf文件的,但是有有个/etc/hadoop目录。所有的配置文件就在该目录下,你需要做的是把该文件move到/yarn/conf位置。再做下一步的安装。如果你解压后的文件没有出现上面情况,那这一步操作可以取消。
对于YARN的安装主要是HDFS安装与MapReduce安装。我们的集群共有四台机器,其中一台作为NameNode所在,一台作为ResourceManager所在,剩下的两台则担任数据节点与计算节点。具体的机器安排如下:
linux-c0001:ResourceManager
linux-c0002:NameNode
linux-c0003:DataNode/NodeManager
linux-c0004:DataNode/NodeManager
  因为我们的集群上已经安装有其他版本的Hadoop,比如Apache的Hadoop还有Intel推出的Intel Distributiong Hadoop,所以很多默认的端口号都要修改,数据文件的存储位置与运行Hadoop的用户都要修改。在安装的过程中因为已经安装的Hadoop的存在,也遇到了很多问题。这在下面还有附录中的连接中都会有说明。
下面针对每个安装进行介绍:

  3.1 HDFS安装与配置

  HDFS安装主要是NameNode与DataNode的安装与配置,其中NameNode还需要一步格式化formate操作。其中HDFS的配置主要存在于core-site.xmlhdfs-site.xml两个文件中。这两个配置文件的默认属性已经在附录中给出,但是在我们集群中不能采用。因为,我们集群中已经安装有其他Hadoop,如果全部采用默认配置则会出现冲突,在安装过程中出现Port in useBindException。主要需要配置的地方如下几点:
  1:NameNode配置
在我们集群中c0002是NameNode所在的机器,在该机器的配置文件中需要修改的是两个文件core-site.xml与hdfs-site.xml。需要修改的配置文件指定namenode所在主机与端口号,NameNode的数据文件目录等几点。
下面分别给出我们的core-site.xml与hdfs-site.xml的配置:
  core-site.xml:
  1. <configuration>
  2. <property>
  3.     <name>fs.defaultFS</name>
  4.         <value>hdfs://linux-c0002:9090</value>
  5.                   </property>
  6.   <property>
  7.       <name>io.file.buffer.size</name>
  8.           <value>131072</value>
  9.                     </property>
  10. </configuration>
对于HDFS的配置,因为有NameNode与DataNode两种节点身份,所以在NameNode与DataNode中配置文件不会完全一致,下面就会贴出两种节点上的不同配置。

hdfs-site.xml(NameNode):

  1. <configuration>
  2.   <property>
  3.       <name>dfs.namenode.http-address</name>
  4.                      <value>linux-c0002:50071</value>
  5.                                        </property>
  6.  <property>
  7.        <name>dfs.namenode.backup.address</name>
  8.                     <value>linux-c0002:50101</value>
  9.                                       </property>
  10.  <property>
  11.        <name>dfs.namenode.backup.http-address</name>
  12.                     <value>linux-c0002:50106</value>
  13.                                       </property>
  14.   <property>
  15.       <name>dfs.namenode.name.dir</name>
  16.         <value>/home/hadoop/YarnRun/name1,/home/hadoop/YarnRun/name2</value>
  17.                   </property>
  18.   <property>
  19.       <name>dfs.blocksize</name>
  20.           <value>268435456</value>
  21.                     </property>
  22.  <property>
  23.        <name>dfs.namenode.handler.count</name>
  24.             <value>100</value>
  25.                     </property>
  26. </configuration>
2:DataNode的配置
DataNode的配置是在hdfs-site.xml中进行个性化配置的,主要包括DataNode的主机和对外端口号,数据文件位置。按照正常DataNode的主机与对外端口号是不用额外配置的,在安装的时候采用默认的端口号就好。但是,还是那个原因,因为我们集群中安装有其他的Hadoop版本,所以不得不对默认的DataNode中的端口号做一定的修改。
下面给出配置后的文件内容:
   hdfs-site.xml(DataNode)

  1. <configuration>
  2.   <property>
  3.       <name>dfs.namenode.http-address</name>
  4.                      <value>linux-c0002:50071</value>
  5.                                        </property>
  6.   <property>
  7.         <name>dfs.datanode.address</name>
  8.                       <value>linux-c0003:50011</value>
  9.                                         </property>
  10.   <property>
  11.         <name>dfs.datanode.http.address</name>
  12.                       <value>linux-c0003:50076</value>
  13.                                         </property>
  14.   <property>
  15.       <name>dfs.datanode.ipc.address</name>
  16.                     <value>linux-c0003:50021</value>
  17.                                       </property>
  18.   <property>
  19.       <name>dfs.blocksize</name>
  20.           <value>268435456</value>
  21.                     </property>
  22.  <property>
  23.         <name>dfs.datanode.data.dir</name>
  24.             <value>/home/hadoop/YarnRun/data1</value>
  25.                     </property>
  26. </configuration>

3:slaves文件的修改
slaves文件主要是指明子节点的位置,即数据节点和计算节点的位置。和一代Hadoop不同的是,在YARN中不需要配置masters文件,只有slaves配置文件。在slaves添加两个子节点主机名(整个集群的主机名在集群中的所有主机的hosts文件中已经注明):

linux-c0003
linux-c0004

4:Namenode的格式化
采用官方文档给出的格式化命令进行格式化(cluster_name没有指定则不需要添加该参数)

  $HADOOP_PREFIX/bin/hdfs namenode -format <cluster_name>

   5:Namenode启动
采用官方文档给出的启动命令直接启动就好。(注意:启动命令与格式化命令的脚本不再同一个文件中)

  $HADOOP_PREFIX/sbin/hadoop-daemon.sh --config $HADOOP_CONF_DIR --script hdfs start namenode

6:检查NameNode是否启动
输入jps命令,看是否有NameNode进程;或者访问其web链接看是否能正常访问。如果不能正常访问,则去看相应的日志进行相应的定向修改。

   7:DataNode的启动
   在YARN中DataNode的启动与第一代Hadoop的启动是不相同的,在第一代Hadoop中你执行一个start-all命令包括NameNode与各个节点上的DataNode都会相继启动起来,但是在YARN中你启动DataNode必须要去各个节点依次执行启动命令。当然你也可以自己写启动脚本,不去各个节点重复执行命令。
  下面贴出启动命令:
[plain] view plain copy

在CODE上查看代码片派生到我的代码片

  1. $HADOOP_PREFIX/sbin/hadoop-daemon.sh –config $HADOOP_CONF_DIR –script hdfs start datanode
3.2 MapReduce安装与配置
  MapReduce其中需要配置安装的地方是ResourceManager与NodeManager的安装与配置。
  1:ResourceManager的配置
  ResourceManager与NameNode一样,都是单独在一个机器节点上,对ResourceManager的配置主要是在yarn-site.xml中进行配置的。可配置的属性主要包括主机与端口号,还有调度器的配置。
  1. <configuration>
  2.   <property>
  3.       <name>yarn.resourcemanager.address</name>
  4.                 <value>linux-c0001:8032</value>
  5.                               <description>the host is the hostname of the ResourceManager and the port is the port on
  6.                                                 which the clients can talk to the Resource Manager. </description>
  7.                                                                     </property>
  8.   <property>
  9.     <name>yarn.resourcemanager.resource-tracker.address</name>
  10.         <value>linux-c0001:8031</value>
  11.             <description>host is the hostname of the resource manager and
  12.                 port is the port on which the NodeManagers contact the Resource Manager.
  13.                     </description>
  14.                       </property>
  15. <!– for scheduler –>
  16.   <property>
  17.       <name>yarn.resourcemanager.scheduler.address</name>
  18.           <value>linux-c0001:8030</value>
  19.               <description>host is the hostname of the resourcemanager and port is the port
  20.                   on which the Applications in the cluster talk to the Resource Manager.
  21.                       </description>
  22.                         </property>
  23.   <property>
  24.       <name>yarn.resourcemanager.scheduler.class</name>
  25.           <value>org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler</value>
  26.               <description>In case you do not want to use the default scheduler</description>
  27.                 </property>
  28.   <property>
  29.       <name>yarn.nodemanager.local-dirs</name>
  30.           <value></value>
  31.               <description>the local directories used by the nodemanager</description>
  32.                 </property>
  33. <!– for nodemanager –>
  34.   <property>
  35.       <name>yarn.nodemanager.resource.memory-mb</name>
  36.           <value>10240</value>
  37.               <description>the amount of memory on the NodeManager in GB</description>
  38.                 </property>
  39.   <property>
  40.       <name>yarn.nodemanager.remote-app-log-dir</name>
  41.           <value>/app-logs</value>
  42.               <description>directory on hdfs where the application logs are moved to </description>
  43.                 </property>
  44.    <property>
  45.        <name>yarn.nodemanager.log-dirs</name>
  46.            <value></value>
  47.                <description>the directories used by Nodemanagers as log directories</description>
  48.                  </property>
  49.   <property>
  50.       <name>yarn.nodemanager.aux-services</name>
  51.           <value>mapreduce_shuffle</value>
  52.               <description>shuffle service that needs to be set for Map Reduce to run </description>
  53.                 </property>
  54. </configuration>
  2:NodeManager的配置
  NodeManager主要是配置计算节点上的对计算资源的控制和对外的端口号,它的配置也在yarn-site.xml中进行配置,所以这里的配置文件和上面贴出的配置文件代码是完全一样的,在这里就不再次粘贴文件具体内容。
  3:mapreduce的配置
   mapreduce的配置主要是在mapred-site.xml中,用来指明计算框架的版本(Hadoop/YARN)还有计算的中间数据位置等。
  1. <configuration>
  2.   <property>
  3.     <name>mapreduce.framework.name</name>
  4.         <value>yarn</value>
  5.                   </property>
  6.   <property>
  7.       <name>mapreduce.cluster.temp.dir</name>
  8.               <value></value>
  9.                                 </property>
  10.   <property>
  11.       <name>mapreduce.cluster.local.dir</name>
  12.           <value></value>
  13.                     </property>
  14. </configuration>

4:ResourceManager启动

  具体代码
[plain] view plain copy

在CODE上查看代码片派生到我的代码片

  1. $HADOOP_YARN_HOME/sbin/yarn-daemon.sh –config $HADOOP_CONF_DIR start resourcemanager
  5:NodeManager的启动
  NodeManager的启动和DataNode的启动也是一样,需要到各个计算节点上执行启动命令。具体代码如下:
[plain] view plain copy

在CODE上查看代码片派生到我的代码片

  1. $HADOOP_YARN_HOME/sbin/yarn-daemon.sh –config $HADOOP_CONF_DIR start nodemanager

6:JobHistory的启动

JobHistory服务主要是负责记录集群中曾经跑过的任务,对完成的任务查看任务运行期间的详细信息。一般JobHistory都是启动在运行任务的节点上,即NodeManager节点上。如果不对JobHistory的配置进行修改,那么直接可以在NodeManager所在节点上运行启动命令即可,具体启动命令如下:

$HADOOP_PREFIX/sbin/mr-jobhistory-daemon.sh start historyserver --config $HADOOP_CONF_DIR
启动了之后就可以在集群运行任务页面查看具体的job history,通过点击每个任务条目的左后history链接就可以查看具体的任务细节。具体的截图如下:

  四 关闭集群

   当集群升级或者需要重启集群的时候,就需要执行关闭命令。分别关闭NameNode与DataNode,然后关闭ResourceManager与NodeManager。但是在关闭的时候可能会遇到no namenode to stop或者no resourcemanager to stop的问题。这是因为YARN在关闭的时候,它会首先获取当前系统中的YARN相关运行进程的ID,如果没有则就会爆出上述两个问题。但是系统中明明运行这YARN相关进程,为何还是会说没有进程stop。这是因为当YARN运行起来之后,运行中的进程ID会存储在/tmp文件夹下,而/tmp文件会定期删除文件,这就导致YARN停止脚本无法找到YARN相关进程的ID,导致不能停止。
   两个解决方案,第一个就是使用kill命令,而第二个则是修改YARN的进程ID的存放文件夹目录。

  五 附录

YARN官方文档:YARN官方介绍
YARN下载:Hadoop 0.2.2.0

多网卡的7种bond模式原理

Linux 多网卡绑定

网卡绑定mode共有七种(0~6) bond0、bond1、bond2、bond3、bond4、bond5、bond6

常用的有三种

mode=0:平衡负载模式,有自动备援,但需要”Switch”支援及设定。

mode=1:自动备援模式,其中一条线若断线,其他线路将会自动备援。

mode=6:平衡负载模式,有自动备援,不必”Switch”支援及设定。

需要说明的是如果想做成mode 0的负载均衡,仅仅设置这里options bond0 miimon=100 mode=0是不够的,与网卡相连的交换机必须做特殊配置(这两个端口应该采取聚合方式),因为做bonding的这两块网卡是使用同一个MAC地址.从原理分析一下(bond运行在mode 0下):

mode 0下bond所绑定的网卡的IP都被修改成相同的mac地址,如果这些网卡都被接在同一个交换机,那么交换机的arp表里这个mac地址对应的端口就有多 个,那么交换机接受到发往这个mac地址的包应该往哪个端口转发呢?正常情况下mac地址是全球唯一的,一个mac地址对应多个端口肯定使交换机迷惑了。所以 mode0下的bond如果连接到交换机,交换机这几个端口应该采取聚合方式(cisco称为 ethernetchannel,foundry称为portgroup),因为交换机做了聚合后,聚合下的几个端口也被捆绑成一个mac地址.我们的解 决办法是,两个网卡接入不同的交换机即可。

mode6模式下无需配置交换机,因为做bonding的这两块网卡是使用不同的MAC地址。

七种bond模式说明:

第一种模式:mod=0 ,即:(balance-rr) Round-robin policy(平衡抡循环策略)

特点:传输数据包顺序是依次传输(即:第1个包走eth0,下一个包就走eth1….一直循环下去,直到最后一个传输完毕),此模式提供负载平衡和容错能力;但是我们知道如果一个连接或者会话的数据包从不同的接口发出的话,中途再经过不同的链路,在客户端很有可能会出现数据包无序到达的问题,而无序到达的数据包需要重新要求被发送,这样网络的吞吐量就会下降

 

第二种模式:mod=1,即: (active-backup) Active-backup policy(主-备份策略)

特点:只有一个设备处于活动状态,当一个宕掉另一个马上由备份转换为主设备。mac地址是外部可见得,从外面看来,bond的MAC地址是唯一的,以避免switch(交换机)发生混乱。此模式只提供了容错能力;由此可见此算法的优点是可以提供高网络连接的可用性,但是它的资源利用率较低,只有一个接口处于工作状态,在有 N 个网络接口的情况下,资源利用率为1/N

 

第三种模式:mod=2,即:(balance-xor) XOR policy(平衡策略)

特点:基于指定的传输HASH策略传输数据包。缺省的策略是:(源MAC地址 XOR 目标MAC地址) % slave数量。其他的传输策略可以通过xmit_hash_policy选项指定,此模式提供负载平衡和容错能力

 

第四种模式:mod=3,即:broadcast(广播策略)

特点:在每个slave接口上传输每个数据包,此模式提供了容错能力

 

第五种模式:mod=4,即:(802.3ad) IEEE 802.3ad Dynamic link aggregation(IEEE 802.3ad 动态链接聚合)

特点:创建一个聚合组,它们共享同样的速率和双工设定。根据802.3ad规范将多个slave工作在同一个激活的聚合体下。

外出流量的slave选举是基于传输hash策略,该策略可以通过xmit_hash_policy选项从缺省的XOR策略改变到其他策略。需要注意的 是,并不是所有的传输策略都是802.3ad适应的,尤其考虑到在802.3ad标准43.2.4章节提及的包乱序问题。不同的实现可能会有不同的适应 性。

必要条件:

条件1:ethtool支持获取每个slave的速率和双工设定

条件2:switch(交换机)支持IEEE 802.3ad Dynamic link aggregation

条件3:大多数switch(交换机)需要经过特定配置才能支持802.3ad模式

 

第六种模式:mod=5,即:(balance-tlb) Adaptive transmit load balancing(适配器传输负载均衡)

特点:不需要任何特别的switch(交换机)支持的通道bonding。在每个slave上根据当前的负载(根据速度计算)分配外出流量。如果正在接受数据的slave出故障了,另一个slave接管失败的slave的MAC地址。

该模式的必要条件:ethtool支持获取每个slave的速率

 

第七种模式:mod=6,即:(balance-alb) Adaptive load balancing(适配器适应性负载均衡)

特点:该模式包含了balance-tlb模式,同时加上针对IPV4流量的接收负载均衡(receive load balance, rlb),而且不需要任何switch(交换机)的支持。接收负载均衡是通过ARP协商实现的。bonding驱动截获本机发送的ARP应答,并把源硬件地址改写为bond中某个slave的唯一硬件地址,从而使得不同的对端使用不同的硬件地址进行通信。

来自服务器端的接收流量也会被均衡。当本机发送ARP请求时,bonding驱动把对端的IP信息从ARP包中复制并保存下来。当ARP应答从对端到达 时,bonding驱动把它的硬件地址提取出来,并发起一个ARP应答给bond中的某个slave。使用ARP协商进行负载均衡的一个问题是:每次广播 ARP请求时都会使用bond的硬件地址,因此对端学习到这个硬件地址后,接收流量将会全部流向当前的slave。这个问题可以通过给所有的对端发送更新 (ARP应答)来解决,应答中包含他们独一无二的硬件地址,从而导致流量重新分布。当新的slave加入到bond中时,或者某个未激活的slave重新 激活时,接收流量也要重新分布。接收的负载被顺序地分布(round robin)在bond中最高速的slave上

当某个链路被重新接上,或者一个新的slave加入到bond中,接收流量在所有当前激活的slave中全部重新分配,通过使用指定的MAC地址给每个 client发起ARP应答。下面介绍的updelay参数必须被设置为某个大于等于switch(交换机)转发延时的值,从而保证发往对端的ARP应答 不会被switch(交换机)阻截。

必要条件:

条件1:ethtool支持获取每个slave的速率;

条件2:底层驱动支持设置某个设备的硬件地址,从而使得总是有个slave(curr_active_slave)使用bond的硬件地址,同时保证每个bond 中的slave都有一个唯一的硬件地址。如果curr_active_slave出故障,它的硬件地址将会被新选出来的 curr_active_slave接管

其实mod=6与mod=0的区别:mod=6,先把eth0流量占满,再占eth1,….ethX;而mod=0的话,会发现2个口的流量都很稳定,基本一样的带宽。而mod=6,会发现第一个口流量很高,第2个口只占了小部分流量

 

 

 

Linux网口绑定

通过网口绑定(bond)技术,可以很容易实现网口冗余,负载均衡,从而达到高可用高可靠的目的。前提约定:

2个物理网口分别是:eth0,eth1

绑定后的虚拟口是:bond0

服务器IP是:192.168.0.100

第一步,配置设定文件:

/etc/sysconfig/network-scripts/ifcfg-bond0

DEVICE=bond0

BOOTPROTO=none

ONBOOT=yes

IPADDR=192.168.0.100

NETMASK=255.255.255.0

NETWORK=192.168.0.0

BROADCAST=192.168.0.255

#BROADCAST广播地址

/etc/sysconfig/network-scripts/ifcfg-eth0

DEVICE=eth0

BOOTPROTO=none

MASTER=bond0

SLAVE=yes

/etc/sysconfig/network-scripts/ifcfg-eth1

DEVICE=eth1

BOOTPROTO=none

MASTER=bond0

SLAVE=yes

第二步,修改modprobe相关设定文件,并加载bonding模块:

1.在这里,我们直接创建一个加载bonding的专属设定文件/etc/modprobe.d/bonding.conf

[root@test ~]# vi /etc/modprobe.d/bonding.conf

#追加

alias bond0 bonding

options bonding mode=0 miimon=200

2.加载模块(重启系统后就不用手动再加载了)

[root@test ~]# modprobe bonding

3.确认模块是否加载成功:

[root@test ~]# lsmod | grep bonding

bonding 100065 0

第三步,重启一下网络,然后确认一下状况:

[root@test ~]# /etc/init.d/network restart

[root@test ~]# cat /proc/net/bonding/bond0

Ethernet Channel Bonding Driver: v3.5.0 (November 4, 2008)

Bonding Mode: fault-tolerance (active-backup)

Primary Slave: None

Currently Active Slave: eth0

……

[root@test ~]# ifconfig | grep HWaddr

bond0 Link encap:Ethernet HWaddr 00:16:36:1B:BB:74

eth0 Link encap:Ethernet HWaddr 00:16:36:1B:BB:74

eth1 Link encap:Ethernet HWaddr 00:16:36:1B:BB:74

从上面的确认信息中,我们可以看到3个重要信息:

1.现在的bonding模式是active-backup

2.现在Active状态的网口是eth0

3.bond0,eth1的物理地址和处于active状态下的eth0的物理地址相同,这样是为了避免上位交换机发生混乱。

任意拔掉一根网线,然后再访问你的服务器,看网络是否还是通的。

第四步,系统启动自动绑定、增加默认网关:

[root@test ~]# vi /etc/rc.d/rc.local

#追加

ifenslave bond0 eth0 eth1

route add default gw 192.168.0.1

#如可上网就不用增加路由,0.1地址按环境修改.

————————————————————————

留心:前面只是2个网口绑定成一个bond0的情况,如果我们要设置多个bond口,比如物理网口eth0和eth1组成bond0,eth2和eth3组成bond1,

那么网口设置文件的设置方法和上面第1步讲的方法相同,只是/etc/modprobe.d/bonding.conf的设定就不能像下面这样简单的叠加了:

alias bond0 bonding

options bonding mode=1 miimon=200

alias bond1 bonding

options bonding mode=1 miimon=200

正确的设置方法有2种:

第一种,你可以看到,这种方式的话,多个bond口的模式就只能设成相同的了:

alias bond0 bonding

alias bond1 bonding

options bonding max_bonds=2 miimon=200 mode=1

第二种,这种方式,不同的bond口的mode可以设成不一样:

alias bond0 bonding

options bond0 miimon=100 mode=1

install bond1 /sbin/modprobe bonding -o bond1 miimon=200 mode=0

仔细看看上面这2种设置方法,现在如果是要设置3个,4个,甚至更多的bond口,你应该也会了吧!

后记:简单的介绍一下上面在加载bonding模块的时候,options里的一些参数的含义:

miimon 监视网络链接的频度,单位是毫秒,我们设置的是200毫秒。

max_bonds 配置的bond口个数

mode bond模式,主要有以下几种,在一般的实际应用中,0和1用的比较多,

如果你要深入了解这些模式各自的特点就需要靠读者你自己去查资料并做实践了。

 

from:http://support.huawei.com/ecommunity/bbs/10155553.html

haproxy+keepalived实现高可用负载均衡及脑裂问题解决方案

软件负载均衡一般通过两种方式来实现:基于操作系统的软负载实现和基于第三方应用的软负载实现。LVS就是基于Linux操作系统实现的一种软负载,HAProxy就是开源的并且基于第三应用实现的软负载。

HAProxy相比LVS的使用要简单很多,功能方面也很丰富。当 前,HAProxy支持两种主要的代理模式:”tcp”也即4层(大多用于邮件服务器、内部协议通信服务器等),和7层(HTTP)。在4层模式 下,HAProxy仅在客户端和服务器之间转发双向流量。7层模式下,HAProxy会分析协议,并且能通过允许、拒绝、交换、增加、修改或者删除请求 (request)或者回应(response)里指定内容来控制协议,这种操作要基于特定规则。

我现在用HAProxy主要在于它有以下优点,这里我总结下:

一、免费开源,稳定性也是非常好,这个可通过我做的一些小项目可以看出来,单Haproxy也跑得不错,稳定性可以与LVS相媲美;

二、根据官方文档,HAProxy可以跑满10Gbps-New benchmark of HAProxy at 10 Gbps using Myricom’s 10GbE NICs (Myri-10G PCI-Express),这个作为软件级负载均衡,也是比较惊人的;

三、HAProxy可以作为MySQL、邮件或其它的非web的负载均衡,我们常用于它作为MySQL(读)负载均衡;

自带强大的监控服务器状态的页面,实际环境中我们结合Nagios进行邮件或短信报警,这个也是我非常喜欢它的原因之一;

HAProxy支持虚拟主机。

===================================================================================

在做反向代理服务器的负载均衡时,我们通常会使用nginx的均衡配置。其实,haproxy的负载均衡也是属于这一类的。那么关于这方面的配置过程我们现在来进行一下讲解。首先,对haproxy进行一个简单的介绍,之后就是安装和配置环节了。

HAProxy介绍

反向代理服务器,支持双机热备支持虚拟主机,但其配置简单,拥有非常不错的服务器健康检查功能,当其代理的后端服务器出现故障, HAProxy会自动将该服务器摘除,故障恢复后再自动将该服务器加入。新的1.3引入了frontend,backend;frontend根据任意 HTTP请求头内容做规则匹配,然后把请求定向到相关的backend.

http://blog.liuts.com/post/223/ (搭建四层负载均衡器)

http://rfyimcool.blog.51cto.com/1030776/413187 (搭建七层负载均衡器)

===================================================================================

keepalived简介

http://www.keepalived.org

keepalived是一个类似于layer3, 4 & 5交换机制的软件,也就是我们平时说的第3层、第4层和第5层交换。Keepalived的作用是检测web服务器的状态,如果有一台web服务器死机,或工作出现故障,Keepalived将检测到,并将有故障的web服务器从系统中剔除,当web服务器工作正常后Keepalived自动将web服务器加入到服务器群中,这些工作全部自动完成,不需要人工干涉,需要人工做的只是修复故障的web服务器。

类似的HA工具还有heatbeat、drbd等,heatbeat、drbd配置都较为复杂。

keepalived理论工作原理

keepalived可提供vrrp以及health-check功能,可以只用它提供双机浮动的vip(vrrp虚拟路由功能),这样可以简单实现一个双机热备高可用功能。

keepalived是一个类似于layer3, 4 & 5交换机制的软件,也就是我们平时说的第3层、第4层和第5层交换。Keepalived的作用是检测web 服务器的状态。 Layer3,4&5工作在IP/TCP协议栈的IP层,TCP层,及应用层,原理分别如下:

Layer3:Keepalived使用Layer3的方式工作式时,Keepalived会定期向服务器群中的服务器

发送一个ICMP的数据包(既我们平时用的Ping程序),如果发现某台服务的IP地址没有激活,Keepalived便报告这台服务器失效,并将它从服务器群中剔除,这种情况的典型例子是某台服务器被非法关机。Layer3的方式是以服务器的IP地址是否有效作为服务器工作正常与否的标准。在本文中将采用这种方式。

Layer4:如果您理解了Layer3的方式,Layer4就容易了。Layer4主要以TCP端口的状态来决定服务器工作正常与否。如web server的服务端口一般是80,如果Keepalived检测到80端口没有启动,则Keepalived将把这台服务器从服务器群中剔除。

Layer5:Layer5就是工作在具体的应用层了,比Layer3,Layer4要复杂一点,在网络上占用的带宽也要大一些。Keepalived将根据用户的设定检查服务器程序的运行是否正常,如果与用户的设定不相符,则Keepalived将把服务器从服务器群中剔除。

 

vip即虚拟ip,是附在主机网卡上的,即对主机网卡进行虚拟,此IP仍然是占用了此网段的某个IP。

keepalived作用

随着你的网站业务量的增长你网站的服务器压力越来越大?需要负载均衡方案!商业的硬件如F5又太贵,你们又是创业型互联公司如何有效节约成本,节省不必要的浪费?同时实现商业硬件一样的高性能高可用的功能?有什么好的负载均衡可伸张可扩展的方案吗?答案是肯定的!有!我们利用 LVS+Keepalived基于完整开源软件的架构可以为你提供一个负载均衡及高可用的服务器。

LVS+Keepalived 介绍

LVS

LVS是Linux Virtual Server的简写,意即Linux虚拟服务器,是一个虚拟的服务器集群系统。本项目在1998年5月由章文嵩博士成立,是中国国内最早出现的自由软件项目之一.目前有三种IP负载均衡技术(VS/NAT、VS/TUN和VS/DR)八种调度算法(rr,wrr,lc,wlc,lblc,lblcr,dh,sh)。

Keepalvied

Keepalived在这里主要用作RealServer的健康状态检查以及LoadBalance主机和BackUP主机之间failover的实现。keepalived简介  keepalived是一个类似于layer3, 4 & 5交换机制的软件,也就是我们平时说的第3层、第4层和第5层交换。Keepalived的作用是检测web服务器的状态,如果有一台web服务器死机,或工作出现故障,Keepalived将检测到,并将有故障的web服务器从系统中剔除,当web服务器工作正常后Keepalived自动将web服务器加入到服务器群中,这些工作全部自动完成,不需要人工干涉,需要人工做的只是修复故障的web服务器。

===================================================================================

Keepalived介绍

Keepalived是一个基于VRRP协议来实现的WEB 服务高可用方案,可以利用其来避免单点故障。一个WEB服务至少会有2台服务器运行Keepalived,一台为主服务器(MASTER),一台为备份服务器(BACKUP),但是对外表现为一个虚拟IP,主服务器会发送特定的消息给备份服务器,当备份服务器收不到这个消息的时候,即主服务器宕机的时候,备份服务器就会接管虚拟IP,继续提供服务,从而保证了高可用性。

1       +————-VIP(192.168.0.7)——————+
2       |                                   |                           |
3       |                                   |                           |
4 server(MASTER) <—-keepalived—-> server(BACKUP)
5  (192.168.0.1)                                      (192.168.0.2)

keepalived是VRRP的完美实现,因此在介绍keepalived之前,先介绍一下VRRP的原理。

VRRP协议简介

在现实的网络环境中,两台需要通信的主机大多数情况下并没有直接的物理连接。对于这样的情况,它们之间路由怎样选择?主机如何选定到达目的主机的下一跳路由,这个问题通常的解决方法有二种:

·        在主机上使用动态路由协议(RIP、OSPF等)

·        在主机上配置静态路由

很明显,在主机上配置路态路由是非常不切实际的,因为管理、维护成本以及是否支持等诸多问题。配置静态路由就变得十分流行,但路由器(或者说默认网关default gateway)却经常成为单点。

VRRP的目的就是为了解决静态路由单点故障问题。

VRRP通过一竞选(election)协议来动态的将路由任务交给LAN中虚拟路由器中的某台VRRP路由器。

工作机制

在一个VRRP虚拟路由器中,有多台物理的VRRP路由器,但是这多台的物理的机器并不能同时工作,而是由一台称为MASTER的负责路由工作,其它的都是BACKUP,MASTER并非一成不变,VRRP让每个VRRP路由器参与竞选,最终获胜的就是MASTER。MASTER拥有一些特权,比如 拥有虚拟路由器的IP地址,我们的主机就是用这个IP地址作为静态路由的。拥有特权的MASTER要负责转发发送给网关地址的包和响应ARP请求。

VRRP通过竞选协议来实现虚拟路由器的功能,所有的协议报文都是通过IP多播(multicast)包(多播地址 224.0.0.18)形式发送的。虚拟路由器由VRID(范围0-255)和一组IP地址组成,对外表现为一个周知的MAC地址。所以,在一个虚拟路由 器中,不管谁是MASTER,对外都是相同的MAC和IP(称之为VIP)。客户端主机并不需要因为MASTER的改变而修改自己的路由配置,对他们来 说,这种主从的切换是透明的。

在一个虚拟路由器中,只有作为MASTER的VRRP路由器会一直发送VRRP广告包(VRRPAdvertisement message),BACKUP不会抢占MASTER,除非它的优先级(priority)更高。当MASTER不可用时(BACKUP收不到广告包), 多台BACKUP中优先级最高的这台会被抢占为MASTER。这种抢占是非常快速的(<1s),以保证服务的连续性。

由于安全性考虑,VRRP包使用了加密协议进行加密。

==========================================

vrrp简介
随着Internet的迅猛发展,基于网络的应用逐渐增多。这就对网络的可靠性提出了越来越高的要求。斥资对所有网络设备进行更新当然是一种很好的可靠性解决方案;但本着保护现有投资的角度考虑,可以采用廉价冗余的思路,在可靠性和经济性方面找到平衡点。

虚拟路由冗余协议就是一种很好的解决方案。在该协议中,对共享多存取访问介质(如以太网)上终端IP设备的默认网关(Default Gateway)进行冗余备份,从而在其中一台路由设备宕机时,备份路由设备及时接管转发工作,向用户提供透明的切换,提高了网络服务质量。

一、协议概述

在基于TCP/IP协议的网络中,为了保证不直接物理连接的设备之间的通信,必须指定路由。目前常用的指定路由的方法有两种:一种是通过路由协议(比如:内部路由协议RIP和OSPF)动态学习;另一种是静态配置。在每一个终端都运行动态路由协议是不现实的,大多客户端操作系统平台都不支持动态路由协议,即使支持也受到管理开销、收敛度、安全性等许多问题的限制。因此普遍采用对终端IP设备静态路由配置,一般是给终端设备指定一个或者多个默认网关(Default Gateway)。静态路由的方法简化了网络管理的复杂度和减轻了终端设备的通信开销,但是它仍然有一个缺点:如果作为默认网关的路由器损坏,所有使用该网关为下一跳主机的通信必然要中断。即便配置了多个默认网关,如不重新启动终端设备,也不能切换到新的网关。采用虚拟路由冗余协议 (Virtual Router Redundancy Protocol,简称VRRP)可以很好的避免静态指定网关的缺陷。

在VRRP协议中,有两组重要的概念:VRRP路由器和虚拟路由器,主控路由器和备份路由器。VRRP路由器是指运行VRRP的路由器,是物理实体,虚拟路由器是指VRRP协议创建的,是逻辑概念。一组VRRP路由器协同工作,共同构成一台虚拟路由器。该虚拟路由器对外表现为一个具有唯一固定IP地址和MAC地址的逻辑路由器。处于同一个VRRP组中的路由器具有两种互斥的角色:主控路由器和备份路由器,一个VRRP组中有且只有一台处于主控角色的路由器,可以有一个或者多个处于备份角色的路由器。VRRP协议使用选择策略从路由器组中选出一台作为主控,负责ARP相应和转发IP数据包,组中的其它路由器作为备份的角色处于待命状态。当由于某种原因主控路由器发生故障时,备份路由器能在几秒钟的时延后升级为主路由器。由于此切换非常迅速而且不用改变IP地址和MAC地址,故对终端使用者系统是透明的。

二、工作原理

一个VRRP路由器有唯一的标识:VRID,范围为0—255。该路由器对外表现为唯一的虚拟MAC地址,地址的格式为00-00-5E-00-01-[VRID]。主控路由器负责对ARP请求用该MAC地址做应答。这样,无论如何切换,保证给终端设备的是唯一一致的IP和MAC地址,减少了切换对终端设备的影响。

VRRP控制报文只有一种:VRRP通告(advertisement)。它使用IP多播数据包进行封装,组地址为224.0.0.18,发布范围只限于同一局域网内。这保证了VRID在不同网络中可以重复使用。为了减少网络带宽消耗只有主控路由器才可以周期性的发送VRRP通告报文。备份路由器在连续三个通告间隔内收不到VRRP或收到优先级为0的通告后启动新的一轮VRRP选举。

在VRRP路由器组中,按优先级选举主控路由器,VRRP协议中优先级范围是0—255。若VRRP路由器的IP地址和虚拟路由器的接口IP地址相同,则称该虚拟路由器作VRRP组中的IP地址所有者;IP地址所有者自动具有最高优先级:255。优先级0一般用在IP地址所有者主动放弃主控者角色时使用。可配置的优先级范围为1—254。优先级的配置原则可以依据链路的速度和成本、路由器性能和可靠性以及其它管理策略设定。主控路由器的选举中,高优先级的虚拟路由器获胜,因此,如果在VRRP组中有IP地址所有者,则它总是作为主控路由的角色出现。对于相同优先级的候选路由器,按照IP地址大小顺序选举。VRRP还提供了优先级抢占策略,如果配置了该策略,高优先级的备份路由器便会剥夺当前低优先级的主控路由器而成为新的主控路由器。

为了保证VRRP协议的安全性,提供了两种安全认证措施:明文认证和IP头认证。明文认证方式要求:在加入一个VRRP路由器组时,必须同时提供相同的VRID和明文密码。适合于避免在局域网内的配置错误,但不能防止通过网络监听方式获得密码。IP头认证的方式提供了更高的安全性,能够防止报文重放和修改等攻击。

三、 应用实例

最典型的VRRP应用:RTA、RTB组成一个VRRP路由器组,假设RTB的处理能力高于RTA,则将RTB配置成IP地址所有者,H1、H2、H3的默认网关设定为RTB。则RTB成为主控路由器,负责ICMP重定向、ARP应答和IP报文的转发;一旦RTB失败,RTA立即启动切换,成为主控,从而保证了对客户透明的安全切换。

在VRRP应用中,RTA在线时RTB只是作为后备,不参与转发工作,闲置了路由器RTA和链路L1。通过合理的网络设计,可以到达备份和负载分担双重效果。让RTA、RTB同时属于互为备份的两个VRRP组:在组1中RTA为IP地址所有者;组2中RTB为IP地址所有者。将H1的默认网关设定为RTA;H2、H3的默认网关设定为RTB。这样,既分担了设备负载和网络流量,又提高了网络可靠性。

VRRP协议的工作机理与CISCO公司的HSRP(Hot Standby Routing Protocol)有许多相似之处。但二者主要的区别是在CISCO的HSRP中,需要单独配置一个IP地址作为虚拟路由器对外体现的地址,这个地址不能是组中任何一个成员的接口地址。

使用VRRP协议,不用改造目前的网络结构,最大限度保护了当前投资,只需最少的管理费用,却大大提升了网络性能,具有重大的应用价值。

===================================================================================

keepalive的简单应用——管理VIP的飘动 

from:http://www.cnblogs.com/killkill/archive/2010/12/31/1922360.html

VIP的飘动可以为我们解决很多问题,以前我试过使用ifup/ifdown的方式控制网卡的up/down来实现,这种方式有个小问题,就是每次VIP 飘动之后都要等上几十秒才能生效,感觉时间比较长,而且还要配合一些逻辑脚本才能很好地工作,有没有更好的方法呢?当然有,这就是本文的主角—— keepalived。

安装很简单:

1 tar zxvf keepalived-1.1.20.tar.gz 
2 cd keepalived-1.1.20
3 ./configure --prefix=/
4 make
5 make install

修改一下 /etc/keepalived/keepalived.conf 这个配置文件就可以用了,以下是我的环境,192.168.10.141和192.168.10.142是两个VIP,可以在两台服务器之间飘动:

 

 

主机的配置:

01 global_defs {
02    notification_email {
03      failover@firewall.loc
04    }
05    notification_email_from Alexandre.Cassen@firewall.loc
06    smtp_server 192.168.0.48
07    smtp_connect_timeout 10
08    router_id nginx
09 }
10
11 vrrp_instance VI_141 {
12     state BACKUP
13     interface eth0
14     virtual_router_id 141
15     priority 50
16     advert_int 1
17     authentication {
18         auth_type PASS
19         auth_pass 141
20     }
21     virtual_ipaddress {
22         192.168.10.141/26 dev eth0
23     }
24 }
25
26 vrrp_instance VI_142 {
27     state BACKUP
28     interface eth0
29     virtual_router_id 142
30     priority 100
31     advert_int 1
32     authentication {
33         auth_type PASS
34         auth_pass 142
35     }
36     virtual_ipaddress {
37         192.168.10.142/26 dev eth0
38     }
39 }

备机的配置:

01 global_defs {
02    notification_email {
03      failover@firewall.loc
04    }
05    notification_email_from Alexandre.Cassen@firewall.loc
06    smtp_server 10.168.0.48
07    smtp_connect_timeout 10
08    router_id nginx
09 }
10
11 vrrp_instance VI_141 {
12     state BACKUP
13     interface eth0
14     virtual_router_id 141
15     priority 100
16     advert_int 1
17     authentication {
18         auth_type PASS
19         auth_pass 141
20     }
21     virtual_ipaddress {
22         192.168.10.141/26 dev eth0
23     }
24 }
25
26 vrrp_instance VI_142 {
27     state BACKUP
28     interface eth0
29     virtual_router_id 142
30     priority 50
31     advert_int 1
32     authentication {
33         auth_type PASS
34         auth_pass 142
35     }
36     virtual_ipaddress {
37         192.168.10.142/26 dev eth0
38     }
39 }

乍一看,主机和备机的配置文件是一样的,仔细看一下priority的值,使用以下命令即可将keepalived加入linux的服务中:

1 chkconfig --add keepalived ;

通过启、停keepalived这个服务即可观察到VIP的飘动,至于为什么VIP飘动后可以很快地生效,还有待研究。

===================================================================================

haproxy+keepalived实现高可用负载均衡

我的环境:
haproxy keepalived 主:192.168.1.192
haproxy keepalived 备:192.168.1.193
vip:192.168.1.200
web:192.168.1.187:80 192.168.1.187:8000
一:安装过程,在192.168.1.192上:
keepalived的安装:
#tar -zxvf keepalived-1.1.17.tar.gz
#ln -s /usr/src/kernels/2.6.18-128.el5-i686/ /usr/src/linux
#cd keepalived-1.1.17
#./configure –prefix=/ –mandir=/usr/local/share/man/ –with-kernel-dir=/usr/src/kernels/2.6.18-128.el5-i686/
#make && make install
#cd /etc/keepalived/
#mv keepalived.conf keepalived.conf.default
#vi keepalived.conf
! Configuration File for keepalivedvrrp_script chk_http_port {
script “/etc/keepalived/check_haproxy.sh”
interval 2
weight 2}global_defs {
router_id MASTER_DEVEL#192.168.1.193改为BACKUP_DEVEL
}
vrrp_instance VI_1 {
state MASTER #192.168.1.193上改为BACKUP
interface eth0
virtual_router_id 51
priority 150 #192.168.1.193上改为120
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}track_script {
chk_http_port
}virtual_ipaddress {
192.168.1.200
}
}

#vi /etc/keepalived/check_haproxy.sh
#!/bin/bash
/etc/init.d/haproxy status &> /dev/null || /etc/init.d/haproxy restart &> /dev/null
if [ $? -ne 0 ];then
/etc/init.d/keepalived stop &> /dev/null
fi
#chmod 755 /etc/keepalived/check_haproxy.sh

haproxy的安装(主备都一样):
#yum install haproxy
#vi /etc/haproxy/haproxy.cfg

global
log 127.0.0.1 local3 info
maxconn 4096
user nobody
group nobody
daemon
nbproc 1
pidfile /usr/local/haproxy/logs/haproxy.pid

defaults
maxconn 2000
contimeout 5000
clitimeout 30000
srvtimeout 30000
mode http
log global
log 127.0.0.1 local3 info
stats uri /admin?stats
option forwardfor

frontend http_server
bind :80
log global
default_backend info_cache
acl test hdr_dom(host) -i test.domain.com
use_backend cache_test if test

backend info_cache
#balance roundrobin
balance source
option httpchk HEAD /haproxy.txt HTTP/1.1\r\nHost:192.168.1.187
server inst2 192.168.1.187:80 check inter 5000 fall 3

backend cache_test
balance roundrobin
#balance source
option httpchk HEAD /haproxy.txt HTTP/1.1\r\nHost:test.domain.com
server inst1 192.168.1.187:8000 check inter 5000 fall 3

下面是我自己的一个配置列子,在这只做参考

{

global
log 127.0.0.1 local0 debug
log 127.0.0.1 local3 err
maxconn 51200
chroot /var/lib/haproxy
uid 0
gid 0
daemon
nbproc 1
pidfile /var/run/haproxy.pid
stats socket /tmp/haproxy.sock mode 666

defaults
mode http
option redispatch
option abortonclose
# timeout connect 5000ms
# timeout client 30000ms
# timeout server 30000ms
# log 127.0.0.1 local0 err
balance roundrobin

listen http
bind 0.0.0.0:80
mode tcp
server  real1 x.x.x.x check weight 1 minconn 1 maxconn 500 check inter 2000
server real2 x.x.x.x check weight 1 minconn 1 maxconn 500 check inter 2000

#listen proxy_monitor
#bind 0.0.0.0:8888
#option httplog
#stats refresh 30s
#stats uri /stats
#stats realm Haproxy Manager
#stats auth admin:admin

#listen rabbitmq_local_cluster
#bind 0.0.0.0:8080
#timeout connect 10000ms
#timeout client 300000ms
#timeout server 300000ms
#mode tcp
#server  lvs-1 x.x.x.x:8080 check weight 1 minconn 1 maxconn 500 check inter 2000
#server lvs-2 x.x.x.x:8080 check weight 1 minconn 1 maxconn 500 check inter 2000
#server lvs-3 x.x.x.x:8080 check weight 1 minconn 1 maxconn 500 check inter 2000

#listen mysql_mycat
#bind 0.0.0.0:8066
#mode tcp
#server mycat-1 x.x.x.x check weight 1 minconn 1 maxconn 500 check inter 2000
#server mycat-2 x.x.x.x check weight 1 minconn 1 maxconn 500 check inter 2000

}

二:再两台机器上都分别启动:
/etc/init.d/keepalived start (这条命令会自动把haproxy启动)

三:测试:
1.再两台机器上分别执行ip add
主: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
link/ether 00:0c:29:98:cd:c0 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.192/24 brd 192.168.1.255 scope global eth0
inet 192.168.1.200/32 scope global eth0
inet6 fe80::20c:29ff:fe98:cdc0/64 scope link
valid_lft forever preferred_lft forever

备: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
link/ether 00:0c:29:a6:0c:7e brd ff:ff:ff:ff:ff:ff
inet 192.168.1.193/24 brd 255.255.255.254 scope global eth0
inet6 fe80::20c:29ff:fea6:c7e/64 scope link
valid_lft forever preferred_lft forever

2.停掉主上的haproxy,3秒后keepalived会自动将其再次启动
3.停掉主的keepalived,备机马上接管服务
备: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
link/ether 00:0c:29:a6:0c:7e brd ff:ff:ff:ff:ff:ff
inet 192.168.1.193/24 brd 255.255.255.254 scope global eth0
inet 192.168.1.200/32 scope global eth0
inet6 fe80::20c:29ff:fea6:c7e/64 scope link
valid_lft forever preferred_lft forever

4.更改hosts
192.168.1.200 test.com
192.168.1.200 test.domain.com
通过IE测试,可以发现
test.com的请求发向了192.168.1.187:80
test.domain.com的请求发向了192.168.1.187:8000


from:http://www.360doc.com/content/11/0425/11/2054285_112154067.shtml
裂出现的情况:一般物理机上面会出现这样的问题,当业务量比较大的时候也会出现
备注:关于keepalived绑定VIP(俗称:脑裂),主备都同时绑定VIP的解决方案:

这样就说明VRRP组播有问题。

既然VRRP组播有问题,就尝试使用单播发送VRRP报文。修改LB1和LB2的配置
LB1
添加以下配置
unicast_src_ip  10.1.1.12  #LB1机器的IP
unicast_peer {
10.1.1.17 #LB2机器的IP
}

LB2

添加以下配置
unicast_src_ip  10.1.1.17 #LB2机器的IP
unicast_peer {
10.1.1.12 #LB1机器的IP
}

unicast_src_ip 表示发送VRRP单播报文使用的源IP地址
unicast_peer 表示对端接收VRRP单播报文的IP地址

 

基于lvs+keepalived的负载均衡集群搭建

一、环境

系统:centos6.6

机器:DR1和DR2

配置LVS + Keepalived高可用负载均衡集群之图文教程

配置LVS + Keepalived高可用负载均衡集群之图文教程

二、安装lvs和keepalived软件(在DR1和DR2)

检查是否安装了IPVS模块: 注:LVS已经是Linux标准内核的一部分,直接被编译在内核中!

方法1: 查看IPVS模块是否真的编译到内核中去了,如下图所示:

配置LVS + Keepalived高可用负载均衡集群之图文教程

由上可知,有3个项,说明这个功能已经编译到内核中了!

方法2: 使用modprobe命令查看,如下图所示:

配置LVS + Keepalived高可用负载均衡集群之图文教程

由上可知,明显安装了支持LVS的IPVS模块!确认了内核支持LVS的IPVS模块,接下来安装该软件

yum install ipvsadm -y

yum install keepalived -y

三、配置keepalived(DR1和DR2)

在DR1上配置keepalived:

最简单的方法:先在记事本中,手工创建之。复制到/etc/keepalived/keepalived.conf中,保存即可!

//手工创建keepalived.conf文件(vim /etc/keepalived/keepalived.conf)

  1. ! Configuration File for keepalived
  2. global_defs {
  3.    notification_email {
  4.      acassen@firewall.loc   #设置报警邮件地址,可以设置多个,每行1个,
  5.      failover@firewall.loc  #需开启邮件报警及本机的Sendmail服务。
  6.      sysadmin@firewall.loc
  7.    }
  8.    notification_email_from Alexandre.Cassen@firewall.loc
  9.    smtp_server 192.168.200.1  #设置SMTP Server地址;
  10.    smtp_connect_timeout 30
  11.    router_id LVS_DEVEL
  12. }
  13.            ########VRRP Instance########
  14. vrrp_instance VI_1 {
  15.     state MASTER    #指定Keepalived的角色,MASTER为主机服务器,BACKUP为备用服务器
  16.     interface eth0  #BACKUP为备用服务器
  17.     virtual_router_id 51
  18.     priority 100    #定义优先级,数字越大,优先级越高,主DR必须大于备用DR。
  19.     advert_int 1
  20.     authentication {
  21.         auth_type PASS  #设置验证类型,主要有PASS和AH两种
  22.         auth_pass 1111  #设置验证密码
  23.     }
  24.     virtual_ipaddress {
  25.         192.168.1.200  #设置主DR的虚拟IP地址(virtual IP),可多设,但必须每行1个
  26.     }
  27. }
  28.            ########Virtual Server########
  29. virtual_server 192.168.1.200 80 {  #注意IP地址与端口号之间用空格隔开
  30.     delay_loop 6        #设置健康检查时间,单位是秒
  31.     lb_algo rr          #设置负载调度算法,默认为rr,即轮询算法,最优秀是wlc算法
  32.     lb_kind DR          #设置LVS实现LB机制,有NAT、TUNN和DR三个模式可选
  33.     nat_mask 255.255.255.0
  34.     persistence_timeout 50  #会话保持时间,单位为秒
  35.     protocol TCP        #指定转发协议类型,有TCP和UDP两种
  36.     real_server 192.168.1.132 80 {
  37.         weight 1          #配置节点权值,数字越大权值越高
  38.         TCP_CHECK {
  39.             connect_timeout 3     #表示3秒无响应,则超时
  40.             nb_get_retry 3        #表示重试次数
  41.             delay_before_retry 3  #表示重试间隔
  42.         }
  43.     }
  44.     real_server 192.168.1.133 80 {  #配置服务器节点,即Real Server2的public IP
  45.         weight 3            #配置节点权值,数字越大权值越高
  46.         TCP_CHECK {
  47.             connect_timeout 3       #表示3秒无响应,则超时
  48.             nb_get_retry 3          #表示重试次数
  49.             delay_before_retry 3    #表示重试间隔
  50.         }
  51.     }
  52. }启动keepalived、设置keepalived服务自动启动、检查是否生效,如下图所示:

    配置LVS + Keepalived高可用负载均衡集群之图文教程

    在DR2上配置keepalived:

    先将上面记事本中的keepalived.conf配置文件,仅仅修改2处,如下图所示:

    No.1处:把“stateMASTER”修改为“stateBACKUP” #即备用服务器;

    No.2处:把“priority 100”修改为“priority 90” #优先级为90。

    修改好后,复制到DR2的/etc/keepalived/keepalived.conf中,保存即可!

    //启动keepalived、设置keepalived服务自动启动、检查是否生效,如下图所示:

    配置LVS + Keepalived高可用负载均衡集群之图文教程

    四、配置Real Server节点:1)yum –y install httpd

    2)启动httpd服务时,出错:

    配置LVS + Keepalived高可用负载均衡集群之图文教程

    //vim /etc/httpd/conf/httpd.conf中,依如下操作:

    #265 ServerName www.example.com:80 //去掉前面的#号即可!

    3)在RealServer1和RealServer2上的脚本realserver.sh:

    1. #add for chkconfig
    2. #chkconfig: 2345 70 30  #234都是文本界面,5就是图形界面X,70启动顺序号,30系统关闭,脚本
    3. #止顺序号
    4. #description: RealServer’s script  #关于脚本的简短描述
    5. #processname: realserver.sh       #第一个进程名,后边设置自动时会用到
    6. #!/bin/bash
    7. VIP=192.168.1.200
    8. source /etc/rc.d/init.d/functions
    9. case “$1” in
    10. start)
    11.        ifconfig lo:0 $VIP netmask 255.255.255.255 broadcast $VIP
    12.        /sbin/route add -host $VIP dev lo:0
    13.        echo “1” >/proc/sys/net/ipv4/conf/lo/arp_ignore
    14.        echo “2” >/proc/sys/net/ipv4/conf/lo/arp_announce
    15.        echo “1” >/proc/sys/net/ipv4/conf/all/arp_ignore
    16.        echo “2” >/proc/sys/net/ipv4/conf/all/arp_announce
    17.        echo “RealServer Start OK”
    18.        ;;
    19. stop)
    20.        ifconfig lo:0 down
    21.        route del $VIP >/dev/null 2>&1
    22.        echo “0” >/proc/sys/net/ipv4/conf/lo/arp_ignore
    23.        echo “0” >/proc/sys/net/ipv4/conf/lo/arp_announce
    24.        echo “0” >/proc/sys/net/ipv4/conf/all/arp_ignore
    25.        echo “0” >/proc/sys/net/ipv4/conf/all/arp_announce
    26.        echo “RealServer Stoped”
    27.        ;;
    28.        *)
    29.        echo “Usage: $0 {start|stop}”
    30.        exit 1
    31. esac
    32. exit 0

    //为realserver.sh添加权限

    配置LVS + Keepalived高可用负载均衡集群之图文教程

    配置LVS + Keepalived高可用负载均衡集群之图文教程

    配置LVS + Keepalived高可用负载均衡集群之图文教程

    配置LVS + Keepalived高可用负载均衡集群之图文教程

    5. 启动keepalived + LVS集群系统:

    配置LVS + Keepalived高可用负载均衡集群之图文教程

    配置LVS + Keepalived高可用负载均衡集群之图文教程

    6. 测试分为三个部分:1)高可用性功能测试:

    高可用性是通过LVS的两个DR1和DR2完成的。为了模拟故障,先将DR1上的Keepalived服务停止或者干脆关掉节点DR1,然后观察备用DR2上Keepalived是否获得虚拟IP地址,即VIP 192.168.1.200

    //在DR1查看哈,信息如下图所示:

    配置LVS + Keepalived高可用负载均衡集群之图文教程

    由上可知,虚拟IP地址在DR1节点上,即VIP 192.168.1.200

    //在DR2查看哈,信息如下图所示:

    配置LVS + Keepalived高可用负载均衡集群之图文教程

    【思考】自己查查看,一定会成功!

    方法1:命令检验法

    使用命令ipvsadm –Ln和ip addr list/show查看虚拟IP漂移情况来判断。

    //关闭DR1,模拟宕机,如下图所示:

    配置LVS + Keepalived高可用负载均衡集群之图文教程

    由上图可知,DR1确实宕机了,而DR2得到了虚拟IP地址(上图红框中的192.168.1.200)

    且访问依然正常,再次证明了高可用性,如下图所示:

    配置LVS + Keepalived高可用负载均衡集群之图文教程

    方法2:查看日志法

    //关闭DR1/停止DR1的keepalived服务,模拟宕机,在DR2中查看日志,如下图日志所示:

    配置LVS + Keepalived高可用负载均衡集群之图文教程

    //再查看DR2是否立刻检测到DR1出现故障,确实非常快就检测到DR1的故障,并马上接管主机的虚拟IP地址(192.168.1.200),注意观察上图和下图!

    配置LVS + Keepalived高可用负载均衡集群之图文教程

    2)负载均衡测试:

    为了便于测试,我们在Real Server1和Real Server2分别配置www服务的网页且内容也好识别,如下图所示:

    方法1:客户端浏览器中测试

    配置LVS + Keepalived高可用负载均衡集群之图文教程

    由上可知,我们在VMWare中的XP操作系统和物理机器win 7的浏览器中分别输入http://192.168.1.200且不断地刷新之都可访问!

    方法2:用压力测试法

    C++代码
    1. [root@CentOS6 ~]# ab –c 100 –n 10000 http://192.168.1.200/index.html

    3)故障切换测试

    故障切换是测试在某个节点出现故障后,keepalived监控模块能否及时发现,然后屏蔽故障节点,同时将服务转移到正常节点上执行之。

    停掉节点Real Server2服务,模拟该节点出现故障,然后查查看主、备机器日志信息:

    方法1:查看主、备机器日志信息

    //先停掉节点Real Server2的httpd服务,如下图所示:

    配置LVS + Keepalived高可用负载均衡集群之图文教程

    //再查看DR1上的日志,如下图所示:

    配置LVS + Keepalived高可用负载均衡集群之图文教程

    //再查看DR2上的日志,如下图所示:

    配置LVS + Keepalived高可用负载均衡集群之图文教程

    由上可知,故障切换测试成功!

    方法2:客户端浏览器中测试

    配置LVS + Keepalived高可用负载均衡集群之图文教程

    不断刷新网页一直就显示如上图所示:Real Server1内容!可见故障切换成功!

 

zabbix监控LVS连接的状态

一、编写lvs监控脚本

vim /etc/zabbix/zabbix_agentd.script/lvs_status.sh

#!/bin/bash
function lvs_total {
#获取lvs的总处理请求数
sudo /sbin/ipvsadm -Ln | awk ‘{print $6}’ |awk ‘BEGIN{sum=0}{sum+=$1}END{print sum}’
}

function lvs_real1 {
#获取realserver10.129.13.208的处理请求数
sudo /sbin/ipvsadm -Ln | grep ‘10.129.13.208’ |awk ‘{print $6}’
}

function lvs_real2 {
#获取realserver10.129.13.208的处理请求数
sudo /sbin/ipvsadm -Ln | grep ‘10.129.13.208’ |awk ‘{print $6}’
}
$1

 

二、创建/etc/zabbix/zabbix_agentd.d/userparameter_lvs.conf

UserParameter=lvs[*],/etc/zabbix/zabbix_agentd.script/lvs_status.sh $1

三、修改sudo的配置文件

#visudo

添加如下内容

zabbix   ALL=(ALL)  NOPASSWD:/sbin/ipvsadm

把Defaults  requiretty 这行注释掉,如果不注释掉这行会出现以下问题:

sudo: sorry, you must have a tty to run sudo
导致这问题的原因是 sudo默认需要在 tty终端里才能正确被调用,我们可以通过修改 /etc/sudoers配置文件来解决这个问题

四、重启zabbix_agent客户端

/etc/init.d/zabbix-agent restart

五、图形化界面配置(略)

Python中第三方的用于解析HTML的库:BeautifulSoup

1.背景

在Python去写爬虫,网页解析等过程中,常常需要涉及到HTML等网页的解析。

当然,对于简单的HTML中内容的提取,Python内置的正则表达式Re模块,就足够用了,

但是对于复杂的HTML的处理,尤其是一些非法的,有bug的html代码的处理,那么最好还是用专门的HTML的解析的库。

Python中的,专门用于HTML解析的库,比较好用的,就是BeautifulSoup。

2.BeautifulSoup简介

Python中,专门用于HTML/XML解析的库;

特点是:

即使是有bug,有问题的html代码,也可以解析。

功能很强大;

BeautifulSoup的主页是:

http://www.crummy.com/software/BeautifulSoup/

 

2.1.BeautifulSoup的版本

BeautifulSoup主要有两个版本:

 

2.1.1.BeautifulSoup 3

之前的,比较早的,是3.x的版本。

2.1.1.1BeautifulSoup 3的在线文档

最新的,可用的,在线文档是:

http://www.crummy.com/software/BeautifulSoup/bs3/documentation.html

中文版的是:

http://www.crummy.com/software/BeautifulSoup/bs3/documentation.zh.html

 

2.1.1.2下载BeautifulSoup 3

http://www.crummy.com/software/BeautifulSoup/bs3/download//3.x/

中可以下载到很多版本,比如我常用的3.0.6的版本:

BeautifulSoup-3.0.6.py

http://www.crummy.com/software/BeautifulSoup/bs3/download//3.x/BeautifulSoup-3.0.6.py

 

2.1.2.BeautifulSoup 4:缩写为bs4

最新的v4版本的BeautifulSoup,改名为bs4了。

 

注意:

使用bs4时,导入BeautifulSoup的写法是:

1
from bs4 import BeautifulSoup;

然后就可以像之前3.x中一样,直接使用BeautifulSoup了。

bs4的在线文档

http://www.crummy.com/software/BeautifulSoup/bs4/doc/

 

下载bs4

http://www.crummy.com/software/BeautifulSoup/bs4/download/

可以下载到对应的bs4的版本,比如:

此时最新的版本是:

beautifulsoup4-4.1.3.tar.gz

http://www.crummy.com/software/BeautifulSoup/bs4/download/beautifulsoup4-4.1.3.tar.gz

3.BeautifulSoup的用法

如何安装BeautifulSoup,3.0.6之前,都是不需要安装的,所以使用起来最简单,直接下载对应的版本,比如:

http://www.crummy.com/software/BeautifulSoup/bs3/download//3.x/BeautifulSoup-3.0.6.py

得到了BeautifulSoup-3.0.6.py,然后改名为:BeautifulSoup.py

然后,放到和你当前的python文件同目录下,比如我当前python文件是:

D:\tmp\tmp_dev_root\python\beautifulsoup_demo\beautifulsoup_demo.py

那就放到

D:\tmp\tmp_dev_root\python\beautifulsoup_demo\

下面,和beautifulsoup_demo.py同目录。

3.0.6之后:需要安装BeautifulSoup后才可使用

使用BeautifulSoup提取html中的某个内容

关于最简单的,最基本的用法,提取html中的某个内容,具体用法,就是使用对应的find函数。

完整代码是:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
Function:
【教程】Python中第三方的用于解析HTML的库:BeautifulSoup
Author:     Crifan Li
Version:    2012-12-26
Contact:    admin at crifan dot com
"""
from BeautifulSoup import BeautifulSoup;
def beautifulsoupDemo():
    demoHtml = """
<html>
<body>
<div class="icon_col">
        <h1 class="h1user">crifan</h1>
 </div>
 </body>
</html>
""";
    soup = BeautifulSoup(demoHtml);
    print "type(soup)=",type(soup); #type(soup)= <type 'instance'>
    print "soup=",soup;
    
    # 1. extract content
    # method 1: no designate para name
    #h1userSoup = soup.find("h1", {"class":"h1user"});
    # method 2: use para name
    h1userSoup = soup.find(name="h1", attrs={"class":"h1user"});
    # more can found at:
    #http://www.crummy.com/software/BeautifulSoup/bs3/documentation.zh.html#find%28name,%20attrs,%20recursive,%20text,%20**kwargs%29
    print "h1userSoup=",h1userSoup; #h1userSoup= <h1 class="h1user">crifan</h1>
    h1userUnicodeStr = h1userSoup.string;
    print "h1userUnicodeStr=",h1userUnicodeStr; #h1userUnicodeStr= crifan
if __name__ == "__main__":
    beautifulsoupDemo();

输出为:

1
2
3
4
5
6
7
8
9
10
11
12
13
D:\tmp\tmp_dev_root\python\beautifulsoup_demo>beautifulsoup_demo.py
type(soup)= <type 'instance'>
soup=
<html>
<body>
<div class="icon_col">
<h1 class="h1user">crifan</h1>
</div>
</body>
</html>
h1userSoup= <h1 class="h1user">crifan</h1>
h1userUnicodeStr= crifan

使用BeautifulSoup修改/改变/替换原先html中的某个内容

如果需要改变原先html中的某个值,可以参考官网解释:

修改属性值

后来证实,只能改(Tag的)中的属性的值,不能改(Tag的)的值本身

完整示例代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
Function:
【教程】Python中第三方的用于解析HTML的库:BeautifulSoup
Author:     Crifan Li
Version:    2013-02-01
Contact:    admin at crifan dot com
"""
from BeautifulSoup import BeautifulSoup;
def beautifulsoupDemo():
    demoHtml = """
<html>
<body>
<div class="icon_col">
        <h1 class="h1user">crifan</h1>
 </div>
 </body>
</html>
""";
    soup = BeautifulSoup(demoHtml);
    print "type(soup)=",type(soup); #type(soup)= <type 'instance'>
    print "soup=",soup;
    
    print '{0:=^80}'.format(" 1. extract content ");
    # method 1: no designate para name
    #h1userSoup = soup.find("h1", {"class":"h1user"});
    # method 2: use para name
    h1userSoup = soup.find(name="h1", attrs={"class":"h1user"});
    # more can found at:
    #http://www.crummy.com/software/BeautifulSoup/bs3/documentation.zh.html#find%28name,%20attrs,%20recursive,%20text,%20**kwargs%29
    print "h1userSoup=",h1userSoup; #h1userSoup= <h1 class="h1user">crifan</h1>
    h1userUnicodeStr = h1userSoup.string;
    print "h1userUnicodeStr=",h1userUnicodeStr; #h1userUnicodeStr= crifan
    
    print '{0:=^80}'.format(" 2. demo change tag value and property ");
    print '{0:-^80}'.format(" 2.1 can NOT change tag value ");
    print "old tag value=",soup.body.div.h1.string; #old tag value= crifan
    changedToString = u"CrifanLi";
    soup.body.div.h1.string = changedToString;
    print "changed tag value=",soup.body.div.h1.string; #changed tag value= CrifanLi
    print "After changed tag value, new h1=",soup.body.div.h1; #After changed tag value, new h1= <h1 class="h1user">crifan</h1>
    print '{0:-^80}'.format(" 2.2 can change tag property ");  
    soup.body.div.h1['class'] = "newH1User";
    print "changed tag property value=",soup.body.div.h1; #changed tag property value= <h1 class="newH1User">crifan</h1>
if __name__ == "__main__":
    beautifulsoupDemo();

更多的,用法和使用心得,部分内容,请参考:

http://www.crifan.com/files/doc/docbook/python_topic_beautifulsoup/release/html/python_topic_beautifulsoup.html

ansible常用的一些方法总结

1.把ansible这台机器上的/etc/hosts远程scp到webserver组下面的所有机器上/root/hosts
ansible webserver -m copy -a “src=/etc/hosts dest=/root/hosts”
2.修改webserver组下面所有的机器上的文件/root/hosts的属主:test,权限755
ansible webserver -m file -a “dest=/root/hosts mode=755 owner=test group=test”

3.在webserver组下面所有的机器上创建kk目录,属主:test,权限755
ansible webserver -m file -a “dest=/root/kk mode=755 owner=test group=test state=directory”

4.删除目录kk(递归的删除)和删除文件
ansible webserver -m file -a “dest=/root/kk state=absent”

5.采用shell的模块在root下创建kk文件,-f 10(一般在配置文件里面写好)是以并行的方式去执行,会fork出10个子进程,速度变快
ansible webserver -m shell -a ‘touch /root/kk’ -f 10

6.指定以test用户,该test用户是指webserver下所有机器的test用户,以普通用户test执行命令
ansible webserver -a “/usr/bin/foo” -u test –ask-sudo-pass

7.确认软件包wget已经安装,确认wget软件包还没有安装
ansible webserver -m yum -a “name=wget state=present”
ansible webserver -m yum -a “name=wget state=absent”

8.确认某个服务在所有的webservers上都已经启动,重启,确认服务已经停止
ansible webserver -m service -a “name=httpd state=started”
ansible webserver -m service -a “name=httpd state=restarted”
ansible webserver -m service -a “name=httpd state=stopped”

9.如果运行命令后,不想获取返回的信息
ansible all -B 3600 -P 0 -a “/usr/bin/long_running_operation –do-stuff”
如果你确定要在命令运行后检查运行的状态,可以使用 async_status 模块.前面执行后台命令后会返回一个 job id, 将这个 id 传给 async_status 模块
ansible web1.example.com -m async_status -a “jid=488359678239.2844”
获取状态的命令,其中 -B 1800 表示最多运行30分钟, -P 60 表示每隔60秒获取一次状态信息
ansible all -B 1800 -P 60 -a “/usr/bin/long_running_operation –do-stuff”

10.在执行一个 playbook 之前,想看看这个 playbook 的执行会影响到哪些 hosts
ansible-playbook playbook.yml –list-hosts

11.ansible-playbook执行相应的rabbitmq_pp_restart.yml脚本
sshpass -p $PP_Pass ansible-playbook rabbitmq_pp_restart.yml –ask-pass -i /root/ansible-playbook/ycq/ycq.host

12.ansible的script模块的执行方式,主要把本地的脚本推送到远端,在远端执行该脚本
sshpass -p $Prd_Pass ansible check-port-8080 -m script -a “/home/appuser/zabbix_ycq/scripts/check_tomcat_port.sh” -i /home/appuser/zabbix_ycq/ansible-playbook/ycq.host –ask-pass

linux的nohup命令的用法

在应用Unix/Linux时,我们一般想让某个程序在后台运行,于是我们将常会用 & 在程序结尾来让程序自动运行。比如我们要运行mysql在后台: /usr/local/mysql/bin/mysqld_safe –user=mysql &。可是有很多程序并不想mysqld一样,这样我们就需要nohup命令,怎样使用nohup命令呢?这里讲解nohup命令的一些用法。

nohup /root/start.sh &

在shell中回车后提示:

[~]$ appending output to nohup.out

原程序的的标准输出被自动改向到当前目录下的nohup.out文件,起到了log的作用。

但是有时候在这一步会有问题,当把终端关闭后,进程会自动被关闭,察看nohup.out可以看到在关闭终端瞬间服务自动关闭。

咨询红旗Linux工程师后,他也不得其解,在我的终端上执行后,他启动的进程竟然在关闭终端后依然运行。

在第二遍给我演示时,我才发现我和他操作终端时的一个细节不同:他是在当shell中提示了nohup成功后还需要按终端上键盘任意键退回到shell输入命令窗口,然后通过在shell中输入exit来退出终端;而我是每次在nohup执行成功后直接点关闭程序按钮关闭终端.。所以这时候会断掉该命令所对应的session,导致nohup对应的进程被通知需要一起shutdown。

这个细节有人和我一样没注意到,所以在这儿记录一下了。

附:nohup命令参考

nohup 命令

用途:不挂断地运行命令。

语法:nohup Command [ Arg … ] [ & ]

描述:nohup 命令运行由 Command 参数和任何相关的 Arg 参数指定的命令,忽略所有挂断(SIGHUP)信号。在注销后使用 nohup 命令运行后台中的程序。要运行后台中的 nohup 命令,添加 & ( 表示”and”的符号)到命令的尾部。

无论是否将 nohup 命令的输出重定向到终端,输出都将附加到当前目录的 nohup.out 文件中。如果当前目录的 nohup.out 文件不可写,输出重定向到 $HOME/nohup.out 文件中。如果没有文件能创建或打开以用于追加,那么 Command 参数指定的命令不可调用。如果标准错误是一个终端,那么把指定的命令写给标准错误的所有输出作为标准输出重定向到相同的文件描述符。

退出状态:该命令返回下列出口值:

126 可以查找但不能调用 Command 参数指定的命令。

127 nohup 命令发生错误或不能查找由 Command 参数指定的命令。

否则,nohup 命令的退出状态是 Command 参数指定命令的退出状态。

nohup命令及其输出文件

nohup命令:如果你正在运行一个进程,而且你觉得在退出帐户时该进程还不会结束,那么可以使用nohup命令。该命令可以在你退出帐户/关闭终端之后继续运行相应的进程。nohup就是不挂起的意思( n ohang up)。

该命令的一般形式为:nohup command &

使用nohup命令提交作业

如果使用nohup命令提交作业,那么在缺省情况下该作业的所有输出都被重定向到一个名为nohup.out的文件中,除非另外指定了输出文件:

nohup command > myout.file 2>&1 &

在上面的例子中,输出被重定向到myout.file文件中。

使用 jobs 查看任务。

使用 fg %n 关闭。

另外有两个常用的ftp工具ncftpget和ncftpput,可以实现后台的ftp上传和下载,这样就可以利用这些命令在后台上传和下载文件了。

Work for fun,Live for love!

PS:在写ansible-playbook脚本是,想把一个脚本推送到远端执行,一直没有成功,后来在同事的帮助下,找到了问题的所在

脚本内容如下:

#!/bin/bash
source ~/.bashrc
port=`sudo netstat -ltunp | grep 8080 | awk -F’:’ ‘{print $4}’`
if [ $port = 8080 ];
then
/app/tomcat-8080/bin/shutdown.sh -force
nohup /bin/sh /app/tomcat-8080/bin/startup.sh &
else
nohup /bin/sh /app/tomcat-8080/bin/startup.sh &
fi

该脚本推送到对端服务器上,在对端服务器上可以执行成功,但推送过去就无法执行,主要原因就是脚本在推送过去的时候就会瞬间执行完后就会关闭,加上nohup+脚本+&后,执行成功,完美的解决刚才的问题。

 

linux screen 命令详解

一、背景

系统管理员经常需要SSH 或者telent 远程登录到Linux 服务器,经常运行一些需要很长时间才能完成的任务,比如系统备份、ftp 传输等等。通常情况下我们都是为每一个这样的任务开一个远程终端窗口,因为它们执行的时间太长了。必须等待它们执行完毕,在此期间不能关掉窗口或者断开连接,否则这个任务就会被杀掉,一切半途而废了。

二、简介

GNU Screen是一款由GNU计划开发的用于命令行终端切换的自由软件。用户可以通过该软件同时连接多个本地或远程的命令行会话,并在其间自由切换。

GNU Screen可以看作是窗口管理器的命令行界面版本。它提供了统一的管理多个会话的界面和相应的功能。

  • 会话恢复
只要Screen本身没有终止,在其内部运行的会话都可以恢复。这一点对于远程登录的用户特别有用——即使网络连接中断,用户也不会失去对已经打开的命令行会话的控制。只要再次登录到主机上执行screen -r就可以恢复会话的运行。同样在暂时离开的时候,也可以执行分离命令detach,在保证里面的程序正常运行的情况下让Screen挂起(切换到后台)。这一点和图形界面下的VNC很相似。
  • 多窗口
在Screen环境下,所有的会话都独立的运行,并拥有各自的编号、输入、输出和窗口缓存。用户可以通过快捷键在不同的窗口下切换,并可以自由的重定向各个窗口的输入和输出。Screen实现了基本的文本操作,如复制粘贴等;还提供了类似滚动条的功能,可以查看窗口状况的历史记录。窗口还可以被分区和命名,还可以监视后台窗口的活动。
  • 会话共享
Screen可以让一个或多个用户从不同终端多次登录一个会话,并共享会话的所有特性(比如可以看到完全相同的输出)。它同时提供了窗口访问权限的机制,可以对窗口进行密码保护。

GNU’s Screen 官方站点:http://www.gnu.org/software/screen/

三、语法

# screen [-AmRvx -ls -wipe][-d <作业名称>][-h <行数>][-r <作业名称>][-s ][-S <作业名称>]

参数说明

-A  将所有的视窗都调整为目前终端机的大小。
-d <作业名称>  将指定的screen作业离线。
-h <行数>  指定视窗的缓冲区行数。
-m  即使目前已在作业中的screen作业,仍强制建立新的screen作业。
-r <作业名称>  恢复离线的screen作业。
-R  先试图恢复离线的作业。若找不到离线的作业,即建立新的screen作业。
-s  指定建立新视窗时,所要执行的shell。
-S <作业名称>  指定screen作业的名称。
-v  显示版本信息。
-x  恢复之前离线的screen作业。
-ls或–list  显示目前所有的screen作业。
-wipe  检查目前所有的screen作业,并删除已经无法使用的screen作业。

四、常用screen参数

screen -S yourname -> 新建一个叫yourname的session
screen -ls -> 列出当前所有的session
screen -r yourname -> 回到yourname这个session
screen -d yourname -> 远程detach某个session
screen -d -r yourname -> 结束当前session并回到yourname这个session

在每个screen session 下,所有命令都以 ctrl+a(C-a) 开始。
C-a ? -> 显示所有键绑定信息
C-a c -> 创建一个新的运行shell的窗口并切换到该窗口
C-a n -> Next,切换到下一个 window
C-a p -> Previous,切换到前一个 window
C-a 0..9 -> 切换到第 0..9 个 window
Ctrl+a [Space] -> 由视窗0循序切换到视窗9
C-a C-a -> 在两个最近使用的 window 间切换
C-a x -> 锁住当前的 window,需用用户密码解锁
C-a d -> detach,暂时离开当前session,将目前的 screen session (可能含有多个 windows) 丢到后台执行,并会回到还没进 screen 时的状态,此时在 screen session 里,每个 window 内运行的 process (无论是前台/后台)都在继续执行,即使 logout 也不影响。
C-a z -> 把当前session放到后台执行,用 shell 的 fg 命令则可回去。
C-a w -> 显示所有窗口列表
C-a t -> Time,显示当前时间,和系统的 load
C-a k -> kill window,强行关闭当前的 window
C-a [ -> 进入 copy mode,在 copy mode 下可以回滚、搜索、复制就像用使用 vi 一样
C-b Backward,PageUp
C-f Forward,PageDown
H(大写) High,将光标移至左上角
L Low,将光标移至左下角
0 移到行首
$ 行末
w forward one word,以字为单位往前移
b backward one word,以字为单位往后移
Space 第一次按为标记区起点,第二次按为终点
Esc 结束 copy mode
C-a ] -> Paste,把刚刚在 copy mode 选定的内容贴上

五、使用 screen

5.1 安装screen

流行的Linux发行版(例如Red Hat Enterprise Linux)通常会自带screen实用程序,如果没有的话,可以从GNU screen的官方网站下载。

[root@TS-DEV ~]# yum install screen
[root@TS-DEV ~]# rpm -qa|grep screen
screen-4.0.3-4.el5
[root@TS-DEV ~]#

5.2 创建一个新的窗口

安装完成后,直接敲命令screen就可以启动它。但是这样启动的screen会话没有名字,实践上推荐为每个screen会话取一个名字,方便分辨:

[root@TS-DEV ~]# screen -S david

screen启动后,会创建第一个窗口,也就是窗口No. 0,并在其中打开一个系统默认的shell,一般都会是bash。所以你敲入命令screen之后,会立刻又返回到命令提示符,仿佛什么也没有发生似的,其实你已经进入Screen的世界了。当然,也可以在screen命令之后加入你喜欢的参数,使之直接打开你指定的程序,例如:

[root@TS-DEV ~]# screen vi david.txt

screen创建一个执行vi david.txt的单窗口会话,退出vi 将退出该窗口/会话。

5.3 查看窗口和窗口名称

打开多个窗口后,可以使用快捷键C-a w列出当前所有窗口。如果使用文本终端,这个列表会列在屏幕左下角,如果使用X环境下的终端模拟器,这个列表会列在标题栏里。窗口列表的样子一般是这样:

0$ bash  1-$ bash  2*$ bash

这个例子中我开启了三个窗口,其中*号表示当前位于窗口2,-号表示上一次切换窗口时位于窗口1。

Screen默认会为窗口命名为编号和窗口中运行程序名的组合,上面的例子中窗口都是默认名字。练习了上面查看窗口的方法,你可能就希望各个窗口可以有不同的名字以方便区分了。可以使用快捷键C-a A来为当前窗口重命名,按下快捷键后,Screen会允许你为当前窗口输入新的名字,回车确认。

5.4 会话分离与恢复

你可以不中断screen窗口中程序的运行而暂时断开(detach)screen会话,并在随后时间重新连接(attach)该会话,重新控制各窗口中运行的程序。例如,我们打开一个screen窗口编辑/tmp/david.txt文件:

[root@TS-DEV ~]# screen vi /tmp/david.txt

之后我们想暂时退出做点别的事情,比如出去散散步,那么在screen窗口键入ctrl+a+d,Screen会给出detached提示:

暂时中断会话

半个小时之后回来了,找到该screen会话:

[root@TS-DEV ~]# screen -ls

重新连接会话:

[root@TS-DEV ~]# screen -r 12865

一切都在。

当然,如果你在另一台机器上没有分离一个Screen会话,就无从恢复会话了。

这时可以使用下面命令强制将这个会话从它所在的终端分离,转移到新的终端上来:

5.5 清除dead 会话

如果由于某种原因其中一个会话死掉了(例如人为杀掉该会话),这时screen -list会显示该会话为dead状态。使用screen -wipe命令清除该会话:

5.6 关闭或杀死窗口

正常情况下,当你退出一个窗口中最后一个程序(通常是bash)后,这个窗口就关闭了。另一个关闭窗口的方法是使用C-a k,这个快捷键杀死当前的窗口,同时也将杀死这个窗口中正在运行的进程。

如果一个Screen会话中最后一个窗口被关闭了,那么整个Screen会话也就退出了,screen进程会被终止。

除了依次退出/杀死当前Screen会话中所有窗口这种方法之外,还可以使用快捷键C-a :,然后输入quit命令退出Screen会话。需要注意的是,这样退出会杀死所有窗口并退出其中运行的所有程序。其实C-a :这个快捷键允许用户直接输入的命令有很多,包括分屏可以输入split等,这也是实现Screen功能的一个途径,不过个人认为还是快捷键比较方便些。

六、screen 高级应用 

6.1 会话共享

还有一种比较好玩的会话恢复,可以实现会话共享。假设你在和朋友在不同地点以相同用户登录一台机器,然后你创建一个screen会话,你朋友可以在他的终端上命令:

[root@TS-DEV ~]# screen -x

这个命令会将你朋友的终端Attach到你的Screen会话上,并且你的终端不会被Detach。这样你就可以和朋友共享同一个会话了,如果你们当前又处于同一个窗口,那就相当于坐在同一个显示器前面,你的操作会同步演示给你朋友,你朋友的操作也会同步演示给你。当然,如果你们切换到这个会话的不同窗口中去,那还是可以分别进行不同的操作的。

6.2 会话锁定与解锁

Screen允许使用快捷键C-a s锁定会话。锁定以后,再进行任何输入屏幕都不会再有反应了。但是要注意虽然屏幕上看不到反应,但你的输入都会被Screen中的进程接收到。快捷键C-a q可以解锁一个会话。

也可以使用C-a x锁定会话,不同的是这样锁定之后,会话会被Screen所属用户的密码保护,需要输入密码才能继续访问这个会话。

6.3 发送命令到screen会话

在Screen会话之外,可以通过screen命令操作一个Screen会话,这也为使用Screen作为脚本程序增加了便利。关于Screen在脚本中的应用超出了入门的范围,这里只看一个例子,体会一下在会话之外对Screen的操作:

[root@TS-DEV ~]# screen -S sandy -X screen ping www.baidu.com

这个命令在一个叫做sandy的screen会话中创建一个新窗口,并在其中运行ping命令。

6.4 屏幕分割

现在显示器那么大,将一个屏幕分割成不同区域显示不同的Screen窗口显然是个很酷的事情。可以使用快捷键C-a S将显示器水平分割,Screen 4.00.03版本以后,也支持垂直分屏,快捷键是C-a |。分屏以后,可以使用C-a <tab>在各个区块间切换,每一区块上都可以创建窗口并在其中运行进程。

可以用C-a X快捷键关闭当前焦点所在的屏幕区块,也可以用C-a Q关闭除当前区块之外其他的所有区块。关闭的区块中的窗口并不会关闭,还可以通过窗口切换找到它。

6.5 C/P模式和操作

screen的另一个很强大的功能就是可以在不同窗口之间进行复制粘贴了。使用快捷键C-a <Esc>或者C-a [可以进入copy/paste模式,这个模式下可以像在vi中一样移动光标,并可以使用空格键设置标记。其实在这个模式下有很多类似vi的操作,譬如使用/进行搜索,使用y快速标记一行,使用w快速标记一个单词等。关于C/P模式下的高级操作,其文档的这一部分有比较详细的说明。

一般情况下,可以移动光标到指定位置,按下空格设置一个开头标记,然后移动光标到结尾位置,按下空格设置第二个标记,同时会将两个标记之间的部分储存在copy/paste buffer中,并退出copy/paste模式。在正常模式下,可以使用快捷键C-a ]将储存在buffer中的内容粘贴到当前窗口。

6.6 更多screen功能

同大多数UNIX程序一样,GNU Screen提供了丰富强大的定制功能。你可以在Screen的默认两级配置文件/etc/screenrc和$HOME/.screenrc中指定更多,例如设定screen选项,定制绑定键,设定screen会话自启动窗口,启用多用户模式,定制用户访问权限控制等等。如果你愿意的话,也可以自己指定screen配置文件。

以多用户功能为例,screen默认是以单用户模式运行的,你需要在配置文件中指定multiuser on 来打开多用户模式,通过acl*(acladd,acldel,aclchg…)命令,你可以灵活配置其他用户访问你的screen会话。更多配置文件内容请参考screen的man页。

Python利用Zabbix API定时报告存在报警的机器(更新:针对zabbix3.x)

前一篇Python利用Zabbix API定时报告存在报警的机器做完后发现仅能运行在zabbix2.x环境,由于我们有一部分环境是在zabbix3.x下检测的,故需要能够运行在3.x下。可是跑脚本发现如下问题:

zabbix2.x返回trigger.get:

{u'jsonrpc': u'2.0', u'result': [{u'hostid': u'10271', u'description': u'Too many MongoDB page_faults', u'hostname': u'192.168.1.161', u'host': u'192.168.1.161', u'priority': u'4', u'triggerid': u'19429'}, {u'hostid': u'10263', u'description': u'Tcp Port 18083 Status', u'hostname': u'192.168.1.153', u'host': u'192.168.1.153', u'priority': u'4', u'triggerid': u'18542'}, {u'hostid': u'10255', u'description': u'Zabbix agent on {HOST.NAME} is unreachable for 5 minutes', u'hostname': u'10.25.20.103', u'host': u'10.25.20.103', u'priority': u'3', u'triggerid': u'17938'}, {u'hostid': u'10281', u'description': u'Free disk space is less than 20% on volume /', u'hostname': u'192.168.1.143', u'host': u'192.168.1.143', u'priority': u'2', u'triggerid': u'18662'}, {u'hostid': u'10231', u'description': u'Free disk space is less than 20% on volume /file', u'hostname': u'192.168.2。108', u'host': u'.192..168.2.108', u'priority': u'2', u'triggerid': u'17499'}, {u'hostid': u'10271', u'description': u'Free disk space is less than 20% on volume /', u'hostname': u'192.168.1.161', u'host': u'192.168.1.161', u'priority': u'2', u'triggerid': u'18421'}, {u'hostid': u'10196', u'description': u'Free disk space is less than 20% on volume /data', u'hostname': u'192.168.1.113', u'host': u'192.168.1.113', u'priority': u'2', u'triggerid': u'16368'}, {u'hostid': u'10257', u'description': u'Free disk space is less than 20% on volume /app', u'hostname': u'192.168.20.105', u'host': u'192.168.20.105', u'priority': u'2', u'triggerid': u'19440'}, {u'hostid': u'10279', u'description': u'Free disk space is less than 20% on volume /', u'hostname': u'192.168.1.149', u'host': u'192.168.1.149', u'priority': u'2', u'triggerid': u'18605'}, {u'hostid': u'10231', u'description': u'Lack of free swap space on {HOST.NAME}', u'hostname': u'192.168.2.108', u'host': u'192.168.2.108', u'priority': u'2', u'triggerid': u'17489'}, {u'hostid': u'10197', u'description': u'Free disk space is less than 20% on volume /data', u'hostname': u'192.168.1.114', u'host': u'192.168.1.114', u'priority': u'2', u'triggerid': u'16374'}, {u'hostid': u'10232', u'description': u'OSS Dev Status Not OK', u'hostname': u'ESB unit test server', u'host': u'192.168.1.200', u'priority': u'1', u'triggerid': u'18749'}, {u'hostid': u'10234', u'description': u'SpKeyAvailableDaysTrigger', u'hostname': u'zabbix_server', u'host': u'zabbix_server', u'priority': u'1', u'triggerid': u'18717'}], u'id': 1}

zabbix3.x返回trigger.get:

{u'jsonrpc': u'2.0', u'result': [{u'status': u'0', u'functions': [{u'itemid': u'30286', u'function': u'last', u'triggerid': u'16195', u'parameter': u'', u'functionid': u'16164'}], u'description': u'Mysql Replication error on {HOST.NAME}', u'state': u'0', u'url': u'', u'type': u'0', u'templateid': u'16191', u'lastchange': u'1478593366', u'value': u'1', u'priority': u'3', u'triggerid': u'16195', u'flags': u'0', u'comments': u'', u'error': u'', u'expression': u'{16164}<>2'}, {u'status': u'1', u'functions': [{u'itemid': u'23310', u'function': u'last', u'triggerid': u'13500', u'parameter': u'0', u'functionid': u'12909'}], u'description': u'Lack of free swap space on {HOST.NAME}', u'state': u'0', u'url': u'', u'type': u'0', u'templateid': u'10012', u'lastchange': u'1464343050', u'value': u'1', u'priority': u'2', u'triggerid': u'13500', u'flags': u'0', u'comments': u'It probably means that the systems requires more physical memory.', u'error': u'', u'expression': u'{12909}<50'}, {u'status': u'0', u'functions': [{u'itemid': u'30068', u'function': u'last', u'triggerid': u'16102', u'parameter': u'0', u'functionid': u'15705'}], u'description': u'Free disk space is less than 20% on volume /app', u'state': u'0', u'url': u'', u'type': u'0', u'templateid': u'0', u'lastchange': u'1487316369', u'value': u'1', u'priority': u'2', u'triggerid': u'16102', u'flags': u'4', u'comments': u'', u'error': u'', u'expression': u'{15705}<20'}], u'id': 1}

仔细对比能发现,3.0中没有任何关于host、hostname或者hostid的信息,这里让我痛苦了好久,网上也没有现成的办法,最后还是看zabbix的官方api想到了办法——根据triggerids来查询相关host信息。

脚本如下:

#!/usr/bin/python
#coding:utf-8
import json
import urllib2
from urllib2 import URLError
import sys
import zabbix_sendmail
#接收人
mailtolist = ['test@163.com',]
#格式:zabbix地址,zabbix帐号,zabbix密码,邮件标题
zabbix_addresses=['http://test1.zabbix.com,admin,123456,test1','http://test2.zabbix.com,admin,123456,test2','http://test3.zabbix.com,admin,123456,test3']
class ZabbixTools:
    def __init__(self,address,username,password):
                                                                       
        self.address = address
        self.username = username
        self.password = password
                                                                       
        self.url = '%s/api_jsonrpc.php' % self.address
        self.header = {"Content-Type":"application/json"}
                                                                       
                                                                       
                                                                       
    def user_login(self):
        data = json.dumps({
                           "jsonrpc": "2.0",
                           "method": "user.login",
                           "params": {
                                      "user": self.username,
                                      "password": self.password
                                      },
                           "id": 0
                           })
                                                                       
        request = urllib2.Request(self.url, data)
        for key in self.header:
            request.add_header(key, self.header[key])
                                                                   
        try:
            result = urllib2.urlopen(request)
        except URLError as e:
            print "Auth Failed, please Check your name and password:", e.code
        else:
            response = json.loads(result.read())
            result.close()
            #print response['result']
            self.authID = response['result']
            return self.authID
                                                                           
    def trigger_get(self):
        data = json.dumps({
                           "jsonrpc":"2.0",
                           "method":"trigger.get",
                           "params": {
                                      "output": [
                                                "triggerid",
                                                "description",
                                                "priority"
                                                ],
                                      "filter": {
                                                 "value": 1
                                                 },
                                      "expandData":"hostname",
                                      "sortfield": "priority",
                                      "sortorder": "DESC"
                                    },
                           "auth": self.user_login(),
                           "id":1              
        })
                                                                       
        request = urllib2.Request(self.url, data)
        for key in self.header:
            request.add_header(key, self.header[key])
                                                                       
        try:
            result = urllib2.urlopen(request)
        except URLError as e:
            print "Error as ", e
        else:
            response = json.loads(result.read())
            result.close()
            issues = response['result']
            content = ''
            if issues:
                for line in issues:
                    triggerid=line['triggerid']
                    host=self.host_get(triggerid)
                    content = content + "%s:%s\r\n" % (host,line['description'])
            return content
   
   def host_get(self,triggerid):
        data = json.dumps({
                           "jsonrpc":"2.0",
                           "method":"host.get",
                           "params": {
                                     "triggerids": triggerid
                                    },
                           "auth": self.user_login(),
                           "id":1
        })

        request = urllib2.Request(self.url, data)
        for key in self.header:
            request.add_header(key, self.header[key])

        try:
            result = urllib2.urlopen(request)
        except URLError as e:
            print "Error as ", e
        else:
            response = json.loads(result.read())
            result.close()
            issues = response['result']
            host=issues[0]['host']
            return host

                                                                           
if __name__ == "__main__":
    for zabbix_addres in zabbix_addresses:
        address,username,password,subject = zabbix_addres.split(',')
        z = ZabbixTools(address=address, username=username, password=password)
        content = z.trigger_get()
        zabbix_sendmail.send_mail(mailtolist, subject, content)
    print "Done!"