keepalived 配置项注解

一、配置项

global_defs {
notification_email {  #指定keepalived在发生切换时需要发送email到的对象,一行一个
sysadmin@fire.loc
}
notification_email_from Alexandre.Cassen@firewall.loc #指定发件人
smtp_server localhost #指定smtp服务器地址
smtp_connect_timeout 30 #指定smtp连接超时时间
router_id LVS_DEVEL #运行keepalived机器的一个标识
}
vrrp_sync_group VG_1{ #监控多个网段的实例
group {
inside_network #实例名
outside_network
}
notify_master /path/xx.sh #指定当切换到master时,执行的脚本
notify_backup /path/xx.sh #指定当切换到backup时,执行的脚本
notify_fault “path/xx.sh VG_1” #故障时执行的脚本
notify /path/xx.sh
smtp_alert #使用global_defs中提供的邮件地址和smtp服务器发送邮件通知
}

#因为Keepalived在转换状态时会依照状态来呼叫:
#当进入Master状态时会呼叫notify_master
#当进入Backup状态时会呼叫notify_backup
#当发现异常情况时进入Fault状态呼叫notify_fault
vrrp_instance inside_network {
state BACKUP #指定那个为master,那个为backup,如果设置了nopreempt这个值不起作用,主备考priority决


interface eth0 #设置实例绑定的网卡 VRRP心跳包从哪块网卡发出
dont_track_primary #忽略vrrp的interface错误(默认不设置)
track_interface{ #设置额外的监控,里面那个网卡出现问题都会切换
eth0
eth1
}
mcast_src_ip #发送多播包的地址,如果不设置默认使用绑定网卡的primary ip
garp_master_delay #在切换到master状态后,延迟进行gratuitous ARP请求
virtual_router_id 50 #VPID标记 相同VRID的LVS属于同一组,根据优先级选举出一个主
priority 99 #优先级,高优先级竞选为master
advert_int 10 #检查间隔,默认1秒 VRRP心跳包的发送周期,单位为s 组播信息发送间隔,两个节点设置必须一样(实际并不一定完全是10秒,测试结果是小于10秒的随机值)
nopreempt #设置为不抢占 注:这个配置只能设置在backup主机上,而且这个主机优先级要比另外一台高

首先nopreemt必须在state为BACKUP的节点上才生效(因为是BACKUP节点决定是否来成为MASTER的),其次要实现类似于关闭auto failback的功能需要将所有节点的state都设置为BACKUP,或者将master节点的priority设置的比BACKUP低。我个人推荐使用将所有节点的state都设置成BACKUP并且都加上nopreempt选项,这样就完成了关于autofailback功能,当想手动将某节点切换为MASTER时只需去掉该节点的nopreempt选项并且将priority改的比其他节点大,然后重新加载配置文件即可(等MASTER切过来之后再将配置文件改回去再reload一下)。
preempt_delay #抢占延时,默认5分钟
debug #debug级别
authentication { #设置认证
auth_type PASS #认证方式
auth_pass 111111 #认证密码(密码只识别前8位)
}
virtual_ipaddress { #设置vip
192.168.202.200
}
}
virtual_server 192.168.202.200 23 {
delay_loop 6 #健康检查时间间隔(实际并不一定完全是6秒,测试结果是小于6秒的随机值?)
lb_algo rr  #lvs调度算法rr|wrr|lc|wlc|lblc|sh|dh
lb_kind DR  #负载均衡转发规则NAT|DR|RUN
persistence_timeout 5 #会话保持时间
protocol TCP #使用的协议
persistence_granularity <NETMASK> #lvs会话保持粒度
virtualhost <string> #检查的web服务器的虚拟主机(host:头)
sorry_server<IPADDR> <port> #备用机,所有realserver失效后启用
real_server 192.168.200.5 23 {
weight 1 #默认为1,0为失效
inhibit_on_failure #在服务器健康检查失效时,将其设为0,而不是直接从ipvs中删除
notify_up <string> | <quoted-string> #在检测到server up后执行脚本
notify_down <string> | <quoted-string> #在检测到server down后执行脚本

TCP_CHECK {
connect_timeout 3 #连接超时时间
nb_get_retry 3 #重连次数
delay_before_retry 3 #重连间隔时间
connect_port 23  健康检查的端口的端口
bindto <ip>
}
HTTP_GET | SSL_GET{
url{ #检查url,可以指定多个
path /
digest <string> #检查后的摘要信息
status_code 200 #检查的返回状态码
}
connect_port <port>
bindto <IPADD>
connect_timeout 5
nb_get_retry 3
delay_before_retry 2
}

SMTP_CHECK{
host{
connect_ip <IP ADDRESS>
connect_port <port> #默认检查25端口
bindto <IP ADDRESS>
}
connect_timeout 5
retry 3
delay_before_retry 2
helo_name <string> | <quoted-string> #smtp helo请求命令参数,可选
}
MISC_CHECK{
misc_path <string> | <quoted-string> #外部脚本路径
misc_timeout #脚本执行超时时间
misc_dynamic #如设置该项,则退出状态码会用来动态调整服务器的权重,返回0 正常,不修改;返回1,

检查失败,权重改为0;返回2-255,正常,权重设置为:返回状态码-2
}
}

 

1、其他配置项说明

keepalived 的核心就是将IPVS配置成高可用,生成ipvs规则来完成负载均衡效果。

virtual server (虚拟服务)的定义:
virtual_server IP port       #定义虚拟主机IP地址及其端口
virtual_server fwmark int    #ipvs的防火墙打标,实现基于防火墙的负载均衡集群
virtual_server group string  #将多个虚拟服务器定义成组,将组定义成虚拟服务
lb_algo{rr|wrr|lc|wlc|lblc|lblcr} #定义LVS的调度算法
lb_kind {NAT|DR|TUN}               #定义LVS的模型
presitence_timeout<INT>           #定义支持持久连接的时长
protocol TCP                       #规则所能支持的协议
sorry_server <IPADDR><PORT>       #如果所有real_server都出现故障了,利用此返回信息

 

二、术语

虚拟路由器

由一个Master路由器和多个Backup路由器组成。主机将虚拟路由器当作默认网关;

VRID

虚拟路由器的标识。有相同VRID的一组路由器构成一个虚拟路由器;

Master路由器

虚拟路由器中承担报文转发任务的路由器;

Backup路由器

Master路由器出现故障时,能够代替Master路由器工作的路由器;

虚拟IP 地址

虚拟路由器的IP 地址。一个虚拟路由器可以拥有一个或多个IP地址;

IP地址拥有者

接口IP地址与虚拟IP地址相同的路由器被称为IP地址拥有者;

虚拟MAC地址

一个虚拟路由器拥有一个虚拟MAC地址。通常情况下,虚拟路由器回应ARP请求使用的是虚拟MAC地址,只有虚拟路由器做特殊配置的时候,才回应接口的真实MAC地址;

优先级

VRRP根据优先级来确定虚拟路由器中每台路由器的地位;

非抢占方式

如果Backup路由器工作在非抢占方式下,则只要Master路由器没有出现故障Backup路由器即使随后被配置了更高的优先级也不会成为Master路由器;

抢占方式

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

 

三、高级使用

1、介绍

Keeaplived 主要有两种应用场景,一个是通过配置keepalived结合ipvs做到负载均衡(LVS+Keepalived)。另一个是通过自身健康检查、资源接管功能做高可用(双机热备),实现故障转移。

以下内容主要针对Keepalived+MySQL双主实现双机热备为根据,主要讲解keepalived的状态转换通知功能,利用此功能可有效加强对MySQL数据库监控。此文不再讲述Keepalived+MySQL双主部署过程,有需求者可参考以往博文:http://blog.jobbole.com/94643/

2、keepalived主要作用

keepalived采用VRRP(virtual router redundancy protocol),虚拟路由冗余协议,以软件的形式实现服务器热备功能。通常情况下是将两台linux服务器组成一个热备组(master-backup),同一时间热备组内只有一台主服务器(master)提供服务,同时master会虚拟出一个共用IP地址(VIP),这个VIP只存在master上并对外提供服务。如果keepalived检测到master宕机或服务故障,备服务器(backup)会自动接管VIP成为master,keepalived并将master从热备组移除,当master恢复后,会自动加入到热备组,默认再抢占成为master,起到故障转移功能。

3、工作在三层、四层和七层原理

Layer3:工作在三层时,keepalived会定期向热备组中的服务器发送一个ICMP数据包,来判断某台服务器是否故障,如果故障则将这台服务器从热备组移除。

Layer4:工作在四层时,keepalived以TCP端口的状态判断服务器是否故障,比如检测mysql 3306端口,如果故障则将这台服务器从热备组移除。

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
示例:
! Configuration File for keepalived
global_defs {
   notification_email {
     example@163.com
   }
   notification_email_from  example@example.com
   smtp_server 127.0.0.1
   smtp_connect_timeout 30
   router_id MYSQL_HA
}
vrrp_instance VI_1 {
    state BACKUP
    interface eth1
    virtual_router_id 50
    nopreempt                   #当主down时,备接管,主恢复,不自动接管
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        ahth_pass 123
    }
    virtual_ipaddress {
        192.168.1.200          #虚拟IP地址
    }
}
virtual_server 192.168.1.200 3306 {        
    delay_loop 6
#    lb_algo rr 
#    lb_kind NAT
    persistence_timeout 50
    protocol TCP
    real_server 192.168.1.201 3306 {       #监控本机3306端口
        weight 1
        notify_down /etc/keepalived/kill_keepalived.sh   #检测3306端口为down状态就执行此脚本(只有keepalived关闭,VIP才漂移 ) 
        TCP_CHECK {         #健康状态检测方式,可针对业务需求调整(TTP_GET|SSL_GET|TCP_CHECK|SMTP_CHECK|MISC_CHECK)
            connect_timeout 3
            nb_get_retry 3
            delay_before_retry 3
        }
    }
}

Layer7:工作在七层时,keepalived根据用户设定的策略判断服务器上的程序是否正常运行,如果故障则将这台服务器从热备组移除。

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
示例:
! Configuration File for keepalived
global_defs {
   notification_email {
     example@163.com
   }
   notification_email_from  example@example.com
   smtp_server 127.0.0.1
   smtp_connect_timeout 30
   router_id MYSQL_HA
}
vrrp_script check_nginx {
    script /etc/keepalived/check_nginx.sh    #检测脚本
    interval 2   #执行间隔时间
}
vrrp_instance VI_1 {
    state BACKUP
    interface eth1
    virtual_router_id 50
    nopreempt                   #当主down时,备接管,主恢复,不自动接管
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        ahth_pass 123
    }
    virtual_ipaddress {
        192.168.1.200          #虚拟IP地址
    }
    track_script {          #在实例中引用脚本
        check_nginx
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
脚本内容如下:
# cat /etc/keepalived/check_nginx.sh
Count1=`netstat -antp |grep -v grep |grep nginx |wc -l`
if [ $Count1 -eq 0 ]; then
    /usr/local/nginx/sbin/nginx
    sleep 2
    Count2=`netstat -antp |grep -v grep |grep nginx |wc -l`
    if [ $Count2 -eq 0 ]; then
        service keepalived stop
    else
        exit 0
    fi 
else
    exit 0
fi

 4、健康状态检测方式

4.1 HTTP服务状态检测

1
2
3
4
5
6
7
8
9
10
11
12
  HTTP_GET或SSL_GET {    
      url {
          path /index.html        #检测url,可写多个
          digest  24326582a86bee478bac72d5af25089e    #检测效验码
          #digest效验码获取方法:genhash -s IP -p 80 -u http://IP/index.html 
          status_code 200         #检测返回http状态码
      }
      connect_port 80 #连接端口
      connect_timeout 3  #连接超时时间
      nb_get_retry 3  #重试次数
      delay_before_retry 2 #连接间隔时间
   }

4.2 TCP端口状态检测(使用TCP端口服务基本上都可以使用)

1
2
3
4
5
6
  TCP_CHECK {    
      connect_port 80     #健康检测端口,默认为real_server后跟端口
      connect_timeout 5
      nb_get_retry 3
      delay_before_retry 3
  }

4.3 邮件服务器SMTP检测

1
2
3
4
5
6
7
8
9
10
  SMTP_CHECK {            #健康检测邮件服务器smtp    
      host {
          connect_ip
          connect_port
      }
      connect_timeout 5
      retry 2
      delay_before_retry 3
      hello_name "mail.domain.com"
  }

4.4 用户自定义脚本检测real_server服务状态

1
2
3
4
5
  MISC_CHECK {    
      misc_path /script.sh    #指定外部程序或脚本位置
      misc_timeout 3      #执行脚本超时时间
      !misc_dynamic       #不动态调整服务器权重(weight),如果启用将通过退出状态码动态调整real_server权重值
  }

5、状态转换通知功能

keepalived主配置邮件通知功能,默认当real_server宕机或者恢复时才会发出邮件。有时我们更想知道keepalived的主服务器故障切换后,VIP是否顺利漂移到备服务器,MySQL服务器是否正常?那写个监控脚本吧,可以,但没必要,因为keepalived具备状态检测功能,所以我们直接使用就行了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
主配置默认邮件通知配置模板如下:
global_defs           # Block id
    {
    notification_email    # To:
        {
        admin@example1.com
        ...
         }
    # From: from address that will be in header
    notification_email_from admin@example.com
    smtp_server 127.0.0.1   # IP
    smtp_connect_timeout 30 # integer, seconds
    router_id my_hostname   # string identifying the machine,
                            # (doesn't have to be hostname).
    enable_traps            # enable SNMP traps
        }

 5.1 实例状态通知

a) notify_master :节点变为master时执行

b) notify_backup : 节点变为backup时执行

c) notify_fault  : 节点变为故障时执行

5.2 虚拟服务器检测通知

a) notify_up   : 虚拟服务器up时执行

b) notify_down  : 虚拟服务器down时执行

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
示例:
    ! Configuration File for keepalived
    global_defs {
       notification_email {
         example@163.com
       }
       notification_email_from example@example.com 
       smtp_server 127.0.0.1
       smtp_connect_timeout 30
       router_id MYSQL_HA
    }
    vrrp_instance VI_1 {
        state BACKUP
        interface eth1
        virtual_router_id 50
        nopreempt           #当主down时,备接管,主恢复,不自动接管
        priority 100
        advert_int 1
        authentication {
            auth_type PASS
            ahth_pass 123
        }
        virtual_ipaddress {
            192.168.1.200
        }
            notify_master /etc/keepalived/to_master.sh
            notify_backup /etc/keepalived/to_backup.sh
            notify_fault /etc/keepalived/to_fault.sh
    }
    virtual_server 192.168.1.200 3306 {
        delay_loop 6
        persistence_timeout 50
        protocol TCP
        real_server 192.168.1.201 3306 {
            weight 1
            notify_up /etc/keepalived/mysql_up.sh
            notify_down /etc/keepalived/mysql_down.sh    
            TCP_CHECK {
                connect_timeout 3
                nb_get_retry 3
                delay_before_retry 3
            }
        }
    }

状态参数后可以是bash命令,也可以是shell脚本,内容根据自己需求定义,以上示例中所涉及状态脚本如下:

1) 当服务器改变为主时执行此脚本

1
2
3
4
5
6
# cat to_master.sh 
#!/bin/bash
Date=$(date +%F" "%T)
IP=$(ifconfig eth0 |grep "inet addr" |cut -d":" -f2 |awk '{print $1}')
Mail="baojingtongzhi@163.com"
echo "$Date $IP change to master." |mail -s "Master-Backup Change Status" $Mail

2) 当服务器改变为备时执行此脚本

1
2
3
4
5
6
# cat to_backup.sh
#!/bin/bash
Date=$(date +%F" "%T)
IP=$(ifconfig eth0 |grep "inet addr" |cut -d":" -f2 |awk '{print $1}')
Mail="baojingtongzhi@163.com"
echo "$Date $IP change to backup." |mail -s "Master-Backup Change Status" $Mail

3) 当服务器改变为故障时执行此脚本

1
2
3
4
5
6
# cat to_fault.sh
#!/bin/bash
Date=$(date +%F" "%T)
IP=$(ifconfig eth0 |grep "inet addr" |cut -d":" -f2 |awk '{print $1}')
Mail="baojingtongzhi@163.com"
echo "$Date $IP change to fault." |mail -s "Master-Backup Change Status" $Mail

4) 当检测TCP端口3306为不可用时,执行此脚本,杀死keepalived,实现切换

1
2
3
4
5
6
7
# cat mysql_down.sh
#!/bin/bash
Date=$(date +%F" "%T)
IP=$(ifconfig eth0 |grep "inet addr" |cut -d":" -f2 |awk '{print $1}')
Mail="baojingtongzhi@163.com"
pkill keepalived
echo "$Date $IP The mysql service failure,kill keepalived." |mail -s "Master-Backup MySQL Monitor" $Mail

5) 当检测TCP端口3306可用时,执行此脚本

1
2
3
4
5
6
# cat mysql_up.sh
#!/bin/bash
Date=$(date +%F" "%T)
IP=$(ifconfig eth0 |grep "inet addr" |cut -d":" -f2 |awk '{print $1}')
Mail="baojingtongzhi@163.com"
echo "$Date $IP The mysql service is recovery." |mail -s "Master-Backup MySQL Monitor" $Mail

tomcat-redis-session-manager

使用redis配置tomcat共享session

结构图:

分析:

分布式web server集群部署后需要实现session共享,针对 tomcat 服务器的实现方案多种多样,
比如 tomcat cluster session 广播、nginx IP hash策略、nginx sticky module等方案,
本文主要介绍了使用 redis 服务器进行 session 统一存储管理的共享方案。

必要环境:

  • java1.7
  • tomcat7
  • redis2.8

nginx 负载均衡配置

  1. 修改nginx conf配置文件加入
     upstream tomcat {
         server 200.10.10.67:8110;
         server 200.10.10.67:8120;
         server 200.10.10.44:8110;
         server 200.10.10.66:8110;
     }
    
  2. 配置 相应的server或者 location地址到 http://tomcat

tomcat session共享配置步骤

  1. 添加redis session集群依赖的jar包到 TOMCAT_BASE/lib 目录下
  2. 修改 TOMCAT_BASE/conf 目录下的 context.xml 文件
     	<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
     	<Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"
          host="localhost"
          port="6379"
          database="0"
          maxInactiveInterval="60"
          sessionPersistPolicies="PERSIST_POLICY_1,PERSIST_POLICY_2,.."
          sentinelMaster="SentinelMasterName"
          sentinels="sentinel-host-1:port,sentinel-host-2:port,.."/>
    

    属性解释:

    • host redis服务器地址
    • port redis服务器的端口号
    • database 要使用的redis数据库索引
    • maxInactiveInterval session最大空闲超时时间,如果不填则使用tomcat的超时时长,一般tomcat默认为1800 即半个小时
    • sessionPersistPolicies session保存策略,除了默认的策略还可以选择的策略有:
      [SAVE_ON_CHANGE]:每次 session.setAttribute() 、 session.removeAttribute() 触发都会保存. 
      	注意:此功能无法检测已经存在redis的特定属性的变化,
      	权衡:这种策略会略微降低会话的性能,任何改变都会保存到redis中.
      
      [ALWAYS_SAVE_AFTER_REQUEST]: 每一个request请求后都强制保存,无论是否检测到变化.
      	注意:对于更改一个已经存储在redis中的会话属性,该选项特别有用. 
      	权衡:如果不是所有的request请求都要求改变会话属性的话不推荐使用,因为会增加并发竞争的情况。
      
    • sentinelMaster redis集群主节点名称(Redis集群是以分片(Sharding)加主从的方式搭建,满足可扩展性的要求)
    • sentinels redis集群列表配置(类似zookeeper,通过多个Sentinel来提高系统的可用性)
    • connectionPoolMaxTotal
    • connectionPoolMaxIdle jedis最大能够保持idel状态的连接数
    • connectionPoolMinIdle 与connectionPoolMaxIdle相反
    • maxWaitMillis jedis池没有对象返回时,最大等待时间
    • minEvictableIdleTimeMillis
    • softMinEvictableIdleTimeMillis
    • numTestsPerEvictionRun
    • testOnCreate
    • testOnBorrow jedis调用borrowObject方法时,是否进行有效检查
    • testOnReturn jedis调用returnObject方法时,是否进行有效检查
    • testWhileIdle
    • timeBetweenEvictionRunsMillis
    • evictionPolicyClassName
    • blockWhenExhausted
    • jmxEnabled
    • jmxNameBase
    • jmxNamePrefix

  3. 重启tomcat,session存储即可生效

原文路径:https://github.com/izerui/tomcat-redis-session-manager#tomcat-redis-session-manager

tomcat 7 session 共享 redis

1.下载 jedis-2.9.0.jar  tomcat-redis-session-manager1.2.jar  commons-pool2-2.2.jar 这三个包,复制到Tomcat_HOME/lib 下

2.修改Tomcat_HOME/conf/context.xml

在<Context></Context> 里增加如下内容

1
2
3
4
5
6
7
<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
<Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"
         host="localhost"
         port="6379"
         password="123456"
         database="1"
         maxInactiveInterval="60"/>

redis 服务器地址 端口 密码 等参数请根据实际情况自行修改

搭建Tomcat集群&通过Redis缓存共享session的一种流行方案

为什么要共享session?

我们使用单台Tomcat的时候不会有共享sesssion的疑虑,只要使用Tomcat的默认配置即可,session即可存储在Tomcat上。

但是随着业务的扩大,增加Tomcat节点构成Tomcat集群大势所趋,分布式带来了增加更大规模并发请求的优势,但是也随之到来了一个问题,每个Tomcat只存储来访问自己的请求产生的session,如果Tomcat-A已经为客户端C创建了会话session,那么Tomcat-B并不知道客户端已与集群中的Tomcat-A产生了会话,在访问时就会为C再创建一份session,如果是基于session的验证会话权限的接口(如用户登录认证后才可访问的数据接口),将会导致在访问集群中不同节点的时候重复认证。session的不共享导致原来的会话管理机制在Tomcat集群中无法工作。

所以,如果有一个Tomcat集群都能访问的公共session存取区就好了,基于这个概念,我们想到了使用Redis来做这个session公共存取区,这样子的话就有一个统一管理回话的地方了。回看我们上文提到的例子,如果Tomcat-A已经为客户端C创建了会话session,这个session信息会直接存储在公共的Redis里面,那么Tomcat-B就可以到公共session存储区里获得已为C产生的session,这样的结果是集群的公共session存取区在逻辑上就像一个tomcat的内部session存取区一样了。

怎么做呢?

有了上述基本的概念,我们就要开始真正施行了。

1. 持久化Tomcat Session到Redis中

Tomcat提供了一个开放的session管理和持久化的org.apache.catalina.session.ManagerBase,继承这个抽象类并做一些简单的配置,即可让你的session管理类接管Tomcat的session读取和持久化。当然,我们在这里使用了一个流行的开源项目:
https://github.com/jcoleman/tomcat-redis-session-manager
,它已经为我们准备好了这样的一个管理类,只要将这个管理类配置在Tomcat中即可发挥功能。它可以帮助我们将tomcat的session存入我们指定的redis,甚至支持redis在sentinel模式调度的redis集群,稍后我们也将详述这样的redis集群该如何部署。

使用这个项目非常简单,如果在Tomcat6或Tomcat7下部署,直接使用项目release出的jar文件到Tomcat的lib下即可,准确来说还需要引入它的其他几个依赖(括号中为建议的):

tomcat-redis-session-manager-VERSION.jar(v1.2)
commons-pool2-VERSION.jar(v2.2)
jedis-VERSION.jar(v2.5.2)

引入后需要在tomcat下修改conf/context.xml

<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
<Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"
         host="{redis.host}"
         port="{redis.port}"
         database="{redis.dbnum}"
         maxInactiveInterval="60"/>

这样一来我们的tomcat即可把session的管理交由我们配置的redis来处理。

需要注意的是,如果在Tomcat8下按照上述部署,会在启动时报错,笔者查看过原因,是tomcat-redis-session-manager最后更新的年代相隔较久,代码中使用的Tomcat api出现了过时删去的情况,在Tomcat8下会出现问题,如果想在Tomcat8下使用,需要自行对过时的api进行修改,替换成新的Tomcat api。笔者自己修改了未经严格验证的一个版本,可供使用Tomcat8的读者试用:
https://github.com/jinhaoplus/tomcat-redis-session-manager

2. nginx反向代理的负载均衡

虽然这不是本文的重点,但是使用负载均衡在搭建集群的过程中重要性不言而喻,使用nginx默认的轮询机制,我们可以将前端的浏览器请求转发到不同的Tomcat实例上。
首先来讲讲正向代理和反向代理,一言以蔽之:正向代理帮助内网client访问外网server用,反向代理将来自外网client的请求f转发到到内网server。
最实际的区别是使用二者时正向代理需要用户主动配置,而反向代理对用户透明,不需要用户做主动配置。
「代理」是指代人理事,即代理服务器是为其他人或机器服务的。
正向代理是替内网中的用户访问外网服务的(即代替用户去访问外网),用户和外网之间的沟通全部交由正向代理服务器完成,用户的请求不发给外网服务器而发给代理服务器让其代为处理,这个过程是隐藏用户的。
反向代理是为真正的服务节点机器服务的(即代替真正的服务节点机器去提供服务),代理服务器接收来自外界的请求,并将请求转给真正的服务节点机器,用户不与真正的机器打交道
,这个过程是隐藏真正的服务实例机器的。

nginx可以作为高效的反向代理服务器,同时起到了负载均衡的作用。如果想要使用反向代理Tomcat集群的负载,方法也非常简单,只需要在其配置nginx.conf中将负载的Tomcat集群的实际地址加入upstream,并将locate导向配好的upstream即可:

http{
    ...
    upstream tomcats {
        server      <tomcat1_ip>:<tomcat1_port>;
        server      <tomcat2_ip>:<tomcat2_port>;
        ...
        server      <tomcatn_ip>:<tomcatn_port>;
      }
    ...
    server {
        listen       80;
        ...
        location / {
            root   html;
            index  index.html index.htm;
            proxy_pass      http://tomcats;
        }
      }
}

默认的轮询机制将每次请求都发至不同的Tomcat实例上,以此实现负载均衡。

3. 基于sentinel的redis集群搭建

上文介绍的方法其实已经可以搭建一个完整的Tomcat集群了,如果系统想要一个更安全可靠的环境,那么nginx其实也可以做集群,这里略去不说,我们想要说的是redis集群。

上面我们已经说到redis是session的公共存储区,如果redis不幸挂掉的话将会导致致命的问题,因为无session源可取,Tomcat中有session读取的接口会直接报错。所以搭建一个redis集群还是很有必要的,幸好redis对分布式HA的搭建支持得很好,原生即有一套sentinel哨兵机制即可用。

以sentinel模式启动的redis实例起到了监控者的作用,redis集群以master-slave的模式启动,消息不再直接发给redis实例,而是发给sentinel,由sentinel同步至所有的redis实例,如果出现redismaster实例挂掉的情况,会由sentinel发现,根据配置还可以由sentinel自己组成的集群去选举产生新的master,新的master将会承担起作用,起到了灾难自动回恢复的作用。

这里来说一下sentinel集群的配置:
我们使用两个redis实例来组成master-slave,需要三个sentinel组成哨兵集群来监控两个redis实例,在master出现问题的时候选举产生新的master。
路径假设如下:
redis1
redis2
sentinel1
sentinel2
sentinel3

配置redis1/redis.conf为master:

    bind 127.0.0.1
    port 6379

配置redis2/redis.conf为redis1的slave:

    bind 127.0.0.1
    port 6379
    slaveof <redis1_ip> 6379

配置sentinel1/redis-sentinel.conf

    port 26379    
    sentinel monitor mymaster <redis1_ip> 6379 2

指定redis1为master,slave信息会在启动后被sentinel监听到并自动写入到配置文件中,因此不需要手动写入,最后的quorum表示当有2个sentinel判断master挂掉的时候即可选举slave为新的master。

sentinel2sentinel3配置相同。

这样之后启动redis和sentinel集群,即可构建一个高可用的redis集群。可以尝试一下把redis1这个master挂掉,sentinel就会探查到并且在2个sentinel都探查到的时候即会选举产生新的master:

# +monitor master mymaster <redis1-ip> 6379 quorum 2
# +sdown master mymaster <redis1-ip> 6379

同时我们的Tomcat配置也将改为使用sentinel集群的版本:

<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
    <Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"
         sentinelMaster="mymaster"
         sentinels="<sentinel1-ip>:26379,<sentinel2-ip>:26379,<sentinel3-ip>:26379"
         maxInactiveInterval="60"/>

这个集群搭建完成后,我们的系统将会更为稳定:

SaltStack Syndic 配置

SaltStack Syndic 介绍

我们使用多 Mater 的功能解决了 Master 单点故障的问题,那么保证高可用之后,我们接下来要考虑的就是性能了,
是的,如果你管理的 Minion 数以万计,一个 Master 可能就有一些吃力了,我们需要更加灵活的架构,那么该 Salt Syndic 出场了。

一个基本的 Salt 配置方式是一个 Master 管理一群 Minion,这就是单一的拓扑结构。那么为了增加多种拓扑架构的支持,Salt 在 0.9.0 版本中加入了 Salt Syndic。Syndic 建立在中心 Master 和 Minions 之间,并允许多层分级 Syndic。

Syndic 运行在一个 Master 上,并且连接到另外一个 Master(比它更高级别,我们后面称之为“高级 Master”),这里需要强调的是 Syndic 必须运行在一个 Master 上。

然后 Syndic Minion 所连接的高级 Master 就可以控制连接到运行 Syndic 的 Master 上 的 Minion。这句话稍有点绕,如果一下子没明白,先加深点记忆“Syndic 必须运行在一个 Master 上”,然后再回过头看就好理解了。

1 Syndic 配置

Syndic 对于它管理的 Minion 来说,就是一个 Master 节点,所以 Syndic 没有自己的配置文件,它是 Salt Master 的一个组件:

[root@linux-node1 ~]# yum install salt-syndic -y
[root@linux-node1 ~]# vim /etc/salt/master
syndic_master: 192.168.56.12	# 设置为高级 Master 的 IP
[root@linux-node1 ~]# systemctl restart salt-master
[root@linux-node1 ~]# systemctl start salt-syndic

2 高级 Master 配置

高级 Master 需要使用 order_masters 参数来进行开启:

[root@linux-node2 ~]# yum install salt-master -y
[root@linux-node2 ~]# grep "order_masters" /etc/salt/master
order_masters: True
[root@linux-node1 ~]# systemctl start salt-master

启动之后,在高级 Master 上通过 salt-key 可以看到,Salt Syndic 已经连接上来了,在前面我们提到过 Syndic 节点对于它管理的 Minion 来说就是一个 Master,但是 Syndic 对于高级 Master 来说,它却是一个特殊的 Minion:

[root@linux-node2 ~]# salt-key -L
Accepted Keys:
Denied Keys:
Unaccepted Keys:
linux-node1.example.com
Rejected Keys:

3 Syndic 测试

高级 Master 可以控制一群同时运行着“syndic 和 master”的节点,通过 Syndic 将操作命令传输给受控 Master,受控 Master 来完成对自己旗下 Minion 的管理,并将结果传回高级 Master,从而实现高级 Master 对所有 Minion 的间接管理。

高级 Master 同意 Syndic 节点的连接后,使用 test.ping 就可以看到,高级 Master 可以管理所有连接到 Syndic 上面的 Minion:

[root@linux-node2 ~]# salt-key -A
[root@linux-node2 ~]# salt '*' test.ping
linux-node1.example.com:
    True
linux-node2.example.com:
    True

4 Syndic 是如何工作的

Syndic 本质上是一个特殊的 Minion,其代码就位于 minion.py 中。 Syndic 需要在本地运行 Master,并将需要管理的 Minions 的 Master 指向 Syndic 所在的主机。Syndic 是这么来工作的:

  • 冒充 Minion,建立与高级别的 Master 的连接,订阅所有来自高级 Master 下发的任务。
  • 接收高级 Master 下发的数据后,首先进行解密,解密完成后,将其扔到本地的 Master 接口上进行二次下发。
  • Syndic 在进行二次下发之后,监听本地 Event 接口,获取所管理的 Minions 的返回。
  • 将返回发送给高级 Master。

5 Syndic 的优缺点

没有真正完美的架构,使用 Syndic 虽然可以建立多层的 Salt 拓扑,但也是有利有弊,需要使用者根据实际的需求进行权衡。

优点

  • 通过 Syndic,可以建立多层级的 Salt 拓扑,Syndic 下的 Minions 即可通过 Syndic 所在的 Master 进行管理,也可以通过高级 Master 进行管理,架构变得异常灵活。
  • 由于 Syndic 只订阅高级 Master 下发下来的任务,对于文件服务等,Syndic 本地进行配置,可以有效地降低高级 Master 的负载。

缺点

  • 需要保证 Syndic 上的 file_roots 及 pillar_roots 与高级 Master 是一致的。
  • 由于 Syndic 管理了旗下 Minions 的认证,这样高级 Master 并不知道有多少 Syndic 主机,Syndic 下边有多少 Minions。 在高级 Master 上使用 Salt 命令行下发远程执行命令时,如果 Syndic 此时与高级 Master 网络之间有抖动,导致没有收到消息或延迟收到消息, 高级 Master 并不知情。Syndic 并没有返回结果或延迟返回结果,高级 Master 并不能感知到,会导致结果不完整。 如果没有其他验证机制,结果将变得不可控。官方提供的解决方案是增大 syndic_wait 选项,但是只能缓解,并不能根治问题。

建议

没有系统或工具是 100% 可靠的。基于这一观点,无论是否使用 Salt 的架构扩展,对于运维使用 SaltStack 来说, 如果能从流程上对每次 SaltStack 的执行结果进行检查和验证,是比较稳妥的方案,可能也算是比较保守吧。

遇到的问题

1.使用之前配置的环境,linux-node1 是无 Master 架构,还有其他报错。

处理方法:最简化 Salt Master 和 Minion 的配置,排除其他原因。

[root@linux-node1 ~]# cat /etc/salt/master
syndic_master: 192.168.56.12
[root@linux-node1 ~]# cat /etc/salt/minion
master: 192.168.56.11

[root@linux-node2 ~]# cat /etc/salt/master
order_masters: True
[root@linux-node2 ~]# cat /etc/salt/minion
master: 192.168.56.11

2.经过第 1 步处理,还有如下报错:

[root@linux-node1 ~]# systemctl status salt-syndic
● salt-syndic.service - The Salt Master Server
   Loaded: loaded (/usr/lib/systemd/system/salt-syndic.service; disabled; vendor preset: disabled)
   Active: failed (Result: exit-code) since Mon 2016-08-01 18:45:20 CST; 11s ago
  Process: 22082 ExecStart=/usr/bin/salt-syndic (code=exited, status=1/FAILURE)
 Main PID: 22082 (code=exited, status=1/FAILURE)

Aug 01 18:45:20 linux-node1.example.com salt-syndic[22082]: [ERROR   ] The master key has changed, the salt master could have been subverted, verify salt master's public key
Aug 01 18:45:20 linux-node1.example.com salt-syndic[22082]: [CRITICAL] The Salt Master server's public key did not authenticate!
Aug 01 18:45:20 linux-node1.example.com salt-syndic[22082]: The master may need to be updated if it is a version of Salt lower than 2016.3.1, or
Aug 01 18:45:20 linux-node1.example.com salt-syndic[22082]: If you are confident that you are connecting to a valid Salt Master, then remove the master public key and restart the Salt Minion.
Aug 01 18:45:20 linux-node1.example.com salt-syndic[22082]: The master public key can be found at:
Aug 01 18:45:20 linux-node1.example.com salt-syndic[22082]: /etc/salt/pki/minion/syndic_master.pub
Aug 01 18:45:20 linux-node1.example.com salt-syndic[22082]: Invalid master key
Aug 01 18:45:20 linux-node1.example.com systemd[1]: salt-syndic.service: main process exited, code=exited, status=1/FAILURE
Aug 01 18:45:20 linux-node1.example.com systemd[1]: Unit salt-syndic.service entered failed state.
Aug 01 18:45:20 linux-node1.example.com systemd[1]: salt-syndic.service failed.


# 以上错误提示 master key 改变了,可以尝试移除 master 公钥,然后重启 minion

[root@linux-node1 ~]# ll /etc/salt/pki/minion/
total 16
-rw-r--r-- 1 root root  450 Jun 29 15:48 minion_master.pub
-r-------- 1 root root 1674 Jun 29 15:48 minion.pem
-rw-r--r-- 1 root root  450 Jun 29 15:48 minion.pub
-rw-r--r-- 1 root root  450 Aug  1 15:59 syndic_master.pub		# 这个 master 公钥是之前配置生成的,需要删除
[root@linux-node1 ~]# rm -f /etc/salt/pki/minion/syndic_master.pub 
[root@linux-node1 ~]# systemctl restart salt-syndic

[root@linux-node1 ~]# ll /etc/salt/pki/minion/
total 16
-rw-r--r-- 1 root root  450 Jun 29 15:48 minion_master.pub
-r-------- 1 root root 1674 Jun 29 15:48 minion.pem
-rw-r--r-- 1 root root  450 Jun 29 15:48 minion.pub
-rw-r--r-- 1 root root  450 Aug  1 18:59 syndic_master.pub		# 重启 salt-syndic 后,master 公钥重新生成了

 

mfs常规设置

Web界面监控MFS状态

mfscgiserv 是用python写的一个web服务器,监听端口是9425,必须在master(管理服务器上)上启动
常用的参数如下:
| 参数| 作用|
|:–| :–:|
|-h | help|
| -H| 绑定的IP,默认为0.0.0.0|
| -P | 绑定端口号,默认是9425|
|-R | mfscgi的root路径,默认是/usr/local/mfs/share/mfscgi|
| -f | 运行HTTP服务器,-f 表示在前台运行,-v表示请求的日志发往标准的错误设备|

一般情况下不需要带任何参数启动即可。

[root@localhost seim]# mfscgiserv 
lockfile created and locked
starting simple cgi server (host: any , port: 9425 , rootpath: /usr/local/mfs/share/mfscgi)

然后在浏览器输入:http://masfterserver ip:9425就可以看到监控图了。
监控图有8个部分组成:

  1. info
    这个部分显示了MFS的基本信息。
  2. Servers
    列出现有的ChunkServer。
  3. Disks
    列出每一台ChunkServer的磁盘目录以及使用量
  4. Exports
    列出共享的目录,既可以被挂载的目录
  5. mounts
    显示被挂载的情况。
  6. Openrations
    显示正在执行的操作。
  7. Master Charts
    显示Master server的操作情况,包括读取,写入,创建目录,删除目录等消息。
  8. Server Charts
    显示ChunkServer的操作情况,数据传输率以及系统状态等信息。

客户端挂载文件系统

主要命令如下:
mfsmount -H [master] [-p port] [-S path] mountpoint
| 参数 | 作用|
|:– | :–:|
| -H| 管理服务器的IP地址|
| -P port | 管理服务器端口,按照mfsmaster.cfg配置文件中的变量MATOCU_LISTEN_PORT的值填写,如果是默认端口则不需要指出。|
| -S path| 指出挂载MFS目录的子目录,默认是/,即挂载整个MFS目录 |
| mountpoint | 指挂载到本地的哪个目录下|

在启动管理服务器进程时,用了一个“-m” 或“-o mfsmaster”的选项,这样可以挂载一个辅助的文件系统mfsmeta,辅助文件系统可以在如下两个方面恢复丢失的数据:

  1. MFS卷上误删除了文件,而此文件又没有过垃圾文件存放期。
  2. 为了释放磁盘空间而删除或者移动的文件,当需要恢复这些文件时,文件又没有过垃圾文件的存放期。

要使用MFS辅助文件系统,可以执行如下指令:

mfsmount -m /mnt/mfsclient  -H mfsmaster

需要注意的是,如果决定了挂载mfsmeta,那么一定要在mfsmaster的mfsexport.cfg文件中添加下面这条记录:

*           .                   rw

原文件有此条目,只要将其前面的#去掉即可。
挂载文件系统就可以执行所所有标准的文件操作了。如创建,删除,复制,重命名文件等。MFS由于是一个网络文件系统,所以操作进度比本地的偏慢。

需要注意的是,每个文件都可以存储为多个副本,在这种情况下,每一个文件所占用的空间要比其他文件本身大的多,此外,被删除且在有效期内的文件都放在一个“垃圾箱”中,所以他们也占用的空间,其大小也依赖文件的分钟。。为防止删除被其他进程打开的文件,数据将一直被存储,直到文件被关闭。

MFS日常操作(都在client端下操作)

1. mfsgetgoal和mfssetgoal命令
目标(goal)是指文件被复制的份数,设定了复制的份数就可以通过mfsgetgoal命令来证实

[root@localhost mfs]# /usr/local/mfs/bin/mfsgetgoal /mnt/mfsclient/    # 这里是get
/mnt/mfsclient/: 2
[root@localhost mfs]# /usr/local/mfs/bin/mfssetgoal 3 /mnt/mfsclient/   # 这里是set
/mnt/mfsclient/: goal: 3
[root@localhost mfs]# /usr/local/mfs/bin/mfssetgoal -r 3  /mnt/mfsclient/    # -r 是递归,对整个树形目录进行递归操作
/mnt/mfsclient/:
 inodes with goal changed:                       0
 inodes with goal not changed:                   1
 inodes with permission denied:                  0
[root@localhost mfs]# /usr/local/mfs/bin/mfsgetgoal -r  /mnt/mfsclient/    
/mnt/mfsclient/:
 directories with goal          3 :          1

需要注意的是,如果你的Chunkserver只有一台服务器,那么就设置为1即可,别设置为3,4或者其他超过1的,不然你写如一个东西,会卡死的。。。。。。。

2. mfscheckfile与mfsfileinfo命令
实际副本数量可以通过mfscheckfile和mfsfileinfo命令证实,如下:

[root@localhost mfs]# touch /mnt/mfsclient/test1          # 新添一个空文件
[root@localhost mfs]# bin/mfscheckfile /mnt/mfsclient/test1      # 虽然有文件(虽然没有设置为非零目标,the noo-zero goal),但是是一个空文件,所以mfscheckfile是为空的结果
/mnt/mfsclient/test1:
[root@localhost mfs]# bin/mfsfileinfo /mnt/mfsclient/test1 
/mnt/mfsclient/test1:
    no chunks - empty file
[root@localhost mfs]# echo "asdsasfasdfasdfasdf" >/mnt/mfsclient/test1 
[root@localhost mfs]# bin/mfscheckfile /mnt/mfsclient/test1 
/mnt/mfsclient/test1:
 chunks with 1 copy:              1
[root@localhost mfs]# bin/mfsfileinfo  /mnt/mfsclient/test1 
/mnt/mfsclient/test1:
    chunk 0: 0000000000000029_00000001 / (id:41 ver:1)
        copy 1: 172.16.22.142:9422 (status:VALID)             # 设置上我们刚才通过mfssetgoal为3,理论上是复制3份副本,但是这里的chunkserver只有1台,所以copy也就为1了。

这里说说目录继承副本数量的问题:

  1. 如果改变一个已经存在的文件副本份数,那么文件的副本份数就会扩大或删除,这个过程会有延迟的。
  2. 对于一个目录设定“目标”,此目录下新创建的文件或子目录均会继承此目录的设定,但不会改变已经存在的文件以及目录副本数量。

3. mfsdirinfo
整个目录树的内容需要通过一个功能增强、等同于“du -s”的命令mfsdirinfo来显示。mfsdirinfo可以显示MFS的具体信息。

[root@localhost mfs]# bin/mfsdirinfo /mnt/mfsclient/
/mnt/mfsclient/:
 inodes:                          2
  directories:                    1
  files:                          1
 chunks:                          1
 length:                         20
 size:                        73728
 realsize:                   221184
[root@localhost mfs]# bin/mfsdirinfo /mnt/mfsclient/test1 
/mnt/mfsclient/test1:
 inodes:                          1
  directories:                    0
  files:                          1
 chunks:                          1
 length:                         20
 size:                        73728
 realsize:                   221184
  • length 表示文件大小的总和
  • size 表示块长度总和
  • realsize 表示磁盘空间的使用,包括所有的副本

4. 为垃圾箱设定隔离时间
删除的文件存放在“垃圾箱(trash bin)”的时间就是隔离时间(quarantine time),这个时间可以用mfsgettrashtime 来验证,也可以用mfssettrashtime来设置。设置的时间是按照小时计算,设置的单位是秒,不满一小时就按一小时计算,如下所示:

[root@localhost mfs]# bin/mfssettrashtime   5000 /mnt/mfsclient/test1    #5000秒大于1小时,不满2小时,按照2小时计算
/mnt/mfsclient/test1: 5000
[root@localhost mfs]# bin/mfsgettrashtime /mnt/mfsclient/test1    
/mnt/mfsclient/test1: 7200
[root@localhost mfs]# bin/mfssettrashtime   8000 /mnt/mfsclient/test1   #8000秒大于2小时,不满3小时,按照3小时计算
/mnt/mfsclient/test1: 8000
[root@localhost mfs]# bin/mfsgettrashtime /mnt/mfsclient/test1
/mnt/mfsclient/test1: 10800

mfssettrashtime -r是对目录进行递归赋值的。为一个目录设定存放时间后,在此目录下新创建的文件和目录就可以继承这个设置了。数字0意味着一个文件被删除后,会立即删除,不可能再恢复。

删除的文件通过一个单独安装的mfsmeta辅助文件系统来恢复。这个文件系统包含了目录trash(含有仍然可以被还原的删除文件的信息)和目录trash/undel(用于获取文件)。只有管理员权限访问mfsmeta辅助文件系统(通常是root)。

下面来模拟下文件被删除后,如何恢复的:
Notice
在做这一步的时候,请先确认使用mfsmount -m参数挂载了mfsmeta。如

mfsmount -m /mnt/mfsmeta/ -H mfsmaster
  1. 先找到被删除的文件
    [root@localhost mfs]# echo "asdfasdfnoijoiujro2er0" >/mnt/mfsclient/haha1    # 创建一个文件
    [root@localhost mfs]# bin/mfsgettrashtime /mnt/mfsclient/haha1     # 确认回收站存放的时间为2小时
    /mnt/mfsclient/haha1: 7200
    [root@localhost mfs]# rm /mnt/mfsclient/haha1                    #删除文件 
    rm: remove regular file `/mnt/mfsclient/haha1'? y
    [root@localhost mfs]# find /mnt/mfsmeta/trash/ -name "*haha*"       #在回收站里面找到被删除的文件
    /mnt/mfsmeta/trash/006/00000006|haha1

    被删除的文件名在垃圾箱里面其实还是可以找到的,文件名是由一个8位16进制数的i-node和被删的文件名组成。在文件名和i-node之间不可以用”/”,而是以“|” 替代。如果一个文件名的长度超过操作系统的限制(通常是255字符),那么超出部分将被删除。从挂载点起全部路径的文件名被删除的文件仍然可以被读写。
    需要注意的是,被删除的文件在使用文件名(注意文件名是两部分),一定要用单引号引起来。如下所示:

    [root@localhost mfs]# cat '/mnt/mfsmeta/trash/006/00000006|haha1'
    haha1
  2. 恢复文件
    移动这个文件到文件所在目录下的undel下面,将会使原始的文件恢复到正确的MFS文件系统原来的路径下。如下所示:

    [root@localhost mfs]# cd /mnt/mfsmeta/trash/006/        # 切换到被删文件下面
    [root@localhost 006]# ls
    00000006|haha1  undel
    [root@localhost 006]# pwd
    /mnt/mfsmeta/trash/006
    [root@localhost 006]# mv 00000006\|haha1 ./undel/
    [root@localhost 006]# ls /mnt/mfsclient/
    haha1  test3  
    [root@localhost 006]# cat /mnt/mfsclient/haha1 
    asdfasdfnoijoiujro2er0

在恢复文件的时候,原来被删文件下面的目录下,不能有同名文件,不然恢复不成功。
从垃圾箱中删除文件的结构是释放之前它占用的空间(删除有延迟,因为数据是异步删除的)。在垃圾箱中删除文件后,就不能够再恢复了。
可以通过mfssetgoal命令来修改文件的副本数,也可以通过mfssettrashtime工具来改变文件存储在垃圾箱中的时间。

5. 快照功能

MFS系统可以利用mfsmakesnapshot工具给文件或者目录做快照(snapshot),如下所示:

[root@localhost ~]# /usr/local/mfs/bin/mfsmakesnapshot destination-file source-file

其中source是源文件路径或者目录,destination是快照文件路径或者目录,需要注意的是,destination 路径必须在MFS文件系统下面,即source与destination路径都必须在MFS体系下,不能将快照放到MFS文件系统之外的其他文件系统下。

mfsmakesnapshot是一次执行中整合了一个或者一组文件的副本,而且对这些文件的源文件进行任何修改都不会影响源文件的快照,就是说任何对源文件的操作,如写入操作,将会不修改副本。
mfsmakesnapshot可以实现这个快照功能,当有多个源文件时,他们的快照会被加入到同一个目标文件中,通过对比快照的测试,可以发现快照的本质:

  1. 一个MFS系统下的文件做快照后,查看两个文件的块信息,他们是同一个块。接着,把原文件删除,删除源文件后(最初会留在回收站上,但过一段时间后回收站的文件也删除了),快照文件仍然存储,并且可以访问。使用mfsfileinfo查看,发现还是原来的块。
  2. 对一个文件做快照后,查看两个文件的块信息,发现是同一个块。把原文件修改后,发现原文件的使用块信息变了,即使用了一个新块。而快照文件仍然使用原来的块,保持文件内容不变。

moosefs分布式存储

MFS学习总结

公司使用moosefs做图片存储,最近学习了一下,在此小小总结一下,主要分以下几部分:

  • MFS概述、特性和新版改进
  • MFS 工作原理和设计架构
  • MFS的安装、部署、配置
  • MFS的高级特性
  • MFS的性能测试
  • MFS集群的维护
  • MFS的常见问题和建议对策

 

一、MFS概述、特性和新版改进

MooseFS是一个分布式存储的框架,其具有如下特性:

  1. Free(GPL)
  2. 通用文件系统,不需要修改上层应用就可以使用(那些需要专门api的dfs很麻烦!)。
  3. 可以在线扩容,体系架构可伸缩性极强。(官方的case可以扩到70台了!)
  4. 部署简单。(sa们特别高兴,领导们特别happy!)
  5. 高可用,可设置任意的文件冗余程度(提供比raid1+0更高的冗余级别,而绝对不会影响读或者写的性能,只会加速!)
  6. 可回收在指定时间内删除的文件(“回收站”提供的是系统级别的服务,不怕误操作了,提供类似oralce 的闪回等高级dbms的即时回滚特性!)
  7. 提供netapp,emc,ibm等商业存储的snapshot特性。(可以对整个文件甚至在正在写入的文件创建文件的快照)
  8. google filesystem的一个c实现。
  9. 提供web gui监控接口。
  10. 提高随机读或写的效率(有待进一步证明)。
  11. 提高海量小文件的读写效率(有待进一步证明)。

 

MooseFS 1.6版本改进:

  • 修复1.5.x中在大批量操作时打开文件过多的bug。报的错误说是打开的文件过多,造成chunker server的链接错误。在1.6.x中解决此问题,就解决了很大的问题。
  • 新增加了masterlogger服务器。这是在1.5.x中所没有的,就是做了master服务器的冗余,进一步的加强的master服务器的稳定性。在mfs体系中master是要求最稳定以及性能要求最高的,因此务必保证master的稳定。
  • 修改1.5.x中存在的对于坏块的修复功能。在mfs1.5.x中遇到chunker坏块校验,错误比较多的时候导致master将出现坏块的chunker自动的剔除出去的情况,此次增加了对坏块的修复功能,很方便的进行修复,简化对坏块的处理功能。
  • 对metadata和changelog的新认识。之前认为changelog记录的是文件的操作,定期的像数据库的日志一样归档到metadata中。发现上面的理解存在误区,真正的是changelog中记录了对文件的操作,metadata记录文件的大小和位置。因此metadata是比较重要的,在进行修复的过程中是采用metadata和最后一次的changelog进行修复的。
  • MFS文档中明确指出对于内存和磁盘大小的要求。
  • 指出了在测试的过程中多个chunker并不影响写的速度,但是能加快读的速度。在原来的基础上增加一个chunker时,数据会自动同步到新增的chunker上以达到数据的平衡和均衡。

 

二、MFS 工作原理和设计架构

角色 角色作用
管理服务器
managing server (master)
负责各个数据存储服务器的管理,文件读写调
度,文件空间回收以及恢复.多节点拷贝
元数据日志服务器
Metalogger server(Metalogger)
负责备份master 服务器的变化日志文件,文
件类型为changelog_ml.*.mfs ,以便于在
master server 出问题的时候接替其进行工作
数据存储服务器
data servers (chunkservers)
负责连接管理服务器,听从管理服务器调度,
提供存储空间,并为客户提供数据传输.
客户机挂载使用
client computers
通过fuse 内核接口挂接远程管理服务器上所
管理的数据存储服务器,.看起来共享的文件
系统和本地unix 文件系统使用一样的效果.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

官方的网络示意图是这样的:

读写原理:

MFS的读数据过程

  1. client当需要一个数据时,首先向master server发起查询请求;
  2. 管理服务器检索自己的数据,获取到数据所在的可用数据服务器位置ip|port|chunkid;
  3. 管理服务器将数据服务器的地址发送给客户端;
  4. 客户端向具体的数据服务器发起数据获取请求;
  5. 数据服务器将数据发送给客户端;

MFS的写数据过程

  1. 当客户端有数据写需求时,首先向管理服务器提供文件元数据信息请求存储地址(元数据信息如:文件名|大小|份数等);
  2. 管理服务器根据写文件的元数据信息,到数据服务器创建新的数据块;
  3. 数据服务器返回创建成功的消息;
  4. 管理服务器将数据服务器的地址返回给客户端(chunkIP|port|chunkid);
  5. 客户端向数据服务器写数据;
  6. 数据服务器返回给客户端写成功的消息;
  7. 客户端将此次写完成结束信号和一些信息发送到管理服务器来更新文件的长度和最后修改时间

MFS的删除文件过程

  1. 客户端有删除操作时,首先向Master发送删除信息;
  2. Master定位到相应元数据信息进行删除,并将chunk server上块的删除操作加入队列异步清理;
  3. 响应客户端删除成功的信号

MFS修改文件内容的过程

  1. 客户端有修改文件内容时,首先向Master发送操作信息;
  2. Master申请新的块给.swp文件,
  3. 客户端关闭文件后,会向Master发送关闭信息;
  4. Master会检测内容是否有更新,若有,则申请新的块存放更改后的文件,删除原有块和.swp文件块;
  5. 若无,则直接删除.swp文件块。

MFS重命名文件的过程

  1. 客户端重命名文件时,会向Master发送操作信息;
  2. Master直接修改元数据信息中的文件名;返回重命名完成信息;

MFS遍历文件的过程

  1. 遍历文件不需要访问chunk server,当有客户端遍历请求时,向Master发送操作信息;
  2. Master返回相应元数据信息;
  3. 客户端接收到信息后显示

 

注:

  • Master记录着管理信息,比如:文件路径|大小|存储的位置(ip,port,chunkid)|份数|时间等,元数据信息存在于内存中,会定期写入metadata.mfs.back文件中,定期同步到metalogger,操作实时写入changelog.*.mfs,实时同步到metalogger中。master启动将metadata.mfs载入内存,重命名为metadata.mfs.back文件。
  • 文件以chunk大小存储,每chunk最大为64M,小于64M的,该chunk的大小即为该文件大小(验证实际chunk文件略大于实际文件),超过64M的文件将被切分,以每一份(chunk)的大小不超过64M为原则;块的生成遵循规则:目录循环写入(00-FF 256个目录循环,step为2)、chunk文件递增生成、大文件切分目录连续。
  • Chunkserver上的剩余存储空间要大于1GB(Reference Guide有提到),新的数据才会被允许写入,否则,你会看到No space left on device的提示,实际中,测试发现当磁盘使用率达到95%左右的时候,就已经不行写入了,当时可用空间为1.9GB。
  • 文件可以有多份copy,当goal为1时,文件会被随机存到一台chunkserver上,当goal的数大于1时,copy会由master调度保存到不同的chunkserver上,goal的大小不要超过chunkserver的数量,否则多出的copy,不会有chunkserver去存。

 

 

三、MFS的安装、部署、配置

测试环境:

IP 作用
192.168.0.1 master server
192.168.0.2 metalogger server
192.168.0.3 chunk server
192.168.0.4 chunk server
192.168.0.5 chunk server
192.168.0.6 mfs client

 

 

 

 

 

 

 

 

3.1  Master Server

在192.168.0.1进行如下操作:

3.1.1 安装:

复制代码
$useradd mfs -s /sbin/nologin
$wget http://www.moosefs.org/tl_files/mfscode/mfs-1.6.20-2.tar.gz$tar zxvf mfs-1.6.20-2.tar.gz
$cd mfs-1.6.20-2/
$./configure --prefix=/usr/local/mfs --with-default-user=mfs --with-default-group=mfs --disable-mfschunkserver --disable-mfsmount
$make
$make install
$cd /usr/local/mfs/etc
$cp mfsmaster.cfg.dist mfsmaster.cfg  #主配置文件
$cp mfsexports.cfg.dist mfsexports.cfg #被挂接目录及权限配置
复制代码

3.1.2 配置:

按 Ctrl+C 复制代码

按 Ctrl+C 复制代码

注意:

  • 凡是用#注释掉的变量均使用其默认值
  • 修改DATA_PATH指定的目录要权限为mfs,chown -R mfs:mfs /usr/local/mfs/var/mfs
  • mfsexports 访问控制对于那些老客户是没用的
  • 注意开通监听的端口
$vi /usr/local/mfs/etc/mfsexports.cfg
    #客户端IP        允许挂载的目录  客户端拥有的权限
    192.168.0.0/24     /            rw,alldirs,maproot=0  # /标识MFS的根
    192.168.0.0/24     .            rw                    # .标识MFSMETA 文件系统

地址可以指定的格式

IP格式 说明
* 所有的ip 地址
n.n.n.n 单个ip 地址
n.n.n.n/b IP 网络地址/位数掩码
n.n.n.n/m.m.m.m IP 网络地址/子网掩码
f.f.f.f-t.t.t.t IP 段

 

 

 

 

 

 

权限说明:

权限 说明
ro 只读模式
rw 读写模式
alldirs 许挂载任何指定的子目录
maproot 映射为root,还是指定的用户
password 指定客户端密码

 

 

 

 

 

 

3.1.3  操作:

$/usr/local/mfs/sbin/mfsmaster [-vdu] [-t locktimeout] [-c cfgfile] [start|stop|restart|reload] #master服务操作
$/usr/local/mfs/sbin/mfscgiserv  #启动WEBUI 监控服务,访问http://192.168.0.1:9425/

注意:

最好不要kill master,安全停止执行mfsmaster stop,否则下次启动因metadata.mfs.back而出现问题,还需要使用备份来恢复。

文件说明:

$ls -l /usr/local/mfs/var/mfs  #此目录见mfsmaster.cfg中DATA_PATH配置
      metadata.mfs.back  #MFS元数据信息,延迟将内存数据写入文件,默认频率1h,默认1d同步一次此文件到metalogger server
      changelog.*.mfs      #文件操作日志记录,实时记录、同步到metalogger server
      sessions.mfs  #客户操作状态记录
      stats.mfs        #?

3.1.4 备注:

  • 用户操作日志见:changelog.*.mfs
  • 进程产生的日志放到系统日志中:/var/log/messages
  • Master可以单独启动,注意开通监听的端口

 

3.2  Chunk Server

在192.168.0.3-在192.168.0.5 进行如下操作

3.2.1 安装:

复制代码
$useradd mfs -s /sbin/nologin
$wget http://www.moosefs.org/tl_files/mfscode/mfs-1.6.20-2.tar.gz
$tar zxvf mfs-1.6.20-2.tar.gz
$cd mfs-1.6.20-2/
$./configure --prefix=/usr/local/mfs --with-default-user=mfs --with-default-group=mfs --disable-mfsmaster --disable-mfsmount
$make
$make install
$cd /usr/local/mfs/etc
$cp mfschunkserver.cfg.dist mfschunkserver.cfg #chunk配置文件
$cp mfshdd.cfg.dist mfshdd.cfg  #MFS使用空间配置
复制代码

3.2.2 配置:

复制代码
$vi /usr/local/mfs/etc/mfschunkserver.cfg
# WORKING_USER = mfs
# WORKING_GROUP = mfs
# DATA_PATH = /usr/local/mfs/var/mfs
# LOCK_FILE = /var/run/mfs/mfschunkserver.pid
# SYSLOG_IDENT = mfschunkserver
# BACK_LOGS = 50
# MASTER_RECONNECTION_DELAY = 30
MASTER_HOST = 192.168.0.1 #元数据服务器的名称或地址,可以是主机名,也可以是ip 地址
# MASTER_PORT = 9420 #为Matser中 MATOCS_LISTEN_PORT指定的端口
# MASTER_TIMEOUT = 60
# CSSERV_LISTEN_HOST = *
# CSSERV_LISTEN_PORT = 9422 这个监听端口用于与其它数据存储服务器间的连接,通常是数据复制
# CSSERV_TIMEOUT = 60
# CSTOCS_TIMEOUT = 60
# HDD_CONF_FILENAME = /usr/local/mfs/etc/mfshdd.cfg 分配给MFS 使用的磁盘空间配置文件的位置
复制代码

注意:

  • MASTER_HOST 不能为localhost或127.0.0.1,做单机测试的童鞋们注意了,要使用对外IP。
  • MASTER_PORT必须为元数据服务器配置中MATOCS_LISTEN_PORT指定的端口,且IP必须被master允许。
  • 注意开通监控的端口(这里是9422)
$chown -R mfs:mfs /data      #没有这一操作,会出现写权限问题,因进程是用mfs运行的。
$vi /usr/local/mfs/etc/mfshdd.cfg
    /data

注意:

  • 在这里/data 是一个给mfs 的分区,但在本机上是一个独立的目录,最好是一个单独的硬盘或者一个raid 卷,最低要求是一个分区。
  • 不要忘了更改目录的权限,因为mfschunkserver进程是用mfs运行的。

3.2.3 操作

/usr/local/mfs/sbin/mfschunkserver [-vdu] [-t locktimeout] [-c cfgfile] [start|stop|restart|reload]  #chunkserver操作命令

 

3.3 MetaLogger Server

在192.168.0.2上执行如下操作:

3.3.1 安装:

同Master的安装步骤。

3.3.2 配置:

复制代码
$vi /usr/local/mfs/etc/mfsmetalogger.cfg
# WORKING_USER = mfs
# WORKING_GROUP = mfs
# SYSLOG_IDENT = mfsmetalogger
# LOCK_MEMORY = 0
# NICE_LEVEL = -19
# DATA_PATH = /usr/local/mfs/var/mfs
# BACK_LOGS = 50
# META_DOWNLOAD_FREQ = 24 元数据备份文件下载请求频率。默认为24小时,即每隔一天从元数据服务器(MASTER)下载一个metadata.mfs.back 文件。当元数据服务器关闭或者出故障时,matedata.mfs.back 文件将消失,那么要恢复整个mfs,则需从metalogger 服务器取得该文件。请特别注意这个文件,它与日志文件一起,才能够恢复整个被损坏的分布式文件系统。
# MASTER_RECONNECTION_DELAY = 5
MASTER_HOST = 192.168.0.1
# MASTER_PORT = 9419
# MASTER_TIMEOUT = 60
# deprecated, to be removed in MooseFS 1.7
# LOCK_FILE = /var/run/mfs/mfsmetalogger.lock
复制代码

注意:

  • MASTER_HOST 不能为localhost或127.0.0.1,做单机测试的童鞋们注意了,要使用对外IP。
  • MASTER_PORT必须为元数据服务器配置中MATOCS_LISTEN_PORT指定的端口,且IP必须被master允许。

3.3.3 操作

/usr/local/mfs/sbin/mfsmetalogger [-vdu] [-t locktimeout] [-c cfgfile] [start|stop|restart|reload]  #chunkserver操作命令

 

3.4 MFS Client

在192.168.0.6上执行如下操作:

3.4.1 FUSE安装:

复制代码
$wget http://nchc.dl.sourceforge.net/project/buluoos/0.2/src/fuse-2.8.5.tar.gz
$tar zxf fuse-2.8.5.tar.gz
$cd fuse-2.8.5
$./configure
$make && make install
$echo 'export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH' >>/etc/profile
$source /etc/profile
$lsmod |grep fuse #检查fuse是否加载到内核,若没有,执行下面命令
$modprobe fuse&&lsmod |grep fuse  #若将下列mfsmount挂载操作加入开机自启动,一定将modprobe fuse也加入开机自启
复制代码

注意:

  • 一定要将fuse环境变量配置ok,否则安装mfsmount会装不上
  • 若将下列mfsmount挂载操作加入开机自启动,一定将modprobe fuse也加入开机自启,且在其执行之前执行。

3.4.2 MFSMount安装:

复制代码
$useradd mfs -s /sbin/nologin
$wget http://www.moosefs.org/tl_files/mfscode/mfs-1.6.20-2.tar.gz
$tar zxvf mfs-1.6.20-2.tar.gz
$cd mfs-1.6.20-2/
$./configure --prefix=/usr/local/mfs --with-default-user=mfs --with-default-group=mfs --disable-mfsmaster --disable-mfschunkserver
$make;make install
复制代码

3.4.3 挂载MFS

$mkdir /mnt/mfs /mnt/mfsmeta  #创建挂载点
$/usr/local/mfs/bin/mfsmount /mnt/mfs -H 192.168.0.1
$/usr/local/mfs/bin/mfsmount -m /mnt/mfsmeta -H 192.168.0.1
$df -h #检查是否挂载成功
$umount /mnt/mfs #操作如下出错,说明客户端本机有正在使用此文件系统,可以查明是什么命令正在使用,然后推出就可以了,最好不要强制退出。

 

到此为止,我们的MFS集群已经搭建完成。

 

四、MFS的高级特性

4.1 冗余goal设置

目标(goal),是指文件被拷贝的份数,设定了拷贝的份数后是可以通过mfsgetgoal 命令来证实的,也可以通过mfsrsetgoal 来改变设定。

$/usr/local/mfs/bin/mfssetgoal 3 /mnt/mfs/test1
$/usr/local/mfs/bin/mfsgetgoal /mnt/mfs/test1
  /mnt/mfs/test1: 3

mfsgetgoal –r 和mfssetgoal –r 同样的操作可以对整个树形目录递归操作,其等效于mfsrsetgoal命令。实际的拷贝份数可以通过mfscheckfilemfsfile info 命令来证实。

注意以下几种特殊情况:

  • 一个不包含数据的零长度的文件,尽管没有设置为非零的目标(the non-zero “goal”),但用mfscheckfile 命令查询将返回一个空的结果;将文件填充内容后,其会根据设置的goal创建副本;这时再将文件清空,其副本依然作为空文件存在。
  • 假如改变一个已经存在的文件的拷贝个数,那么文件的拷贝份数将会被扩大或者被删除,这个过程会有延时。可以通过mfscheckfile 命令来证实。
  • 对一个目录设定“目标”,此目录下的新创建文件和子目录均会继承此目录的设定,但不会改变已经存在的文件及目录的拷贝份数。

可以通过mfsdirinfo来查看整个目录树的信息摘要。

4.2 垃圾回收站

一个删除文件能够存放在一个“ 垃圾箱”的时间就是一个隔离时间, 这个时间可以用mfsgettrashtime 命令来验证,也可以用mfssettrashtime 命令来设置。如:

$/usr/local/mfs/bin/mfssettrashtime 64800 /mnt/mfs/test1
$/usr/local/mfs/bin/mfsgettrashtime /mnt/mfs/test1
  /mnt/mfs/test1: 64800

时间的单位是秒(有用的值有:1 小时是3600 秒,24 – 86400 秒,1天 – 604800 秒)。就像文件被存储的份数一样, 为一个目录设定存放时间是要被新创建的文件和目录所继承的。数字0 意味着一个文件被删除后, 将立即被彻底删除,在想回收是不可能的。

删除文件可以通过一个单独安装MFSMETA 文件系统。特别是它包含目录/ trash (包含任然可以被还原的被删除文件的信息)和/ trash/undel (用于获取文件)。只有管理员有权限访问MFSMETA(用户的uid 0,通常是root)。

$/usr/local/mfs/bin/mfsmount -m /mnt/mfsmeta -H 192.168.0.1

被删文件的文件名在“垃圾箱”目录里还可见,文件名由一个八位十六进制的数i-node 和被删文件的文件名组成,在文件名和i-node 之间不是用“/”,而是用了“|”替代。如果一个文件名的长度超过操作系统的限制(通常是255 个字符),那么部分将被删除。通过从挂载点起全路径的文件名被删除的文件任然可以被读写。

移动这个文件到trash/undel 子目录下,将会使原始的文件恢复到正确的MooseFS 文件系统上路径下(如果路径没有改变)。如果在同一路径下有个新的同名文件,那么恢复不会成功。

从“垃圾箱”中删除文件结果是释放之前被它站用的空间(删除有延迟,数据被异步删除)。

在MFSMETA中还有另一个目录reserved,该目录内的是被删除但依然打开的文件。在用户关闭了这些被打开的文件后,reserved 目录中的文件将被删除,文件的数据也将被立即删除。在reserved 目录中文件的命名方法同trash 目录中的一样,但是不能有其他功能的操作。

4.3 快照snapshot

MooseFS 系统的另一个特征是利用mfsmakesnapshot 工具给文件或者是目录树做快照。

$/usr/local/mfs/bin/mfsmakesnapshot source ... destination

Mfsmakesnapshot 是在一次执行中整合了一个或是一组文件的拷贝,而且任何修改这些文件的源文件都不会影响到源文件的快照, 就是说任何对源文件的操作,例如写入源文件,将不会修改副本(或反之亦然)。

也可以使用mfsappendchunks

$/usr/local/mfs/bin/mfsappendchunks destination-file source-file ...

当有多个源文件时,它们的快照被加入到同一个目标文件中(每个chunk 的最大量是chunk)。

 

五、MFS的性能测试

下面是网上得到的一童鞋的简单测试数据,直接拿过来了。

 

写:time dd if=/dev/zero of=/mnt/mfs/test2/test500M bs=1024k count=500
读:time dd if=/mnt/mfs/test2/test500M of=/dev/null
1copy写 2copy写 1copy读 2copy读
1M 0m0.042s 0m0.042s 0m0.017s 0m0.017s
5M 0m0.073s 0m0.079s 0m0.070s 0m0.071s
20M 0m0.250s 0m0.288s 0m0.291s 0m0.376s
50M 0m0.514s 0m0.589s 0m0.896s 0m0.886s
100M 0m0.977s 0m7.497s 0m1.677s 0m1.787s
200M 0m7.910s 0m22.270s 0m2.683s 0m3.129s
500M 0m22.465s 0m51.735s 0m6.559s 0m6.990s
1G 0m52.346s 1m48.056s 0m17.319s 0m17.154s
2G 1m46.224s 3m46.283s 0m41.608s 0m34.435s
10G 9m29.037s 19m26.237s 3m55.222s 3m24.914s

 

 

 

 

 

 

 

 

 

 

 

 

  • 读速度:ca 71M/s 写速度:ca 22M/s 9M/s (以500M计算)
  • goal的设置和读写速度的关系?貌似没有太大关系。

另一份比较详细的测试数据,就不粘出来了,直接把结果拿过来:

  • 单盘多进程性能没有提升,因为都在io wait,甚至增加进程会消耗大量调度时间
  • MFS多进程时性能会提升,主要性能消耗集中在CPU系统时间。因此实际海量小文件性能要大大强于本地文件系统

 

六、MFS集群的维护

6.1 启动MFS集群

最安全的启动MooseFS 集群(避免任何读或写的错误数据或类似的问题)的方式是按照以下命令步骤:

  1. 启动mfsmaster 进程
  2. 启动所有的mfschunkserver 进程
  3. 启动mfsmetalogger 进程(如果配置了mfsmetalogger)
  4. 当所有的chunkservers 连接到MooseFS master 后,任何数目的客户端可以利用mfsmount 去挂接被export 的文件系统。(可以通过检查master 的日志或是CGI 监视器来查看是否所有的chunkserver被连接)。

6.2 停止MFS集群

安全的停止MooseFS 集群:

  1. 在所有的客户端卸载MooseFS 文件系统(用umount 命令或者是其它等效的命令)
  2. 用mfschunkserver stop 命令停止chunkserver 进程
  3. 用mfsmetalogger stop 命令停止metalogger 进程
  4. 用mfsmaster stop 命令停止master 进程

6.3 MFS chunkservers 的维护

若每个文件的goal(目标)都不小于2,并且没有under-goal 文件(这些可以用mfsgetgoal –r和mfsdirinfo 命令来检查),那么一个单一的chunkserver 在任何时刻都可能做停止或者是重新启动。以后每当需要做停止或者是重新启动另一个chunkserver 的时候,要确定之前的chunkserver 被连接,而且要没有under-goal chunks。

6.4 MFS元数据备份

通常元数据有两部分的数据:

  • 主要元数据文件metadata.mfs,当mfsmaster 运行的时候会被命名为metadata.mfs.back
  • 元数据改变日志changelog.*.mfs,存储了过去的N 小时的文件改变(N 的数值是由BACK_LOGS参数设置的,参数的设置在mfschunkserver.cfg 配置文件中)。

主要的元数据文件需要定期备份,备份的频率取决于取决于多少小时changelogs 储存。元数据changelogs 实时的自动复制。1.6版本中这个工作都由metalogger完成。

6.5 MFS Master的恢复

一旦mfsmaster 崩溃(例如因为主机或电源失败),需要最后一个元数据日志changelog 并入主要的metadata 中。这个操作时通过mfsmetarestore 工具做的,最简单的方法是:

$/usr/local/mfs/bin/mfsmetarestore -a

如果master 数据被存储在MooseFS 编译指定地点外的路径,则要利用-d 参数指定使用,如:

$/usr/local/mfs/bin/mfsmetarestore -a -d /opt/mfsmaster

6.6 从MetaLogger中恢复Master

有些童鞋提到:如果mfsmetarestore -a无法修复,则使用metalogger也可能无法修复,暂时没遇到过这种情况,这里不暂不考虑。

  1. 找回metadata.mfs.back 文件,可以从备份中找,也可以中metalogger 主机中找(如果启动了metalogger 服务),然后把metadata.mfs.back 放入data 目录,一般为{prefix}/var/mfs
  2. 从在master 宕掉之前的任何运行metalogger 服务的服务器上拷贝最后metadata 文件,然后放入mfsmaster 的数据目录。
  3. 利用mfsmetarestore 命令合并元数据changelogs,可以用自动恢复模式mfsmetarestore –a,也可以利用非自动化恢复模式
$mfsmetarestore -m metadata.mfs.back -o metadata.mfs changelog_ml.*.mfs

或:

强制使用metadata.mfs.back创建metadata.mfs,可以启动master,但丢失的数据暂无法确定。

 

七、MFS的常见问题和建议对策

7.1 Master性能瓶颈

master本身的性能瓶颈。
短期对策:按业务切分

7.2 体系架构存储文件总数的瓶颈

mfs把文件系统的结构缓存到master的内存中,个人认为文件越多,master的内存消耗越大,8g对应2500kw的文件数,2亿文件就得64GB内存。
短期对策:按业务切分

7.3 单点故障解决方案的健壮性

7.4 垃圾回收

默认的垃圾回收时间是86400,存在一种可能性是垃圾还没回收完,你的存储容量就暴掉了。
方案一:设置垃圾回收时间,积极监控存储容量。
经过测试,把垃圾回收时间设置300秒,完全可以正确回收容量。
方案二:手动周期性去删除metamfs里的trash目录下的文件(健壮性还有待测试,反正删除后容量是回收了,不晓得有没有什么后遗症。)

7.5 理想的平均写和读的速度是多少?

原始的读/写速度很明显是主要取决于所使用的硬盘的性能、网络的容量和拓扑结构的,使用的硬盘和网络的吞吐量越好,整个系统的性能也就会越好。官方的测试环境中,将MFS安装在linux(Debian)上设置存储的份数为2,一般的测试服务器(还做了其他较大量的计算),G太网络,使用Pbyte级别的数据,测试的结果为写的速度大约在20-30MB/s,读的速度为30-50MB/s。对于小文件写的速度有些下降,但是对于读的速度是没有影响的。

7.6 设置文件存储的份数是否影响写/读的速度?

一般来说,它是有影响的。在一定条件下,存储份数的设置会影响的读取的速度。例如,当文件设置存储两份而不是一份时能加快对同一文件有多个客户端读取的速度。但是在真实的环境中,多个机器同时读取同一个文件的机率是比较小的,因此,存储份数的设置对读取的速度影响是比较小的。

同样,设置存储份数对写的速度影响也是不太大的。(只有文件超过64M的时候)

 

 

本次总结主要来自一些网络上的资料和当前生产环境使用MFS的一些经验。

参考资料:《MFS知识大汇总》、《MFS权威指南》、《MFS文件系统使用手册》

Corosync + Pacemaker + pcs

以Centos 7为例说明安装配置过程

crmsh是管理pacemaker命令行工具,pcs是管理pacemaker + corosync设置工具

这里以pcs为例进行配置

1.设置主机名做好/etc/hosts解析

类似:

12.12.12.129    controller1
12.12.12.130    controller2
12.12.12.131    controller3

注意:后面将会用户vip,规划vip为12.12.12.200,后面会说明

 

2.安装pcs

$ yum install pcs #会自动依赖安装corosync和pacemaker

Make sure pcs is running and configured to start at boot time:

# systemctl enable pcsd

# systemctl start pcsd

3.为每台主机的hacluster用户设置密码:

# echo hapassword| passwd –stdin hacluster

4.使用该密码对将构成集群的节点进行身份验证:

# pcs cluster auth controller1 controller2 controller3 -u hacluster -p hapassword –force

注意:执行该步骤请检查selinux和防火墙设置

5.创建并命名集群,然后启动它:

# pcs cluster setup –force –name openstack-cluster  controller1 controller2 controller3

# pcs cluster start –all

6.查看集群运行状态:

# pcs status

Cluster name: demo-cluster
Stack: corosync
Current DC: controller3 (version 1.1.15-11.el7_3.2-e174ec8) – partition with quorum
Last updated: Fri Jan 20 13:54:04 2017        Last change: Fri Jan 20 11:46:45 2017 by root via cibadmin on controller1

3 nodes and 4 resources configured

Online: [ controller1 controller2 controller3 ]

Full list of resources:

vip    (ocf::heartbeat:IPaddr2):    Started controller2
Clone Set: lb-haproxy-clone [lb-haproxy]
Started: [ controller2 ]
Stopped: [ controller1 controller3 ]

Daemon Status:
corosync: active/disabled
pacemaker: active/disabled
pcsd: active/enabled

发现corosyc和pacemaker都是禁用状态。

7.启用开机自动启动:

# pcs cluster enable –all
controller1: Cluster Enabled
controller2: Cluster Enabled
controller3: Cluster Enabled

8.检查pacemaker状态,会发现类似如下错误日志:

# systemctl status pacemaker

error: Resource start-up disabled since no STONITH resources have been defined

error: Either configure some or disable STONITH with the stonith-enabled option

error: NOTE: Clusters with shared data need STONITH to ensure data integrity

主要是因为STONITH resources没有定义,这里没有STONITH设备,所以我们先关闭这个属性:

# pcs property set stonith-enabled=false

注意:另外由于集群选举需要超过法定票数的一半票数,所以必须至少三节点来做,而有的实验场景中

没有三节点环境,所以执行如下命令忽略quorum(法定票数)不能满足的集群状态检查(但实际上

不推荐这样做):

# pcs property set no-quorum-policy=”ignore”

然后重启pacemaker服务:

# systemctl restart pacemaker

9.为资源指定默认黏性值,防止其他节点重启后,资源被抢夺而在此段时间内服务中断:

# pcs resource defaults resource-stickiness=100

10.设置集群的基本属性:

# pcs property set pe-warn-series-max=1000 pe-input-series-max=1000 pe-error-series-max=1000 cluster-recheck-interval=5min

11.配置vip

# pcs resource create vip ocf:heartbeat:IPaddr2 params ip=”12.12.12.200″ cidr_netmask=”24″ op monitor interval=”30s”

12.将nginx添加为集群资源并确保vip只工作在活动节点上:

# pcs resource create webserver systemd:nginx

# pcs constraint order start vip then webserver kind=Optional

# pcs constraint colocation add webserver with vip

13.查看配置文件中生成的定义:

# pcs config

对于示例的这个ha,更好的做法是将haproxy加入到集群资源中,然后利用haproxy去做负载均衡转发,haproxy自身能剔除故障节点。

NGINX+PACEMAKER高可用

环境:CENTOS7
源:官方源和epel源
rpm -ivh http://mirror.sfo12.us.leaseweb.net/epel/7/x86_64/e/epel-release-7-2.noarch.rpm

配置负载均衡
nginx的配置比较简单,详细配置可参考这位朋友的文章,个人感觉写的很好,我就不重复造轮子了
http://freeloda.blog.51cto.com/2033581/1288553
关键代码:
upstream ha.com {
server 192.168.8.177 weight=4;
server 192.168.8.176 weight=3;
server 192.168.8.1 weight=2;
#server 127.0.0.1:8080 backup;
}
location / {
proxy_pass http://ha.com;
proxy_set_header X-Real-IP $remote_addr;
}
#这里配置nginx-status页面是便于nginx状态监测
location /nginx-status {
stub_status on;
access_log off;
}

配置高可用
注:文章内容在http://clusterlabs.org/doc/摘抄整理得出,官方也提供两个中文文档,详细内容请查看官方文档
1、安装
yum install pacemaker pcs
注:corosync和resource-agents自动解析为依赖,当然你也可以手动加上!
这里我选用的是pacemaker自家的集群控制shell,由于新版的pacemaker已经把crmsh脱离了出来,喜欢使用经典的crmsh可以到官网下载,
或使用opensuse HA源的rpm包

2、配置
(一)设置网络【所有节点需要设置】
为了方便通信,设置host(如果方便设置一下主机名也不错)
192.168.8.150 clus-1
192.168.8.178 clus-2

开启防火墙端口;
corosync:UDP5404/5405
pcsd:TCP2224
pacemaker_remote:TCP3121
dlm:TCP21064(未测试,不清楚作用)

端口详情在/usr/lib/firewalld/services/high-availability.xml

firewall-cmd –permanent –add-service=high-availability
firewall-cmd –reload

关闭selinux(centos7下不关闭没发现啥问题,不过官方建议关闭的肯定有它的道理的,毕竟selinux经常会导致一些奇怪的问题)
sed -i.bak “s/SELINUX=enforcing/SELINUX=disabled/g” /etc/selinux/config

(二)配置corosync并启动集群【在任意一个节点操作】
修改hacluster用户密码,用于建立认证(也可以另创建用户)
pcs cluster auth clus-1 clus-2

生成corosync配置
pcs cluster setup –name nginx-ha clus-1 clus-2
注:如果不按照pcs而是用传统的crmsh,这里需要手动修改/etc/corosync/corosync.conf,这里使用pcs命令简化了操作

启动集群
pcs cluster start –all
此时pcs status已显示
Online: [ clus-1 clus-2 ]

(三)创建集群配置
1、禁用stonith
pcs property set stonith-enabled=false
注:每一行的配置都可以使用crm_verify -L来检查语法是否有误

2、创建VIP
pcs resource create ClusterIP ocf:heartbeat:IPaddr2 \
ip=192.168.8.210 cidr_netmask=32 op monitor interval=15s
3、设置忽略集群达不到法定人数时停止所有的资源(此选项在双节点集群下使用)
pcs property set no-quorum-policy=ignore
4、设置“资源粘性值”避免资源在节点之间迁移(防止主备之间频繁切换)
pcs resource defaults resource-stickiness=100
5、开启nginx status页面,用于存活检测
详细配置查看上述的nginx板块
6、手动加载nginx资源代理
pcs resource agents ocf:heartbeat
查看所有可用资源代理,发现没有nginx的
rpm -q resource-agents发现版本为3.9.5与github的版本https://github.com/ClusterLabs/resource-agents相符,
已经是最新了!但是github的版本含有更多的资源代理文件,于是下载拷贝至/usr/lib/ocf/resource.d/heartbeat
[root@centos7 ~]# pcs resource agents ocf:heartbeat|grep nginx
nginx
7、添加nginx集群配置
pcs resource create Nginx ocf:heartbeat:nginx \
configfile=/etc/nginx/nginx.conf \
op monitor interval=30s

过一会已经看到nginx已经启动,但问题是两个服务不在同一个节点启动
[root@centos7 ~]# pcs status
Cluster name: nginx-ha
Last updated: Tue Oct 14 09:38:45 2014
Last change: Tue Oct 14 09:38:42 2014 via crm_attribute on clus-2
Stack: corosync
Current DC: clus-1 (1) – partition with quorum
Version: 1.1.10-32.el7_0.1-368c726
2 Nodes configured
2 Resources configured
Online: [ clus-1 clus-2 ]

Full list of resources:

ClusterIP (ocf::heartbeat:IPaddr2): Started clus-1
Nginx (ocf::heartbeat:nginx): Started clus-2

PCSD Status:
clus-1: Online
clus-2: Online

Daemon Status:
corosync: active/disabled
pacemaker: active/disabled
pcsd: active/disabled

8、让资源在同一个节点运行
pcs constraint colocation add Nginx ClusterIP INFINITY
这里集群Nginx只能在有ClusterIP的节点上运行,如果ClusterIP没有在任何节点运行,那么Nginx也不能运行

9、指定资源的启停先后顺序
pcs constraint order ClusterIP then Nginx
意思是先有IP再有服务

10、额外的配置
设置权重,如果你想服务侧重跑在一个节点(比如服务器性能较强)
pcs constraint location Nginx prefers clus-2=50
查看分值
crm_simulate -sL

11、后续操作
此时集群配置完毕,nginx高可用已经可以使用了
别忘了把服务设置为开机启动
systemctl enable pcsd(所有节点)
pcs cluster enable –all

使用pcs status可以很直观的看到这几个服务的状态
Daemon Status:
corosync: active/enabled
pacemaker: active/enabled
pcsd: active/enabled

(四)附录
1、手动模拟测试故障迁移
让clus-2节点设置为standby,模拟clus-2宕机
pcs cluster standby clus-2
使用pcs status查看状态
此时资源运行在clus-1,随后设置unstandby,模拟clus-2重新上线
pcs cluster unstandby clus-2
使用pcs status查看状态,依然运行在clus-1,资源粘性起效

2、手动迁移集群
想迁移到哪个节点,就将哪个节点设置为INFINITY
pcs constraint location Nginx prefers clus-2=INFINITY
查看迁移
pcs constraint –full
移除INFINITY,把控制权交回集群
pcs constraint remove location-Nginx-clus-2-INFINITY

3、crm配置
crm configure property stonith-enabled=false
crm configure primitive ClusterIP ocf:heartbeat:IPaddr2 \
params ip=192.168.8.210 cidr_netmask=32 \
op monitor interval=10s
crm configure property no-quorum-policy=ignore
crm configure primitive nginx ocf:heartbeat:nginx params configfile=/etc/nginx/nginx.conf \
op monitor interval=15s
crm configure colocation website-with-ip INFINITY: nginx ClusterIP
crm configure order nginx-after-ip mandatory: ClusterIP nginx
crm_verify -L

CentOS7安装KVM虚拟机

一.前期准备

1.确定机器有VT
终端输入命令:

grep ‘(vmx|svm)’ /proc/cpuinfo
如果flags: 里有vmx 或者svm就说明支持VT;如果没有任何的输出,说明你的cpu不支持,将无法使用KVM虚拟机。

2.确保BIOS里开启VT: Intel(R) Virtualization Tech [Enabled] 使用如下命令

lsmod | grep kvm
modprobe kvm-intel
二.桥接网络

使用桥接网络,虚拟机即可与其他机器互相访问。 1.复制ifcfg-em1 为 ifcfg-br0,并将ifcfg-br0改为如下配置

TYPE=Bridge
BOOTPROTO=static
DEFROUTE=yes
PEERDNS=yes
PEERROUTES=yes
IPV4_FAILURE_FATAL=no
NAME=br0
DEVICE=br0
ONBOOT=yes
IPADDR=192.168.1.116
NETMASK=255.255.255.0
GATEWAY=192.168.1.1
2.原网卡ifcfg-em1只保留如下配置,其他都注释掉:

NAME=em1
DEVICE=em1
ONBOOT=yes
BRIDGE=br0
3.重启网络

service network restart
三.安装kvm

1.安装kvm

yum -y install libcanberra-gtk2 qemu-kvm.x86_64 qemu-kvm-tools.x86_64 libvirt.x86_64 libvirt-cim.x86_64 libvirt-client.x86_64 libvirt-java.noarch libvirt-python.x86_64 libiscsi-1.7.0-5.el6.x86_64 dbus-devel virt-clone tunctl virt-manager libvirt libvirt-python python-virtinst
2.安装x-windows,使用图形界面管理虚拟机

yum groupinstall “X Window System”
3.安装中文字符,解决界面乱码问题

yum install dejavu-lgc-sans-fonts
yum groupinstall “Fonts” -y
错误: 1. couldn’t connect to accessibility bus failed to connect to socket /tmp/dbus 解决方法:

export NO_AT_BRIDGE=1
四.使用 1.使用virt-manager管理虚拟机,可以完成克隆,安装,开启,关闭等大部分的动作 如是ssh命令登录服务器,需加上XC参数。 如是在windows环境下通过putty登录服务器,需打开x-windows,方法如下:

在http://sourceforge.net/projects/xming/下载Xming,并安装运行。
putty.exe ==> Connection/SSH/X11 ==> X11 forwarding/Enable打勾即可,X dispaly location可以空着
2.创建预分配文件

qemu-img create -f qcow2 -o preallocation=metadata /data/test.qcow2 80G
3.让虚拟机开机自动启动

virsh list –all 查看虚拟机名称及状态
virsh autostart +虚拟机名称
设置后可在/etc/libvirt/qemu/autostart下看到已设置自动启动KVM配置文件链接