NiFi 集群长期运维:并发线程、content 仓库、日志与漏洞面

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 个?三条纪律:

  1. 一次不超过 25% 。24 → 28 是 +16.7%。一次翻倍会把所有潜在问题同时放大,出了问题也分不清是谁的。
  2. 调完观察 24 小时。要覆盖日间高峰和夜间批量两个窗口,只看一小时的高峰不算数。
  3. 先确认瓶颈在调度,不在 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 起的 → 转出。

每条漏洞的处置流程固定成五步,不跳步:

  1. 拿到扫描报告,先按端口聚类,同一台机器上的多个洞合并成一条处置单;
  2. ss + ps 定位进程,记下 PID、命令行、所属包;
  3. 判归属:NiFi 依赖 / 基础镜像 / 其它平台;
  4. 给出结论:修(排窗口)、补偿(写清控制措施)、转出(附五项证据);
  5. 回写台账:归属、责任人、结论、日期四列缺一不可。

5.2 三条原则

  1. 能修的修。 真正属于 NiFi 依赖、且有官方修复版本的,排窗口改。
  2. 不能修的要写清"为什么不修、用什么补偿控制"。 10443 是 S2S 端口,跨实例传数据必须对内网开放,关了就断业务;而对端实例的证书、信任库是配套签的,换端口等于全集群重来一次。所以这一条的结论不是"修",是"收敛":网络策略只放通可信网段,其余全部拒绝,并把这个决定写进台账。
  3. 不归你的转出去并留证据。 证据不是一句话,是"端口、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 四条纪律

  1. 分发必带 backup=yes。 出事时 krb5.conf.<时间戳>~ 这类备份文件就是回滚点。省这个参数,等于把回滚能力交给运气。
  2. 分发后必跑一致性校验。 md5、行数、指纹,任选一种,但必须有。25 台靠人眼看是不可能看出差异的。
  3. 属主统一 nifi:nifi。 目录、jar、配置、keytab,一个都不能漏。
  4. -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. 五条变更纪律

两年的变更事故,归到最后就是五条。

  1. 永远不用 root 启动 NiFi。 目录属主会一直错下去,而且错得静默------等到几周后 archive 清理失败、某台节点拖垮整条流程时,没人会记得是哪个夜里用 root 起过一次。真要用 root 排障,排完 chown -R nifi:nifi 收尾,并写进变更记录。
  2. 任何配置改动先 md5 比对、改完再比对。 25 台只能有一种值。改之前存一份 md5 清单,改完重算一遍,uniq -c 只允许出现一行。比对不出差异的改动,等于没改。
  3. 证书和 nifi.sensitive.props.key 进密码管理,丢了等于重装。 前者丢了大不了重签,后者丢了所有敏感属性都解不开,只能一条条重新录入(第 5 篇「Unable to decrypt」一节)。
  4. 扩容、换证书是停机变更,先停流程。 别指望滚动着换(详见第 2 篇「扩容前,先把流程全停」一节)。流程不停,换证书期间队列还在写,恢复后要么数据重复,要么节点起来就被队列压死。
  5. 变更后跑一遍一致性脚本 + 看一眼队列,再宣布完成。 "服务起来了"和"数据在动"是两件事。前者看 systemctl,后者看队列有没有回落、HDFS 有没有新文件。

9. 小结

把两年运维浓缩成三句:

  1. 容量靠三个仪表:CPU、队列、content repo。它们不是三个独立指标,是同一个"攒"的过程的三个切面,看曲线比看瞬时值有用。
  2. 稳定靠属主和一致性 :所有目录 nifi:nifi,所有配置 25 台一种值。这两件事听起来最无聊,出的问题最贵。
  3. 安全靠"能修的修,不能修的写清楚" :漏洞修不完是常态,但每一条都要有归属、有责任人、有结论。写清楚"为什么不修",比硬修一个不该修的更负责。

这套环境现在还在跑,25 台主集群加 5 台小集群,每周记一次数,每月对一次 md5,每季度跟安全部门对一次台账。没有哪一步是"高级技巧",全是把简单的事重复做对。

下一篇?没有下一篇了------这套环境还在跑。


本文基于 NiFi 1.17.0 生产集群(3 → 7 → 13 → 25 三轮扩容 + 5 节点小集群)多年运维记录整理,主机名、IP、域名、主体、口令均已脱敏,命令可直接套用。

相关推荐
freejackman5 小时前
【工程化】从“单兵作战”到“多引擎架构”:基于门面模式的重构实战
大数据·数据库·hive·sql·测试工具·设计模式·架构
字节跳动数据平台7 小时前
火山引擎知识引擎VeKnow进化之路(上):从“人有数可用”到“AI有智可循”
大数据
Flynn Liang9 小时前
Apache Superset 重要特性详解(三):语义层 / 权限 / 缓存 / 嵌入的实战最佳实践(2026 版)
大数据·clickhouse·数据可视化·架构设计·superset·数据分析平台
小田学Python20 小时前
工具调用:让 Agent 从“说”到“做”
大数据·大模型·agent·ai工具·deepseek
GrowingIO技术社区1 天前
用户行为分析怎么做?从业务目标到改进验证的完整流程
大数据·数据挖掘·数据分析
瑞克-桑切斯1 天前
docker Harbor 单机部署手册
大数据·运维·elasticsearch·docker·容器
演艺大赏1 天前
NiFi 起不来、登不上、节点被挤掉:一份按日志定位的排错清单
大数据
freejackman1 天前
【实战三】撕开脱敏盲区:深度解析 CASE WHEN 与窗口函数血缘
大数据·数据库·sql
wafrdggh2 天前
2026电商快递批量查询怎么用?固乔助手模板设置评测
大数据·数据库·小程序