一, 背景
基于公司发展硬性需求, 生产 VM 服务器要统一迁移到 ZStack 虚拟化服务器. 检查自己项目使用的服务器, 其中 zookeeper 集群中招, 所以需要进行迁移.
二, 迁移计划
为了使迁移不对业务产生影响, 所以最好是采用扩容 -> 缩容 的方式进行.
说明:
1. 原生产集群为 VM-1,VM-2,VM-3 组成一个 3 节点的 ZK 集群;
2. 对该集群扩容, 增加至 6 节点 (新增 ZS-1,ZS-2,ZS-3), 进行数据同步完成;
3. 进行缩容, 下掉原先来的三个节点 (VM-1,VM-2,VM-3);
4. 替换 nginx 解析地址.
OK! 目标很明确, 过程也很清晰, 然后开干.
三, 步骤 (过程已在测试环境验证无问题):
对新增的三台服务器进行 zk 环境配置, 和老集群配置一样即可, 最好使用同一版本 (版主使用的是 3.4.6);
对老节点的 zoo.cfg 增加新集群的地址 (逐一增加), 然后对新增加节点逐一重启.
四, 问题
ZS-1 启动成功, zkServer.sh status 报错, 用 zkServer.sh status 查看, 反馈如下异常:
- [root@localhost bin]# ./zkServer.sh status
- ZooKeeper JMX enabled by default
- Using config: /usr/zookeeper/zookeeper-3.4.6/bin/../conf/zoo.cfg
- Error contacting service. It is probably not running.
此时查看数据, 数据同步正常
ZS-1 数据同步正常, 但是无法查看节点的状态信息;
怀疑是因为老节点没有重启的原因; 此时去查看原集群节点信息, 发现原集群节点状态异常. 经排查定位, 原集群的状态一直处于异常状态.
初步定位原因可能是原集群的选举存在异常, 导致新节点无法正常纳入, 继续排查.
恢复集群初始状态, 如果集群节点的状态一直没法正常查看. OK 继续定位...
五, 排查过程
以下方法来自于网络:
可能有以下几个原因:
第一, zoo.cfg 文件配置: dataLogDir 指定的目录未被创建.
- 1.zoo.cfg
- [root@SIA-215 conf]# cat zoo.cfg
- ...
- dataDir=/App/zookeeperdata/data
- dataLogDir=/App/zookeeperdata/log
- ...
2. 路径
- [root@SIA-215 conf]# cd /App/zookeeperdata/
- [root@SIA-215 zookeeperdata]# ll
- total 8
- drwxr-xr-x 3 root root 4096 Apr 23 19:59 data
- drwxr-xr-x 3 root root 4096 Aug 29 2015 log
经排查 排除该因素.
第二, myid 文件中的整数格式不对, 或者与 zoo.cfg 中的 server 整数不对应.
- [root@SIA-215 data]# cd /App/zookeeperdata/data
- [root@SIA-215 data]# cat myid
- 2[root@SIA-215 data]#
定位排查后排除不是该原因.
第三, 防火墙未关闭.
使用 service iptables stop 关闭防火墙;
使用 service iptables status 确认;
使用 chkconfig iptables off 禁用防火墙.
确认防火墙是关闭的.
- [root@localhost ~]# service iptables status
- iptables: Firewall is not running.
确认防火墙是关闭的
第四, 端口被占用.
- [root@localhost bin]# netstat -tunlp | grep 2181
- tcp 0 0 :::12181 :::* LISTEN 30035/java
- tcp 0 0 :::22181 :::* LISTEN 30307/java
确认端口没有被占用
第五, zoo.cfg 文件中主机名出错.
经测试环境测试, 主机名正确, 多域名解析也正常, 不存在此问题
第六, hosts 文件中, 本机的主机名有两个对应, 只需保留主机名和 ip 地址的映射.
经测试环境测试, 主机名正确, 多域名解析也正常, 不存在此问题 排除.
第七, zkServer.sh 里的 nc 命令有问题.
可能是机器上没有安装 nc 命令, 还有种说法是在 zkServer.sh 里找到这句:
STAT=`echo stat | nc localhost $(grep clientPort "$ZOOCFG" | sed -e 's/.*=//') 2> /dev/null| grep Mode`
在 nc 与 localhost 之间加上 -q 1 (是数字 1 而不是字母 l)
zookeeper 版本是 3.4.6,zkServer.sh 里根本没有这一句 (获取状态的语句没有用 nc 命令)
- # -q is necessary on some versions of Linux where nc returns too quickly, and no stat result is output
- clientPortAddress=`grep "^[[:space:]]*clientPortAddress[^[:alpha:]]" "$ZOOCFG" | sed -e 's/.*=//'`
- if ! [ $clientPortAddress ]
- then
- clientPortAddress="localhost"
- fi
- clientPort=`grep "^[[:space:]]*clientPort[^[:alpha:]]" "$ZOOCFG" | sed -e 's/.*=//'`
- STAT=`"$JAVA" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \
- -cp "$CLASSPATH" $JVMFLAGS org.apache.zookeeper.client.FourLetterWordMain \
- $clientPortAddress $clientPort srvr 2> /dev/null \
- | grep Mode`
- if [ "x$STAT" = "x" ]
- then
- echo "Error contacting service. It is probably not running."
- exit 1
- else
- echo $STAT
- exit 0
- fi
- ;;
六, 以下是自己排查的方式:
目前现象老集群数据同步正常, 也能进行 leader 选举 (从日志获取), 但是无法查看节点状态, 同异常信息; 进行集群扩容, 数据不能同步.
解决方法:
1, 尝试进行 foreground 模式启动, 选择一台非主节点进行重启, 可以前台查看启动日志.
zkserver.sh start-foreground
节点启动正常, 无异常输出.
2, 查看 shell 脚本: 分析 zkServer.sh.
"Error contacting service. It is probably not running." 这块日志出现以下脚本中.
- STAT=`"$JAVA" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \
- -cp "$CLASSPATH" $JVMFLAGS org.apache.zookeeper.client.FourLetterWordMain \
- $clientPortAddress $clientPort srvr 2> /dev/null \
- | grep Mode`
- if [ "x$STAT" = "x" ]
- then
- echo "Error contacting service. It is probably not running."
- exit 1
- else
- echo $STAT
- exit 0
- fi
- ;;
截取其中一部分脚本内容: 我们可以初步定为应该是 $STAT 获取存在异常 如果 STAT 变量为空, 则会显示 Error contacting service. It is probably not running.:
OK, 那就分析下这个 $STAT 到底是什么鬼?
- if [ "x$STAT" = "x" ]
- then
- echo "Error contacting service. It is probably not running."
- exit 1
- else
- echo $STAT
- exit 0
- fi
3, 尝试用 shell 的 debug 模式 看下执行过程:
截取片段执行日志如下 : 果然 STAT 变量确实为空, 导致输出 Error contacting service. It is probably not running. 并且退出.
- ++ grep '^[[:space:]]*clientPort[^[:alpha:]]' /App/zookeeper-3.4.6/bin/../conf/zoo.cfg
- + clientPort=5181
- ++ grep Mode
- ++ /opt/jdk1.8.0_131/bin/java -Dzookeeper.log.dir=. -Dzookeeper.root.logger=INFO,CONSOLE -cp '/app/zookeeper-3.4.6/bin/../build/classes:/app/zookeeper-3.4.6/bin/../build/lib/*.jar:/app/zookeeper-3.4.6/bin/../lib/slf4j-log4j12-1.6.1.jar:/app/zookeeper-3.4.6/bin/../lib/slf4j-api-1.6.1.jar:/app/zookeeper-3.4.6/bin/../lib/netty-3.7.0.Final.jar:/app/zookeeper-3.4.6/bin/../lib/log4j-1.2.16.jar:/app/zookeeper-3.4.6/bin/../lib/jline-0.9.94.jar:/app/zookeeper-3.4.6/bin/../zookeeper-3.4.6.jar:/app/zookeeper-3.4.6/bin/../src/java/lib/*.jar:/app/zookeeper-3.4.6/bin/../conf:.:/opt/jdk1.8.0_131/lib/dt.jar:/opt/jdk1.8.0_131/lib/tools.jar' org.apache.zookeeper.client.FourLetterWordMain localhost 5181 srvr
- + STAT=
- + '[' x = x ']'
- + echo 'Error contacting service. It is probably not running.'
- Error contacting service. It is probably not running.
- + exit 1
4, 修改 shell 脚本: 分析 zkServer.sh 在脚本总增加输出 STAT 内容, 这次我们不进行过滤.
- STAT1=`"$JAVA" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \
- -cp "$CLASSPATH" $JVMFLAGS org.apache.zookeeper.client.FourLetterWordMain \
- $clientPortAddress $clientPort srvr 2> test.log \ `
- echo "$STAT1"
最好的方式是 copy 一个新脚本, 以免污染原本的脚本. 我是这么做的; 然后运行该脚本.
- [root@localhost bin]# ./zkServer.sh status
- ZooKeeper JMX enabled by default
- Using config: /usr/zookeeper/zookeeper-3.4.10/bin/../conf/zoo.cfg
- Error contacting service. It is probably not running.
然后查看生成的 test.log 文件: 果然存在异常内容.
- in thread "main" java.lang.NumberFormatException: For input string: "2181
- at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
- at java.lang.Integer.parseInt(Integer.java:492)
- at java.lang.Integer.parseInt(Integer.java:527)
- at org.apache.zookeeper.client.FourLetterWordMain.main(FourLetterWordMain.java:76)
从日志信息来看, 提示说 2181 这个端口号造成的. 不是一个合法的数字.
zkServer.sh 里有这么一句:
clientPort=`grep "^[[:space:]]*clientPort[^[:alpha:]]" "$ZOOCFG" | sed -e 's/.*=//'`
grep "^[[:space:]]*clientPort[^[:alpha:]]" "$ZOOCFG" | sed -e 's/.*=//'在执行过程中, 实际命令如下:
grep '^[[:space:]]*clientPort[^[:alpha:]]' /App/zookeeper-3.4.6/bin/../conf/zoo.cfg | sed -e 's/.*=//'
最终可以基本确认配置文件存在问题.
替换配置文件: 重启 问题解决.
存在原因可能是编辑 zoo.cfg 编码格式等等引起文件内容解析异常.
来源: https://yq.aliyun.com/articles/709139