快速掌握 Kubernetes 網絡基礎

《快速掌握 Kubernetes 網絡基礎》最先發布在 blog.hdls.me/15376903337…html

由於 Pod 的易變性(重建以後 IP 可能會改變),Service 承擔了服務入口的職能,是 k8s 網絡部分的核心概念,在 k8s 中,Service 主要擔任了四層負載均衡的職責。本文從負載均衡、外網訪問、DNS 服務的搭建及 Ingress 七層路由機制等方面,講解 k8s 的網絡相關原理。node

Service 詳解

Service 是主要用來實現應用程序對外提供服務的機制。nginx

如上圖所示,Service 是對 Pod 的一層抽象,主要經過 TCP/IP 機制及監聽 IP 和端口號來對外提供服務。與 Pod 不一樣的是,Service 一旦建立,系統會爲其分發一個 ClusterIP (也能夠本身指定),且在其生命週期內不會發生變化。web

Service 的建立

命令行快速建立

在建立好 RC 後,能夠經過命令行 kubectl expose 來快速建立一個對應的 Service 。好比現已有一個名爲 hdls 的 rc:數據庫

kubectl expose rc hdls
複製代碼

這種方式建立出來的 Service,其 ClusterIP 是系統自動爲其分配的,而 Service 的端口號是從 Pod 中的 containerPort 複製而來。後端

經過 YAML 建立

apiVersion: v1
kind: Service
metadata:
  name: hdls
spec:
  ports:
  - port: 8080   # Service 的虛擬端口
    targetPort: 8000  # 指定後端 Pod 的端口號
  selector:  # Label 選擇器
    app: hdls
複製代碼

定義好 YAML 文件後,經過命令 kubectl create -f <service.yml> 便可建立。Service 的定義須要指定如下幾個關鍵字段:api

  • ports
    • port: Service 的虛擬端口
    • targetPort: 後端 Pod 的端口號,若不填則默認與 Service 的端口一致
  • selector: Label 選擇器,指定後端 Pod 所擁有的 Label

負載分發策略

k8s 提供了兩種負載分發策略:bash

  • RoundRobin:輪詢方式。即輪詢將請求轉發到後端的各個 Pod 上。
  • SessionAffinity:基於客戶端 IP 地址進行會話保持模式。即相同 IP 的客戶端發起的請求被轉發到相同的 Pod 上。

在默認狀況下,k8s 採用輪詢模式進行路由選擇,但咱們也能夠經過將 service.spec.SessionAffinity 設置爲 「ClusterIP」 來啓用 SessionAffinity 模式。網絡

一些特殊狀況

Headless Serviceapp

在這種狀況下,k8s 經過 Headless Service 的概念來實現,即不給 Service 設置 ClusterIP (無入口 IP),僅經過 Label Selector 將後端的 Pod 列表返回給調用的客戶端。

apiVersion: v1
kind: Service
metadata:
  name: hdls
spec:
  ports:
  - port: 8080   
    targetPort: 8000 
  clusterIP: None
  selector: 
    app: hdls
複製代碼

該 Service 沒有虛擬的 ClusterIP ,對其訪問能夠得到全部具備 app=hdls 的 Pod 列表,客戶端須要實現本身的負責均衡策略,再肯定具體訪問哪個 Pod。

無 LabelSelector Service

通常來講,應用系統須要將外部數據庫做爲後端服務進行鏈接,或另外一個集羣或 namespace 中的服務做爲後端服務。這些狀況,能夠經過創建一個無 Label Selector 的 Service 來實現:

apiVersion: v1
kind: Service
metadata:
  name: hdls
spec:
  ports:
  - port: 8080   
    targetPort: 8000 
複製代碼

該 Service 沒有標籤選擇器,即沒法選擇後端 Pod。這時系統不會自動建立 Endpoint,須要手動建立一個與該 Service 同名的 Endpoint,用於指向實際的後端訪問地址。

apiVersion: v1
kind: Endpoints
metadata:
  name: hdls  # 與 Service 同名
subsets:
  - addresss:
   - IP: 1.2.3.4   # 用戶指定的 IP 
   ports:
   - port: 8000
複製代碼

此時,如上面的 YAML 建立出來 Endpoint,訪問無 Label Selector 的 Service ,便可將請求路由到用戶指定的 Endpoint 上。

多端口的 Service

在 service.spec.ports 中定義多個 port 便可,包括指定 port 的名字和協議。

apiVersion: v1
kind: Service
metadata:
  name: hdls
spec:
  ports:
  - name: dns
    port: 8080   
    protocol: TCP
  - name: dns-tcp
    port: 8080 
    protocol: UDP
  selector: 
    app: hdls
複製代碼

外網訪問

Pod 和 Service 都是 k8s 集羣內部的虛擬概念,因此集羣外的客戶沒法訪問。但在某些特殊條件下,咱們須要外網能夠訪問 Pod 或 Service,這時咱們須要將 Pod 或 Service 的端口號映射到宿主機,這樣客戶就能夠經過物理機訪問容器應用。

外網訪問 Pod

將容器應用的端口號映射到物理機上。有兩種方式,以下。

設置容器級別的 hostPort

這種是將容器應用的端口號映射到物理機。設置以下:

apiVersion: v1
kind: Pod
metadata:
  name: hdls-pod
spec:
  containers:
  - name: hdls-container
   image: ***
   ports:
   - containerPort: 8000
     hostPort: 8000
複製代碼

設置 Pod 級別的 hostNetwork=true

這種是將該 Pod 中全部容器端口號都直接映射到物理機上。此時須要注意的是,在容器的 ports 定義部分,若不指定 hostPort,默認 hostPort=containerPort,若設置了 hostPort,則 hostPort 必須等於 containerPort。設置以下:

apiVersion: v1
kind: Pod
metadata:
  name: hdls-pod
spec:
  hostNetwork: true
  containers:
  - name: hdls-container
   image: ***
   ports:
   - containerPort: 8000
複製代碼

外網訪問 Service

也有兩種方式。

設置 nodePort 映射到物理機

首先須要設置 nodePort 映射到物理機,同時須要設置 Service 的類型爲 NodePort:

apiVersion: v1
kind: Service
metadata:
  name: hdls
spec:
  type: NodePort  # 指定類型爲 NodePort
  ports:
  - port: 8080 
    targetPort: 8000 
    nodePort: 8000   # 指定 nodePort
  selector: 
    app: hdls
複製代碼

設置 LoadBalancer 映射到雲服務商提供的 LoadBalancer 地址

這種用法僅用於在公有云服務提供商的雲平臺上設置 Service 的場景。須要將 service.status.loadBalancer.ingress.ip 設置爲雲服務商提供的負載均衡器的 IP。則對該 Service 的訪問請求將會經過 LoadBalancer 轉發到後端 Pod,且負載均衡的實現方式依賴於雲服務商提供的 LoadBalancer 的實現機制。

DNS 搭建

爲了可以實現經過服務名在集羣內部進行服務的相互訪問,須要建立一個虛擬的 DNS 服務來完成服務名到 ClusterIP 的解析。

k8s 提供的 DNS

k8s 提供的 DNS 服務名爲 skydns,由下面四個組件組成:

  • etcd: DNS 存儲;
  • kube2sky: 將 k8s Master 中的 Service 註冊到 etcd ;
  • skyDNS: DNS 域名解析服務;
  • healthz: 對 skyDNS 的健康檢查。

skyDNS 服務由一個 RC 和一個 Service 組成。在 RC 的配置文件中,須要定義 etcd / kube2sky / skydns / healthz 四個容器,以保證 DNS 服務正常工做。須要注意的是:

  1. kube2sky 容器須要訪問 k8s Master,因此須要在配置文件中爲其配置 Master 所在物理主機的 IP 地址和端口;
  2. 須要將 kube2sky 和 skydns 容器的啓動參數 --domain 設置爲 k8s 集羣中 Service 所屬域名。容器啓動後 kube2sky 會經過 API Server 監控集羣中全部 service 的定義,生成相應的記錄並保存到 etcd ;
  3. skydns 的啓動參數 -addr=<IP:Port> 表示本機 TCP 和 UDP 的 Port 端口提供服務。

在 DNS Service 的配置文件中,skyDNS 的 ClusterIP 須要咱們指定,每一個 Node 的 kubelet 都會使用這個 IP 地址,不會經過系統自動分配;另外,這個 IP 須要在 kube-apiserver 啓動參數 --service-cluster-ip-range 內。

在 skydns 容器建立以前,須要先修改每一個 Node 上 kubelet 的啓動參數:

  • --cluster_dns=<dns_cluster_ip> ,dns_cluster_ip 爲 DNS 服務的 ClusterIP ;
  • --cluster_domain=<dns_domain> , dns_domain 爲 DNS 服務中設置的域名。

DNS 工做原理

  1. 首先 kube2sky 容器應用經過調用 k8s Master 的 API 得到集羣中全部 Service 信息,並持續監控新 Service 的生成,寫入 etcd;
  2. 根據 kubelet 的啓動參數的設置,kubelet 會在每一個新建立的 Pod 中設置 DNS 域名解析配置文件 /etc/resolv.conf 中增長一條 nameserver 配置和 search 配置,經過 nameserver 訪問的實際上就是 skydns 在對應端口上提供的 DNS 解析服務;
  3. 最後,應用程序就能夠像訪問網站域名同樣,僅經過服務的名字就能訪問服務了。

Ingress

Service 工做在 TCP/IP 層,而 Ingress 將不一樣的 URL 訪問請求轉發到後端不一樣的 Service ,實現 HTTP 層的業務路由機制。而在 k8s 中,須要結合 Ingress 和 Ingress Controller ,才能造成完整的 HTTP 負載均衡。

Ingress Controller

Ingress Controller 用來實現爲全部後端 Service 提供一個統一的入口,須要實現基於不一樣 HTTP URL 向後轉發的負載分發規則。Ingress Controller 以 Pod 的形式運行,須要實現的邏輯:

  • 監聽 APIServer,獲取全部 Ingress 定義;
  • 基於 Ingress 的定義,生成 Nginx 所需的配置文件 /etc/nginx/nginx.conf
  • 執行 nginx -s reload ,從新加載 nginx.conf 配置文件的內容。

定義 Ingress

k8s 中有一種單獨的名爲 Ingress 的資源,在其配置文件中能夠設置到後端 Service 的轉發規則。好比,爲 hdls.me 定義一個 ingress.yml:

apiVersion: extensions/v1beta1
kind: Ingress
metadata: 
  name: hdls-ingress
spec:
  rules:
  - host: hdls.me
   http:
     paths:
     - path: /web
       backend:
         serviceName: hdls
         servicePort: 8000
複製代碼

最後採用 kubectl create -f ingress.yml 建立 Ingress。能夠登陸 nginx-ingress Pod 查看其自動生成的 nginx.conf 配置文件內容。

相關文章
相關標籤/搜索