判断一套大数据环境的成熟度,不是看它现在能不能跑,而是看重启之后会发生什么。

01重启后的第一现场

链路的表现很干脆:Spark 段 FAIL、Doris 装载段 FAIL,两段各有各的死法。

  • NameNode 没起来 → 读写 HDFS 全部失败;
  • BE 没起来 → FE 活着,SQL 能编译,执行阶段报错;
  • Spark Master / Worker 全都需要手工拉起;
  • 四个节点里除了 MySQL,没有任何服务配了自启。

也就是说,之前的“稳定运行”完全依赖于一台机器一直没重启。这类脆弱在单机开发时看不出来,因为没人会去验证冷启动。

02每个服务的自启都有自己的坑

服务不自启的原因处置
Doris BE检测到 swap 就直接退出,而 fstab 每次开机都把 swapfile 挂上启动序列里先 swapoff
Doris FE拒绝在带 http_proxy 的环境下启动,而重启后 profile.d 的代理变量又回来了启动序列里显式 unset
Doris BE(systemd)systemd 默认 LimitNOFILE=1024,被 start_be.sh 自检直接拒启unit 内显式抬高上限
Spark Master / Worker没有任何自启配置纳入启动序列

fstab 那条尤其阴:它不是“配置错了”,而是每次重启都自动恢复成错误状态。幂等性在操作系统层面反噬了你——你以为改过一次就好的东西,它每天都在复原。

03启动序列必须是有序的

把这些散乱动作收成一个服务,按依赖顺序执行:

text
swapoff → 清代理变量 → HDFS(YARN) → Doris FE → Doris BE → Spark Master/Worker

顺序不能随便排:FE 起来才谈得上 BE 注册,Spark Worker 要能找到 Master 地址。以前手工敲命令时靠人记住顺序,现在把它写死成一个 unit。

BE 上 systemd,FE 反而不上。 理由是实测出来的:BE 配 Type=forking + PIDFile + Restart=on-failure,故意 kill -9 之后能在几十秒内自愈;而 start_fe.sh 是异步写 pid 的,pgrep 还会自匹配,systemd 的判活逻辑在这种情况下反而不可靠,差点制造一次真故障。

同一个软件包的两个组件,托管方式不同——这不是洁癖,是被实测教育过:自动化只对“能被准确判活”的进程有利。

04隧道端口残留导致的永久不自愈

反向隧道那次比较难查。现象是服务一直 activating,永远起不来。

链条是:旧隧道异常断开时,远端 sshd 还占着那个转发端口;新隧道带着 ExitOnForwardFailure=yes 一启动就因端口占用立刻退出;而 Restart=always 又无脑把它拉起来再撞一次。于是形成“永远在重试、永远不可能成功”的循环。

修法是给隧道加上 ClientAliveInterval 这类心跳,让连接异常时两端都能及时发现并释放端口。

这里有个通用教训:**Restart=always 只有在“重试有可能成功”时才是好设计。**否则它把一个可诊断的失败,变成一个永不收敛的状态。

05SSH 免密的两个根因

集群内互信失效时,报错看起来完全不像同一个问题:

  1. 部分节点的 /etc/hosts 缺一条主机名映射,主机名被 DNS 解析到一个 RFC2544 测试网段的地址上——它不是“连不上”,而是“连到了别处”;
  2. 对端没有 ~/.ssh/config,直接 Host key verification failed。

第二点里还藏了一个细节:即使设了 StrictHostKeyChecking=no,SSH 仍然会拒绝已变更的密钥。所以节点重装之后,所有自动化会静默中断——no 只免除首次询问,不覆盖冲突。

对纯内网、可重放的测试集群,最后用 UserKnownHostsFile=/dev/null(三台 hadoop 节点的主机密钥本来就相同)配合 LogLevel=ERROR:后者避免把警告污染进生产日志。

06公钥和口令的边界

集群统一口令这件事,我最后的原则是:不写进任何会被提交的文档或脚本,脚本里只从环境变量取。免密登录用密钥,密钥只在需要它的那台机器上。这不是安全表演,而是因为文档一旦泄露,“口令在哪”就变成了“谁都能看到”。

07相关文章