NiFi on K8s 節點之間連不上了怎麼辦

問題

公司政策變更禁止 wildcard 以及 cluster.local 申請憑證,而且規定 cluster domain 必須是 sitdownplz.com (化名),這影響了現有的 NiFi 叢集。

NiFi 叢集在同步 flow / configuration change 時,node 之間需要透過 cluster communication 彼此溝通;在 secured cluster 中,這條通訊會使用雙向 TLS,因此每個 NiFi node 都會作為 client 和其他 node 溝通,這時候就會需要用到 keystore/truststore 憑證了,原本是透過 wildcard 搭配 cluster.local 申請一張憑證給所有 NiFi node 使用,但現在這條路不能走了。

如果對 NiFi 裡 keystore / truststore 或 mTLS 不熟,可以先參考另一篇筆記:從一個 NiFi mTLS 問題搞懂 Keystore 與 Truststore

最終解法

將 NiFi 部署在 K8s 中,會用 StatefulSet 來部署,每個 NiFi node 在 K8s 中的實作就是一個 Pod,下面會用 NiFi node 或簡單用 pod 來稱呼。

方案由三部分組成:

1. 憑證(mTLS 身分驗證)
因為不想每新增一個 NiFi node (pod) 就要重新申請憑證,所以申請一張憑證用 SAN 涵蓋所有 node FQDN。

2. hostAlias + per-pod Service
為每個 pod 建一個 ClusterIP Service(selectorstatefulset.kubernetes.io/pod-name 鎖定單一 pod),透過 hostAlias 把每個 FQDN 對應到 per-pod Service 的 ClusterIP。

使用 per-pod Service 是為了讓公司規定的 FQDN 可以找到對應的 NiFi node。hostAlias 不能直接寫 pod 的 Pod IP 是因為它是動態的,這時候 per-pod Service 就派上用場。

那為什麼 StatefulSet 搭配的 headless Service 提供的 FQDN 不能用?

因為憑證不能用這個 FQDN 申請,所以我們才選擇這個方法。

3. IaC(Terraform)
Service 與 hostAlias 都用 Terraform 管理,隨 replicas 展開。這裡有個先後順序問題:hostAlias 要填的 ClusterIP,是前面的 per-pod Service 建好後 K8s 才分配的,那在建立 NiFi node (pod) 時該怎麼填 hostAlias?

解法是讓 hostAlias 直接引用 Service 的 cluster_ip 屬性,而非寫死:

1
2
3
4
5
6
cluster_host_aliases = [
for ord in range(var.nifi_replicas) : {
ip = kubernetes_service_v1.cluster_pod_service[tostring(ord)].spec[0].cluster_ip
hostnames = ["${var.nifi_name}-${ord}.${local.apps_domain}"]
}
]

Terraform 因此自動排出順序:先建 Service → 取得 ClusterIP → 再建帶 hostAlias 的 pod,單次 apply 完成,不需 depends_on。Service 與 hostAlias 都隨 replicas 展開,數量可以保持一致。

hostAlias 把每個 node FQDN 直接對到內部 IP,寫進 pod 的 /etc/hosts,就能繞過 DNS 解決。 /etc/hosts 的查找優先於 DNS,名字走不到 CoreDNS 就先被解析掉。

pod 內 /etc/hosts 長這樣(格式是 IP 在前、名字在後):

1
2
3
172.30.198.250  nifi-0.apps.sitdownplz.com
172.30.197.194 nifi-1.apps.sitdownplz.com
172.30.234.205 nifi-2.apps.sitdownplz.com

Deep Dive (TL;DR)

理解這個問題前,要先分清兩條完全不同的流量路徑:

Vertical(對外) Horizontal(node-to-node)
參與者 使用者瀏覽器 NiFi node (pod) 之間
Path Gateway → HTTPRoute → Service → pod pod 直接連對方 FQDN(mTLS)
URL nifi.apps...(主入口) nifi-<N>.apps...(每個 node)
Impact 正常 壞掉

Vertical(打開 NiFi UI) 的完整路徑:

1
browser → Gateway → HTTPRoute → ClusterIP Service(LB)→ 某個 NiFi node (pod)

這裡對外使用普通 ClusterIP Service,不是 headless。另外,NiFi 的 nifi.web.proxy.host 必須跟 HTTPRoute 註冊的 hostname 一致:不然 NiFi proxy host 就會把這個流量擋下來。

改成把所有 FQDN 包在同一張憑證這個作法不難,但這會遇到一個問題就是上面提到的 horizontal 路徑:NiFi node 在做 node-to-node 連線,也就是 Pod 要連到 Pod 時,nifi-2.apps.sitdownplz.com 的 IP 在 CoreDNS 中查不到,往 K8s 外的公司 DNS Server 找也找不到。

雖然可以在 CoreDNS 中把 sitdownplz.com 註冊進去,但 K8s cluster 不是我們團隊管理的,在這個被採納前總是要先有一個 workaround 作法。

要解決 DNS 找不到的這個問題,可以透過把所有指向 NiFi node 的 FQDN 透過 hostAlias 寫進每個 NiFi node 的 /etc/hosts 中,這樣就能在不查詢 DNS 的情況下找到其他 NiFi node。

但這也衍生了另一個問題,FQDN 如果對到的是 NiFi node (pod) 的 IP,而這是動態的,為了解決這個問題使用了上面提到的 per-pod Service 解法。

搞定 DNS 查詢後,新的問題來了,那我們的 IaC (Terraform) 怎麼在建立 Pod YAML 時知道 hostAlias 怎麼填呢?

作法是先建 per-pod Service,然後利用 kubernetes_service_v1.cluster_pod_service[tostring(ord)].spec[0].cluster_ip 取得 ClusterIP。

小結

很高興可以和同事一起集思廣益解決問題,在現在都用 AI 開發的環境下,唯有遇到這種問題才能激起思考的慾望,雖然可能不是最佳解,但看到東西串起來能 work 還是小有成就感。

這個作法的缺點有幾個:

  • 憑證會有字數上限,SAN 不能無限填入所有 NiFi node 的 FQDN
  • IT 直接改 CoreDNS 設定就不用後面這麼多麻煩事了

NiFi on K8s 節點之間連不上了怎麼辦
https://weiblog.me/2026-08-05/2026-nifi-k8s-node-to-node-dns/
Author
wei
Posted on
August 5, 2026
Licensed under