HDFS 快照、加密区与配额:数据保护与多租户
引言
写过 HDFS 架构、写过读写路径、写过一致性,这一篇我想聊聊另一类问题:数据治理。在一个真正上了规模的生产集群上,”能存、能读、能写”只是入门,真正让人头疼的往往是这三件事——
- 误删:某个同学的
hdfs dfs -rm -r多敲了一个路径,PB 级数据秒级消失。回收站未必救得了跨任务的大批量删除。 - 泄露:用户明细、财务数据、模型权重躺在 HDFS 上,谁都能
cat,合规审计过不了。 - 挤占:A 业务一次性灌了几百 TB 把磁盘打满,B 业务的实时任务跟着写失败。
这三类问题分别对应 HDFS 的三个治理能力:快照(Snapshot)防误删、透明加密区(Encryption Zone)防泄露、配额(Quota)防占用。我在某集群上把这三件武器配齐,前后花了大概两个迭代 ,踩过不少坑。本篇把原理和生产用法都讲透,区别于网上那种只贴命令的教程,我会重点讲清”为什么这么设计”以及”线上怎么用才不翻车”。
一、快照 Snapshot
1.1 原理:copy-on-write 的零拷贝只读副本
快照的核心机制是 copy-on-write(写时复制),但 HDFS 的实现方式比很多人想象的更轻量——它根本不拷贝数据。
开启快照能力的目录,NameNode 会在其 inode 上挂一个 SnapshotManager。对一个目录打快照,NN 只是记录下”当前这个目录树下,每个文件/目录的 inode 列表和元数据状态”这样一个逻辑视图,对应的数据块(block)一个都不动。真正的开销发生在后续修改时:
- 当被快照引用的文件被 删除或截断 时,NN 不能真正释放这些 block,而是让它们继续被快照”锚住”,物理上保留。
- 当被快照引用的文件被 重命名或追加 时,NN 通过差异记录(snapshot diff)维护快照视图的不变性。
这就是为什么官方说快照”几乎不占空间”——它只占一份元数据,数据空间只有在源文件被改写时才”锁住”旧版本。
1 | 开启 snapshot 的目录 /finance |
关键结论:快照不是备份,它和源数据共享 block,源盘坏了快照也救不了。要防物理故障仍需异地副本/EC/ DistCp 到另一集群。快照防的是”逻辑误操作”——误删、误改、误覆盖。
1.2 基本操作
先对一个目录开启快照能力(注意是”能力”,不是打快照本身):
1 | # 允许某目录打快照 |
访问快照里的文件用 .snapshot 这个保留路径,比如 /finance/.snapshot/finance_daily/report.parquet。这个路径对普通 hdfs dfs -cat、-get、MapReduce/Spark 输入都透明可用。
1.3 误删恢复:先 diff 后恢复
生产里我用得最多的是 getSnapshotDiff。某天审计发现 /finance 下少了几个分区,先别急着恢复,先看清楚到底被改了什么:
1 | hdfs snapshotDiff /finance finance_daily finance_s_20260702 |
拿到 diff 列表,就能精准 hdfs dfs -cp /finance/.snapshot/finance_daily/被删文件 /finance/ 定点恢复,而不是整目录覆盖(整目录覆盖会丢掉快照之后正常写入的新数据)。
定期快照策略上,我们约定每个关键业务目录每天打一个快照、保留 7 天,每周打一个保留 4 周 ,用 crontab + 一段 shell 调 createSnapshot/deleteSnapshot 滚动维护。不要手写脚本去遍历删旧快照,社区有现成的 hdfs snapshotDiff 配合 ozone 之类的工具,也可以简单点直接按命名规则 grep 删。
1.4 快照对删除的影响:空间不会真的释放
这是最容易踩的坑。很多人以为”我 rm 了 100TB,磁盘该腾出来了吧”——结果磁盘还是满的,因为某个快照还锚着这些 block。
1 | NameNode 块回收逻辑(简化): |
所以快照策略要和容量规划一起做:保留多少天的快照,就等于可能多锁住多少倍被改写的数据。对于写多改少、又有大量 append 的业务目录,快照保留期不能拉太长,否则 NN 端的 BlocksMap 和 DN 磁盘都会吃紧。
1.5 配合 DistCp 做增量同步
这是快照一个被低估的用法。跨集群同步数据时,传统 DistCp 要全量比对,慢且吃带宽。如果两端都开了快照,DistCp 支持基于 snapdiff(快照差异) 做增量同步:
1 | # 源集群先打两个快照,间隔为同步周期 |
NN 直接吐出两个快照间的 inode 差异列表,DistCp 只拷贝增删改的部分。在 PB 级目录、每天只变几个分区的场景下,同步时间从小时级降到分钟级 。
二、透明加密区 Encryption Zone
2.1 原理:端到端的 EDEK + KMS
HDFS Transparent Encryption 叫”透明”,是因为对上层应用完全无感——同一个 hdfs dfs -cat,在加密区内外写法一模一样,加解密由 HDFS 在底层完成。但底层其实是一条精心设计的端到端链路。
先理清三个密钥的层级:
1 | ┌─────────────────────────────────────────────────────────────┐ |
- EZ Key(Encryption Zone Key):每个加密区绑定一个,由 KMS 管理,永不离开 KMS 明文。
- EDEK(Encrypted Data Encryption Key):创建加密区或新建文件时,KMS 用 EZ Key 加密一个随机生成的 DEK,把加密后的 EDEK 存在 NN 的文件元数据里。NN 永远拿不到明文 DEK。
- DEK(Data Encryption Key):真正用来加解密文件内容的对称密钥。client 读文件时,把 EDEK 发给 KMS 解密成 DEK,DEK 只在 client 进程内存里短暂存在,用来对落盘/读取的字节流做 AES 加解密。
整条链路的关键设计是:DataNode 拿到的永远是密文,NN 拿到的永远是密文(EDEK),只有持有合法 Kerberos 身份、且 KMS 授权的 client 才能解出 DEK。这保证了即便 DN 磁盘被拔走、NN 元数据被偷,攻击者也解不开数据。
1 | client 写文件到加密区: |
2.2 基本操作
1 | # 1. 在 KMS 创建一个 EZ 密钥(密钥名 = 加密区的身份) |
之后所有写进 /finance 的文件自动加密,读出来自动解密,对 Spark/Hive/Flink 完全透明。
2.3 与 Kerberos / Ranger 配合
加密区不是孤立的访问控制,它必须和 Kerberos 认证 + Ranger 鉴权 一起才构成完整闭环:
- Kerberos 解决”你是谁”——没有合法 TGT,连 KMS 都进不去,更别说解 EDEK。
- Ranger 解决”你能干什么”——即便你有 Kerberos 身份,Ranger 策略没授权你读
/finance,NN 直接拒绝 open,根本到不了 KMS 解密那一步。 - 加密区 解决”绕过 HDFS 还能不能读”——哪怕有人把 DN 磁盘挂到别的机器上,没有 EZ Key 就解不开。
三者叠加:谁有 key 谁能读,而”谁有 key”又被 Kerberos + Ranger 严格约束。合规审计时,”敏感数据访问 = Ranger 审计日志 + KMS 解密日志”两份对账,几乎不可抵赖。
2.4 踩坑实录
坑一:KMS 单点。KMS 挂了,所有加密区的读写都会卡在”解 EDEK”那一步。生产上 KMS 必须 HA,至少 2 个实例前置负载均衡。我们初期只起了一个 KMS,一次 GC 停顿 8 秒 ,整集群加密区读写集体超时,教训深刻。
坑二:性能开销。加解密是 CPU 密集型。client 侧默认用 AES-CTR,JDK 没开硬件加速时吞吐能掉 15%~30% 。务必确认 JDK 启用了 AES-NI(-XX:+UseAES -XX:+UseAESIntrinsics),并在 KMS/client 节点观察 CPU。
坑三:密钥轮换。合规要求定期换 EZ Key,但轮换 EZ Key 只影响新创建文件的 EDEK,老文件的 EDEK 是用旧 key 包装的,KMS 必须同时保留新旧 key 才能读老文件。别以为 rollover 完就能把旧 key 删了——删了等于老文件全读不出来。
坑四:加密区文件不能跨区 move。hdfs dfs -mv /finance/a.txt /public/ 会失败,因为源文件带 EDEK、目标目录不是同一个加密区,HDFS 不允许密钥”泄漏”到区外。必须先 get 到本地再 put,或用 DistCp 显式 re-encrypt。
三、配额 Quota
配额是 HDFS 多租户治理的根基。一个集群跑十几条业务线,没有配额就是”谁先灌满谁赢”。
3.1 三类配额
| 配额类型 | 限制对象 | 命令 | 典型场景 |
|---|---|---|---|
| Name Quota | 目录下文件+子目录个数 | hdfs dfsadmin -setQuota 100000 /path |
防小文件洪水 |
| Space Quota | 目录下总字节数 | hdfs dfsadmin -setSpaceQuota 500t /path |
防容量挤占 |
| Storage Type Quota(3.x) | 按 STORAGE_TYPE 分别限字节 | hdfs dfsadmin -setQuotaByStorageType -storageType DISK 500t /path |
分层存储管控 |
查询当前配额与使用量:
1 | hdfs dfs -count -q /finance |
3.2 Name Quota:防小文件
Name Quota 限制的是 inode 数量。NN 内存里每个文件/目录占约 150~300 字节元数据 ,小文件过多会撑爆 NN 堆。给每个业务目录设一个 Name Quota 上限,等于在 NN 内存层面划红线:超了写入直接失败,逼着业务去做合并或走 HBase。
3.3 Space Quota:防容量挤占
Space Quota 限制的是逻辑字节数(副本数也算)。给 /finance 设 500t,意味着该目录下数据加起来不能超过 500TB(含三副本就是 500TB 物理占用 ,注意 HDFS 配额计的是逻辑字节 × 副本数后的物理占用,具体口径以版本为准)。
3.4 Storage Type Quota:分层治理
3.x 引入存储类型后,可以按 DISK/SSD/ARCHIVE 分别限额。典型用法:热数据目录限 SSD 配额、冷数据目录限 ARCHIVE 配额,逼业务把冷数据走迁移策略落到归约存储。
1 | # /hot 只能用 50TB SSD |
3.5 多租户实践
我们的做法是按业务线建独立 namespace 子目录 + 独立配额:
1 | / ← 集群根 |
每个业务目录再配独立的 Ranger 策略、独立快照计划、必要时独立加密区。业务线之间天然隔离,A 业务再怎么失控也只撞自己的配额墙,不会拖垮 B。
3.6 踩坑
坑一:配额超限导致写入失败。Spark/Flink 任务写到一半撞 Space Quota,文件处于半截状态,重试又重新创建。务必在任务侧做好配额预检查,监控每个关键目录的 quota 使用率(我们接了 Prometheus,超 85% 告警 )。
坑二:EC 文件的配额计算。开了纠删码(Erasure Coding)的文件,Space Quota 按编码后大小算,不是原始大小。比如 RS-6-3 把 6 块数据编码成 9 块(6 数据 + 3 校验),占用是原始的 1.5 倍而非 3 倍。设配额时要换算清楚,否则你以为能写 100TB,实际只能写约 66TB 。
坑三:快照占用的空间。前面说过,被快照锚住的 block 不释放,这部分空间也算在 Space Quota 里。如果某目录快照保留期长、数据改写频繁,配额会被快照”吃掉”一大块,业务实际可用空间比配额数字小得多。监控时要看 SPACE_USED 的真实值,别只盯配额上限。
四、组合实战:一段命令序列
下面这段是为某业务目录 /biz_finance 同时配上快照、加密区、配额的完整序列,可作为生产上线 checklist:
1 | # ===== 0. 前置:Kerberos 已启用、Ranger 策略已下发 ===== |
注意顺序:先建加密区再写数据,已经存在的明文文件不会被自动加密;配额在写数据前设好,避免业务灌满才发现没限额;快照基线在治理就绪后打,作为后续 diff 的参照。
小结
这三件事看似各自独立,其实串成一条治理主线:
- 快照给数据上了”撤销键”,逻辑误操作秒级回滚,但要记得它和源数据共盘,不防物理故障。
- 透明加密区给数据上了”防读锁”,DN 被拔盘也解不开,但要保证 KMS 的 HA 与密钥的妥善保管。
- 配额给集群上了”容量闸”,多租户各管一摊互不干扰,但要算清 EC 与快照对实际占用的影响。
三者组合起来,HDFS 才从一个”能存的文件系统”变成”安全可控的企业存储底座”。在我运维的某集群上,这套治理体系上线后,误删恢复从”翻备份要半天”变成”一条 snapshotDiff 几分钟定位” ;敏感数据合规审计顺利通过;容量事故从月均两三起到基本归零 。这三件武器,值得每个 HDFS 运维者认真吃透。
—— 王飞宇,写于某次治理复盘之后。
