本文介紹 Ingress 基本概念、Ingress Controller 工作原理和 Nginx Ingress Controller 的使用說明。
在 Kubernetes 叢集中,Ingress 作為叢集內服務對外暴露的存取存取點,幾乎承載著叢集內服務存取的所有流量。 Ingress 是 Kubernetes 中的一個 resource object,用來管理叢集外部存取叢集內部服務的方式。可以透過 Ingress 資源來設定不同的轉送規則,從而實現根據不同的規則設定存取叢集內不同的 Service 所對應的後端 Pod。
Ingress 將叢集外部的 HTTP 和 HTTPS 請求路由到叢集內的服務。流量路由是根據 Ingress 資源上定義的規則來控制的。
以下是一個簡單的例子,Ingress 將所有的流量導向一個服務:
Ingress 可以設定為給服務一個外部可訪問的網址、負載均衡的流量、terminate SSL / TLS,以及提供 name-based ( 基於名稱 ) 的 virtual hosting 功能。Ingress Controller 負責實現 Ingress,一般會搭配 load balancer 一起使用,也可能設定 edge router 或額外的前端來幫助處理流量。
Ingress 不會暴露任意的 ports (埠) 或 protocols (通訊協定)。如果需要將 HTTP 和 HTTPS 以外的服務暴露到互聯網,通常會使用 Service.Type=NodePort 或 Service.Type=LoadBalancer 類型的服務來實現。
要讓 Ingress 在你的 K8s 叢集中運作,必須有一個 Ingress controller 在運行。你需要選擇至少一個 Ingress controller 並確保它已經在叢集中設定好。
和其他在 kube-controller-manager 二進位程式中運行的 controller 不同,Ingress controller 並不會隨著叢集自動啟動。
Ingress Controller 用於解析 Ingress 的轉送規則。 Ingress Controller 收到請求,匹配 Ingress 轉發規則轉發到後端 Service 所對應的 Pod,由 Pod 處理請求。 Kubernetes 中 Service、Ingress 與 Ingress Controller 有著以下關係:
Ingress Controller 透過 API Server 取得 Ingress 資源的變化,動態地產生 Load Balancer 所需的設定文件,然後依序產生新的路由轉送規則。
執行結果 :
確認 pod 的狀態是 up 和 running
執行結果 :
執行結果 :
這是一個典型的 Deployment,前面有兩個 Service 在支援,以下是為什麼需要這樣做的原因:
ingress-nginx-controller,它是一個 load balancer 類型的 service,用來將 Ingress controller 的功能暴露給外部。它透過穩定的 IP 地址和 DNS 名稱,讓外部客戶端可以連接到 Ingress controller。load balancer service 也幫助 Ingress controller 將進來的流量分散到多個 Pod 分身上,提升可用性和擴展性。ingress-nginx-controller-admission,它是一個 cluster IP service,用來控制 Ingress controller 的准入。這個 service 在 Ingress 資源被 Ingress controller 處理之前,用於驗證和修改這些資源。它在叢集內部運行,不對外部客戶端暴露,使用 Kubernetes 的 admission control mechanism (准入控制機制) 攔截並修改 Ingress 資源。如果我們查看 Endpoints,就會發現兩個服務都指向相同的 Pod
執行結果 :
而這個 pod 就是 ingress-nginx-controller
執行結果 :
在某些環境中,如果沒有 load balancer 可用,而且不能使用 NodePorts,可以設定 ingress-nginx 的 Pod 使用它們所在主機的網路,而不是使用獨立的 network namespace。這樣做的好處是 Ingress-Nginx Controller 可以直接將 80 和 443 埠綁定到 Kubernetes 節點的網卡上,而不需要透過 NodePort 服務進行額外的網路轉換。
!!! 注意:這種做法不會利用任何 Service 物件來暴露 Ingress-Nginx Controller。如果目標叢集中存在 ingress-nginx 的 Service,建議將其刪除。
執行結果 :
…
    spec:
      ## 1. 新增以下這行 
      hostNetwork: true
      containers:
      - args:
        - /nginx-ingress-controller
        ## 2. 註解掉以下這行的設定
        #- --publish-service=$(POD_NAMESPACE)/ingress-nginx-controller
        ...
        ## 3. 新增以下這行
        - --report-node-internal-ip-address
!!! 危險 "安全考量"
啟用這個選項會讓每個系統的 daemon 都會在任何網路介面(包括主機的 loopback)上暴露給 Ingress-Nginx Controller。請仔細評估這樣做對系統安全性的影響。
!!! 例子
考慮這個由 2 個副本組成的 ingress-nginx-controller 部署,NGINX Pod 繼承了它們所在主機的 IP 地址,而不是使用內部的 Pod IP。
這種部署方式的一個主要限制是,每個叢集節點上只能調度一個 Ingress-Nginx 控制器 Pod,因為在同一個網路介面上綁定相同的埠多次在技術上是不可能的。因為這個原因而無法調度的 Pod 會出現以下的事件錯誤:
執行結果
確保只建立可排程 Pod 的方法之一,是將 Ingress-Nginx Controller 部署為 DaemonSet,而非傳統的 Deployment。
除非某個節點被設定為拒絕這些 Pod,否則 DaemonSet 只會為每個叢集節點(包括主節點)調度一種 Pod。
像使用 NodePorts 一樣,這種方法也有一些需要注意的小問題。
hostNetwork: true 的 Pod 不會使用內部的 DNS resolver(例如 kube-dns 或 CoreDNS),除非它們的 dnsPolicy 設定為 ClusterFirstWithHostNet。如果 NGINX 需要解析內部名稱,建議使用這個設定。--publish-service flag 不適用,所有 Ingress 物件的狀態會保持為空白。相反,由於 bare-metal 節點通常沒有 ExternalIP,因此必須啟用 --report-node-internal-ip-address flag,將所有 Ingress 物件的狀態設定為執行 Ingress-Nginx Controller 的所有節點的內部 IP 位址。
!!! 範例 給定一個由 2 個 replicas 組成的 ingress-nginx-controller DaemonSet
controller 會將其管理的所有 Ingress 物件的狀態設定為下列值:
!!! 注意 另外,也可以使用 --publish-status-address 標誌來覆寫寫入 Ingress 物件的位址。請參閱命令列參數。
如果我們現在查看 Ingress Nginx Controller pod log,我們會發現上一步建立的 Ingress 有一些 log 資訊:
執行結果 :
基本上,Ingress Nginx Controller pod 會一直觀察 Kubernetes API Server。一旦接收到 Ingress 資源已建立的事件,它就會檢查其語法,以及是否已定義了 IngressClass=nginx。
如果新的 Ingress manifest 符合規定,Nginx Controller 就會更新用於 routing 的設定檔,並在不關閉 Pod 的情況下啟動重新載入。
routing 設定是存儲在 Ingress Nginx Controller Pod 的設定文件中。這裡我提取了一些高層次的相關部分:
執行結果 :
基本上,如果這個 Nginx 伺服器收到針對根路徑(也就是請求中沒有指定路徑)的請求,它會使用 OpenResty 的 ngx_http_lua_module 模組來重寫請求,並將其傳遞給應用服務。
讓我們來看看目前為止的設定:
hi-service Service 將流量以 load balanced 或 random 的方式傳給網站應用的 Pod。
class=nginx 屬性的 Ingress ,來將流量 routes 到這個 Service。這會在 Kubernetes 中觸發一個事件。檢視 Ingress 狀態
可以看到 Ingress 的 Address 和 Ports 對應的就是 Ingress Nginx Controller Pod 的 IP 跟 Port。