k8s暴露叢集內和叢集外服務的方法

語言: CN / TW / HK

叢集內服務

一般 pod 都是根據 service 資源來進行叢集內的暴露,因為 k8s 在 pod 啟動前就已經給排程節點上的 pod 分配好 ip 地址了,因此我們並不能提前知道提供服務的 pod 的 ip 地址。那麼 service 服務提供的功能就是,使用者根本無需關心後端提供服務 pod 的數量,以及各自對應的 ip 地址。

服務資源會被 k8s 分配一個 cluster-ip 叢集 ip,只能在叢集內部可以被訪問。而在叢集內 k8s 如何配置服務網路的,可以參考之前的網路服務,實際上就是利用 iptables 來進行網路的配置。除此之外 k8s 還可以配置會話的親和性,因為有可能需要請求落入容一個pod,設定服務的 sessionAffinity 屬性為 clientIP。

apiVersion: v1
kind: Service
spec:
    sessionAffinity: ClientIP

這種方式會將服務代理將來自同一個 clientip 的所有請求轉發到同一個 pod 上。k8s 僅僅支援兩種形式的會話親和性服務:None和ClientIP。但是不支援 cookie 的會話親和性選項,因為 k8s 服務不是在 HTTP 層面上工作,服務處理 TCP 和 UDP 包,並不關心其中資料包內容,畢竟解包封包都是需要耗費資源的。

而且同一個服務還可以暴露多個埠,比如一個 pod 中監聽兩個埠,HTTP 監聽 8080,HTTPS 監聽 8443,可以使用一個服務從埠 80 和 443 轉發到 pod 埠 8080 和 8443。比如宣告 service 暴露多個埠

apiVersion: v1
kind: Service
metadata:
    name: kubia
spec:
    ports:
    - name: http
      port: 80
      targetPort: 8080    // pod 的8080埠對映成 80 埠
    - name: https
      port: 443
      targetPort: 8443
    selector:
      app: kubia

叢集外暴露的服務

將服務暴露給外部客戶端的方法有以下幾種:

  • 將服務型別設定成 NodePort:會在叢集中節點內開啟一個埠,外部可以直接從節點埠訪問內部服務,k8s 將節點接受的流量衝定向到內部服務中。
  • 將服務型別設定成 LoadBalance:NortPort 型別的一種擴充套件,這使得服務可以通過一個專用的負載均衡器來訪問,這是由 k8s 提供的雲基礎設施來實現的,負載均衡負責將流量重定向到跨節點的節點埠,這裡勢必有路由策略和交換器相關。外部客戶端直接通過負載均衡器IP來訪問叢集內部服務
  • 建立一個 Ingress 資源。這是建立在 HTTP 層,也就是 7 層網路層上來進行分發工作,比原本 k8s 的四層網路的服務提供更多的功能。

建立 NodePort 型別的服務

apiVersion: v1
kind: Service
metadata:
    name: kubia-nodeport
spec:
    type: NodePort
    ports:
    - port: 80
      targetPort: 8080
      nodePort: 30123    // 通過部署的叢集節點中的 30123 埠可以訪問該服務
    selector:
        app: kubia

在叢集內兩個節點暴露了 30123 埠,到達任何一個節點上埠的傳入連線將被重定向到一個隨機選擇的pod。

通過負載均衡器建立服務

k8s 必須在支援 LoadBalance 服務的環境下才能建立此型別的負載均衡器服務。宣告如下

apiVersion: v1
kind: Service
metadata:
    name: kubia-loadbalancer
spec:
    type: LoadBalancer
    ports: 
    - port: 80
      targetPort: 8080
    selector:
        app: kubia

那麼 nodeport 和 LB 型別的有什麼不同呢?

$ kubectl get svc kubia-nodeport
NAME               TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
kubia-nodeport   NodePort   10.99.194.15   <nodes>        9000:31090/TCP   1m

$ kubectl get svc kubia-nodeport
NAME               TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
kubia-loadbalance   LoadBalancer   10.99.194.15   130.211.53.173        80:32143/TCP   1m

注意的是在

nodeport 型別和 LoadBalance 型別的服務在 external-ip 不一樣的是,LB是有外部ip暴露的。通過訪問 LB 的外部ip就可以訪問到叢集內部服務了。

建立 Igress 服務

為什麼需要LB的服務,其實最重要的是有獨立公有 ip 地址,當客戶端向 Ingress 傳送 HTTP 請求時,Ingress 會根據請求的主機名和路徑決定請求轉發到的服務。

Ingress 通過在網路棧 http 的應用層操作,可以提供一些服務不能實現的功能,比如 cookie 的會話親和性等功能。

建立 Ingress 資源

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
    name: kubia
spec:
    rules:
    - host: kubia.example.com    // ingress 將域名對映到你的服務上
      http:
        path: /
        backend:
            serviceName: kubia-nodeport         // 將請求傳送到 kubia-nodeport 服務的 80 埠
            servicePort: 80    

要讓 LB 正常工作,也就是通過 kubia.example.com 訪問到對應的服務,必須確保在 k8s 叢集內域名解析為 Ingress 的控制器,所以這需要基礎設施的支援。

在 k8s 檢視 ingress 資源

NAME    HOST                   ADDRESS         PORTS   AGE
kubia   kubia-example.com   192.168.99.100  80      75d

所以在叢集中需要確保這樣的解析,需要路由器支援配置

192.168.99.100    kubia.example.com

如圖,首先客戶端會對 kubia.example.com 執行 DNS 查詢,DNS 伺服器或者本地作業系統返回了 Ingress 控制器的 IP。客戶端然後向 Ingress 控制器傳送 HTTP 請求,並在 host 頭中指定 kubia.example.com。控制器從該頭部確定客戶端應該訪問哪個服務,通過與該服務關聯的 endpoint 物件檢視 podip,並將請求傳送到某個 pod。

最後再說明一下將不同服務對映到相同主機的不同路徑:

- host: kubia.example.com
  http:
     paths:
     - path: /kubia
       backend:
            serviceName: kubia
            servicePort: 80
     - path: /foo
       backend:
            serviceName: bar
            servicePort: 80