天天看點

Service一、Service

文章目錄

  • 一、Service
    • 二、三種 IP
    • 三、定義 Service
    • 四、kube-proxy
    • 五、Service 類型
    • NodePort 類型
    • 六、ExternalName

一、Service

Pod 的生命是有限的,死亡之後是不會複活的。但是RC 和 Deployment 可以用來動态的建立和銷毀 Pod。盡管每個 Pod 都有自己的 IP 位址,但是如果 Pod 重新啟動了的話那麼他的 IP 很有可能也就變化了,這就會帶來一個問題:比如我們有一些後端的 Pod 的集合為叢集中的其他前端的 Pod 集合提供 API 服務,如果我們在前端的 Pod 中把所有的這些後端的 Pod 的位址都寫死,然後去某種方式去通路其中一個 Pod 的服務,這樣看上去是可以工作的,但是如果這個 Pod 挂掉了,然後重新啟動起來了,是不是 IP 位址有可能就變了,這個時候前端就極大可能通路不到後端的服務了。

遇到這種的問題怎麼解決呢,不一定是 IP 變化的問題,比如我們在部署一個 WEB 伺服器的時候,前端一般部署一個Nginx 作為服務的入口,然後Nginx 後面肯定就是挂載的這個服務的大量後端,很早以前我們可能就是去手動更改 Nginx 配置中的 upstream 選項, 來動态改變提供服務的數量,到後面出現了一些服務發現的工具,比如 Consul 、 Zookeeper 還有我們熟悉的 etcd 等工具,有了這些工具過後我們就可以隻需要把我們的服務注冊到這些服務發現中心去就可以,然後這些工具動态的去更新 Nginx 的配置就可以了,我們完全不用去手工操作。

Service一、Service

當我們的 Pod 被銷毀或者建立過後,我們可以把這個 Pod 的位址注冊到這個服務發現中心去就可以,但是這樣的話我們的前端的 Pod 結合就不能直接去連接配接背景的 Pod 集合了吧,應該連接配接到一個能夠做服務發現的中間件上面。

Kubernetes為了我們提供了 -Service ,Service是一種抽象的對象,它定義了一組 Pod 的邏輯集合和一個用于通路它們的政策,其實這個概念和微服務非常類似。一個 Service 下面包含的 Pod 集合一般是由 Label Selector 來決定的。

比如上面的例子,假如我們後端運作了3個副本,這些副本都是可以代替的,因為前端并不關心它們使用的是哪個後端服務,盡管由于各種原因後端的 Pod 集合會發送變化,但是前端缺不需要知道這些變化,也不需要自己用一個清單來記錄這些後端的服務,Service 的這種抽象就可以幫我們打到這種解耦的目的。

二、三種 IP

  • Node IP :Node節點的 IP 位址
  • Pod IP : Pod 的 IP位址
  • Cluster IP : Service 的 IP 位址

首先, Node IP 是 Kubernetes 叢集中節點的實體網卡 IP 位址(一般為内網)所有屬于這個網絡的伺服器之間都可以直接通信,是以 Kubernetes 叢集外要想通路 Kubernetes 叢集内部的某個節點或者服務,肯定得過 Node IP 進行通信(這個時候一般是通過外網 IP 了)

然後 Pod 是每個 Pod 的 IP 位址,它是 Docker Engine 根據 docker網橋的IP 位址段進行配置設定的(我們這裡使用的是 flannel 這種網絡插件保證所有節點的 Pod IP 不會沖突)

最後 cluster IP 是一個虛拟的 IP ,僅僅作用于 Kubernetes Service 這個對象,由 Kubernetes 自己來進行管理和配置設定位址,當然我們也無法ping 這個位址,他沒用一個真正的實體對象來響應,他隻能結合 Service Port 來組成一個可以通信的服務。

三、定義 Service

定義 Service 的方式和我們之前定義各種資源的方式類型一樣,例如:我們有一組Pod 服務他們對外暴露了 8080 端口 ,同時都被打上了 app=myapp 這樣的标簽,那麼我們可以像以下定義一個 Service 對象:

apiVersion: v1
kind: Service
metadata:
  name: myservice
spec:
  selector:
    app: myapp
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
    name: myapp-http
           

然後通過使用 kubectl create -f myservice.yaml 就可以建立一個名為 myservice 的Service 對象,它會将請求代理到使用 TCP 端口為 8080 ,具有标簽 app=myapp 的 Pod 上,這個 Service 會被系統配置設定一個我們上面所說的 Cluster IP 。該 Service 會持續監聽 selector 下面的 pod ,會把這些 pod 資訊 更新到一個名為 myservice 的 Endpoints 對象上去,這個對象就類似于我們上面說的 Pod 集合了。

需要注意的是, Service 能夠将一個接受端口映射到任意的 targetPort。預設情況下, targetPort 将會被設定為與 Port 字段相同的值。targetPort 可以是一個字元串,引用了 backend Pod 的一個端口的名稱,因指派給端口名稱的端口号,在每個 backend Pod 中可能并不相同,是以對于部署和設計 Service ,這種方式會提供更大的靈活性。

另外 Service 能夠支援 TCP 和UDP 協定,預設是 TCP 協定。

四、kube-proxy

在 Kubernetes 叢集中,每個 Node 會運作一個 kube-proxy 程序,負責為 Service 實作一種 VIP(虛拟IP,就是cluster IP)的代理形式,現在的 Kubernetes 中預設是使用的 iptables 這種模式來代理,這種模式, kube-proxy 會監視 Kubernetes master 對 Service 對象和 Endpoints 對象的添加和移除。對每個 Service ,它會添加上 iptables 規則,進而捕獲到該 Service 的 cluster IP (虛拟IP)和端口的請求,進而将請求重定向到 Service 的一組 backend 中的 某一個上面,對于每個 Endpoints 對象,它也會安裝 iptables 規則,這個規則會選擇一個 backend Pod 。

預設的政策是,随機選擇一個 backend ,我們也可以實作基于用戶端 IP 的會話親和性,可以将 service.spec.sessionAffinit 的值設定為 ClientIP (預設為None)。

另外需要了解的是如果是最開始選擇的 Pod 沒有響應,iptables 代理能夠自動的重試另一個 Pod ,是以它需要依賴 readiness probes。

Service一、Service

五、Service 類型

Service 預設是 Cluster IP 類型

我們可以使用的服務類型如下:

  • Cluster IP :通過叢集的内部 IP 暴露服務,選擇該值,服務隻能夠在叢集内部可以通路,這也是預設的 ServiceType。
  • NodePort:通過每個 Node 節點上的 IP 和靜态端口(NodePort)暴露服務,NodePort服務會路由到 ClusterIP服務,這個ClusterIP 服務會自動建立。通過請求,可以從叢集的外部通路一個 NodePort服務。
  • LoadBalancer:使用雲提供商的負載均衡器,可以向外暴力服務,外部的負載均衡器可以路由到 NodePort 服務和 ClusterIP 服務,這個需要結合具體的雲廠商進行操作。
  • ExternalName:通過傳回CNAME和它的值,可以将服務映射到 externalName 字段的内容(例如:foo.bar.example.com),沒有任何類型代理被建立,這隻有 Kubernetes 1.7或者更高的版本 kube-dns才支援。

NodePort 類型

如果設定 type 的值為 NodePort ,Kubernetes master 将從給定的配置範圍内(預設:30000-32767)配置設定端口,每個Node 将從該端口,(每個Node上的同一端口)代理到Service 。該端口将通過 Service 的 spec.ports[*].NodePort字段被指定,如果不指定的話會自動生成一個端口。

需要注意的是,Service 将能夠通過:spec.ports[].NodePort和 spec.clusterIP:spec.ports[].port而對外可見。

我們建立一個 NodePort 的服務來通路我們前面的 Nginx 服務:(儲存為service-demo.yaml)

apiVersion: v1
kind: Service
metadata:
  name: myservice
spec:
  selector:
    app: myapp
  type: NodePort
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
    name: myapp-http
           

建立該Service :

$ kubectl create -f service-demo.yaml
           

然後我們可以檢視 Service 對象資訊:

$ kubectl get svc
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP        27d
myservice    NodePort    10.104.57.198   <none>        80:32560/TCP   14h
           

我們可以看到 myservice 的type類型已經變成了 NodePort ,後面的 Ports 部分也多了一個32560的映射端口。

六、ExternalName

ExternalName 是 Service 的特例,它沒有 selector ,也沒有定義任何的端口和 Endpoints,對于運作在叢集外部的服務,它通過傳回該外部服務的别名這種方式來提供。

kind: Service
apiVersion: v1
metadata:
  name: my-service
  namespace: prod
spec:
  type: ExternalName
  externalName: my.database.example.com
           

當查詢主機 my-service.prod.svc.cluster.local 時,叢集的 DNS 服務将會傳回一個值為my.database.eaxmple.com的 CNAME記錄。通路這個服務的工作方式與其他的相同,唯一不同的是重定向發生在DNS層,而且不會進行代理或轉發,如果後續決定要将資料庫遷移到Kubernetes 叢集中,可以啟動對應的 Pod,增加合适的 Selector 或 Endpoint ,修改 Service 的type,完全不需要修改調用的代碼,這樣就解耦了。

繼續閱讀