返回文章列表

Kubernetes 服務暴露型別與 Ingress 負載平衡

本文探討 Kubernetes 中的三種服務暴露型別:NodePort、LoadBalancer 和 ExternalName,並解析其使用場景、優缺點及 YAML 設定範例。此外,文章也涵蓋了 Ingress 控制器、第七層負載平衡、nip.io 網域名稱解析和 MetalLB

容器技術 網路管理

Kubernetes 服務暴露是將應用程式服務釋出到叢集外部的關鍵機制。NodePort 提供簡單的連線埠轉發,LoadBalancer 則整合雲端供應商的負載平衡功能,而 ExternalName 則用於連線到外部服務。理解這些服務型別對於構建穩健且可擴充套件的 Kubernetes 應用程式至關重要。除了服務型別,Ingress 控制器也扮演著重要的角色,它作為第七層負載平衡器,可以根據網域名稱或 URL 路徑將流量路由到不同的服務。在沒有 DNS 伺服器的情況下,nip.io 提供了一種便捷的網域名稱解析方案,方便開發者在本地測試 Ingress 規則。最後,MetalLB 作為一個功能強大的 Layer 4 負載平衡器,可以與 Ingress 控制器配合使用,提供更完善的流量管理和負載平衡解決方案。

Kubernetes 服務暴露:NodePort、LoadBalancer 與 ExternalName 服務型別解析

在 Kubernetes 中,服務(Service)扮演著將應用程式暴露給外部請求的重要角色。本文將探討三種主要的服務型別:NodePort、LoadBalancer 和 ExternalName,並分析其使用場景、優缺點及實作細節。

NodePort 服務型別

NodePort 是一種將服務暴露給叢集外部的簡單方法。它在每個節點上開啟一個特定的連線埠,將流量轉發到對應的 Pod。

NodePort 的運作原理

當建立一個 NodePort 服務時,Kubernetes 會在每個節點上開啟一個指定的連線埠(NodePort)。外部請求可以透過任何一個節點的 IP 地址加上 NodePort 連線埠來存取服務。Kubernetes 會自動將流量轉發到對應的 Pod。

apiVersion: v1
kind: Service
metadata:
  name: nginx-frontend
spec:
  selector:
    app: nginx
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 80
  - name: https
    protocol: TCP
    port: 443
    targetPort: 443
  type: NodePort

NodePort 的優缺點

優點:

  • 簡單易用,無需額外的基礎設施支援。
  • 可以快速將服務暴露給外部請求。

缺點:

  • NodePort 的連線埠號是隨機分配的,如果服務被刪除並重新建立,連線埠號可能會改變。
  • 需要記住每個服務對應的 NodePort 連線埠號,容易造成混淆。
  • 如果目標節點離線或故障,請求將會失敗。

LoadBalancer 服務型別

LoadBalancer 是一種將服務暴露給外部請求的高階方法。它需要一個外部的 LoadBalancer 元件來支援,通常由雲端供應商提供。

LoadBalancer 的運作原理

當建立一個 LoadBalancer 服務時,Kubernetes 會向外部的 LoadBalancer 元件請求一個外部 IP 地址,並將流量轉發到對應的 Pod。

LoadBalancer 的優缺點

優點:

  • 提供了一個外部 IP 地址,可以直接被外部請求存取。
  • 負載平衡功能可以提高服務的可用性和擴充套件性。

缺點:

  • 需要外部的 LoadBalancer 元件支援,通常需要雲端供應商提供。
  • 在本地叢集中,可能無法正常運作,因為缺乏必要的基礎設施支援。

ExternalName 服務型別

ExternalName 是一種特殊的服務型別,它將一個外部的 DNS 名稱對映到一個 Kubernetes 服務名稱。

ExternalName 的運作原理

當建立一個 ExternalName 服務時,Kubernetes 會建立一個 DNS 記錄,將服務名稱對映到外部的 DNS 名稱。

apiVersion: v1
kind: Service
metadata:
  name: sql-db
  namespace: finance
spec:
  type: ExternalName
  externalName: sqlserver1.foowidgets.com

ExternalName 的優缺點

優點:

  • 可以將外部服務整合到 Kubernetes 的 DNS 系統中。
  • 可以簡化應用程式的組態,避免硬編碼外部服務的 DNS 名稱。

缺點:

  • 需要小心處理 SSL/TLS 證書驗證問題,因為外部服務的 DNS 名稱可能與證書上的名稱不匹配。
  • 需要額外的組態和管理,以確保 ExternalName 服務的正確運作。

Kubernetes 中的服務、負載平衡與外部 DNS

在 Kubernetes 中,要實作服務的外部存取,需要使用服務(Service)和負載平衡(Load Balancer)。本章節將介紹如何使用服務和負載平衡來暴露 Kubernetes 中的應用程式。

刪除 ExternalName 服務並建立新服務

要實作第二個目標,請按照以下步驟進行:

  1. 刪除 ExternalName 服務。
  2. 建立一個新的服務,使用名稱 ext-sql-db,並使用 app=sql-app 作為選擇器。該服務的 YAML 清單如下所示:
apiVersion: v1
kind: Service
metadata:
  labels:
    app: sql-db
  name: sql-db
  namespace: finance
spec:
  ports:
  - port: 1433
    protocol: TCP
    targetPort: 1433
    name: sql
  selector:
    app: sql-app
  type: ClusterIP

內容解密:

此 YAML 清單定義了一個名為 sql-db 的服務,該服務選擇標籤為 app=sql-app 的 Pod,並將流量轉發到這些 Pod 的 1433 埠。由於我們使用相同的服務名稱,因此應用程式無需進行任何更改。

負載平衡簡介

在討論不同型別的負載平衡之前,瞭解 OSI 模型(開放式系統互連模型)非常重要。OSI 模型是一個七層模型,每層提供不同的功能。

OSI 模型的七層

層級名稱描述
1物理層定義物理裝置之間的連線
2資料鏈路層提供錯誤檢測和糾正
3網路層路由資料包
4傳輸層提供可靠的資料傳輸
5會話層建立和管理會話
6表示層提供資料表示和轉換
7應用層提供應用程式之間的通訊

對於本章節,我們關注的是第 4 層和第 7 層。

  • 第 4 層:負責裝置之間的通訊流量,提供 TCP/UDP 資訊。根據第 4 層的負載平衡器提供應用程式服務於任何 TCP/UDP 埠的能力。
  • 第 7 層:負責為應用程式提供網路服務,例如 HTTP 和 HTTPS。

第 7 層負載平衡器

Kubernetes 提供第 7 層負載平衡器,以 Ingress 控制器的形式出現。有多種解決方案可以為叢集提供 Ingress 功能,包括 NGINX、Envoy、Traefik 和 HAProxy。

使用 NGINX 作為 Ingress 控制器

NGINX 可以用作 Kubernetes 的 Ingress 控制器,用於路由傳入的 HTTP/HTTPS 請求到暴露的服務。

名稱解析和第 7 層負載平衡器

要處理 Kubernetes 叢集中的第 7 層流量,需要佈署 Ingress 控制器。Ingress 控制器依賴傳入的名稱來路由流量到正確的服務。

示例:使用泛網域名稱進行 Ingress

假設我們的公司名為 FooWidgets,我們有三個 Kubernetes 叢集,前端由外部負載平衡器和多個 Ingress 控制器的端點組成。我們的 DNS 伺服器將為每個叢集建立一個泛網域名稱,指向負載平衡器的虛擬 IP 地址。

叢集名稱泛網域名稱
cluster1*.cluster1.foowidgets.com
cluster2*.cluster2.foowidgets.com
cluster3*.cluster3.foowidgets.com

下圖顯示了請求的整個流程: 圖表翻譯: 此圖表顯示了使用者請求到達 Kubernetes 叢集中的服務的整個流程。首先,使用者傳送請求到 DNS 伺服器,DNS 解析後將請求轉發到負載平衡器。然後,負載平衡器將請求轉發到 Ingress 控制器,最後由 Ingress 控制器將請求路由到正確的服務。

  1. 使用者使用瀏覽器請求 URL https://timesheets.cluster1.foowidgets.com
  2. DNS 查詢被髮送到 DNS 伺服器。DNS 伺服器查詢 cluster1.foowidgets.com 的區域詳細資訊。在 DNS 區域中,有一個條目解析為負載平衡器的虛擬 IP 地址。
  3. 負載平衡器的虛擬 IP 地址有三個後端伺服器,指向佈署了 Ingress 控制器的三個工作節點。
  4. 使用其中一個端點,將請求傳送到 Ingress 控制器。
  5. Ingress 控制器將請求的 URL 與 Ingress 規則列表進行比較。當找到匹配的請求時,Ingress 控制器將請求轉發到分配給 Ingress規則的服務。

使用 nip.io 進行名稱解析

大多數個人開發叢集,如 KinD 安裝,可能沒有足夠的許可權來新增記錄到 DNS 伺服器。為了測試 Ingress 規則,我們需要針對對映到 Kubernetes 服務的唯一主機名稱。沒有 DNS 伺服器的情況下,您需要在本地主機檔案中新增多個名稱,指向 Ingress 控制器的 IP 位址。

舉例來說,如果您佈署了四個網頁伺服器,您需要在本地主機檔案中新增所有四個名稱,如下所示:

192.168.100.100 webserver1.test.local
192.168.100.100 webserver2.test.local
192.168.100.100 webserver3.test.local
192.168.100.100 webserver4.test.local

也可以將其表示在一行中,而不是多行:

192.168.100.100 webserver1.test.local webserver2.test.local webserver3.test.local webserver4.test.local

如果您使用多台機器來測試您的佈署,您需要在每台計劃用於測試的機器上編輯主機檔案。在多台機器上維護多個檔案是一項管理噩夢,並將導致使測試成為挑戰的問題。

幸運的是,有免費服務可用,提供 DNS 服務,我們可以在不為 KinD 叢集組態複雜 DNS 基礎架構的情況下使用。Nip.io 是我們將用於 KinD 叢集名稱解析需求的服務。

nip.io 的工作原理

使用前面的網頁伺服器範例,我們不需要建立任何 DNS 記錄。我們仍然需要將不同伺服器的流量傳送到執行在 192.168.100.100 上的 NGINX 伺服器,以便 Ingress 可以將流量路由到適當的服務。Nip.io 使用包含 IP 位址的主機名格式來解析名稱到 IP。

例如,假設我們有四個網頁伺服器,分別命名為 webserver1webserver2webserver3webserver4,並且 Ingress 規則執行在 192.168.100.100 上。我們可以使用以下命名標準:<所需名稱>.<INGRESS IP>.nip.io

四個網頁伺服器的名稱如下表所示:

名稱nip.io 網域名稱
webserver1webserver1.192.168.100.100.nip.io
webserver2webserver2.192.168.100.100.nip.io
webserver3webserver3.192.168.100.100.nip.io
webserver4webserver4.192.168.100.100.nip.io

當您使用上述任何名稱時,nip.io 將解析它們到 192.168.100.100。您可以在下面的截圖中看到每個名稱的 ping 範例:

使用 nip.io 進行名稱解析範例

圖表翻譯: 此圖示展示了客戶端如何透過 nip.io 解析網域名稱並將請求路由到對應的 Kubernetes 服務。首先,客戶端向 nip.io 傳送 DNS 請求,nip.io 傳回對應的 IP 位址。然後,客戶端向該 IP 位址傳送請求,Ingress 控制控制器根據請求中的網域名稱將流量路由到正確的服務。

建立 Ingress 規則

Ingress 規則使用名稱來路由傳入請求到正確的服務。以下是一個圖形表示,顯示了傳入請求如何被 Ingress 路由:

Ingress 流量流程

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title Kubernetes 服務暴露型別與 Ingress 負載平衡

package "Kubernetes Cluster" {
    package "Control Plane" {
        component [API Server] as api
        component [Controller Manager] as cm
        component [Scheduler] as sched
        database [etcd] as etcd
    }

    package "Worker Nodes" {
        component [Kubelet] as kubelet
        component [Kube-proxy] as proxy
        package "Pods" {
            component [Container 1] as c1
            component [Container 2] as c2
        }
    }
}

api --> etcd : 儲存狀態
api --> cm : 控制迴圈
api --> sched : 調度決策
api --> kubelet : 指令下達
kubelet --> c1
kubelet --> c2
proxy --> c1 : 網路代理
proxy --> c2

note right of api
  核心 API 入口
  所有操作經由此處
end note

@enduml

圖表翻譯: 此圖示展示了客戶端的請求如何透過 Ingress 被路由到後端的 Pod。首先,客戶端向 nip.io 傳送 DNS 請求以取得 IP 位址。然後,客戶端向該 IP 位址傳送請求,Ingress 控制控制器根據組態的規則將流量路由到正確的 Kubernetes 服務,最終轉發到後端的 Pod。

建立 Ingress 規則的步驟

  1. 使用者在瀏覽器中請求一個 URL 名為 webserver1.192.168.200.20.nip.io。DNS 請求被傳送到本地 DNS 伺服器,最終被傳送到 nip.io DNS 伺服器。
  2. nip.io 伺服器將網域名稱解析為 IP 位址 192.168.200.20,並傳回給客戶端。
  3. 客戶端將請求傳送到執行在 192.168.200.20 上的 Ingress 控制控制器。請求中包含完整的 URL 名稱 webserver1.192.168.200.20.nip.io
  4. Ingress 控制控制器在組態的規則中查詢所請求的 URL 名稱,並將 URL 名稱與服務進行匹配。
  5. 伺服器端點將被用於將流量路由到分配的 Pod。
  6. 請求被路由到執行網頁伺服器的端點 Pod。

建立 Kubernetes 物件

  1. 首先,我們需要在一個名稱空間中執行一個簡單的網頁伺服器。我們將在預設名稱空間中佈署一個基本的 NGINX 網頁伺服器。使用以下 kubectl run 命令可以快速建立佈署:

    kubectl run nginx-web --image bitnami/nginx
    

    內容解密:

    • kubectl run:用於建立一個新的佈署。
    • nginx-web:佈署的名稱。
    • --image bitnami/nginx:指定使用的 Docker 映象。
  2. 使用 kubectl expose 命令為佈署建立一個服務。Bitnami NGINX 映象執行在埠 8080 上,因此我們將使用相同的埠來暴露服務:

    kubectl expose deployment nginx-web --port 8080 --target-port 8080
    

    內容解密:

    • kubectl expose:用於為一個佈署或 Pod 建立一個服務。
    • deployment nginx-web:指定要暴露的佈署名稱。
    • --port 8080:指定服務要暴露的埠。
    • --target-port 8080:指定後端 Pod 的目標埠。

這樣,我們就成功地建立了一個簡單的網頁伺服器佈署和對應的 Kubernetes 服務,為進一步組態 Ingress 規則奠定了基礎。

使用Ingress規則與MetalLB進行負載平衡

建立Ingress規則

在建立了Deployment和Service之後,最後一步是建立Ingress規則。要建立Ingress規則,需要建立一個使用Ingress物件型別的manifest檔。以下是一個範例Ingress規則,它假設Ingress控制器正在192.168.200.20上執行。

nginx-ingress.yaml 範例

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: nginx-web-ingress
spec:
  rules:
  - host: webserver1.192.168.200.20.nip.io
    http:
      paths:
      - path: /
        backend:
          serviceName: nginx-web
          servicePort: 8080

建立Ingress規則

使用kubectl apply指令建立Ingress規則:

kubectl apply -f nginx-ingress.yaml

測試Ingress規則

在內部網路中的任何客戶端上,可以透過瀏覽http://webserver1.192.168.200.20.nip.io來測試Deployment是否成功。

Layer 4 負載平衡器

Layer 4 負載平衡器執行在OSI模型的第四層,負責處理TCP和UDP等協定的流量。根據IP位址和埠號接受傳入的流量,並根據一組規則將流量轉發到目標IP位址和埠號。

Layer 4 負載平衡器選項

有多種Layer 4 負載平衡器可供選擇,包括:

  • HAproxy
  • NGINX Pro
  • SeeSaw
  • F5 Networks
  • MetalLB

使用MetalLB作為Layer 4 負載平衡器

MetalLB是一個免費且易於組態的Layer 4 負載平衡器。它提供了強大的組態選項,使其能夠執行在開發實驗室或企業叢集中。

安裝MetalLB

要佈署MetalLB到KinD叢集,需要使用MetalLB的GitHub儲存函式庫中的manifests檔。

  1. 建立一個名為metallb-system的新名稱空間:
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.9.3/manifests/namespace.yaml
  1. 佈署MetalLB到叢集中:
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.9.3/manifests/metallb.yaml
  1. 建立一個用於加密通訊的secret:
kubectl create secret generic -n metallb-system memberlist --from-literal=secretkey="$(openssl rand -base64 128)"
設定MetalLB

MetalLB使用ConfigMap來儲存組態。由於我們將在Layer 2模式下使用MetalLB,因此所需的組態檔相對簡單,只需要提供一個IP範圍。

要取得Docker正在使用的子網路,可以檢查預設的橋接網路:

docker network inspect bridge

設定MetalLB的ConfigMap範例

apiVersion: v1
kind: ConfigMap
metadata:
  name: config
  namespace: metallb-system
data:
  config: |
    address-pools:
    - name: default
      protocol: layer2
      addresses:
      - 192.168.200.20/32

詳細解說:

此ConfigMap定義了MetalLB的組態。其中,address-pools欄位定義了可用的IP位址範圍。在此範例中,我們使用layer2協定,並指定了一個IP位址範圍。

MetalLB BGP模式

MetalLB還提供了BGP模式,允許建立對等節點以交換網路路由。有關MetalLB的BGP模式的更多資訊,請參閱MetalLB官方檔案

重點回顧

  • 使用Ingress規則來管理傳入的HTTP請求。
  • Layer 4 負載平衡器執行在OSI模型的第四層,負責處理TCP和UDP等協定的流量。
  • MetalLB是一個免費且易於組態的Layer 4 負載平衡器,提供了強大的組態選項。
  • 設定MetalLB需要提供一個IP範圍,並使用ConfigMap來儲存組態。