自由论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
热搜: 活动 交友 discuz
查看: 558|回复: 0

【学习笔记】高可用集群(二)

[复制链接]

85

主题

97

帖子

9329

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
9329
发表于 2016-4-17 18:37:30 | 显示全部楼层 |阅读模式
haproxy    支持4层,7层负载均衡,反向代理,会话保持,大并发。
LVS    稳定,效率高,四层调度。不支持7层的内容分发或过滤。不支持会话保持。
nginx    支持七层调度,现在也有开发的新的模块来扩展调度相关的功能。在会话保持,内容分发过滤方面比haproxy相比要差

squid
varnish

www.haproxy.com

下图中haproxy用了两个网段(这里模拟内外网),实际时也可以只用一个网卡(只有内网网卡),公网IP在前端就可以了

--这里尽量用两个内网来做,防止桥接网络IP冲突

      客户端(宿主机) 1.1.1.1
        |   
        |
  外网      |  1.1.1.3
     haproxy   
  内网      |  2.2.2.3
        |      
        |
   web1      web2
  2.2.2.5      2.2.2.6

实验前准备:
1,主机名
2.2.2.3   haproxy.cluster.com     haproxy
2.2.2.5   node1.cluster.com       node1
2.2.2.6   node2.cluster.com       node2

2,时间同步
3,关闭iptables,selinux

软件包路径
笔记目录/arch/haproxy_soft/haproxy-1.4.24.tar.gz

第一步:
在haproxy.cluster.com安装haproxy
[root@haproxy ~]# tar xf /haproxy-1.4.24.tar.gz -C /usr/src/
[root@haproxy ~]# cd /usr/src/haproxy-1.4.24/
[root@haproxy haproxy-1.4.24]# make target=linux26 CPU=x86_64 PREFIX=/usr/local/haproxy install

--如果你的操作系统为32位,则把上面编译参数CPU=x86_64换成CPU=i686(如果不知道,用uname -m查看)

# ls /usr/local/haproxy/doc/haproxy/
architecture.txt   架构体系和简单应用例子说明
configuration.txt  配置文档,所有参数都有说明
haproxy-en.txt     使用手册
haproxy-fr.txt

[root@haproxy haproxy-1.4.24]# cd /usr/local/haproxy/
[root@haproxy haproxy]# ls
doc  sbin  share
# cp /usr/local/haproxy/share/man/man1/haproxy.1 /usr/share/man/man1/
--拷man文档到相应的目录,然后就可以man haproxy了



-此软件配置文件复杂,并且配置灵活,与nginx,varnish,squid类似
--可以手动写配置文件,也可以拷一个
[root@haproxy haproxy]# cp /usr/src/haproxy-1.4.24/examples/haproxy.cfg /usr/local/haproxy/

# vim haproxy.cfg      --先改一个最简单的负载配置文件;如下
global
        #log 127.0.0.1  local0
        #log 127.0.0.1  local1 notice
        log 127.0.0.1   local0 info --日志记录
        maxconn 65535   --最大链接
        chroot /usr/local/haproxy --笼环境
        uid 99    --运行haproxy的用户UID,99就是默认用户nobody
        gid 99
        daemon    --后台daemon方式运行
        nbproc 1   --打开一个进程,默认也是1,如果打开两个的话,启动后,可以使用lsof -i:80查到两个进程,并且使用RR调度的话会两次web1,再两次web2
        pidfile /var/run/haproxy.pid --指定pid文件,可用于一些reload,或关闭操作
        #debug    --debug模式
        #quiet    --静默模式,先不要打开这句,否则启动时报错你看不到

defaults   --defaults里的设置对后面的(frontend,backend,listen)这些配置段都生效
        log     global  --日志记录和global一样
        mode    http  --七层负载,如果改成tcp,就是四层负载
        option  httplog  --日志记录http的会话等信息
        option  dontlognull --记录一些空链接
        retries 3  --客户端连接失败后,允许你重试几次
        option redispatch
        maxconn 65535
        contimeout      5000
        clitimeout      50000
        srvtimeout      50000

listen  haproxy.cluster.com *:80
        balance roundrobin
        server node1.cluster.com 2.2.2.5:80
        server node2.cluster.com 2.2.2.6:80

----------------------------------------
上面的listen这个配置段,可以换成下面的这两个配置段,也是一样的效果;
这就是haproxy配置灵活的方面
frontend  haproxy.cluster.com *:80
         default_backend servers
backend servers
        balance roundrobin
        server node1.cluster.com 2.2.2.5:80
        server node2.cluster.com 2.2.2.6:80

----------------------------------------
# cd /usr/local/haproxy
# ./sbin/haproxy -f haproxy.cfg  启动服务.

-------------------------------------------------------
# kill $(</var/run/haproxy.pid)  关闭服务的方式
# ./sbin/haproxy -f haproxy.cfg -p /var/run/haproxy.pid -sf $(cat /var/run/haproxy.pid)      刷新服务的方式

man haproxy 查看相关参数

#./sbin/haproxy –help
haproxy   -f   <配置文件>   [-n 最大并发连接总数] [-N 每个侦听的最大并发数] [-d] [-D] [-q] [-V] [-c] [-p <pid文件>] [-s] [-l] [-dk]
   [-ds] [-de] [-dp] [-db] [-m <内存限制M>] [{-sf|-st} pidlist...]
   -d     前台,debug模式
   -D     daemon模式启动
   -q     安静模式,不输出信息
   -V     详细模式
   -c     对配置文件进行语法检查
   -s     显示统计数据
   -l     显示详细统计数据
   -dk 不使用kqueue
   -ds 不使用speculative epoll
   -de 不使用epoll
   -dp 不使用poll
   -db 禁用后台模式,程序跑在前台
   -sf <pidlist>
   程序启动后向pidlist里的进程发送FINISH信号,这个参数放在命令行的最后
   -st <pidlist>
   程序启动后向pidlist里的进程发送TERMINATE信号,这个参数放在命令行的最后

----------------------------------------------------------------

第二步:
把node1和node2装好httpd,然后启动起来,做一个不同的主页方便验证
node1上做:
# yum install httpd* -y
# /etc/init.d/httpd restart
# echo web1 > /var/www/html/index.html
node2上做:
# yum install httpd* -y
# /etc/init.d/httpd restart
# echo web2 > /var/www/html/index.html

第三步:
使用客户端elinks  1.1.1.3 (--haproxy的外网IP)测试   可以负载均衡
或者在/etc/hosts里加上
1.1.1.3 haproxy.cluster.com
使用elinks  haproxy.cluster.com  来测试 可以负载均衡

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

haproxy状态页面
--配置文件listen配置段加上stats的四行
listen  haproxy.cluster.com *:80
stats uri /haproxy-stats  --指定访问的路径
        stats realm Haproxy\ statistics --指定统计信息提示
        stats auth li:li123  --需要验证的用户名和密码才能登录查看
stats hide-version  --隐藏客户端访问统计页面时的haproxy版本号
        balance roundrobin
        server node1.cluster.com 2.2.2.5:80
        server node2.cluster.com 2.2.2.6:80
# ./sbin/haproxy -f haproxy.cfg -p /var/run/haproxy.pid -sf $(cat /var/run/haproxy.pid) --刷新服务
--客户端使用下面的去访问
http://haproxy.cluster.com/haproxy-stats

或者换成下面的配置,效果一样
frontend  haproxy.cluster.com *:80
   default_backend servers
backend servers
stats uri /haproxy-stats
        stats realm Haproxy\ statistics
        stats auth li:li123
stats hide-version
        balance roundrobin
        server node1.cluster.com 2.2.2.5:80
        server node2.cluster.com 2.2.2.6:80

==============================
关于haproxy日志

--rhel5的做法
把日志配置写到syslog服务里
# vim /etc/syslog.conf  --加上下面一句
local0.*                        /var/log/haproxy.log
# vim /etc/sysconfig/syslog
SYSLOGD_OPTIONS="-r -m 0" --加上-r参数,不加不记录,它这里是用远程日志来做的
# /etc/init.d/syslog restart

--rhel6的做法
把日志配置写到syslog服务里
# vim /etc/rsyslog.conf  --加上下面一句
local0.*                        /var/log/haproxy.log

vim /etc/rsyslog.conf --把下面两句话打开,只要打开udp的两句就可以(haproxy这里没有使用tcp的514,用的是udp的514)
$ModLoad imudp
$UDPServerRun 514
# /etc/init.d/rsyslog restart

然后可以去重启一下haproxy
客户端访问后再来验证查看 /var/log/haproxy.log文件就有日志了

===============================
haproxy的健康检查功能和日志处理
--再加上下面的option 两句
listen  haproxy.cluster.com *:80
        stats uri /haproxy-stats
        stats realm Haproxy\ statistics
        stats auth li:li123
        stats hide-version
        balance roundrobin
        option forwardfor  --日志forward,让后台web记录客户端的IP,而不是haproxy的IP
        option httpchk HEAD /check.txt HTTP/1.0 --健康检查功能如果后台web服务器家目录中没有check.txt文件,则表示后台web挂掉;此版本要使用http的1.0版,1.1版还不支持
        server node1.cluster.com 2.2.2.5:80 check inter 2000 rise 2 fall 5
        server node2.cluster.com 2.2.2.6:80 check inter 2000 rise 2 fall 5 --加上检查的间隔2秒,rise 2是2次正确表示服务器可用;fall 5表示5次失败表示服务器不可用

# ./sbin/haproxy -f haproxy.cfg -p /var/run/haproxy.pid -sf $(cat /var/run/haproxy.pid) --刷新

客户端验证:有check.txt文件的则可以被调,没有的就不可以

日志问题1
后台web里每2秒都会有一句healthcheck的日志,想删除他,方法如下
方法一:
写一个脚本就如下两句,定时去执行一次就可以了
sed -i '/check.txt\ HTTP\/1.0/d' /var/log/httpd/access_log
kill -HUP `cat /var/run/httpd/httpd.pid`

方法二:
httpd不记录检测日志: --健康检查有大量的日志,指定不记录它
SetEnvIf Request_URI "^/check\.txt$" dontlog
CustomLog logs/access_log combined env=!dontlog

重启后端的web服务器
/etc/init.d/httpd reload


日志问题2:
查看后端web的access.log,客户端的正常访问日志的IP并不是实际客户端IP,而是haproxy的内网IP

解决方法如下:
方法一:
直接使用前端haproxy的日志

方法二:
后端apache日志处理   --为了让access.log显示客户端的IP,而不是haproxy调度器的IP
配置httpd.conf
LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b " combined   --把原来的combined日志注释,再加上这一句
CustomLog logs/access_log combined

课后扩展:
上网查关于
1,日志切割
logrotate    shell脚本    apache(cronolog工具)
2,日志合并
追加到一个文件或者sort -o排序并合并到一个文件
[root@haproxy test]# cat 1.txt
2
1
6
4
[root@haproxy test]# cat 2.txt
5
9
[root@haproxy test]# sort -n -o 3.txt 1.txt 2.txt
[root@haproxy test]# cat 3.txt
1
2
3
4
5
6
9
3,日志分析软件,如awstats,webalizer等

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

使用haproxy做动静分离或网站数据切分

什么是动静分离?
其实就是七层调度,把动态文件的请求和静态文件的请求分别调到不同的后台服务器

什么是网站数据切分?
其实也是七层调度
比如我要把新浪新闻,新浪体育给分开

方法1:
用dns的二级域名(直接dns解析成不同的ip)
新浪新闻   news.sina.com   新浪国内新闻 news.sina.com/china/  --说明没有用二级域名   
     新浪国际新闻 news.sina.com/world/
    新浪国内新闻 china.news.sina.com   --用了二级域名  
     新浪国际新闻  world.news.sina.com
新浪体育  sports.sina.com  新浪体育nba  sports.sina.com/nba/
      新浪体育nba  nba.sports.sina.com   

方法2:
前端使用代理(squid,varnish,apache,nginx,haproxy)
通过代理软件七层调度来分离

      客户端(真实机) 1.1.1.1
        |   
        |
  外网      |  1.1.1.3
     haproxy   
  内网      |  2.2.2.3
        |      
        |
   web1      web2
  2.2.2.5      2.2.2.6
  静态文件    动态文件


[root@haproxy haproxy]# cat haproxy.cfg  --配置文件如下:
global
        #log 127.0.0.1  local0
        #log 127.0.0.1  local1 notice
        log 127.0.0.1   local0 info
        maxconn 4096
        chroot /usr/local/haproxy
        uid 99
        gid 99
        daemon
        nbproc 1
        pidfile /var/run/haproxy.pid
        #debug
        #quiet

defaults
        log     global
        mode    http
        option  httplog
        option  dontlognull
        option redispatch
retries 3
        balance roundrobin
        option forwardfor
        maxconn 2000
        timeout connect      5000
        timeout client      50000
        timeout server      50000
option httpchk HEAD /check.txt HTTP/1.0

        stats uri /haproxy-stats
        stats realm Haproxy\ statistics
        stats auth li:li123
        stats hide-version

frontend haproxy.cluster.com *:80
#       acl invalid_src src 1.1.1.1    --如果你要拒绝它访问,就把注释打开
#       block if invalid_src
        acl url_static path_end .html .png .jpg .css .js
        use_backend static if url_static
        default_backend dynamic

backend static
        server node1.cluster.com 2.2.2.5:80 check inter 2000 rise 2 fall 5
backend dynamic
server node2.cluster.com 2.2.2.6:80 check inter 2000 rise 2 fall 5


--重启服务后

在后端两个web都去创建一些不同类型的文件,写上不同的内容
然后在客户端访问,会发现所有的定义的静态文件会访问web1,除此之外的都访问web2
如:在客户端firefox上访问 http://haproxy.cluster.com/index.html --只会显示web1上的内容,静态的都指向它

----------------------------

把上面的
        acl url_static path_end .html .png .jpg .css .js
        use_backend static if url_static
        default_backend dynamic
这三句换成下面的这几句,重启haproxy,请问它做的是什么效果?
        acl url_static path_end .html .png .jpg .css .js
        acl url_static  path_beg    /static /images /img
        acl host_www    hdr_beg(host) -i www
        acl host_static hdr_beg(host) -i img. video. download.
        use_backend static if host_static or host_www url_static
        use_backend dynamic if host_www

访问img.xxx.com或video.xxx.com或download.xxx.com会调度给static后台
访问
www.xxx.com/static/
www.xxx.com/images/
www.xxx.com/img/

www.xxx.com里所有的以.html .png .jpg .css .js结尾的文件都会调度给static后台

www.xxx.com开头,不满足上面的都会调度给dynamic后台


我的配置文件如下

global
# log 127.0.0.1 local0
# log 127.0.0.1 local1 notice
log 127.0.0.1 local0 info
maxconn 4096
chroot /usr/local/haproxy
pidfile /var/run/haproxy.pid
uid 99
gid 99
daemon
#debug
#quiet
nbproc 1
defaults
log global
mode http
option httplog
option dontlognull
retries 3
option redispatch
maxconn 2000
contimeout 5000
clitimeout 50000
srvtimeout 50000
stats uri /haproxy-stats
        stats realm Haproxy\ statistics
        stats auth li:li123
stats hide-version
option httpchk HEAD /check.txt HTTP/1.0
frontend  haproxy.cluster.com *:80
option forwardfor
# acl invalid_src src 1.1.1.1
# block if invalid_src
acl url_static path_end .html .png .jpg .css .js
acl url_static  path_beg    /static
acl host_www    hdr_beg(host) -i www
acl host_static hdr_beg(host) -i img.
        use_backend static if host_static or host_www url_static
        default_backend dynamic
backend static
        server node1.cluster.com 2.2.2.5:80 check inter 2000 rise 2 fall 5

backend dynamic
        server node2.cluster.com 2.2.2.6:80 check inter 2000 rise 2 fall 5


--测试的时候,可以在客户端的/etc/hosts里去绑定域名来模拟DNS
在客户端
vim /etc/hosts --这样模拟1.1.1.3这个haproxy代理两个域名的网站
1.1.1.3   www.cluster.com
1.1.1.3   img.cluster.com

在客户端
elinks img.cluster.com/文件路径     --这个域名下url都调给node1
elinks www.cluster.com/文件路径     --这个域名下url是静态的(就是定义的.html .png .css .js结尾或者/static目录的)都调给node1;其它都调给node2
elinks 1.1.1.3/文件路径   --如果是用IP访问,都调给node2

课后扩展;
上网查squid,varnish,nginx,lvs,haproxy等软件的区别与优缺点?

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

RHCS (redhat cluster suite) 红帽集群套件
参考文档:
笔记目录下/arch/pdf/Red_Hat_Enterprise_Linux-6-Cluster_Suite_Overview-zh-CN.pdf

四种类型的集群:
storage
high availability
load balancing
high performance

RHCS 包含的组件:
cluster infrastructrue
high-avaliablity
cluster administration tools
linux virtual server

可选的包(非rhcs内):
GFS2  
CLVM  cluster logical volume  manager (集群lvm)   注意:使用clvm,需要确认clvmd守护进程在运行
gnbd(rhel5才有,rhel6没有)或iscsi(rhel6有)

--------------------------------------------------------------
cluster infrastructure 的功能 :
1,cluster management
2,lock management
3,fencing
4,cluster configuration management
--注意:集群支持的最大节点数为16

-------------------------------------------------------------
1,集群管理
cluser management管理集群 quorum 和集群节点联系。
cman服务: 如果多于一半的节点为active状态,集群则有quorum.
     如果一半或少于一半节点为acitve状态,集群则没有quorum,所有的集群节点停止。

cluster quorum 用于防止脑裂(split-brain)的情况。  
quorum 由经由以太网的节点信息来决定。可选的,quorum 可以经由quorum disk来实现。
默认,每个节点投票数为1,可以配置多于1。

集群也是有一个基于民主的投票制度来保证集群不挂掉。也就是能工作的节点的票数要大于总票数的一半+1

如果集群有16个节点, 每个节点默认1票。需要16/2+1=9票才能继续正常工作。必须要坏8个以上的节点整个集群才会挂掉。
但16个里坏8个的机率非常小

但在2个节点的集群环境中,坏一个节点,整个集群就挂掉。如果加多服务器只投票的话太浪费资源。所以可以做一个仲裁磁盘,固定投票(默认为3)来保证这种节点较少的集群在只有一个节点能工作的情况下仍然可以工作。


cman有监控节点信息的功能,当一个节点改变状态,会通知其它的节点。

2,锁管理  
lock management 是一个基于的cluster-infrastructrue服务提供多节点访问共享资源的机制。
在红帽集群中,DLM(distributed lock manager,分布式锁管理)就是锁管理者,它运行在每一个节点。

3,fencing
fencing就是一个节点与集群共享存储失去了联系( disconnection)。 fencing cuts off I/O from shared storage.
集群是由fenced这个fence daemon来运行。
当cman发现一个节点failed,它会通知基础架构的其它所有组件。
fenced会开始 fences the failed node.
DLM从失败的节点释放锁。
GFS恢复失败节点的日志。

红帽提供的fencing类型:
  power fencing    如ibm bladecenters,PAP,DRAC/MC,HP ILO,IPMI,or IBMRSAII
  
  SCSI3 persistent reservation fencing
  fibre channel switch fencing
  GNBD fencing

配置一个节点具有一个保护方法时,这个方法就是保护该节点的唯一可用保护方法。当配置多个时,则会按照配置顺序进行层叠。
如果节点出现故障,按第一个保护方法对它进行保护;如果第一个没有成功,第二个方法将被使用。所有不成功,则又从第一个开始循环,直到被成功保护为止。

stonith   shoot the other node in the head

4,集群配置管理
cluter configuration system  (CCS)管理集群配置文件。
CCS运行在每一个节点,只要一个节点的配置文件有更新,则CCS会传播更新到其它所有节点。

配置文件(/etc/cluster/cluster.conf)是一个XML文件,它包含了以下四部分:
  集群名
  集群节点名,节点ID,每个节点的投票数和fencing的类型
  fence 设备 (它的IP,登录名和密码等)

  管理资源

--------------------------------------------------
high-availability service management    高可用服务管理

高可用服务提供了从一个节点到另一个节点的failover(对集群客户端的操作不打断,透明)
在多节点中或failover域中可以定义failover的优先级,如果没有定义优先级,则可能会failover到任意节点。

  如 A B C D E
  AB是同一个failover domain  只能AB之间failover
  BCD是同一个failover domain  B优先级为2,C优先级为1,D优先级为3  如果C挂了,则优先failover给B
  CDE是同一个failover domain  不配置优先级的话,挂一个,任意failover给另两个之一

--------------------------------------------------------------------------

red hat globle file system    红帽GFS
GFS/GFS2最大支持的节点数为16

GFS/GFS2基于64位架构,理论上可以支持8EB大小的文件系统大小。然而,它只支持25TB,如果要大于25TB,需要红帽公司的服务。

一个GFS/GFS2文件系统应该被创建成LVM(条带化或镜像,由CLVM管理)

CLVM在单节点上与LVM2提供相同的容量,但在红帽集群里让所有的节点可以访问到。
CLVM的核心组件是clvmd这个守护进程,clvmd运行在每一个红帽节点上,用于分发集群的数据更新,让每个节点都可以看到更新的内容。

配置CLVM的三种方式:
1,LVM graphical user interface
2, conga LVM graphical user interface
3, createing logical volumes pvcreate-> vgcreate -> lvcreate

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

使用RHCS在rhel6.5上的kvm(一定要用kvm,因为要模拟电源管理)上搭建apache高可用


        客户端(真实机)
   vip 192.168.122.100
  
  web1     web2
        192.168.122.139  192.168.122.140
   
   
   存储服务器   --使用宿主机来模拟存储,也提供kvm的fence
   192.168.122.1

实验前准备:下面三台都要做如下步骤
1,把所有的IP都配置成静态IP,用network服务,不要用rhel6里的NetworkManager服务(因为后面的cman服务不支持NetworkManager)

/etc/init.d/NetworkManager stop
/etc/init.d/network start
chkconfig network on
chkconfig NetworkManager off

2,主机名三步 三台服务器都在/etc/hosts绑定
192.168.122.1   li.cluster.com   fence
192.168.122.139  node1.cluster.com       node1
192.168.122.140  node2.cluster.com       node2


3,关闭防火墙,selinux
# iptables -F
# iptables -t nat -F
# iptables -t mangle -F  --把这三句加到/etc/rc.local里
# service iptables stop
# chkconfig iptables off
# setup  --去把firewall里的enabled前面的勾去掉

# setenforce 0
# vim /etc/selinux/config
SELINUX=disabled --最好把这改成disabled,因为后面实验会重启,不改的话selinux又会变成enforcing
4,时间同步  

5,配置好yum
我的如下,配置完整yum源,因为很多软件包在其它的软件仓库里
我下面的yum是通过宿主机的ftp服务来共享软件仓库的
# cat /etc/yum.repos.d/rhel-source.repo

[server]
name=sdfsafsafsadfdsaf
baseurl=ftp://192.168.122.1/Server
enabled=1
gpgcheck=0
[loadbalancer]
name=rhel6.3sdfdsafas
baseurl=ftp://192.168.122.1/LoadBalancer
enabled=1
gpgcheck=0
[HighAvailability]
name=HighAvailability
baseurl=ftp://192.168.122.1/HighAvailability
enabled=1
gpgcheck=0
[ScalableFileSystem]
name=ScalableFileSystem
baseurl=ftp://192.168.122.1/ScalableFileSystem
enabled=1
gpgcheck=0
[ResilientStorage]
name=ResilientStorage
baseurl=ftp://192.168.122.1/ResilientStorage
enabled=1
gpgcheck=0

第一大步:三个节点都安装软件包(conga套件)
1,在宿主机(fence机,存储机)上安装luci
# yum install luci
2,在两个节点(node1,node2)上安装ricci
# yum install ricci   --ricci得要安装到集群各节点,luci是管理端,可以装到任意节点,或者像我这样装到宿主机
# yum install rgmanager --这个服务是用于高可用切换的(会依赖性安装cman服务)



3,在安装luci的机器上,启动luci
# /etc/init.d/luci start
Point your web browser to https://li.cluster.com:8084 (or equivalent) to access luci
--启动时的提示就是到时候这样conga的web访问接口路径
# chkconfig luci on
在安装ricci的机器上(node1,node2)启动ricci
# /etc/init.d/ricci start
# lsof -i:11111
# chkconfig ricci on

4,手动尝试在node1和node2启动cman服务(cluster manager服务),会看到下面的错误,我们要提前解决,因为后面配置图形时,集群启动会自动按顺序来启动cman,rgmanger(主备切换相关服务),modclusterd服务;
如果这里不解决,图形配置完后启动集群也会造成cman启不了
# /etc/init.d/cman start
Starting cluster:
   Checking if cluster has been disabled at boot... [  OK  ]
   Checking Network Manager...
Network Manager is either running or configured to run. Please disable it in the cluster.
[FAILED]   --启动时会发现与NetworkManager服务冲突,所以要chkconfig NetworkManager off掉这个服务(光stop这个服务不行),再来启动cman
Stopping cluster:
   Leaving fence domain... [  OK  ]
   Stopping gfs_controld... [  OK  ]
   Stopping dlm_controld... [  OK  ]
   Stopping fenced... [  OK  ]
   Stopping cman... [  OK  ]
   Unloading kernel modules... [  OK  ]
   Unmounting configfs... [  OK  ]
所以以下步骤在所有节点(node1和node2)上都做
# /etc/init.d/NetworkManager stop
# chkconfig NetworkManager off
# chkconfig cman on
# chkconfig rgmanager on
# chkconfig modclusterd  on
--这里不用start,因为还没有配置集群,start不启来,只是做成开机自动启动


第二大步: conga图形配置
1,使用一台能够访问https://li.cluster.com:8084这个地址的机器打开浏览器访问(访问时用的是ssl,会报证书问题,加上这个安全例外,确认证书)
登录名和密码就是li.cluster.com这台机器的root用户和root密码
--见图rhcs01.png

2,点create创建一个集群,password里是所有节点的ricci用户的密码

所有节点(node1和node2)需要为ricci创建验证密码
# passwd ricci  --创建的这个密码就是节点通迅的密码
Changing password for user ricci.
New password:
BAD PASSWORD: it is WAY too short
BAD PASSWORD: is too simple
Retype new password:   
passwd: all authentication tokens updated successfully.
--见图rhcs02.png
如果ricci密码写错的话,在创建cluster时,会报如下的错误
The following errors occurred while creating cluster "web_ha": Authentication to the ricci agent at node1.cluster.com:11111 failed, Authentication to the ricci agent at node2.cluster.com:11111 failed

3,正常的话成功创建好集群了   
--见图rhcs03.png

========================================================
如果这一步创建集群不成功,你想重新创建,步骤如下
a, 在conga配置界面remove这个集群
b,在node1和node2上删除
rm -rf /etc/cluster/cluster.conf
c,在luci那台机器上
/etc/init.d/luci stop
rm -rf /var/lib/luci/data/luci.db
/etc/init.d/luci start
d,重新再创建集群
========================================================

4,手动点其中任意一个节点查看,可以看到最下面ricci,cman,rgmanager,modclusterd,clvmd五个服务都是running状态
也就是说是在创建集群时,conga自动帮我们启动的(我们前面没有start这些服务的,只是chkconfig on起来而已)
--见图rhcs04.png


5,把这两个节点做一个失败切换域(做apache的主备切换)
点failover domain的选项再点add
prioritized    这个选项是指是否对节点实现优先级(数字越小,优先级越高),优先级高,就决定了谁会优先提供这个服务.或者说是服务跑的那台挂了,会优先切换到哪一台去
我这里打勾,把node1优先级设置为1,node2优先级设置2

restricted  表示切换只可能在失败转移域的成员内切换(我这里做的总共才两个节点,所以打不打勾无所谓)

no failback  表示主服务器修好启动后,会不会再把服务抢回来(打勾表示不抢回来,不打勾表示会抢回来)
--见图rhcs05.png

6,失败转移域配好后,还没有配置失败时到底切换什么东西(在这里称为resources资源)
点resources再点add
选择ip address这个资源
IP写的就是切换的浮动ip  我这里为 192.168.122.100  
netmask写的是位数 我这里写   24
--见图rhcs06.png

再增加一个资源,选择script
名字:随便叫  我这里叫httpd
脚本路径:  我这里是/etc/init.d/httpd    --可以高可用任意服务,只要把服务的启动写成脚本,并且所有节点脚本路径要一致
--见图rhcs07.png

7,点service  groups 再点add
service name:  随便取  我这里叫apache_service
Automatically Start This Service : 打勾表示会自动把你这个service group给启动(会在主节点上把资源启动)
failover domain: 这里把前面加的web_failover这个失败转移域给选上
Recovery Policy: 默认relocate
--见图rhcs08.png

点add resource
把前一步的两个资源(一个是vip,另一个是服务脚本)给加上
(--注意不要点add child resource,子资源是表示两个资源有依赖关系时用的.我这里两个资源没有这种关系)
--见图rhcs09.png
加完资源后,点submit提交

8,在两个节点(node1和node2)上安装httpd服务,并做两个不同的主页文件,方便测试,但不要把服务启动,因为服务是由集群去帮你启动的
node1.cluster.com上做
# yum install httpd*  -y
# echo web1 > /var/www/html/index.html
node2.cluster.com上做
# yum install httpd*  -y
# echo web2 > /var/www/html/index.html

9,再回到conga图形界面,把刚才配置的service给启动
把apache_service服务前的勾打上,然后点上面的start(要看前面rhcs08.png图里Automatically Start This Service这个选项是否打了勾,如果已经打了勾,这里都不用再start了,httpd一安装完后,会自动在主上start)
刷新后就可以看到状态了,我这里apache启动在node1.cluster.com上
--见rhcs10.png
或者到节点(node1和node2都行)的shell命令行使用clustat命令查看也行
[root@node1 ~]# clustat  
Cluster Status for web_ha @ Sat Feb 22 15:06:43 2014
Member Status: Quorate
Member Name                             ID   Status
------ ----                             ---- ------
node1.cluster.com                           1 Online, Local, rgmanager
node2.cluster.com                           2 Online, rgmanager
Service Name                   Owner (Last)                   State         
------- ----                   ----- ------                   -----         
service:apache_service         node1.cluster.com              started

================================================================
--在rhel6.5上遇到下面的问题,网上查不到,初步断定是rhel6.5上版本稳定性的问题
Starting cluster "web_ha" service "apache_service" from node "node1.cluster.com" failed: parseXML(): couldn't parse xml
或者下面的报错
Restarting cluster "web_ha" service "apache_service" from node "node1.cluster.com" failed: apache_service is in unknown state 115

解决方法:
重启这两个节点(因为我前面把服务都chkconfig on了,所以重启后集群会自动OK)
virsh destroy node1
virsh destroy node2
virsh start node1
virsh start node2  --两台启动时不要间隔太久,因为集群服务启动时要一起启动
================================================================

目前成功后的配置文件如下
# cat /etc/cluster/cluster.conf
<?xml version="1.0"?>
<cluster config_version="6" name="web_ha">
<clusternodes>
  <clusternode name="node1.cluster.com" nodeid="1"/>
  <clusternode name="node2.cluster.com" nodeid="2"/>
</clusternodes>
<cman expected_votes="1" two_node="1"/>
<rm>
  <failoverdomains>
   <failoverdomain name="web_failover" ordered="1" restricted="1">
    <failoverdomainnode name="node1.cluster.com" priority="1"/>
    <failoverdomainnode name="node2.cluster.com" priority="2"/>
   </failoverdomain>
  </failoverdomains>
  <resources>
   <ip address="192.168.122.100/24" sleeptime="10"/>
   <script file="/etc/init.d/httpd" name="httpd"/>
  </resources>
  <service domain="web_failover" name="apache_service" recovery="relocate">
   <ip ref="192.168.122.100/24"/>
   <script ref="httpd"/>
  </service>
</rm>
</cluster>

================================================================
第三大步:验证测试
1,测试vip
[root@node1 ~]# ip addr    --在node1上验证vip,确实存在,而不在node2上
2,测试服务状态
httpd在node1是启动了,在node2上是stop状态

3,测试集群切换
测试方法:
在node2上使用clustat -i 2监控查看状态   --表示2秒显示一次,也可以在conga的图形界面去看状态也可以
然后在node1上使用reboot或init 6命令来模拟node1的故障重启

---------------------------------------------------
特别注意:在rhel6.5(rhel6.3上没有此问题),你在node1使用init 6,可能会造成高可用切换失败),因为rhel6.5增加两个节点的一个信息同步机制,使用下面的方法去解决,再在node1上使用init 6来测试
a),在宿主机上修改下面的内核参数(我这里只改了br0和virbr0,主要是virbr0是我的实验网络)
# echo 0 > /sys/class/net/br0/bridge/multicast_snooping
# echo 0 > /sys/class/net/virbr0/bridge/multicast_snooping
b),再把上面两句加到宿主机的/etc/rc.local里让其开机自动生效
# vim /etc/rc.local
echo 0 > /sys/class/net/br0/bridge/multicast_snooping
echo 0 > /sys/class/net/virbr0/bridge/multicast_snooping
c),还要在两个节点上(node1,node2)上,修改主集群配置文件,在里面增加下面一行
# vim /etc/cluster/cluster.conf
<cluster config_version="12" name="web_ha">
        <totem netmtu="1500" window_size="300" />     ---增加一行
....
....
</cluster>

d),做完上面的在宿主机上把两个节点虚拟机关闭,再同时启动
# virsh destroy node1
# virsh destroy node2
# virsh start node1
# virsh start node2

--------------------------------------------------

这个过程可以看到node2的clustat

Member Name                             ID   Status
------ ----                             ---- ------
node1.cluster.com                           1 Offline  --这里看到node1是offline了
node2.cluster.com                           2 Online, Local, rgmanager
Service Name                   Owner (Last)                   State         
------- ----                   ----- ------                   -----         
service:apache_service         node2.cluster.com              started   --这里看到node2抢到资源了

Member Name                             ID   Status
------ ----                             ---- ------
node1.cluster.com                           1 Online, rgmanager --node1启动成功后,又变为online了
node2.cluster.com                           2 Online, Local, rgmanager
Service Name                   Owner (Last)                   State         
------- ----                   ----- ------                   -----         
service:apache_service         node1.cluster.com              started     --这里当node1重启OK后,又抢回资源了,因为我前面的no failback选项没有打勾(表示优先级高的启动后,会再抢回资源)


还可以查看node2的/var/log/message的日志来查看刚才的过程状态
相关日志如下
Jul 19 20:31:46 node2 rgmanager[4926]: Member 1 shutting down
Jul 19 20:31:46 node2 rgmanager[4926]: Starting stopped service service:apache_service
Jul 19 20:31:47 node2 rgmanager[11725]: [ip] Adding IPv4 address 192.168.122.100/24 to eth2
Jul 19 20:31:49 node2 avahi-daemon[1452]: Registering new address record for 192.168.122.100 on eth2.IPv4.
Jul 19 20:31:51 node2 rgmanager[11826]: [script] Executing /etc/init.d/httpd start
Jul 19 20:31:52 node2 rgmanager[4926]: Service service:apache_service started
Jul 19 20:32:16 node2 corosync[4657]:   [QUORUM] Members[1]: 2
Jul 19 20:32:16 node2 corosync[4657]:   [TOTEM ] A processor joined or left the membership and a new membership was formed.
Jul 19 20:32:16 node2 corosync[4657]:   [CPG   ] chosen downlist: sender r(0) ip(192.168.122.140) ; members(old:2 left:1)
Jul 19 20:32:16 node2 corosync[4657]:   [MAIN  ] Completed service synchronization, ready to provide service.
Jul 19 20:32:16 node2 kernel: dlm: closing connection to node 1
Jul 19 20:32:24 node2 rgmanager[12138]: [script] Executing /etc/init.d/httpd status
Jul 19 20:32:49 node2 corosync[4657]:   [TOTEM ] A processor joined or left the membership and a new membership was formed.
Jul 19 20:32:49 node2 corosync[4657]:   [QUORUM] Members[2]: 1 2
Jul 19 20:32:49 node2 corosync[4657]:   [QUORUM] Members[2]: 1 2
Jul 19 20:32:49 node2 corosync[4657]:   [CPG   ] chosen downlist: sender r(0) ip(192.168.122.139) ; members(old:1 left:0)
Jul 19 20:32:49 node2 corosync[4657]:   [MAIN  ] Completed service synchronization, ready to provide service.
Jul 19 20:32:54 node2 rgmanager[12560]: [script] Executing /etc/init.d/httpd status
Jul 19 20:32:57 node2 kernel: dlm: got connection from 1
Jul 19 20:33:09 node2 rgmanager[4926]: State change: node1.cluster.com UP
Jul 19 20:33:09 node2 rgmanager[4926]: Relocating service:apache_service to better node node1.cluster.com
Jul 19 20:33:10 node2 rgmanager[4926]: Stopping service service:apache_service
Jul 19 20:33:10 node2 rgmanager[12778]: [script] Executing /etc/init.d/httpd stop
Jul 19 20:33:10 node2 rgmanager[12843]: [ip] Removing IPv4 address 192.168.122.100/24 from eth2
Jul 19 20:33:10 node2 avahi-daemon[1452]: Withdrawing address record for 192.168.122.100 on eth2.
Jul 19 20:33:21 node2 rgmanager[4926]: Service service:apache_service is stopped

4,现在服务是在node1上跑的,我测试到node1的网卡给down掉,会出现什么情况

在node1上
# ifconfig eth0 down   
在node2上查看日志可以看到下面一段
Jul 19 20:39:56 node2 fenced[4723]: fencing node node1.cluster.com
Jul 19 20:39:56 node2 fenced[4723]: fence node1.cluster.com dev 0.0 agent none result: error no method
Jul 19 20:39:56 node2 fenced[4723]: fence node1.cluster.com failed
Jul 19 20:39:59 node2 fenced[4723]: fencing node node1.cluster.com
Jul 19 20:39:59 node2 fenced[4723]: fence node1.cluster.com dev 0.0 agent none result: error no method
Jul 19 20:39:59 node2 fenced[4723]: fence node1.cluster.com failed
Jul 19 20:40:02 node2 fenced[4723]: fencing node node1.cluster.com
Jul 19 20:40:02 node2 fenced[4723]: fence node1.cluster.com dev 0.0 agent none result: error no method
Jul 19 20:40:02 node2 fenced[4723]: fence node1.cluster.com failed
在node1上也能看到有下面一段
Jul 19 20:39:56 node1 kernel: dlm: closing connection to node 2
Jul 19 20:39:56 node1 rgmanager[2117]: State change: node2.cluster.com DOWN
Jul 19 20:39:56 node1 fenced[1514]: fencing node node2.cluster.com
Jul 19 20:39:56 node1 fenced[1514]: fence node2.cluster.com dev 0.0 agent none result: error no method
Jul 19 20:39:56 node1 fenced[1514]: fence node2.cluster.com failed
Jul 19 20:39:59 node1 fenced[1514]: fencing node node2.cluster.com
Jul 19 20:39:59 node1 fenced[1514]: fence node2.cluster.com dev 0.0 agent none result: error no method
Jul 19 20:39:59 node1 fenced[1514]: fence node2.cluster.com failed
Jul 19 20:40:02 node1 fenced[1514]: fencing node node2.cluster.com
Jul 19 20:40:02 node1 fenced[1514]: fence node2.cluster.com dev 0.0 agent none result: error no method
Jul 19 20:40:02 node1 fenced[1514]: fence node2.cluster.com failed

上面两段看到两个节点互相fence,都无法成功;因为日志里写的是no method(也就是我们根本就没有配置fence设备)
现在的服务状态可以用clutstat去查看,服务还是在node1上跑;但node1的网卡都已经挂掉了;所以客户端根本无法通过vip 192.168.122.100来访问你的web
所以现在整个集群就出现了问题
现在解决的方法就是把node1重启,让node2能够抢到这个服务和vip资源;但手动重启只能一次解决当前问题;实际情况应该希望它自动重启.所以需要配置fence

第四大步:
讨论fence的配置
1,在配置之前,先手动用init 6命令把node1重启(因为刚才关闭它的网卡出现了上面的问题)
这里可能重启不能成功,可以手动在宿主机上把两节点
# virsh destroy node1 --先强制关闭node1,我这里node1的虚拟机名也叫node1
# virsh start node1 --再启动node1这个虚拟机
# virsh destroy node2
# virsh start node2
2,保证集群恢复到正常状态,然后我们就继续来配置fence

3,我们使用kvm来模拟fence电源设备,命令是fence_virsh,但是在conga图形配置界面是没有fence_virsh的选项,但有一个APC power switch的选项,它会调用fence_apc命令;所以我们在这里做一个欺骗,让它在调用fence_apc时实际是调用fence_virsh

--下面三条命令,node1和node2都要做(宿主机不用做)
# mv /usr/sbin/fence_apc /usr/sbin/fence_apc.bak
# ln -s /usr/sbin/fence_virsh /usr/sbin/fence_apc
# ll /usr/sbin/fence_apc
lrwxrwxrwx 1 root root 21 Feb 22 16:16 /usr/sbin/fence_apc -> /usr/sbin/fence_virsh

4,回到conga的配置界面
点Fences Devices再点add
再选择apc power switch
名字:随便取
IP: 192.168.122.1 --kvm宿主机的ip
登录名:root  --宿主机的root用户  
密码:xxxxxx  --宿主机的root密码

--见图rhcs11.png

5,增加完fence设备后,然后要做的就是把这个fence应用到两个节点上去
在conga图形界面点nodes再点node1.cluster.com
在下面点add fence method
名字:随便取    --rhcs这套集群软件设计为可以使用多种fence,甚至不同fence之间有优先级,所以多种fence要取名来区别
--见图rhcs12.png
提交后,再点 add fence instance
在下拉菜单选择前面做好的fence设备名,我这里就是前面叫的kvm_fence_virsh
port:  node1   --这里要注意,写的是node1.cluster.com这个kvm虚拟机的domainname,也就是虚拟机名;我这里是node1
--见图rhcs13.png

6,重复上一步,把node2.cluster.com也加上,在最后的port那里填 node2 (因为我node2.cluster.com使用的kvm虚拟机叫node2)

都提交后,我来检查一下集群配置文件,我的配置文件如下
在这里最好检查一下node1和node2的配置文件是否一致;
如果因为一些原因选成两边文件不一致,把config_version="11"版本号大的文件scp到另一个节点(版本号大,表示版本新)

<?xml version="1.0"?>
<cluster config_version="11" name="web_ha">
<clusternodes>
  <clusternode name="node1.cluster.com" nodeid="1">
   <fence>
    <method name="fence1">
     <device name="kvm_fence" port="node1"/>
    </method>
   </fence>
  </clusternode>
  <clusternode name="node2.cluster.com" nodeid="2">
   <fence>
    <method name="fence1">
     <device name="kvm_fence" port="node2"/>
    </method>
   </fence>
  </clusternode>
</clusternodes>
<cman expected_votes="1" two_node="1"/>
<rm>
  <failoverdomains>
   <failoverdomain name="web_failover" ordered="1" restricted="1">
    <failoverdomainnode name="node1.cluster.com" priority="1"/>
    <failoverdomainnode name="node2.cluster.com" priority="2"/>
   </failoverdomain>
  </failoverdomains>
  <resources>
   <script file="/etc/init.d/httpd" name="httpd"/>
   <ip address="192.168.122.100/24" sleeptime="10"/>
  </resources>
  <service domain="web_failover" exclusive="1" name="apache_service" recovery="relocate">
   <script ref="httpd"/>
   <ip ref="192.168.122.100/24"/>
  </service>
</rm>
<fencedevices>
  <fencedevice agent="fence_apc" ipaddr="192.168.122.1" login="root" name="kvm_fence" passwd="123456"/> --这里的root密码会以明文显示
</fencedevices>
</cluster>


第五大步:
fence的测试
1,我这里现在服务资源都在node1上,所以我现在在node1上关闭node1的网卡来测试
# ifconfig eth0 down

2,在关闭node1网卡的同时,可以在node2上tail -f /var/log/messages监控日志信息,如下
Jul 20 11:35:23 node2 corosync[1686]:   [TOTEM ] A processor failed, forming new configuration.
Jul 20 11:35:25 node2 corosync[1686]:   [QUORUM] Members[1]: 2
Jul 20 11:35:25 node2 corosync[1686]:   [TOTEM ] A processor joined or left the membership and a new membership was formed.
Jul 20 11:35:25 node2 corosync[1686]:   [CPG   ] chosen downlist: sender r(0) ip(192.168.122.140) ; members(old:2 left:1)
Jul 20 11:35:25 node2 corosync[1686]:   [MAIN  ] Completed service synchronization, ready to provide service.
Jul 20 11:35:25 node2 kernel: dlm: closing connection to node 1
Jul 20 11:35:25 node2 rgmanager[2349]: State change: node1.cluster.com DOWN
Jul 20 11:35:25 node2 fenced[1742]: fencing node node1.cluster.com
Jul 20 11:35:31 node2 fenced[1742]: fence node1.cluster.com success
Jul 20 11:35:31 node2 rgmanager[2349]: Taking over service service:apache_service from down member node1.cluster.com
Jul 20 11:35:31 node2 rgmanager[8253]: [ip] Adding IPv4 address 192.168.122.100/24 to eth2
Jul 20 11:35:33 node2 avahi-daemon[1903]: Registering new address record for 192.168.122.100 on eth2.IPv4.
Jul 20 11:35:35 node2 rgmanager[8356]: [script] Executing /etc/init.d/httpd start
Jul 20 11:35:35 node2 rgmanager[2349]: Service service:apache_service started
Jul 20 11:35:58 node2 corosync[1686]:   [TOTEM ] A processor joined or left the membership and a new membership was formed.
Jul 20 11:35:58 node2 corosync[1686]:   [QUORUM] Members[2]: 1 2
Jul 20 11:35:58 node2 corosync[1686]:   [QUORUM] Members[2]: 1 2
Jul 20 11:35:58 node2 corosync[1686]:   [CPG   ] chosen downlist: sender r(0) ip(192.168.122.139) ; members(old:1 left:0)
Jul 20 11:35:58 node2 corosync[1686]:   [MAIN  ] Completed service synchronization, ready to provide service.
Jul 20 11:36:05 node2 kernel: dlm: got connection from 1
Jul 20 11:36:07 node2 rgmanager[8667]: [script] Executing /etc/init.d/httpd status
Jul 20 11:36:17 node2 rgmanager[2349]: State change: node1.cluster.com UP
Jul 20 11:36:17 node2 rgmanager[2349]: Relocating service:apache_service to better node node1.cluster.com
Jul 20 11:36:17 node2 rgmanager[2349]: Stopping service service:apache_service
Jul 20 11:36:17 node2 rgmanager[8750]: [script] Executing /etc/init.d/httpd stop
Jul 20 11:36:17 node2 rgmanager[8808]: [ip] Removing IPv4 address 192.168.122.100/24 from eth2
Jul 20 11:36:17 node2 avahi-daemon[1903]: Withdrawing address record for 192.168.122.100 on eth2.
Jul 20 11:36:27 node2 rgmanager[2349]: Service service:apache_service is stopped

--从上面日志可以完整看到node1关闭网卡后,被fence掉,重新启动,node2在这个时候成功的抢占资源,node1启动成功后,又加入到了集群(因为配置了failback,所以node1启动后又抢回了资源)

到此的话,使用rhcs搭建高可用httpd,用kvm虚拟实现fence成功了


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

一些常见的问题总结:
1,一般来说,在conga图形界面修改配置后,点提交,改变的配置会应用到所有的结点
,可以查看所有节点的/etc/cluster/cluster.conf里的配置版本号来确认是否两边一致
如果两边不一致,就可以把新版本号(config_version数字大的)的cluster.conf用scp覆盖到另一边节点,使两边一致
也可以使用ccs_sync命令来同步.下面就是把node2上的cluster.conf同步到node1上的过程
[root@node2 ~]# ccs_sync  --第一次同步,需要先输入自己的ricci的密码,再输入对方的ricci的密码
You have not authenticated to the ricci daemon on node2.cluster.com
Password:
You have not authenticated to the ricci daemon on node1.cluster.com
Password:
[root@node2 ~]# ccs_sync  --第二次同步就不需要密码了

2,因为一些配置问题,造成cman启不了
cman启不来的话,最先要去查看两边的cluster.conf配置文件是否一致,不一致肯定启不来,所以可以参照上面的做法把两边的cluster.conf做成一致再启
有时候如果rgmanager启动了, cman因为一些原因停了,再启也会启不来
因为cman,rgmanager,modclusterd要按顺序来启动,所以你要把rgmanager强制停掉(stop应该停不了,可以ps -ef |grep rgm查找进程,再kill -9杀掉)
重新按照顺序来启动

3,如果集群配置有问题,想重做集群,在conga界面remove掉了集群,或者是在所有节点rm -rf /etc/cluster/cluster.conf可能会造成,再重建集群时,总会提示你的节点已经被另一个集群占用(其实就是你删掉的那个集群);
解决方法:
在luci上删除/var/lib/luci/data/luci.db数据文件(也就是说这个数据文件还保存你上一个集群相关的信息),再把所有服务都停掉,cluster.conf也删掉,重新再配置

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Prostar Inc.

GMT+8, 2022-9-27 22:10 , Processed in 0.065396 second(s), 9 queries , Memcache On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表