19

吐血整理 | HDFS运维问题大全

 4 years ago
source link: http://mp.weixin.qq.com/s?__biz=Mzg4NjA4NTAzNQ%3D%3D&%3Bmid=2247487422&%3Bidx=1&%3Bsn=36e6196ca5b8b582c71aefbebd8df95b
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

| 作者: 周晓,腾讯游戏 CROS 体系高级工程师,负责多套HDFS集群的维护管理,并为Apache Hadoop社区提交过2个Patch。同时也作为游戏DBA,稳定支撑包括穿越火线、天天酷跑等在内多款游戏的DB管理维护。

这次我吐血整理了一些在维护hdfs工作中遇到的问题,有的是血的教训,有的是花了不少功夫定位,也有的是一些知识点或者技巧,其中有两个补丁已经合并到apache hadoop官方。最后根据这些问题处理经验,汇总了hadoop hdfs集群需要关注的告警指标。

一、定期block全盘扫描,引起dn心跳超时而脱离集群

1. 现象:

hdfs有一个目录扫描机制,默认6小时会全盘扫描一次所有block,判断与内存里的那份blockMap是否一致。参考链接
https://blog.cloudera.com/hdfs-datanode-scanners-and-disk-checker-explained/ 。

在小文件比较多的情况,扫描的时候特征很明显——磁盘的iops很高,但吞吐量很低。当然这不是引起datanode心跳超时的原因,真正的原因是处理扫描后的结果,比如比较完发现有20000个block不一致,在修复这些block时不断的持有了 FsDatasetImpl 这个对象的一把锁,在磁盘比较慢的情况下,可能需要5分钟甚至10分钟处理完,从而一直阻塞读、写、心跳的线程。

详细的可以了解 HDFS-14476 (网址链接https://issues.apache.org/jira/browse/HDFS-14476),包括一些特征、证据,以及block修复逻辑,细节比较多。

2. 解决:

我们这边加了个patch(已合入官方版本 2.10和3.x),在处理异常block的时候,中间休息2秒,处理一下正常的请求,不至于datanode卡住甚至离线。

修复后的结果也是很明显,datanode心跳平滑了许多,如下图所示。

rYjMN32.png!web

二、namenode迁移裁撤,遇到客户端无法写入

1. 现象:

在需要迁移/裁撤namenode时,一般思路是保持 namenode hostname 不变,滚动迁移 standby 的方式迁移。

但是在我们的迁移实践中,发现 hdfs namenode 完成迁移后,集群正常,但 hdfs 客户端访问异常。在 yarn 这样的长任务场景下,会导致文件读写一直失败,直到 yarn nodemanager 重启。

具体问题是这样的, client使用的是 ConfiguredFailoverProxyProvider ,client启动之后会根据当时的 inetsocket创建nn1,nn2两个namenode proxy,这个在任何网络异常的情况下都不会重新创建。

client 的 updateAddress 方法能检测到namenode ip发生了变化,但由于那个异常没有捕获,本该在下次循环使用正确的 namenode ip 就能正常,但抛出异常后导致client重新连接namenode,然而上面的 namenode proxy 还是旧地址,SetupConnection 异常,又进入updateAddress判断逻辑,返回true又去建连接,陷入了死结。

2. 复现步骤:

① 打开一个hdfsclient,长时间写一个文件 hdfs put;

② 更新hdfs新namenode hostname-ip;

③ stop old nn2, start new nn2;

④ 更新客户端的namenode hostname-ip (client还在操作文件);

⑤ 切换到新namenode hdfs haadmin -failover nn1 nn2;

⑥ 此时会发现client一直报错, 在yarn客户端启动的周期内,哪怕是新文件写入,依旧会报错。

3.解决:

对 ConfiguredFailoverProxyProvider 打了个patch,就是在client failover之后,也进行updateAddress判断,如果有ip变动,就重新 createProxy。验证这个patch同样有效。不过在client那边统一捕获会比较好,因为还有其他类型的HaProvider可能也有这个问题。

这个问题的 patch 已经被合入 Apache Hadoop 3.4,见 HADOOP-17068(网址链接https://issues.apache.org/jira/browse/HADOOP-17068)。我们用的版本是 2.6.0-cdh5.4.11 ,patch也已合入官方版本。

除了从根源问题上解决,也可以在 namenode 迁移操作时,在老节点上启用端口转发,再逐个重启 yarn,避免引起大范围故障。

三、集群dn不均衡导致文件写入失败

1. 现象:

集群将满时,扩容了批机器缓解空间。运行了2个星期客户端突然报文件写入失败。

2. 原因:

hdfs在部分datanode空间满的情况下,理论会自动挑选其它可用的空闲节点。由于 dfs.datanode.du.reserved配置不当,导致依然会选中满节点。具体是dfs.datanode.du.reserved如果小于分区block reserved,在磁盘用满时就会出现。

org.apache.hadoop.ipc.RemoteException(java.io.IOException): File /kafka/xxxtmp.parquet could only be replicated to 0 nodes instead of minReplication (=1).  \
There are 14 datanode(s) running and no node(s) are excluded in this operation.

3. 解决:

①扩容完,跑rebalance;

②修改磁盘分区的block reserved,使其小于 dfs.datanode.du.reserved;

③增加单个datanode容量告警。

四、做 rebalance 时速度很慢

1. 解决:

启动 rebalance 命令./start-balancer.sh -threshold 10,如果需要提高速度可以修改限流带宽hdfs dfsadmin -setBalancerBandwidth 52428800 。

但是 datanode 上同时接收 blocks 并发数,是不能在线调整的(或者说只能调小),调整hdfs-site.xml默认的balance参数,并重启。

dfs.balancer.moverThreads=1000
dfs.balancer.dispatcherThreads=200
dfs.datanode.balance.max.concurrent.moves=50

如果启动balance时,尝试以更高的并发执行,datanode会判断没有足够的线程接收 block: IOException: Got error, status message Not able to copy block ... because threads quota is exceeded。

当 move 出现失败时,迁移速度是指数级下降的,因为move block失败默认会sleep一段时间。

./start-balancer.sh -threshold 5\
 -Ddfs.datanode.balance.max.concurrent.moves=20 \
 -Ddfs.datanode.balance.bandwidthPerSec=150000000 \
 -Ddfs.balancer.moverThreads=500 \
 -Ddfs.balancer.dispatcherThreads=100
五、给datanode在线增加磁盘

1. 解决办法:

腾讯云上的机器,可以直接在原有 datanode 上直接挂在新的磁盘,快速给hdfs扩容。

增加磁盘,不需要重启datanode。 (前提是设置了 dfs.datanode.fsdataset.volume.choosing.policy为AvailableSpaceVolumeChoosingPolicy)。

① 挂 载后,先建立hadoop数据目录并修正权限;

②在hdfs-site.xml 里加上新目录配置 dfs.datanode.data.dir;

③可以使用 reconfig 命令使其生效:  hdfs dfsadmin -reconfig datanode dn-x-x-x-x:50020 start。

六、namenode设置了HA,但故障时未成功切换

1. 现象:

active namenode 内存故障,主备切换失败

2. 原因:

dfs.ha.fencing.methods设置为了ssh,但是并不能登录其他namenode执行fence

3. 解决:

生成ssh key,免密码登录。或者改成shell(/bin/true),强切。注意,修改fence方式后,需要重启zkfc。

七、hdfs client input/output error

1. 现象:

执行 hdfs 客户端命令报错 input/output error,试着拷贝 hadoop / jdk 的介质目录,亦发现文件损坏。有时会发现 jvm core。

2.原因:

磁盘存在坏块,刚好hdfs或者jdk的 jar 库损坏。通过观察 messages 发现有 sda IO Input/Output Error 。

使用badblocks -s -v -o bb.log /dev/sda 可以看到磁盘损坏了哪些扇区。

3. 解决:

从其他机器,拷贝一份正常的介质。

八、hdfs误将 data 盘作为数据盘

1. 现象:

误将系统盘作为了dfs.datanode.data.dir,运行一段时间后,这个分区很容易最先满。

2.原因:

这个是配置上的问题,理解datanode的工作方式,可以快速的将这个分区里的block挪到正确的磁盘分区。

3. 解决:

处理方法就是停止datanode,拷贝/data block到其它分区,删掉/data的配置。因为datanode上block的位置是每次启动的时候,扫描上报给namenode,所以可以做物理拷贝。

可以使用拷贝命令cp -a /data/hadoopdata/current/BP-*-*/current/finalized/* /data1/hadoopdata/current/BP-*-*/current/finalized/ ,不能拷贝整个 hadoopdata 目录,因为VERSION文件里面的storageID不同。

九、使用decomiss方式将datanode退服时,客户端读写异常

1. 现象:

将datanode加入 exclude ,正常 decomissing 的方式退役节点,应用层反馈 spark 任务部分异常,报错 Unable to close file because the last block doest not have enough number of replicas ,但该集群一些其它的文件读写任务正常。

2. 原因:

spark任务会频繁的创建、删除application目录。在decomissing时,部分磁盘性能低的节点,磁盘更加繁忙,导致出现 last contact 心跳时间长。

3. 解决:

经过验证,发现直接 kill datanode进程的方式,不影响spark任务。但必须保证一个一个的kill,否则会出现 missing block. (这不一定是解决问题最好的办法,但的确有效)。

十、namenode editlog 长时间未做checkpoint

1. 现象:

standby namenode 的一个作用是,定期合并从journalnode上获取的editlog,生成新的元数据fsimage,然后推送到active namenode。

当standby namenode出现异常,如进程退出、软件bug(比如我们遇到过 IOException: No image directories available!),导致长时间未合并editlog。一旦需要发生切换或者重启namenode,有可能导致启动时间过长,严重的editlog合并需要的内存不足,无法启动namenode.

2.解决:

如果内存不足,一种解决办法是借一台高内存临时机器合并editlog:

① 把standby停下来,将hdfs的软件介质和配置文件,拷贝到高内存机器;

② 同时拷贝dfs.namenode.name.dir 目录中最新能用的 fsimage_xxx 和它之后的所有 edits_xxx-xxx;

③ 在临时机器上启动 namenode 进程,会自动从对应目录加载 fsiamge 、合并editlog;

预防比补救要重要,一定要监控namenode上 TransactionsSinceLastCheckpoint 这个指标,我们的阈值是达到 5000000 就告警。

十一、HDFS 3.x datanode 出现大量 CLOSE-WAIT

1. 现象:

这个问题 HDFS-15402 是在定期对 datanode http://127.0.0.1:50075/jmx jmx 进行探测的时候产生的,我们有 5 个 hadoop 3.1.3 的集群都存在该问题。在 hadoop 2.x 中正常。

50075 端口上产生过多 close-wait 的影响是,正常的 webhdfs 会出现 504 Gateway-timeout

[root@dn-9-4-xxx-yy /tmp]# ss -ant|grep :50075 |grep CLOSE-WAIT|wc -l
16464
[root@dn-9-4-xxx-yy /tmp]# ss -ant|grep :50075 |grep CLOSE-WAIT|head -3
CLOSE-WAIT 123    0                9.4.xxx.yy:50075           9.4.xxx.yy:39706
CLOSE-WAIT 123    0                9.4.xxx.yy:50075           9.4.xxx.yy:51710
CLOSE-WAIT 123    0                9.4.xxx.yy:50075           9.4.xxx.yy:47475
 
lsof -i:39706
COMMAND    PID USER   FD   TYPE    DEVICE SIZE/OFF NODE NAME
java    134304 hdfs *307u  IPv4 429yy7315      0t0  TCP dn-9-4-xxx-yy:50075->dn-9-4-xxx-yy:39706 (CLOSE_WAIT)
  
Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name
tcp      123      0 9.4.xxx.yy:50075            9.4.xxx.yy:39706            CLOSE_WAIT  134304/java

CLOSE-WAIT 状态是客户端(curl)发起关闭tcp连接时,服务端(datanode)收到了FIN-ACK,但在关闭socket时一直没有完成。正常流程是关闭socket完成,然后向客户端发送FIN:

UvumEjN.png!web

2. 原因:

所以问题出在datanode server上,与knox还是haproxy客户端没有关系。并且这个问题调整os内核参数是没有用的,除非kill datanode,否则close-wait状态会永久存在。使用网上的kill_close_wait_connections.pl 能够清理这些 close-wait,之后 webhdfs 请求变得好转。

3.解决:

目前避开的方法就是,不再请求 datanode jmx 做监控,只获取 namenode 上的指标。datanode 上采集 os 级别的指标。

十二、knox 无法上传 8G 文件

1. 现象:

在官方 jira 里我们提了这个问题 KNOX-2139 Can not handle 8GB file when using webhdfs ,当我们使用 webhdfs with knox 上传 8589934592 bytes 大小的文件,会出现 (55) Send failure: Broken pipe,在 hdfs 只能看到一个空文件。而且在版本 knox 1.1、1.2 中是必现,在 0.8 版本正常。

简单 debug 了一下代码,knox 拿到的请求 contentLength 为 0,8G 以外的情况 contentLength 为 -1。

2. 解决:

我们后来使用 haproxy 代替 knox 解决 knox 自身上传速度慢和这个 8G 文件的问题。

不过在最新的 1.4 版本,8G问题又消失了。根据官方的恢复,可能跟 jetty 的升级有关。

十三、Unable to load native-hadoop library for your platform

1.现象

Unable to load native-hadoop library for your platform... using builtin-java classes

经常在执行 hdfs 客户端命令时会有这样的提示,其实是个老生常谈的问题。

简单说就是系统里没有找到原生的 hadoop 库 libhdfs.so,这个库是 C 写的,性能比较好。缺少但不影响使用,因为 hadoop 里有 java 实现的客户端库。

出现这个总结原因有 3 个:

① hadoop 安装包里没有自带 libhdfs.so。

这个情况占很大一部分。 去到目录${HADOOP_HOME}/lib/native/,看下是否有libhdfs.so,libhdfs.a,libhadoop.so,libhadoop.a。 如果没有的话,可以重新下一个完整的二进制包,把lib/native拷出来用。

这种看到才是正常的。

./bin/hadoop checknative
20/05/14 20:13:39 INFO bzip2.Bzip2Factory: Successfully loaded & initialized native-bzip2 library system-native
20/05/14 20:13:39 INFO zlib.ZlibFactory: Successfully loaded & initialized native-zlib library
Native library checking:
hadoop:  true /data1/hadoop-hdfs/hadoop-dist/target/hadoop-2.6.0-cdh5.4.11-tendata/lib/native/libhadoop.so.1.0.0
zlib:    true /lib64/libz.so.1
snappy:  true /data1/hadoop-hdfs/hadoop-dist/target/hadoop-2.6.0-cdh5.4.11-tendata/lib/native/libsnappy.so.1
lz4:     true revision:10301
bzip2:   true /lib64/libbz2.so.1
openssl: true /usr/lib64/libcrypto.so

实在不行就在自己的 os 上编译一个。

mvn clean package -Pdist,native -DskipTests -Dtar -Dbundle.snappy -Dsnappy.lib=/usr/local/lib

② so 文件存在,但路径不对。


现在的版本,默认路径都能找得到 so 库。这个  https://stackoverflow.com/questions/19943766/hadoop-unable-to-load-native-hadoop-library-for-your-platform-warning 里面介绍的大部分方法,都是在教怎么设置路径。真实原因很少会因为路径不对,不过这个答案靠谱 https://stackoverflow.com/a/30927689 ,也就是我们的情况 3。

③ 编译的版本,在我们的 os 上依赖库不全。


遇到过这种,glibc 库版本不够:

$ ldd lib/native/libhadoop.so
lib/native/libhadoop.so: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by lib/native/libhadoop.so)
linux-vdso.so.1 =>  (0x00007ffd1db6d000)
/$LIB/libonion.so => /lib64/libonion.so (0x00007f5bfd37d000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f5bfce40000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f5bfcc23000)
libc.so.6 => /lib64/libc.so.6 (0x00007f5bfc88f000)
/lib64/ld-linux-x86-64.so.2 (0x00007f5bfd266000)

$ strings /lib64/libc.so.6 |grep GLIBC_
可以看当前系统支持哪些版本的 glibc

但是 glibc 安装升级有风险,如果要安装 2.14 版本务必先做好测试。

十四、处理 missing blocks

1. 现象:

hdfs 集群出现 missing block,无非就是 namenode 里还记录的 block 元数据信息,但是所有副本都丢失了。如果是同时挂了多个机器,或者损坏了多个机器上的磁盘,是有可能会出现。

遇到过 2 次人为产生 missing blocks:

① kill 一个 datanode 进程,就出现 missing block

② 先设置所有文件的 replication 为 1,一小段时间后,再设置为 2

这两种情况都算是 bug,对应的文件确实无法 get 下来了。但第 1 中情况还好,经过排除日志,发现实际这些丢失的 blocks 本就接收到了删除命令,过一段时间后,missing block 一般会自动消失。第 2 种情况,是真的意外丢 block 了,比较严重。不要轻易把 replication 设置为 1,再改回去可能丢 block。

如果确认这些 missing block 可以消除,可以通过 fsck 命令手动处理:

// 如果missing blocks数不是很多,可以直接逐个delete
hdfs fsck file_name -delete

// 如果missing blocks较多,可以从namenode上拿到corrupt块
hdfs fsck / -list-corruptfileblocks -openforwrite | egrep -v '^\.+$' | egrep "MISSING|OPENFORWRITE" | grep -o "/[^ ]*" | sed -e "s/:$//" > missing_blocks.txt

1

总结  应该关注的告警

实际还有些许多问题,比如用户supergroup 权限问题、rack-aware.sh文件缺失的问题,限于篇幅就不列举了。

问题是不断会出现的,但及时对大部分场景做到监控工具,能够提前发现问题。下面是整理并上线的关键告警指标:

1. datanode lastcontact

datanode 与 namenode 心跳监控。 心跳时间长意味着这个 dn 没响应了,默认超过10m30s 没响应,dn会脱离集群。

2. namenode and datanode web probe

namenode 50070 与 datanode 50075 从外部探测,并且 datanode 会根据 include里面的地址自动增减。 我们使用修改过了 telegraf http_response 插件,支持动态读取url,比如 exec bash get_datanode_urls.sh

3. d ir ctory max files

单目录下的文件数告警。 hdfs默认限制单目录下最大的文件数100万,由配置项dfs.namenode.fs-limits.max-directory-items决定, 这个指标数据来源于 fsimage 目录画像分析。

4. transactions not merged

standby 未滚动的editlog数。 长期未checkpoint会导致下次namenode启动消耗过多内存,甚至启动失败。

5 . missing blocks

异常blocks数。

6. t est write file

在2个namenode节点上,定期使用 hdfs put/get 写入文件。 如果失败会告警。

7 . non-active namenode

hdfs集群namenode有且只有一个active,一个standby。 其它情况告警。

8. cluster capacity

集群总体容量监控。

9. node usage, ioutil

单个 datanode 磁盘空间使用率预警,ioutil持续5分钟大于95%预警。

10. failover occurs

hdfs namenode发生failover

11. namenode heap size

namenode heap size使用比率。 blocks数量多,内存使用越多。

特惠体验云数据库 

jaUb6fF.png!web

↓↓更多惊喜优惠请点这儿~  


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK