Kubernetes網路一年發展動態與未來趨勢(下)
何謂 Ingress?從字面意思解讀,就是“入站流量”。K8S 的 Ingress 資源物件是指授權入站連線到達叢集內服務的規則集合。具體含義看下面這個例子便一目瞭然:

通常情況下,Service 和 Pod 僅可在叢集內部網路中通過 IP 地址訪問。所有到達邊界路由的流量或被丟棄或被轉發到其他地方。Ingress 就是在邊界路由處開個口子,放你進來。因此,Ingress 是建立在 Service 之上的 L7 訪問入口,它支援通過 URL 方式將 Service 暴露到 K8S 叢集外;支援自定義 Service 的訪問策略;提供按域名訪問的虛擬主機功能;支援 TLS 通訊。

:
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: test-ingress spec: tls: - secretName: testsecret backend: serviceName: testsvc servicePort: 80 複製程式碼
把上面這個 Ingress 物件建立起來後, kubectl ge
一把,會看到:

其中,ADDRESS 即 Ingress 的訪問入口地址,由 Ingress Controller 分配,一般是 Ingress 的底層實現 LB 的 IP 地址,例如:Ingress,GCE LB,F5 等;BACKEND 是 Ingress 對接的後端 K8S Service IP + Port;RULE 是自定義的訪問策略,主要是基於 URL 的轉發策略,若為空,則訪問 ADDRESS 的所有流量都轉發給 BACKEND。
下面給出一個Ingress的rules不為空的例子。
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: test spec: rules: - host: foo.bar.com http: paths: - path: /foo backend: serviceName: s1 servicePort: 80 - path: /bar backend: serviceName: s2 servicePort: 80 複製程式碼
這個例子和上面那個最明顯的區別在於,rules 定義了 path 分別為 /foo
和 /bar
的分發規則,分別轉發給 s1:80 和 s2:80。 Kubectl get
一把一目瞭然:

ADDRESS
欄位。而在我們的例子中,這個 LB 顯然還未 ready。
Ingress 是一個非常“極客”和需要 DIY 的產物,K8S 只負責提供一個 API 定義,具體的 Ingress Controller 需要使用者自己實現!官方倒是提供了 Nginx 和 GCE 的 Ingress Controller 示例供開發者參考。實現一個 Ingress Controller 的大致框架無非是, List/Watch
K8S 的 Service,Endpoints,Ingress 物件,重新整理外部LB的規則和配置。
這還不算,如果想要通過域名訪問 Ingress?需要使用者自己配置域名和 Ingress IP 的對映關係,比如:host 檔案,自己的 DNS(不是 kube-dns
)。下文會講到,“高冷”的 kube-dns
只會負責叢集內的域名解析,叢集外的一概不管。 如果嫌麻煩,懶得開發/配置 Ingress?Huawei CCE瞭解一下? Ingress + 高效能ELB :)
K8S DNS
K8S DN S說,剛剛誰唸叨起本宮了?
一言以蔽之,K8S 的 DNS,就是用來解析 K8S 叢集內的 Pod 和 Service 域名的,而且一般是供 Pod 內的程序使用的!血統高貴,一般不給外人使用。那可能會有人好奇問一句,Pod 到底怎麼使用K8S DNS呢?原來,kubelet配置--cluster-dns把DNS的靜態IP傳遞給每個容器。K8S DNS 一般通過外掛方式部署到 K8S 上,併為之繫結一個 Service,而 Service 的 Cluster IP往往是固定的。
K8S DNS 目前有兩個實現,分別是 kube-dns 和 CoreDNS。
對於 Service,K8S DNS 伺服器會生成兩類 DNS 記錄,分別是:A 記錄和SRV記錄。而 A 記錄又對普通 Service 和 headless Service有所區別。
普通 Service 的 A 記錄是:
{service name}.{service namespace}.svc.cluster.local -> Cluster IP
的對映關係。後面域名後面一串子域名: svc.cluster.local
是 Kubelet 通過 --cluster-domain
配置的偽域名。
Headless Service的A記錄是:
{service name}.{service namespace}.svc.cluster.local ->
後端 Pod IP 列表 的對映關係。
至於SRV記錄,則是按照一個約定俗稱的規定:
_{port name}._{port protocol}.{service name}.{service namespace}.svc.cluster.local –> Service Port
實現了對服務埠的查詢。
對於Pod,A記錄是:
{pod-ip}.{pod namespace}.pod.cluster.local -> Pod IP
如果Pod IP 是 1.2.3.4,上面的 {pod-ip}
即 1-2-3-4。Pod的A記錄其實沒什麼用,因為如果都知道 Pod IP 了,還用查 DNS 嗎? 如果在 Pod Spec 指定 hostname 和 subdomain,那麼 K8S DNS 會額外生成Pod的A記錄就是: {hostname}.{subdomain}.{pod namespace}.pod.cluster.local –> Pod IP
同樣,後面那一串子域名 pod.cluster.local
是 kubelet 配置的偽域名。
讓我們看下kube-dns的架構吧。

- kubedns: /Watch K8S Service 和 Endpoints 變化。接入 SkyDNS,在記憶體中維護 DNS 記錄,是 dnsmasq 的上游。
- dnsmasq: DNS 配置工具,監聽 53 埠,為叢集提供 DNS 查詢服務。提供 DNS 快取,降低 kubedns 壓力。
- exechealthz: 健康檢查,檢查 kube-dns 和 dnsmasq 的健康 需要注意的是,dnsmasq 是個 C++ 寫的一個小程式,記憶體洩露的“老毛病”。 雖然 kube-dns 血統純正,而且早早地進入到 K8S 的“後宮”,也早有“名分”,但近來 CoreDNS 卻獨得 K8S SIG Network 的聖寵。CoreDNS 是個 DNS 伺服器,原生支援 K8S,而且居然還是一個 CNCF 的專案!
與 kube-dns 的三程序架構不同,CoreDNS 就一個程序,運維起來更加簡單。而且採用 Go 語言編寫,記憶體安全,高效能。值得稱道的是,CoreDNS 採用的是“外掛鏈”架構,每個外掛掛載一個 DNS 功能,保證了功能的靈活、易擴充套件。儘管資歷不深,但卻“集萬千寵愛於一身”,自然是有兩把刷子的。
值得一提的是,以上效能測試資料是不帶 cache 情況下取得的,明顯要高於 kube-dns。那麼為什麼建議使用 CoreDNS 呢?
K8S 官方已經將 CoreDNS 扶正,成為了預設模式。除了效能好以外,還有什麼其他優勢嗎?Core DNS修復了 kube-dns 的一些“令人討厭”的“老生常談”的問題:
- dns#55 - Allow custom DNS entries for kube-dns
- dns#116 - Missing ‘A’ records for headless service with pods sharing * hostname
- dns#131 - ExternalName not using stubDomains settings
- dns#167 - Enable round robin A/AAAA records
- dns#190 - kube-dns cannot run as non-root user
- dns#232 - Use pod’s name instead of pod’s hostname in DNS SRV records
同時,還有一些吸引人的特性:
- Zone transfers - list all records, or copy records to another server
- Namespace and label filtering - expose a limited set of services
- Adjustable TTL - adjust up/down default service record TTL
- Negative Caching - By default caches negative responses (e.g. NXDOMAIN)
其中,原生支援基於 namespace 隔離和過濾 Service 和 Pod 的 DNS 記錄這一條特性,在多租戶場景下格外有用。
Network Policy
K8S預設情況下,底層網路是“全連通”的。但如果我們需要實現以下願景:

即,只允許訪問 default namespace 的 Label 是 app = web 的 Pod,default namespace 的其他 Pod 都不允許外面訪問。這個隔離需求在多租戶的場景下十分普遍。K8S 的解決方案是 Network Policy。
Network Policy 說白了就是基於 Pod 源 IP(所以 K8S 網路不能隨隨便便做SNAT啊!)的訪問控制列表,限制 Pod 的進/出流量,用白名單實現了一個訪問控制列表(ACL)。Network Policy 作為 Pod 網路隔離的一層抽象,允許使用 Label Selector,namespace selector,埠,CIDR 這四個維度限制 Pod 的流量進出。和I ngress 一副德行的是,K8S 對 Netowrk Policy 還是隻提供了 API 定義,不負責實現!

一般情況下,Policy Controller 是由網路外掛提供的。支援 Network Policy 的網路外掛有 Calico,Cilium,Weave Net,Kube-router,Romana。需要注意的是,flannel 不在這個名單之列,似乎又找到了一個不用 flannel 的理由?
讓我們先來見識幾個預設網路策略:

如果要拒絕所有流量進入呢?比如,場景長這樣:

那麼 Network Policy 物件應該定義成:

如果要限制部分流量進入呢?比如,場景長這樣:

那麼 Network Policy 物件應該定義成:

如果只允許特定 namespace 的 Pod 流量進入呢?比如,場景長這樣:

那麼 Network Policy 物件應該定義成:

如果限制流量從指定埠進入呢?比如,場景長這樣:

那麼,Network Policy物件應該定義成:
