hdfs如何通过解析fsimage来监控目录

栏目: 服务器 · 发布时间: 5年前

内容简介:hdfs的目录结构跟linux文件系统的目录结构比较类似,业务方提出了这样一些需求:这2类需求在已有的hdfs指标体系内,是无法获得的,但好在hdfs namenode上元数据文件 fsimage 里有这类信息,但需要工具解析出来。

hdfs的目录结构跟 linux 文件系统的目录结构比较类似,业务方提出了这样一些需求:

  1. 监控某一类子目录下的文件数,不能超过2万

  2. 哪些目录下的文件长时间没有访问,可以清除

这2类需求在已有的hdfs指标体系内,是无法获得的,但好在hdfs namenode上元数据文件 fsimage 里有这类信息,但需要 工具 解析出来。

1. 了解fsimage结构

hdfs客户端命令提供 oiv 子命令,可以对二进制image文件进行解析,甚至反解析。 获取并转化fsimage:


 

hdfs getconf -confKey dfs.namenode.name.dir

hdfs dfsadmin -fetchImage ./

hdfs oiv -p XML -i /tmp/fsimage_0000000041624290295 -o fsimage.xml


//也可以转换成csv格式:

hdfs oiv -p Delimited -delimiter "," -i fsimage_0000000041624290295 -o default_fsimage_0000000043920135025.csv -t /data1/hdfsimage/tmp_default

22G image,文件目录总数约2.1亿,其中文件数约1.7亿。

  • 解析成xml耗时30m,内存占用450MB,解析后体积79G

  • 解析成csv耗时2.5h,内存占用1.3G,解析后体积37G

一个简单的目录结构:


 

/

|-- testdba

| |-- test100

| | `-- zookeeper-3.4.5-cdh5.4.11.tar.gz

| `-- test300

| `-- test3000


5 directories, 1 files

解析后xml输出:


 

<?xml version="1.0"?>

<fsimage>

<NameSection>

<genstampV1>1000</genstampV1><genstampV2>1003</genstampV2><genstampV1Limit>0</genstampV1Limit><lastAllocatedBlockId>1073741827</lastAllocatedBlockId><txid>2632</txid>

</NameSection>

<INodeSection>

<lastInodeId>16406</lastInodeId>

<inode><id>16385</id><type>DIRECTORY</type><name></name><mtime>1555489279780</mtime><permission>hadoop:supergroup:rwxrwxrwx</permission><nsquota>9223372036854775807</nsquota><dsquota>-1</dsquota></inode>

<inode><id>16386</id><type>DIRECTORY</type><name>testdba</name><mtime>1555504839494</mtime><permission>hadoop:supergroup:rwxr-xr-x</permission><nsquota>-1</nsquota><dsquota>-1</dsquota></inode>

<inode><id>16388</id><type>DIRECTORY</type><name>test100</name><mtime>1555349720236</mtime><permission>hadoop:supergroup:rwxr-xr-x</permission><nsquota>-1</nsquota><dsquota>-1</dsquota></inode>

<inode><id>16389</id><type>FILE</type><name>zookeeper-3.4.5-cdh5.4.11.tar.gz</name><replication>2</replication><mtime>1555349720231</mtime><atime>1555349720000</atime><perferredBlockSize>134217728</perferredBlockSize><permission>hadoop:supergroup:rw-r--r--</permission>

<blocks><block><id>1073741826</id><genstamp>1002</genstamp><numBytes>27595964</numBytes></block></blocks></inode>

<inode><id>16405</id><type>DIRECTORY</type><name>test300</name><mtime>1555504851619</mtime><permission>hadoop:supergroup:rwxr-xr-x</permission><nsquota>-1</nsquota><dsquota>-1</dsquota></inode> <inode><id>16406</id><type>DIRECTORY</type><name>test3000</name><mtime>1555504851619</mtime><permission>hadoop:supergroup:rwxr-xr-x</permission><nsquota>-1</nsquota><dsquota>-1</dsquota></inode>

</INodeSection>

<INodeReferenceSection></INodeReferenceSection> <SnapshotSection><snapshotCounter>0</snapshotCounter></SnapshotSection>

<INodeDirectorySection>

<directory><parent>16385</parent><inode>16386</inode></directory> <directory><parent>16386</parent><inode>16388</inode><inode>16405</inode></directory> <directory><parent>16388</parent><inode>16389</inode></directory> <directory><parent>16405</parent><inode>16406</inode></directory>

</INodeDirectorySection>

<FileUnderConstructionSection></FileUnderConstructionSection>

<SnapshotDiffSection><diff><inodeid>16385</inodeid></diff></SnapshotDiffSection>

<SecretManagerSection><currentId>0</currentId><tokenSequenceNumber>0</tokenSequenceNumber></SecretManagerSection> <CacheManagerSection><nextDirectiveId>1</nextDirectiveId></CacheManagerSection>

</fsimage>

可以看到有以下信息可提取:(文件代表文件和目录)

  1. 文件大小。目录和link文件的大小为0

  2. 文件block数

  3. 文件block大小

  4. 文件副本数

  5. 文件的访问时间、修改时间

  6. 文件权限、用户与属组

  7. nsquota,dsquota 容量配额信息

提示: namenode本身是不知道block存在哪个节点,这是datanode每次在启动或者更新的时候上报给nn的。

输出xml格式不太方便输出完整的文件路径,csv格式输出要简洁许多:


 

Path,Replication,ModificationTime,AccessTime,PreferredBlockSize,BlocksCount,FileSize,NSQUOTA,DSQUOTA,Permission,UserName,GroupName

/,0,2019-04-10 20:48,1970-01-01 08:00,0,0,0,9223372036854775807,-1,drwxr-xr-x,hadoop,supergroup

/testdba,0,2019-05-14 18:48,1970-01-01 08:00,0,0,0,-1,-1,drwxr-xr-x,hadoop,supergroup

/testdba/media_test.tar.gz,2,2019-04-10 20:49,2019-04-10 20:49,134217728,32,4190366633,0,0,-rw-r--r--,hadoop,supergroup

...

完美,虽然解析出csv格式耗时要长一些,但格式很容易入库,通过指定 tmp 目录降低解析csv时内存的使用。

2. 业内方案

  1. 基于FsImage的HDFS数据深度分析

    参考: https://zhuanlan.zhihu.com/p/32203951 文章作者在今日头条采用的方案,使用impala来分析,设计表的时候使用star schema,将元数据拆成了实体表fact和维度表dim,目的是减少数据冗余,加快查询速度。

  2. paypal NNAnalytics

    参考: https://medium.com/paypal-engineering/namenode-analytics-paypals-big-data-guardian-6bcb1a630862

    它的思路是另外启动一个修改版的 readonly standby namenode,第一次启动的时候加载完整的fsimage,后续从journalNode持续获取editlog,能够保持内存里的元数据几乎是实时的。基于内存里的元数据实现了一套查询引擎,暴露http api给用户查询。目前这个项目还比较活跃。

    hdfs如何通过解析fsimage来监控目录

    它的优点是统计信息是实时的,并且不依赖别的组件,但许多地方不满足我们的需求,而且是致命的:

    1. 不能展示子目录历史趋势,因为内存里只有当前一份实时数据

    2. 不能指定目录 , 新版本已经支持指定目录,如 /filter?set=files&filters=fileSize:eq:0,path:startsWith:/a/b/&sum=count ,但是查询速度对cpu要求较高。

    3. 其它比如,每个集群要搭一个NNA,根据项目官方给的数据,内存要求比较高。

我们实现最终架构图:

hdfs如何通过解析fsimage来监控目录

总体与第1个方案比较接近。

3. 存储分析方案

我们文件数最多的一个集群,小文件数特别多,image文件22G,解析成xml有79G,解析成csv有37G,总文件数1.7亿,总目录树4000万。要针对这么大数据在指定任意目录的情况下,文件大小、文件数量、访问时间等维度进行分析,并要近实时返回结果,评估方案前先看下汇总的需求:

  1. 指定目录,查看该目录近2个月的文件数趋势、空间大小趋势

  2. 指定目录,查看该目录下空目录个数、空文件个数、replicator分布

  3. 指定目录,超过1年未访问过的文件数量(atime分布: 1day, 7day, 14day, 30day, 180day, 1year, 2year, >2year

  4. 指定目录,单文件大小分布: <1MB, <64MB, <128MB, <1G, <10G, <50G, >50G

  5. 指定目录下,哪个最底层目录中文件数量最多 top 10

  6. 指定目录下,哪个最底层目录中空间大小最多 top 10

  7. 指定目录下,哪个子目录文件数最多,哪些子目录空间占用最大

  8. 指定目录下,近7天空间增长最快的子目录

  9. 指定根目录 / 则表示集群级别

  10. 秒级返回

对于我们来说,统计是实时性要求并不高,一天分析一次已经够用。 那么每天2亿,数据量并不是很大,以每天存一份,最大保留2个月,数据量还是很可观的,考虑存储方案spider, es, hbase都可以往里面存,但分析的需求还是比较大的:

  1. spider

    可以在文件目录、时间、文件大小等维度,各自建立二级索引,但 mysql 一个查询几乎只能用到一个索引,即使指定目录的情况可能还要聚合千万级别的数据,速度肯定快不起来。

    hbase也类似,无法建立二级索引。

  2. elasticsearch

    es的倒排索引在多维度查找上做的很极致,要实现指定任意目录实现检索聚合的需求,有两种方式:

  • 直接存filepath,通过 prefix 前缀查找 es的前缀匹配,在结果集较少时,速度是很快,但在匹配的前缀有百万或者千万级别,整个集群会有比较大的压力。见官方文档 prefix 前缀查询

  • 将目录拆分级别,比如一个5级目录 /a/b/c/d/d.txt ,需要 depth1=a/ , depth2=b/ , depth3=c/ , depth4=d/ 四个tag,查询目录 /a/b/ 时进行条件拆分,查询性能应该是比较高。 但对于需求5,查询哪些最底层目录的文件数最多,除非再做设计一个tag指定底层目录级别,否则比较难实现。

  • impala parquet列式存储比较适合分析型场景,通过上文有先例,至少impala应该是行的通,其它方案像Spark-SQL、kylin、kudu、clickhouse等就没做过多测试了。

  • 4. image转换器hdfsimager

    要实现上面的需求,特别是像文件大小分布、文件时间分布,如果不做预处理,直接在impala中计算那会是个灾难。所以需要一个工具把 hdfs oiv 解析出来的csv进行二次处理,增加必要的字段:

    • 提取父目录、目录深度

    • 增加cluster, partPath, partDay三个分区字段

    • atime, atime后加:00 impala才能识别的日期格式

    • 增加 fileSizeStep 来对文件大小进行分类

    • 增加 mtimeStep 来对文件修改时间进行分类

    
     

    $ ./hdfsimager -h

    Usage of ./hdfsimager:

    -batch int

    read fsimage csv batch lines (default 10000)

    -cluster string

    hdfs nameservice/clustername (default "default")

    -file string

    hdfs oiv csv format (default "fsimage.csv")

    -filetime int

    time flag to saveTime. 0: fileModTime, 1: currentTime, 2: use -time to set like '2019-04-25 23:00:00'

    -partitionDepth int

    the path depth to partition in impala

    -separator string

    csv fields separator, if it's a tab, use $' ' (default "|")

    -threads int

    goroutine max num (default 4)

    -time string

    use time only when filetime flag is 2

    转换示例:

    
     

    ./hdfsimager -cluster default -file fsimage_0000000043920135025.csv -separator '|' -filetime 2 -time '2019-04-23 23:30:00' -partitionDepth 2

    fsimage file info:

    filename=default_fsimage_0000000043920135025.csv, saveTime=2019-04-23 23:30:00, cluster=default, batchLines=10000, threads=4 destFilename=[default_fsimage_0000000043920135025_file_1559314800.csv default_fsimage_0000000043920135025_dir_1559314800.csv]

    结果:

    
     

    src csv:

    file,dirname|replication|atime|mtime|blockSize|blocks|fileSize|nsquota|dsquota|permission|user|group

    /a/b/20171112.check|3|2017-11-16 18:06|2017-11-1618:06|134217728|1|57005|0|0|-rw-r--r--|root|supergroup

    /a/b/20171112|0|2017-11-16 18:10|1970-01-01 08:00|0|0|0|-1|-1|drwxr-xr-x|root|supergroup


    dest csv:

    file,dirname/|replication|atime:00|mtime:00|blockSize|blocks|fileSize|nsquota|dsquota|permission|user.group|parentPath|atimeStep|mtimeStep|fileSizeStep|pathDepth|saveTime|partitionDepth|cluster|partDay

    /a/b/20171112.check|3|2017-11-16 18:06:00|2017-11-16 18:06:00|134217728|1|57005|0|0|-rw-r--r--|root.supergroup|/a/b/|730|730|1|2|2019-05-28 23:00:00|/a/b/|sh_cr|2019-05-28

    /a/b/20171112/|0|2017-11-16 18:10|1970-01-01 08:00|0|0|0|-1|-1|drwxr-xr-x|root.supergroup|/a/b/|730|730|1|2|2019-05-28 23:00:00|/a/b/|sh_cr|2019-05-28

    hdfsimager使用 go 语言开发, 将前面37G csv 转换进行,耗时8min45s,目标csv大小52G。这个文件就是我们将要入impala的文件。如果采用parquet列存储,放到hdfs上大概7-8G。

    5. 数据导入impala

    将数据入impala需要一系列步骤

    1. 创建目标表 hdfsimage_file,注意指定分区字段以及 PARQUET 存储

    2. 创建外部临时表 hdfsimage_file_IMAGEFILE__tmp,用于加载hdfs上的csv文件

    3. 向目标表插入数据,数据源就是上面的外部表

    4. 为了达到每天导入的效果,每次使用不同的外部临时表名,数据写入完成后即可删除

    另外我们目前没有使用到 image 里面 目录信息 ,所有信息都可以从 _众多文件信息 _ 统计出来。如果后续要统计空目录的个数之类的,需要导入目录信息。

    1. 创建 hdfsimage_file 表

    
     

    CREATE TABLE default.hdfsimage_file (

    path STRING,

    repl SMALLINT,

    mtime TIMESTAMP,

    atime TIMESTAMP,

    preferredblocksize INT,

    blockcount INT,

    filesize BIGINT,

    nsquota INT,

    dsquota INT,

    permission STRING,

    usergroup STRING,

    ppath STRING,

    mtime_step_day INT,

    atime_step_day INT,

    filesize_step_mb INT,

    time TIMESTAMP

    )

    PARTITIONED BY (

    partpath STRING,

    cluster STRING,

    partday STRING

    )

    STORED AS PARQUET;

    1. 建外部表: (可将 IMAGEFILE 替换成  < CLUSTER > _ < DATE >  ,区分每次导入)

    
     

    drop table if exists default.hdfsimage_file_IMAGEFILE__tmp;


    CREATE EXTERNAL TABLE hdfsimage_file_IMAGEFILE__tmp (

    path string ,

    repl smallint ,

    mtime TIMESTAMP ,

    atime TIMESTAMP ,

    preferredblocksize INT ,

    blockcount INT,

    filesize BIGINT ,

    nsquota INT ,

    dsquota INT ,

    permission STRING ,

    usergroup STRING ,

    ppath STRING,

    mtime_step_day int,

    atime_step_day int,

    filesize_step_mb int,

    depth smallint,

    time timestamp,

    partpath string,

    cluster string,

    partday string

    )

    ROW FORMAT DELIMITED FIELDS TERMINATED BY '|'

    LOCATION '/dba/hadoop/fsimage/CLUSTERNAME/DATE/files'

    1. 写入数据

    
     

    INSERT INTO hdfsimage_file

    PARTITION (cluster, partpath, partday)

    SELECT

    path,

    repl,

    mtime,

    atime,

    preferredblocksize,

    blockcount,

    filesize,

    nsquota,

    dsquota,

    permission,

    usergroup,

    ppath,

    mtime_step_day,

    atime_step_day,

    filesize_step_mb,

    time,

    cluster,

    partpath,

    partday

    FROM

    hdfsimage_file_IMAGEFILE__tmp;


    drop table if exists default.hdfsimage_file_IMAGEFILE__tmp;

    导入1.7亿耗时 286.93s 。

    hue中感受下:

    hdfs如何通过解析fsimage来监控目录

    经过3个维度的impala表分区之后,查询响应耗时,从7s下降到1.5s。

    目前通过定时任务,将各个集群的 fsimage 文件,每天拉取一次入库。

    6. 面板展示grafana

    我们并没有打算单独为hdfs集群打造一个 fsimage 展示平台,基于中心已经广泛使用的 grafana 进行扩展。但grafana本身没有 impala datasource,所以要从impala查询数据,我们基于 SimpleJsonDatasource 做了一个backend,参考 https://github.com/grafana/simple-json-datasource .

    通过把来自grafana页面的sql,请求到实现了 /query /search / 三个api的接口,通过 python impala jdbc 库 impyla 来访问imapla,结果组装成 jsondatasource 所能识别的时序数据格式。

    另外要注意每个查询尽可能把impala分区字段都作为条件带上,其中比较难做到的是 partPath,比如:

    
     

    -- 子目录文件空间最大 :


    -- grafana中的sql

    select 1 as time, concat('$path', split_part(ppath, '/', $depth)) as sub_path, sum(filesize) /1024/1024 as filesize_mb

    from hdfsimage_file

    where (cluster='$cluster' and partday = '2019-05-27' and partpath like concat(case when $depth >= 4 then concat('/', split_part('$path', '/', 2), '/', split_part('$path', '/', 3), '/') else '$path' end, "%") )

    and ppath like '$path%'

    group by split_part(ppath, '/', $depth)

    order by filesize_mb desc


    -- 通过后端的 jsondatasource backend 转换后

    -- $path=/api/flow/ $depth=4

    select 1 as time, concat('\/api\/flow\/', split_part(ppath, '/', 4)) as sub_path, sum(filesize) /1024/1024 as filesize_mb

    from hdfsimage_file

    where (cluster='default' and partday = '2019-05-27' and partpath like concat(case when 4 >= 4 then concat('/', split_part('\/api\/flow\/', '/', 2), '/', split_part('\/api\/flow\/', '/', 3), '/') else '\/api\/flow\/' end, "%") )

    and ppath like '\/api\/flow\/%'

    group by split_part(ppath, '/', 4)

    order by filesize_mb desc

    不同集群给不同的人访问权限问题,已通过 grafana-proxy 对请求的 cluster_name 进行判断过滤。

    某ai集群展示样例:

    指定任意目录,可查看各子目录空间大小、文件数量、文件大小分布等信息:

    hdfs如何通过解析fsimage来监控目录

    提示:这里展示的空间大小,不包括副本

    最底层目录的文件数和空间大小排行:

    hdfs如何通过解析fsimage来监控目录

    指定某个父目录,显示各子目录最近一周的空间变化:

    hdfs如何通过解析fsimage来监控目录

    7. 应用

    1. 某ai集群从面板看到,某目录文件数占总量94%,共有4000多万,而且显示文件大小 100%在范围 [0, 1)MB 。

      使用方确认是曾经写入了大量小文件,已经不使用。删除后总文件数下降了一个数量级,与之长时间存在datanode心跳时间长的问题消失。

    1. 蓝鲸数据平台海外环境部署,需要根据国内hdfs使用容量来预估海外的机器资源。

      以前需要登录机器手动 hdfs du 去获取目录大小,现在在面板可直接看到容量,以及近期的增长情况,大大提高了效率。

    1. 蓝鲸某核心计算集群,每天10点看到有短时间namenode无响应,影响计算任务。

      排查后发现是定期对许多目录做 du 即 contentSummary 对namenode锁挣用导致的。现在有对每个目录的大小统计,可以直接去掉 du ,降低了计算任务失败率。

    8. 后续计划

    1. 针对需求里面的第8点,某目录 /a/b/ (2500万文件)下的各子目录空间在30天内的变化趋势,需要15s才能返回 目前使用方都能接受,后续可以优化

    2. 减少入库数据量 当前每天都把fsimage file部分全量入库,但实际大部分存量文件并没有变化。增量入库可以减少数据量,加快查询速度。 但增量入库,涉及到查询 sql 和表结构设计需要较大变化。

    3. 每个目录的读写量 为提供比较完善的hdfs服务,我们希望能够通过审计日志,统计每个目录的实时访问量。 在集群访问量增大时,我们能够快速告诉使用方那个目录/文件访问量变大了。


    以上所述就是小编给大家介绍的《hdfs如何通过解析fsimage来监控目录》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

    查看所有标签

    猜你喜欢:

    本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

    重启

    重启

    米奇•乔尔 / 曲强 / 中信出版社 / 2014-6-10 / 45.00元

    罗振宇、丹尼尔•平克、赛斯•高汀、丹•艾瑞里、谢家华、阿里安娜•赫芬顿强烈推荐! 美国亚马逊2013年年度商业&投资类图书榜前20名! 互联网时代五大剧变让企业和个人无处可逃 进化,或被扔在旧时代? 全球顶尖的数字预言家独特分享 商业转型与思维转型的实践指南 当个人变为互联世界中的一个节点,如何开启新的工作方式? 如何与顾客建立直接关系?如何进行实用主义营......一起来看看 《重启》 这本书的介绍吧!

    HTML 压缩/解压工具
    HTML 压缩/解压工具

    在线压缩/解压 HTML 代码

    JS 压缩/解压工具
    JS 压缩/解压工具

    在线压缩/解压 JS 代码

    HEX HSV 转换工具
    HEX HSV 转换工具

    HEX HSV 互换工具