NiFi 集群长期运维:并发线程、content 仓库、日志与漏洞面
这是「NiFi 生产集群」系列的第 6 篇。前五篇在讲怎么搭、怎么扩、怎么授权、怎么写数据流、怎么排错;这一篇讲几年里真正让集群"稳住"的那些日常动作。
上一篇:NiFi 起不来、登不上、节点被挤掉:一份按日志定位的排错清单 本篇:并发、容量、日志、漏洞面、批量运维
搭起来只是开始。这套环境从 2022 年的 3 台跑到 25 台,两年里真正把人从床上叫起来的,没有一次是"配置写错了",全是攒出来的:线程不够、盘快满了、日志涨疯了、某个洞修不了。
第 5 篇讲出事之后怎么定位,这一篇讲平时怎么让它别出事。两篇的分工很清楚:本篇里凡是"怎么翻日志关键字"的细节,都一句话带过,指回第 5 篇。
1. 先看三个仪表
判断这套集群健不健康,不需要看十几个监控面板。三个数就够:
| 指标 | 正常范围 | 危险信号 | 对应动作 |
|---|---|---|---|
| CPU 使用率(全集群) | 40%--60% | 持续 > 85% | 先查是不是有流程在空转或反复重试,再谈加线程 |
| 队列积压条数(各连接) | 高峰能涨、之后能回落到 0 | 连续 2 小时单向增长 | 上游降并发,同时确认下游是不是变慢了 |
| content repository 使用率 | < 70% | > 70% 开始节流;> 85% 会写不进去 | 清 archive、缩短保留期、加盘 |
三个数各自在说什么:
- CPU 基本等于"线程数 × 每个线程在干的活"。低于 60% 而队列在涨,说明线程不够或者下游慢;高于 85% 说明线程已经够了,活太多,这时候再加线程只会更糟。
- 队列是水位计。绝对值没意义,要看"涨得起来、也落得下去"。一个连接常年积压 5 万条但每天能清空一次,是健康的;积压 2000 条但两周没动过,是有病的。
- content repository 存的是 FlowFile 正文,写进去之后占多少盘不完全由 NiFi 说了算,得靠 archive 清理。它比 flowfile repository 危险得多------后者只存元数据,几个 GB 就到顶了。
这三个数合在一起,暴露的是同一条曲线:
erlang
CPU 长期贴着 85%
某天开始队列不再消化
队列连续几天只涨不落
节点心跳超时, 被集群踢掉
content repo 到 70%
写文件不完整, 流程卡在 list
NiFi 的多数"故障"其实是容量问题积累出来的,只是会在某一天以"队列卡死、节点掉线、写文件不完整"的形式爆发。爆的时候看着像突发故障,翻回去看监控,前两周就有征兆了。
所以三个数不是"交接班时看一眼",是"每周记一次"。有曲线,才能提前两周算出"这块盘照这个速度还能撑二十天";没曲线,只能等它满。值班交接记录里写"CPU 52%、队列峰值已回落、content 68%",比写"一切正常"有用得多。
三个数分别在三个地方看:CPU 和磁盘在主机监控里,队列在画布上(每个连接中间那个数字),content repo 使用率要自己算------df 出来的已用比例不等于 repository 的比例,同一块盘上还住着别的目录。我们是用 df 的数据当上限,再用 repository 目录的 du 当实际值,两个都记。
2. 线程模型:Maximum Timer Driven Thread Count
NiFi 的处理器有两类调度,很多人调优调不对,是因为没分清这两类。
- timer driven :按
Run Schedule轮询。绝大多数处理器属于这一类,0 sec表示上一轮跑完立刻下一轮,30 sec表示每 30 秒看一眼有没有活。它们抢的是同一个全局线程池。 - event driven :被上游触发。FlowFile 进队列就唤醒一个线程,队列空着就不占线程。
UpdateAttribute、RouteOnAttribute这种"来一条处理一条"的适合它。
改成 event driven 前要确认一件事:这一类处理器的 Run Schedule 不再生效。原来靠"每 30 秒跑一次"来兜底的逻辑(比如顺便清理一下状态、检查一下外部系统),改完之后如果上游一直不来数据,它就一直不跑。我们有个流程就是这么"静默"了半天,最后发现是调度方式改了、心跳组件再也不执行了。
两个全局池都在 Controller Settings 里,参数叫 Maximum Timer Driven Thread Count 和 Maximum Event Driven Thread Count,默认都是 10。也就是说,一个刚装好的 NiFi,全集群最多同时跑 10 个定时任务------节点再多也没用,瓶颈在单节点的池子上。
| 参数 | 含义 | 我们的值 | 什么时候该动 |
|---|---|---|---|
| Maximum Timer Driven Thread Count | 全局定时调度线程池,所有 timer driven 处理器共享 | 28 | CPU < 60% 且队列有积压,同时下游 IO 不忙 |
| Maximum Event Driven Thread Count | 事件驱动线程池,只服务被上游触发的处理器 | 10(默认) | 大量处理器改成 event driven 之后 |
| Concurrent Tasks(组件级) | 单个处理器同时跑几个 task,从全局池里借线程 | 按处理器 1--4 | 单个处理器是瓶颈,且确认是 IO 型 |
| Run Schedule(组件级) | 多久调度一次 | List* 类 30 sec,其余 0 sec |
处理器在空转、CPU 白烧 |
25 台 × 28,理论上能同时跑 700 个 task。听起来很多,但一个 FTP 接入流程里 ListFTP → FetchFTP → RouteOnAttribute → PutHDFS 四步,高峰时每一步都在排队,700 个 task 摊到几十条流程上,一点都不富余。
2.1 一次真实的调参:24 → 28
现象:下午接入高峰,队列积压涨到几万条,CPU 只有 50% 出头,内存和磁盘都不忙------资源明明有富余,数据就是不动。
日志 :没有 ERROR。只有 INFO 级别的调度记录,处理器按 Run Schedule 准点被调度,然后很快又空手回来。
根因 :timer driven 线程池被打满。所有处理器共享那 24 个线程,PutHDFS 在等 HDFS 应答的时候还占着线程不放,ListFTP、FetchFTP 就排不上队。表面看是"下游慢",实际是"上游压根没被调度"。
处置 :24 调到 28,滚动重启(一次一台,等 ACTIVE 再下一台,别 25 台一起重启,理由和第 2 篇「停机窗口与分批启动」一样)。CPU 从 50% 左右升到 75% 左右,队列消化速度肉眼可见地变快,高峰期积压从"几小时才回落"变成"一小时内回落"。
为什么只加 4 个?三条纪律:
- 一次不超过 25% 。24 → 28 是 +16.7%。一次翻倍会把所有潜在问题同时放大,出了问题也分不清是谁的。
- 调完观察 24 小时。要覆盖日间高峰和夜间批量两个窗口,只看一小时的高峰不算数。
- 先确认瓶颈在调度,不在 IO 。
iostat -x 1看 content repo 那几块盘的%util,再看队列堆在哪个处理器前面。如果队列全堆在PutHDFS之前,加线程只是让更多线程一起等 HDFS。
2.2 反例:组件级并发调太高,把 IO 打满
现象 :PutHDFS 的 Concurrent Tasks 从 4 调到 12,吞吐短暂上去,随后整条流程变慢,连带同节点的其它流程一起卡。
日志 :HDFS 客户端报 slow waitForAckedSeqno,DataStreamer 反复重连,看起来像集群要挂了。
根因:单位时间并发写请求超过了 DataNode 管道的承受能力,客户端互相抢带宽。这是 IO 被打满,不是 NiFi 挂了。
处置 :并发退回 4,改用批次大小和 MergeContent 合并小文件来提吞吐。组件级并发的取舍在第 4 篇「性能调优的几个实际动作」里讲过,这里只提醒一句:全局池放大的是整条流程的并发度,组件级并发放大的是单个处理器的压力,两者不能一起加。
线程数不是越多越快,要配到下游能吃进去的量。
3. content repository:最先撑爆的一定是它
25 台里,第一个满的永远不是 /opt,是 content repo 所在的盘。
3.1 盘怎么分
每个节点三块数据盘,各挂一个 content repository 目录,NiFi 自动按容量轮询写入:
bash
# nifi.properties
nifi.content.repository.directory.default=/data01/nifi/content_repository
nifi.content.repository.directory.content2=/data02/nifi/content_repository
nifi.content.repository.directory.content3=/data03/nifi/content_repository
# 元数据和溯源不跟正文抢盘
nifi.flowfile.repository.directory=/data04/nifi/flowfile_repository
nifi.provenance.repository.directory.default=/data05/nifi/provenance_repository
python
/data01/nifi/content_repository/
├── archive/ # 归档,按容器/section 分层,可被清理
│ └── <container-id>/<section-id>/<content-file>
└── <container-id>/<section-id>/<content-file> # 当前写入的正文
三个关键参数:
ini
nifi.content.repository.archive.enabled=true
nifi.content.repository.archive.max.usage.percentage=70%
nifi.content.repository.archive.max.retention.period=12 hours
max.usage.percentage=70% 的意思是:archive 占到这块盘的 70%,就开始删最老的 archive 文件;max.retention.period 是"不管占多少,超过这个时间的 archive 都可以删"。两个条件是"或"的关系,谁先到按谁删。
这个保留期我们调了三轮:7 days → 3 days → 12 hours 。原因很朴素------出处(provenance)里点开看内容,实际只有当天在查;保留 7 天只是让几百 GB 的盘长期有 70% 被死数据占着。改成 12 hours 之后,archive 常年在 20% 以下,Unable to write ... due to archive file size constraints 这类告警基本消失。
还有一个容易走偏的方向:把 archive.enabled 直接关掉。关掉之后确实不占盘了,但出处里点开 FlowFile 详情看不到内容,只剩元数据。查"这条数据到底传了什么"的时候,就只能去下游 HDFS 翻文件。我们试过两周,最后还是打开、把保留期压到 12 hours------关 archive 是把排查能力换成了磁盘空间,不划算。
3.2 一次真实的 archive 告警
现象:夜间批量开始后,多个流程同时变慢,UI 打开卡顿,队列长度不长但处理速度掉到平时的三分之一。不是某一台,是好几台一起。
日志:
css
WARN [Timer-Driven Process Thread-7] o.a.n.c.repository.FileSystemRepository
Unable to write flowfile content to content repository Container content2
due to archive file size constraints; waiting for archive cleanup.
Total number of file currently archived = 10707
根因 :这不是"写失败",是主动节流 。每次写 content 之前,NiFi 会检查 archive 用量;超过阈值就等 cleanup 线程删掉老文件再写。删的速度跟不上写的速度,等待就会传染到所有写 content 的线程------表现出来是整个集群变慢,而不是某一台报错。Total number of file currently archived = 10707 这一行是证据:archive 里堆了一万多个文件,清理线程已经在拼命删了。
处置 :真的去清。这类告警不能靠"等它自己好",因为它只会越等越久。三个动作同时做:清 archive、把保留期缩短、给监控加 70%/85% 两级阈值。清完回头看 df,确认真的降了(判断标准见第 5 篇「磁盘与仓库」一节)。
3.3 清理:先看再删
三块盘各一条,先 -ls 看,确认删的是什么再删:
bash
# 第一步:只看,不删。确认文件时间、大小、路径都对
find /data01/nifi/content_repository/*/*/* -type f -mtime +3 -ls | head
# 第二步:确认无误后后台删,三块盘各跑一条
nohup find /data01/nifi/content_repository/*/*/* -type f -mtime +3 | xargs rm -rf &
nohup find /data02/nifi/content_repository/*/*/* -type f -mtime +3 | xargs rm -rf &
nohup find /data03/nifi/content_repository/*/*/* -type f -mtime +3 | xargs rm -rf &
两条经验:
- 别在业务高峰删 。删文件本身不慢,慢的是同一块盘上同时有几千个写请求,
rm和写抢 IO,会把本来就在节流的流程彻底按住。 - 删完必须验证 。
df -h看释放了多少,ls /data0*/nifi/content_repository/archive | wc -l看 archive 是否真的降下来。只看命令返回 0 不算数。
3.4 权限事故:root 起过一次服务
这是本篇最值得记住的一段,因为它教的东西比任何参数都贵。
现象 :某天开始,一条一直很稳的接入流程每到 list 阶段就卡住,队列里干干净净,一个文件都没有。重启流程无效,重启节点无效。停掉其中一台节点(nifi-node-14)之后,流程立刻恢复正常。
日志 :没有 ERROR。没有异常堆栈。只有处理器在 list 阶段超时、重试、再超时。
根因 :这台节点的 content repository 目录结构和其他 24 台不一样。正常的结构是"所有正文都在 archive/ 子目录下",而这台上有一批文件直接躺在 content_repository/ 下面,和 archive 目录同级。顺着往下查,archive 目录的属主是 root------某个夜里有人用 root 起过一次 NiFi。
属主错位的链条是这样的:NiFi 以 nifi 用户运行,写不进 root 属主的 archive 目录 → cleanup 线程删不掉老文件 → archive 用量一直贴着阈值 → 每次写 content 都进入"等 cleanup"的节流状态 → 这台节点上的所有流程都变慢,而 list 这类需要读取队列内容并写回状态的处理器最先卡死。集群里其他节点是好的,所以整条流程只在轮到这台节点处理时才卡------这也解释了为什么"停掉这一台就恢复"。
处置:
bash
# 1) 停止该节点,别让 cleanup 线程一边写一边被改属主
/opt/nifi/bin/nifi.sh stop
# 2) 三个盘的目录属主全部掰回 nifi
chown -R nifi:nifi /data01/nifi /data02/nifi /data03/nifi
# 3) 删掉那些不在 archive 目录里的"野生"文件:它们既不被 archive 统计,也永远不会被清理
find /data01/nifi/content_repository/ -maxdepth 2 -mindepth 2 -type f \
-not -path "*/archive/*" -delete
# 4) 启动,观察 archive 用量是否回落、list 阶段是否还卡
/opt/nifi/bin/nifi.sh start
第 3 步那条 find 建议先去掉 -delete 跑一遍看输出,确认删的都是"直接躺在容器目录下的孤立文件",再执行删除。这些文件是清理失败期间留下的孤儿,没有任何队列引用它们。
NiFi 的所有目录必须 nifi:nifi,一旦有人用 root 起过服务,问题会在几周后以"某台节点拖垮整条流程"的形式出现。
为什么是"几周后"?因为 root 起服务只影响那一次运行期间新写的文件。存量文件还是 nifi 的,服务照常跑,一切看起来都对;等到 archive 涨到阈值、cleanup 线程第一次真去删 root 的文件时,才失败。中间那几周,是完全静默的。
3.5 两件收尾的事
- content repository 单独挂盘 ,不要和
/opt混。/opt满了,NiFi 起不来,日志能看见;content 盘满了,NiFi 还在跑,只是所有流程慢慢卡死,最难查。 - 监控做两级:70% 预警(该安排清了),85% 严重(今晚就得清)。只有一级阈值的话,等你收到告警时已经在节流了。
4. 日志治理:一次认证失败能把 /opt 写满
4.1 事件经过
现象:某天上午,三台节点的业务同时登录不了,平台侧先收到的是"磁盘使用率 100%"的阻断告警,然后才是业务系统的报障电话。
日志:NiFi 侧刷的是
css
WARN [Kerberos Authentication] o.a.n.k.KerberosProvider
Cannot locate KDC
根因 :这三台的 /etc/krb5.conf 被一次配置基线覆盖了,指向的 KDC 地址不对。Kerberos 认证失败后,客户端不是"报一次错就停",而是无限重试------每次都写一条日志,每次都往上叠。nifi-app.log 在几十分钟里涨到把 /opt 挂载点写到 100%,磁盘一满,NiFi 的 repository 写不进去,整个服务进入"暂停一切"的状态,业务登录自然也就断了。(Cannot locate KDC 怎么定位、怎么验 keytab,见第 5 篇「Kerberos 认证失败」一节。)
处置 :顺序很重要------先恢复配置止血,再清日志。反过来做,清完十分钟又满。
bash
# 1) 用基线文件覆盖回去(25 台一起推,见第 6 节)
ansible -b -m copy -a 'src=/data/pkg/krb5.conf dest=/etc/krb5.conf owner=root group=root mode=0644 backup=yes' nifi
# 2) 确认 KDC 通、票据能拿到
kinit -kt /home/nifi/nifi.keytab nifi/nifi-node-01.example.com@EXAMPLE.COM && klist
# 3) 清历史日志(只保留当天的)
find /opt/nifi/logs -name 'nifi-app_*.log.gz' -mtime +1 -delete
# 4) 重启,验证日志速率是否回到正常
4.2 三件事,缺一件就会重演
第一件:日志滚动要设上限。
xml
<!-- conf/logback.xml -->
<appender name="APP_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${org.apache.nifi.bootstrap.config.log.dir}/nifi-app.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${org.apache.nifi.bootstrap.config.log.dir}/nifi-app_%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>200MB</maxFileSize>
<maxHistory>7</maxHistory>
<totalSizeCap>10GB</totalSizeCap>
</rollingPolicy>
</appender>
三个参数的分工要记清:maxFileSize 管单个文件多大就切;maxHistory 管留几份,单位跟着 fileNamePattern 走 ------写成 %d{yyyy-MM-dd} 就是 7 天,写成 %d{yyyy-MM-dd_HH} 就是 7 小时,别想当然;totalSizeCap 是最后一道闸门,总量超了从最老的开始删,优先级高于 maxHistory。三个都设,才算真的有上限。
第二件:日志目录单独挂盘。
/opt/nifi/logs 直接挂一块独立盘(比如 /data06/nifi/logs)。做法很土但很有效:logs 是个挂载点,NiFi 照旧往里写,但写满的爆炸半径从"整个 /opt 挂载点"缩到"日志盘"。这一步做完,上面那个事故最多就是丢两天日志,不会拖死服务。
第三件:把"日志速率"本身当指标监控。
正常的 nifi-app.log 是几百行/分钟,故障时是几万行/分钟------差两个数量级,非常好识别。采集方式很土:每分钟 wc -l 取差值,超过阈值就告警。阈值不要拍脑袋,拿一周的正常值取三倍即可;我们一开始定得太低,每天下午高峰都误报。
有了这条,认证失败十分钟内就能被发现,而不是等磁盘满了被平台侧通知。
| 日志文件 | 看什么 | 保留多久 |
|---|---|---|
nifi-app.log |
主日志:启动、处理器报错、集群通信、仓库告警 | 7 天,单文件 200MB,压缩 |
nifi-user.log |
登录与授权:谁在什么时候被拒、被拒在哪个资源 | 30 天,单独归档备查 |
nifi-bootstrap.log |
JVM 启动参数、进程退出码、内存不足 | 7 天 |
nifi-app_*.log.gz |
历史滚动文件,事故复盘时才翻 | 7 天,totalSizeCap 兜底 |
| Bulletin(UI) | 近 5 分钟的组件级告警,不落盘 | 靠 UI 和 API,不依赖文件 |
磁盘满不是"存储问题",是"日志速率问题",先限速再清理。
5. 漏洞面治理:不是所有洞都能修
安全部门每季度扫一次主机,一次扫出六类问题,报告发过来是一张平铺的清单。第一次拿到这份清单时,我按"逐条整改"的方式往下派,结果是:改了两个 NiFi 参数、关了一个端口,业务断了半小时,而清单还剩四条,且没有一条是 NiFi 的。
后来换了个顺序:先判归属,再谈修不修。
5.1 六类问题,按归属分
| 漏洞类别 | 端口 | 归属 | 能不能修 | 处置 |
|---|---|---|---|---|
| 某中间件组件的 TLS 漏洞 | 10443(S2S 端口) | NiFi 节点 | 当前版本未修复 | 收敛网络策略,只对可信网段开放 |
| etcd client/server | 2379 / 2380 | 不属于大数据集群(某节点上是 K8s 组件) | 转出 | 交平台侧处置并留证据 |
| DNS 相关 | 53 | 平台 / PaaS 侧 | 转出 | 网络侧处理 |
| 某数据服务同步进程触发的漏洞 | 5151 | 由另一个平台的同步进程引起 | 转出 | 关端口或改绑定地址,从清单剔除 |
| 扫描器自带的 profile 类问题 | 无 | 不在本平台节点 | 转出 | 说明归属,从清单剔除 |
| 历史版本组件(非 NiFi) | 若干 | 宿主机基础镜像 | 随镜像升级 | 排入系统补丁窗口 |
判归属只需要几条命令:
perl
# 端口是谁在听
ss -lntp | grep -E '2379|2380|5151|53'
# 顺着 PID 看进程是谁的、从哪来的
ps -ef | grep <PID>
rpm -qf $(which <进程名>) 2>/dev/null
三条结果基本就能定性:进程命令行里有 k8s 字样 → 平台侧;rpm 包能查到但不在 NiFi 依赖里 → 基础镜像;端口是另一个平台的 agent 起的 → 转出。
每条漏洞的处置流程固定成五步,不跳步:
- 拿到扫描报告,先按端口聚类,同一台机器上的多个洞合并成一条处置单;
ss+ps定位进程,记下 PID、命令行、所属包;- 判归属:NiFi 依赖 / 基础镜像 / 其它平台;
- 给出结论:修(排窗口)、补偿(写清控制措施)、转出(附五项证据);
- 回写台账:归属、责任人、结论、日期四列缺一不可。
5.2 三条原则
- 能修的修。 真正属于 NiFi 依赖、且有官方修复版本的,排窗口改。
- 不能修的要写清"为什么不修、用什么补偿控制"。 10443 是 S2S 端口,跨实例传数据必须对内网开放,关了就断业务;而对端实例的证书、信任库是配套签的,换端口等于全集群重来一次。所以这一条的结论不是"修",是"收敛":网络策略只放通可信网段,其余全部拒绝,并把这个决定写进台账。
- 不归你的转出去并留证据。 证据不是一句话,是"端口、PID、进程命令行、所属包、所属平台"这五项。给出去之后从自己的清单里标记"已转出,责任方 X",不然每季度都要重新查一遍同一个端口。
能修的修;不能修的要写清"为什么不修、用什么补偿控制";不归你的转出去并留证据。
5.3 台账要有的两列
漏洞台账里最容易被省掉、又最致命的两列是归属 和责任人。没有这两列,清单永远是"减一条、涨两条",因为没人能回答"这条到底该谁改"。有了这两列,季度复盘只看两个数:本季新增几条、转出几条被接收。
再补一句经验:S2S 端口(10443)是最容易被扫出来的那个,因为它必须对内网开放,而它承载的 TLS 实现又跟着 NiFi 版本走。能用网络策略收敛就别指望升级------升级一次 25 台的重签、重启、回归,成本比一条安全组规则高两个数量级。
6. 批量运维:Ansible 替代 for + ssh
25 台规模下,for 循环串行 ssh 是一件很折磨人的事:每台 3 到 8 秒,一轮下来十几分钟;中途某台网络抖一下,脚本继续往下跑,你以为都成功了;再跑一遍,前面已经改过的又要再改一次,不幂等。
换成 Ansible 之后,同一件事变成十几秒,而且有统一的输出可以核对。
6.1 inventory
ini
# inventory.ini
[nifi]
nifi-node-0[1:9].example.com
nifi-node-1[0:9].example.com
nifi-node-2[0:5].example.com
[nifi_zk]
nifi-node-0[1:3].example.com
[nifi_small]
node-s0[1:5].example.com
[nifi:vars]
ansible_user=etl_user_01
ansible_become=true
分三组的意义:nifi 是全部 25 台主集群,nifi_zk 只包含跑内置 ZooKeeper 的那 3 台,nifi_small 是那套 5 节点小集群。小集群永远单独成组 ,任何 nifi 组的批量操作都不会误伤它------这条和第 2 篇「那套 5 节点小集群:别和主集群混」是一个道理。
6.2 ansible.cfg
ini
# ansible.cfg
[defaults]
inventory = ./inventory.ini
forks = 25
host_key_checking = False
timeout = 20
[ssh_connection]
pipelining = True
forks 默认是 5,25 台会被分成 5 批跑,等于还是串行四轮。改成 25,一轮推完。
6.3 四条常用命令
| 用途 | 命令 |
|---|---|
| 查状态(不落地) | ansible -m shell -a 'ls -al /opt/nifi/conf/krb5.conf' nifi -o |
| 分发配置(带备份) | ansible -b -m copy -a 'src=/data/pkg/krb5.conf dest=/etc/krb5.conf owner=root group=root mode=0644 backup=yes' nifi |
| 建目录并定属主 | ansible -b -m file -a 'path=/opt/nifi/cluster/emr state=directory owner=nifi group=nifi mode=0755' nifi |
| 一致性校验 | `ansible -m shell -a 'md5sum /opt/nifi/conf/authorizers.xml' nifi |
最后一条的输出是"计数 + md5",期望只有一行、计数 25。如果出现两行,说明有节点被单独改过------这是 25 台规模下唯一靠得住的检查方式。命令里的 awk 会把主机名之类的噪声也打出来,嫌乱可以加一层过滤:
bash
ansible -m shell -a 'md5sum /opt/nifi/conf/authorizers.xml' nifi -o \
| grep -oE '^[0-9a-f]{32}' | sort | uniq -c
需要连做几步的时候,写成 playbook 比堆 ad-hoc 稳:
yaml
- hosts: nifi
become: yes
tasks:
- name: 分发 krb5.conf(必须备份)
copy:
src: /data/pkg/krb5.conf
dest: /etc/krb5.conf
owner: root
group: root
mode: '0644'
backup: yes
- name: 分发后立刻校验
shell: md5sum /etc/krb5.conf | cut -d' ' -f1
changed_when: false
6.4 四条纪律
- 分发必带
backup=yes。 出事时krb5.conf.<时间戳>~这类备份文件就是回滚点。省这个参数,等于把回滚能力交给运气。 - 分发后必跑一致性校验。 md5、行数、指纹,任选一种,但必须有。25 台靠人眼看是不可能看出差异的。
- 属主统一
nifi:nifi。 目录、jar、配置、keytab,一个都不能漏。 -o让确认一次性走完 ,但生产变更还是要人盯着终端。-o省的是敲 25 次 yes 的时间,不是省掉观察。
6.5 为什么属主会错
Ansible 默认以 root 执行,copy 模块不写 owner 就是 root:root。用 root 拷过去的 jar、配置、目录,NiFi 以 nifi 身份读不到,最后以两种形式出现:节点起来但连不上集群,或者某个组件报类加载失败/权限不足。查起来极绕------日志只说"连不上""读不到",不会告诉你"因为属主是 root"(详见第 5 篇「节点一直 CONNECTING / 被别的节点挤下线」一节)。
规矩很简单:凡是 copy、file、template,owner 和 group 必须显式写。 不写就是 root,而 root 造成的问题,要等到几周后 archive 清理失败或者某次重启才爆出来------和第 3.4 节那次事故是同一个病根。
7. 日常巡检清单
巡检不是"每天登上去看一眼",而是"每天花五分钟,确认这几个数没变坏"。三张表,按频率分。
7.1 每日
| 检查项 | 命令或位置 | 通过标准 | 异常动作 |
|---|---|---|---|
| 节点是否全 ACTIVE | Cluster 页面 / nifi-api/controller/cluster |
25/25 ACTIVE |
看 nifi-app.log 有无 Disconnected,按第 5 篇「节点一直 CONNECTING / 被别的节点挤下线」一节 |
| 队列水位 | 画布上各连接中间的数字 | 无持续单向增长,无 full |
查下游处理器是否 Stopped、是否在等票据 |
| content repo 使用率 | df -h /data01 /data02 /data03 |
< 70% | 清 archive、缩短保留期 |
| ERROR 关键字 | `grep -E 'UninheritableFlow | SSH_MSG_UNIMPLEMENTED | ConnectionLoss |
| HDFS 落盘文件数 | 本地 HDFS 目录与云上 EMR 集群目录,各对一次 | 数量与上游队列一致(允许分钟级延迟) | 查 PutHDFS 报错、查票据过期 |
UninheritableFlow 这条特别值得单列:它出现时通常意味着某个进程组在集群间同步失败,UI 上看不出来,但那条流程的改动会"只在 primary 上生效",最容易在变更后第二天发现数据不对。
7.2 每周
| 检查项 | 命令或位置 | 通过标准 | 异常动作 |
|---|---|---|---|
| 证书到期时间 | `keytool -list -v -keystore /opt/nifi/conf/keystore.jks -storepass '<KEYSTORE_PASS>' | grep 'Valid from'` | 剩余 > 90 天 |
| ZooKeeper 状态 | `echo stat | nc <zk_ip> 22181` | 3 台,1 leader 2 follower |
| 日志增速 | du -sh /opt/nifi/logs,对比上周 |
平稳,无 10 倍级突增 | 查是否有认证失败/重试风暴 |
| Controller Services | UI → Controller Settings | 无长期 DISABLED 的服务 |
查依赖服务是否 ENABLED、是否缺授权 |
日志增速这一项,最好直接看曲线而不是看总量。总量大可能是正常的积累,增速突变才是信号。
7.3 每月
| 检查项 | 命令或位置 | 通过标准 | 异常动作 |
|---|---|---|---|
| 全集群 md5 一致性 | authorizers.xml、nifi.properties 的 md5 汇总 |
每种文件只有一种值,计数 25 | 找出异类节点,重推并记录原因 |
| 磁盘容量趋势 | 监控曲线 + df -h |
剩余可用 > 3 个月 | 扩盘或缩短 archive 保留期 |
| 漏洞台账 | 台账文件 | 每条有归属、责任人、状态 | 催办、转出、升级排期 |
| 证书剩余天数 | 同每周检查 | < 90 天预警 | 提前一个月排重签窗口 |
三张表不是三份文档,是一个文件:每天填日检那五行,每周五行加一次汇总,每月再补一次趋势。填完的表格按月存一个目录,出事时往回翻三周,比翻聊天记录快得多。
7.4 巡检脚本骨架
把上面的日检压成一个脚本,跑完输出一张表,贴进交接记录:
bash
#!/bin/bash
# 每日巡检:ansible 并发跑 25 台,输出四列表
row() { printf '%-24s %-16s %-6s\n' "$1" "$2" "$3"; }
row 检查项 结果 判定
bad=$(ansible -m shell -a 'systemctl is-active nifi' nifi -o 2>/dev/null | grep -vc active)
row "NiFi 进程" "$((25-bad))/25" "$([ "$bad" -eq 0 ] && echo OK || echo FAIL)"
repo=$(ansible -m shell -a "df /data01 | awk 'NR==2{print $5}'" nifi -o \
| grep -oE '[0-9]+%' | tr -d % | sort -n | tail -1)
row "content 最高占用" "${repo}%" "$([ "$repo" -lt 70 ] && echo OK || echo WARN)"
err=$(ansible -m shell -a \
"grep -cE 'UninheritableFlow|ConnectionLoss|No space left' /opt/nifi/logs/nifi-app.log" \
nifi -o | grep -oE '[0-9]+$' | paste -sd+ | bc)
row "ERROR 关键字" "$err" "$([ "$err" -eq 0 ] && echo OK || echo FAIL)"
zk=$(ansible -m shell -a 'echo stat | nc <zk_ip> 22181 | grep -c Mode' nifi_zk -o | grep -c 1)
row "ZK 可应答" "$zk/3" "$([ "$zk" -eq 3 ] && echo OK || echo FAIL)"
二十行以内,四个数,一张表。它替代不了看画布,但能保证"每天真的有人看过"。
8. 五条变更纪律
两年的变更事故,归到最后就是五条。
- 永远不用 root 启动 NiFi。 目录属主会一直错下去,而且错得静默------等到几周后 archive 清理失败、某台节点拖垮整条流程时,没人会记得是哪个夜里用 root 起过一次。真要用 root 排障,排完
chown -R nifi:nifi收尾,并写进变更记录。 - 任何配置改动先 md5 比对、改完再比对。 25 台只能有一种值。改之前存一份 md5 清单,改完重算一遍,
uniq -c只允许出现一行。比对不出差异的改动,等于没改。 - 证书和
nifi.sensitive.props.key进密码管理,丢了等于重装。 前者丢了大不了重签,后者丢了所有敏感属性都解不开,只能一条条重新录入(第 5 篇「Unable to decrypt」一节)。 - 扩容、换证书是停机变更,先停流程。 别指望滚动着换(详见第 2 篇「扩容前,先把流程全停」一节)。流程不停,换证书期间队列还在写,恢复后要么数据重复,要么节点起来就被队列压死。
- 变更后跑一遍一致性脚本 + 看一眼队列,再宣布完成。 "服务起来了"和"数据在动"是两件事。前者看
systemctl,后者看队列有没有回落、HDFS 有没有新文件。
9. 小结
把两年运维浓缩成三句:
- 容量靠三个仪表:CPU、队列、content repo。它们不是三个独立指标,是同一个"攒"的过程的三个切面,看曲线比看瞬时值有用。
- 稳定靠属主和一致性 :所有目录
nifi:nifi,所有配置 25 台一种值。这两件事听起来最无聊,出的问题最贵。 - 安全靠"能修的修,不能修的写清楚" :漏洞修不完是常态,但每一条都要有归属、有责任人、有结论。写清楚"为什么不修",比硬修一个不该修的更负责。
这套环境现在还在跑,25 台主集群加 5 台小集群,每周记一次数,每月对一次 md5,每季度跟安全部门对一次台账。没有哪一步是"高级技巧",全是把简单的事重复做对。
下一篇?没有下一篇了------这套环境还在跑。
本文基于 NiFi 1.17.0 生产集群(3 → 7 → 13 → 25 三轮扩容 + 5 节点小集群)多年运维记录整理,主机名、IP、域名、主体、口令均已脱敏,命令可直接套用。