Ingress 與叢集 DNS:一個入口進來、一個名字相認
· tech
📑 目錄
第四篇給了短命 Pod 一個固定門牌 Service,但留了兩個尾巴:一是「外面怎麼用一個入口進來、再按網址分流到不同服務?」——那張對外類型圖裡最上面的 Ingress;二是叢集內服務彼此互打時,那個 http://web 的名字到底是誰在解析?這篇把兩件事收掉:對外靠 Ingress、對內靠叢集 DNS。它們是同一枚硬幣的兩面——都在解決「怎麼靠名字、而不是 IP 找到服務」。
先看對外這半邊,關鍵是 Service 與 Ingress 分工在不同的網路層:
Ingress:一個入口,按網址分流
回想 Service 的 LoadBalancer 類型:每開一個對外服務,雲端就配一個對外 IP。十個服務就十個 IP、十筆帳單,而且它是 L4——只看目標 IP 與 port,看不到 HTTP 網址,沒辦法「同一個網域,/api 走這、/img 走那」。
Ingress 補的就是這層。 它是 L7(HTTP 層)的入口,一份 Ingress 規則長這樣:
apiVersion: networking.k8s.io/v1
kind: Ingress
spec:
rules:
- host: shop.com
http:
paths:
- path: /
backend: { service: { name: web-svc, port: { number: 80 } } }
- path: /api
backend: { service: { name: api-svc, port: { number: 80 } } }
tls: # https 在 Ingress 這一層卸掉,後端只收 http
- hosts: [shop.com]
secretName: shop-tls
一個對外 IP,靠 Host + path 把流量分給背後一堆 ClusterIP Service,還能順手在這層做 TLS 終結(把 https 解密成 http 再往後送,後端 Pod 不用各自處理憑證)。這正是 第四篇說的「Ingress 擺在前面 + 一堆內部 ClusterIP Service」那個最常見的組合。
一個坑:Ingress 只是「規則」,得有 Controller 來執行
這是最多人卡的地方:你 kubectl apply 一份 Ingress,什麼事都不會發生。 因為 Ingress 物件只是一張規則表——它需要一個真正在跑的 Ingress Controller(常見的是 ingress-nginx、Traefik)去讀這些規則、把自己內部的反向代理設好,才會真的有流量進來。
叢集裡可以同時跑好幾種 Controller,靠 IngressClass 指定某份 Ingress 該歸誰管。這又是那個熟悉的模式:你宣告期望(路由規則),一個 controller 持續把現實(代理設定)收斂過去——跟 Deployment、Endpoints 一模一樣的迴圈,只是這次收斂的是 nginx 的設定檔。
叢集 DNS:服務怎麼靠「名字」相認
換到對內這半邊。第四篇說服務之間用 http://web 這種名字互打就好,但誰把名字翻成 ClusterIP? 答案是叢集內建的 DNS——CoreDNS(以一個 Deployment 跑在 kube-system,再用一個 Service 對外)。每顆 Pod 建立時,它的 /etc/resolv.conf 都被指向 CoreDNS,所以 Pod 裡任何 DNS 查詢都會問到它。
每個 Service 自動有一個固定的 DNS 名字,規則是 <service>.<namespace>.svc.cluster.local:
web 就行——resolv.conf 的 search domain 會自動補成完整名,交給 CoreDNS 換出 ClusterIP;跨 namespace 才需要寫到 web.other-ns實務上你幾乎只會用到短名:同 namespace 打 web、跨 namespace 打 web.payments 就夠了,後面那串 .svc.cluster.local 是 search domain 自動補的。有一個例外值得記:headless Service(clusterIP: None)不回傳單一虛擬 IP,而是回傳每一顆 Pod 的 IP,還讓每顆 Pod 有自己的 DNS 名字(pod-0.web.default.svc...)——這是 StatefulSet 那種「要點名到特定一顆」的有狀態服務才需要的,一般無狀態服務用普通 Service 就好。
反思
L4 與 L7 分清楚,「該用哪個」就不再猶豫
我剛接觸時,LoadBalancer 和 Ingress 常搞混——兩個看起來都是「對外」。分水嶺其實很利落:看不看得懂 HTTP。 Service 停在 L4,只認 IP:port,適合「就是要把這個 port 開出去」(資料庫、gRPC、非 HTTP 的東西);Ingress 站上 L7,讀得懂 Host 與 path,適合「一個網域下一堆 HTTP 服務、想在同一個入口收 TLS、按網址分流」。想通這條線之後,我的預設就變成:對外的 HTTP 一律走 Ingress、一個 IP 收全部;非 HTTP 或要獨立 IP 的才單開 LoadBalancer。 省 IP、省帳單,憑證也集中一處管。
「規則」和「執行規則的人」是兩回事——這觀念能救很多 debug
Ingress apply 下去卻沒反應,是新手最常見的鬼打牆,而它背後是一個更通用的道理:K8s 裡很多物件只是「期望」,得有一個 controller 在跑才會發生事情。 Ingress 要 Ingress Controller、NetworkPolicy 要 CNI 支援、CRD 要對應的 operator。我養成一個習慣:當某個資源「apply 了卻沒動靜」,第一反應不是懷疑寫錯,而是問——「執行這條規則的那個 controller,到底有沒有在跑?」 這一問常常直接命中要害。規則本身從不會自己動,動的永遠是那個盯著它的迴圈。
服務發現是 K8s 最被低估的送分題
從自建服務的年代一路走來,我對「服務發現」是有陰影的——Consul、Eureka、自己拼 etcd,光把「誰在哪」維護對就夠折騰。K8s 直接把這件事變成內建、零設定:建一個 Service 就自動有 DNS 名、CoreDNS 自動解析、Pod 換 IP 名字也不變。這跟 第四篇講的「依賴名字不依賴位置」是同一件事的底層支撐——正是因為有這套 DNS,「用名字相認」才從一個願望變成預設行為。 我現在會刻意提醒團隊別再自己造服務發現的輪子:K8s 已經把最難的那塊免費給你了,珍惜它。