ASP.NET Core on K8S深入學習(4)你必須知道的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在每台機器上綁定一個端口,這樣就可以通過<NodeIP>: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創建一個外部的負載均衡器,並將請求轉發到<NodeIP>: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-node1:

  

   訪問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的理解

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM