天天看點

ASP.NET Core on K8S深入學習(4)你必須知道的Service一、認識Service二、Service的建立與運作三、DNS通路Service四、小結參考資料

本篇已加入《 .NET Core on K8S學習實踐系列文章索引

》,可以點選檢視更多容器化技術相關系列文章。

前面幾篇文章我們都是使用的ClusterIP供叢集内部通路,每個Pod都有一個自己的IP位址,那麼問題來了:當控制器使用新的Pod替代發生故障的Pod時又或者增加新的副本Pod時,新Pod會配置設定到新的IP位址,那麼想要對外提供服務時,用戶端如何找到并通路這個服務?沒關系,别摳腦殼了,本文介紹的Service就是解決方案。

一、認識Service

1.1 什麼是Service?

  Service是一個抽象概念,它定義了邏輯集合下通路Pod組的政策。通過使用Service,我們就可以不用關心這個服務下面的Pod的增加和減少、故障重新開機等,隻需通過Service就能夠通路到對應服務的容器,即通過Service來暴露Pod的資源。

  這樣說可能還是有點難懂,舉個例子,假設我們的一個服務Service A下面有3個Pod,我們都知道Pod的IP都不是持久化的,重新開機之後就會有變化。那麼Service B想要通路Service A的Pod,它隻需跟綁定了這3個Pod的Service A打交道就可以了,無須關心下面的3個Pod的IP和端口等資訊的變化。換句話說,就像一個Service Discovery服務發現的元件,你無須關心具體服務的URL,隻需知道它們在服務發現中注冊的Key就可以通過類似Consul、Eureka之類的服務發現元件中擷取它們的URL一樣,還是實作了負載均衡效果的URL。

1.2 Service的幾種類型

(1)ClusterIP

   ClusterIP 服務是 Kubernetes 的預設服務。它給你一個叢集内的服務,叢集内的其它應用都可以通路該服務,但是叢集外部無法通路它。

  是以,這種服務常被用于内部程式互相的通路,且不需要外部通路,那麼這個時候用ClusterIP就比較合适,如下面的yaml檔案所示:

apiVersion: v1
kind: Service
metadata:  
name: my-internal-service
selector:    
app: my-app
spec:
type: ClusterIP
ports:  
- name: http
port: 80
targetPort: 80
protocol: TCP           

  那麼,如果需要從外部通路呢(比如我們在開發模式下總得調試吧)?可以啟用K8S的代理模式:

$ kubectl proxy --port=8080           

  如此一來,便可以通過K8S的API來通路了,例如下面這個URL就可以通路在yaml中定義的這個my-internal-service了:

http://localhost:8080/api/v1/proxy/namespaces/default/services/my-internal-service:http/           
_PS:_ClusterIP是一個虛拟IP,由K8S節點上的iptables規則管理的。iptables會将通路Service的流量轉發到後端Pod,而且使用類似于輪詢的負載均衡政策轉發的。  

(2)NodePort

   除了隻在内部通路的服務,我們總有很多是需要暴露出來公開通路的服務吧。在ClusterIP基礎上為Service在每台機器上綁定一個端口,這樣就可以通過:NodePort來通路這些服務。例如,下面這個yaml中定義了服務為NodePort類型:

apiVersion: v1
kind: Service
metadata:  
name: my-nodeport-service
selector:    
app: my-app
spec:
type: NodePort
ports:  
- name: http
port: 80
targetPort: 80
nodePort: 30036
protocol: TCP           
_PS_:這種方式顧名思義需要一個額外的端口來進行暴露,且端口範圍隻能是 30000-32767,如果節點/VM 的 IP 位址發生變化,你需要能處理這種情況。

(3)LoadBalancer

   LoadBalancer 服務是暴露服務到 internet 的标準方式,它借助Cloud Provider建立一個外部的負載均衡器,并将請求轉發到:NodePort(向節點導流)。

  例如下面這個yaml中:

kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9376
  clusterIP: 10.0.171.239
  loadBalancerIP: 78.11.24.19
  type: LoadBalancer
status:
  loadBalancer:
    ingress:
    - ip: 146.148.47.155           
_PS_:每一個用 LoadBalancer 暴露的服務都會有它自己的 IP 位址,每個用到的 LoadBalancer 都需要付費,這将是比較昂貴的花費。  

二、Service的建立與運作

2.1 建立Deployment

  這裡仍然以我們的一個ASP.NET Core WebAPI項目為例,準備一個Deployment的YAML檔案:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: edc-webapi-deployment
  namespace: aspnetcore
spec:
  replicas: 2
  selector:
    matchLabels:
      name: edc-webapi
  template:
    metadata:
      labels:
        name: edc-webapi
    spec:
      containers:
      - name: edc-webapi-container
        image: edisonsaonian/k8s-demo
        ports:
        - containerPort: 80
        imagePullPolicy: IfNotPresent           

  這裡我們需要注意的就是給該Deployment标注selector的matchLabels以及template中的labels,這是一個Key/Value對,用于後續Service來比對要挑選哪些Pod作為Service的後端,即需要給哪些Pod提供服務發現以及負載均衡的效果。

  同樣,通過kubectl建立資源:

kubectl apply -f edc-api.yaml           

  然後,通過curl指令驗證一下:(這裡的兩個IP位址是ClusterIP,它們分别位于我的兩個K8S Node節點上)

curl 10.244.1.40/api/values
curl 10.244.2.31/api/values           

   可以看到,我們的ASP.NET Core WebAPI正常的傳回了JSON資料。

2.2 建立Service

  接下來我們就為上面的兩個Pod建立一個Service:

apiVersion: v1
kind: Service
metadata:
  name: edc-webapi-service
  namespace: aspnetcore
spec:
  ports:
    - port: 8080
      targetPort: 80
  selector:
    name: edc-webapi           

  這裡需要注意的幾個點:

  (1)port : 8080 => 指将Service的8080端口映射到Pod的對應端口上,這裡Pod的對應端口由 targetPort 指定。

  (2)selector => 指将具有 name: edc-webapi 這個label的Pod作為我們這個Service的後端,為這些Pod提供統一IP和端口。

  這裡我們來進行驗證一下:

kubectl get service -n aspnetcore
curl 10.1.59.71:8080/api/values           

   可以看到,預設情況下Service的類型時ClusterIP,隻能提供叢集内部的服務通路。如果想要為外部提供通路,那麼需要改為NodePort。

2.3 使用NodePort

  下面為Service增加NodePort通路方式:

apiVersion: v1
kind: Service
metadata:
  name: edc-webapi-service
  namespace: aspnetcore
spec:
  type: NodePort
  ports:
    - port: 8080
      targetPort: 80
  selector:
    name: edc-webapi           

  再次進行建立,會覆寫已有配置:

kubectl apply -f edc-api-service.yaml           

  再次進行驗證,會發現已經改為了NodePort方式:

   這裡的PORT已經變為了8080:32413,意味着它将Service中的8080端口映射到了Node節點的32413端口,我們可以通過通路Node節點的32413端口擷取ASP.NET Core WebAPI傳回的接口資料了。

  通路k8s-node1:

  通路k8s-node2:

2.4 指定特定端口

  剛剛的NodePort預設情況下是随機選擇一個端口(30000-32767範圍内),不過我們可以使用nodePort屬性指定一個特定端口:

apiVersion: v1
kind: Service
metadata:
  name: edc-webapi-service
  namespace: aspnetcore
spec:
  type: NodePort
  ports:
    - nodePort: 31000 
      port: 8080
      targetPort: 80
  selector:
    name: edc-webapi           

  這裡我們自己指定了一個外部通路端口:31000,通過kubectl覆寫之後,我們再次驗證一下:

   通路k8s-node2:

  最後,再次總結一下三個端口配置:

  (1)nodePort => Node節點上監聽的端口,也就是外部通路的Service端口

  (2)port => ClusterIP上監聽的端口,即内部通路的Service端口

  (3)targetPort => Pod上監聽的端口,即容器内部的端口

三、DNS通路Service

  Kubernetes預設安裝了一個dns元件coredns,它位于kube-system命名空間中:

   每當有新的Service被建立時,coredns會添加該Service的DNS記錄,于是在Cluster中的Pod便可以通過servicename.namespacename來通路Service了,進而可以做到一個服務發現的效果。

  這裡我們來驗證一下,通過臨時建立一個busybox Pod來通路edc-webapi-service.aspnetcore:8080:

kubectl run busybox --rm -ti --image=busybox /bin/sh           

   可以看到,coredns元件為剛剛建立的Service edc-webapi-service建立了DNS記錄,在Cluster中的Pod無須知道edc-webapi-service的IP位址隻需要知道ServiceName即可通路到該Service。

四、小結

  本文介紹了K8S中Service的基本概念及常用類型,然後通過一個具體的例子示範了如何建立Service和使用NodePort的方式對外提供通路,最後介紹了如何通過DNS的方式通路Service進而實作服務發現的效果。當然,筆者也是初學,很多東西沒有介紹到,也請大家多多參考其他資料更加深入了解。

參考資料

(1)CloudMan,《

每天5分鐘玩轉Kubernetes

(2)李振良,《

一天入門Kubernets教程

(3)馬哥(馬永亮),《

Kubernetes快速入門

(4)小黑老,《

K8S中Service的了解

繼續閱讀