Kubernetes Service學習筆記和實踐練習

今天這篇文章裏咱們來說一下Kubernetes裏的Service對象。其實前面的文章《Kubernetes初體驗--部署運行Go項目》裏咱們已經與Service有過一次短暫接觸了,在那篇文章裏我說用Deployment對象部署完應用後還須要向外界暴露入口才能經過HTTP訪問到Kubernetes集羣裏的應用Pod,當時使用的是這樣一條命令,其實就是建立的Service對象:node

kubectl expose deployment my-go-app --type=NodePort ...
複製代碼

那麼在這篇文章裏咱們就來聊一下:程序員

  • 什麼是Service對象,在Kubernetes裏它是幹什麼用的;
  • Kubernetes裏怎麼發現Service
  • 如何建立和使用Service
  • nodePort,port,targetPort都是啥;

文章前面半部分理論知識多一點,稍顯枯燥,後半部分會用一個實踐練習給以前用Deployment部署好的應用Pod們加上Service,讓外部請求能訪問到Kubernetes集羣裏的應用,併爲Pod提供負載均衡。web

Kubernetes Service

和以前文章裏介紹的PodReplicaSetDeployment同樣,Service也是Kubernetes裏的一個API對象,而 Kubernetes 之因此須要 Service,一方面是由於Pod 的 IP 不是固定的,另外一方面則是由於一組Pod 實例須要Service提供複雜均衡功能。因此Service是在邏輯抽象層上定義了一組Pod,爲他們提供一個統一的固定IP和訪問這組Pod的負載均衡策略shell

下面是Service對象的經常使用屬性設置:後端

  • 使用label selector,在集羣中查找目標Pod;
  • ClusterIP設置Service的集羣內IP讓kube-proxy使用;
  • 經過prot和targetPort將訪問端口與目標端口建議映射(不指定targetPort時默認值和port設置的值同樣);
  • Service支持多個端口映射
  • Service支持HTTP(默認),TCP和UDP協議;

下面是一個典型的Service定義:api

apiVersion: v1
kind: Service
metadata:
  name: hostnames
spec:
  selector:
    app: hostnames
  ports:
  - name: default
    protocol: TCP
    port: 80
    targetPort: 9376
複製代碼

都有哪些類型的Service

Kubernetes中有四種Service類型:微信

  • ClusterIP。這是默認的Service類型,會將Service對象經過一個內部IP暴露給集羣內部,這種類型的Service只可以在集羣內部使用**:**訪問。
  • NodePort。會在每一個宿主機節點的一個指定的固定端口上暴露Service,與此同時還會自動建立一個ClusterIP類型的Service,NodePort類型的Service會將集羣外部的請求路由給ClusterIP類型的Service。你可使用**:**訪問NodePort類型的Service,NodePort的端口範圍爲30000-32767。
  • LoadBalancer。適用於公有云上的Kubernetes服務,使用公有云服務的CloudProvider建立LoadBalancer類型的Service,同時會自動建立NodePortClusterIP類型的ServiceLoadBalancer會把請求路由到NodePortClusterIP類型的Service上。
  • ExternalName。ExternalName 類型的 Service,是在 kube-dns 裏添加了一條 CNAME 記錄。這個CNAME記錄是在Service的spec.externalName裏指定的,

以上四種類型除了ExternalNameKuberneteskube-proxy組件都會爲Service提供VIP(虛擬IP),kube-proxy支持兩種模式:iptablesipvs。涉及到很多知識,感興趣的能夠去極客時間上看這篇文章:Service, DNS與服務發現markdown

上面的第三和第四種類型的Service在本地試驗不了,因此後面的例子咱們主要經過NodePort類型的Service學習它的基本用法。網絡

怎麼發現Service

Kubernetes裏的內部組件kube-dns會監控Kubernetes API,當有新的Service對象被建立出來後,kube-dns會爲Service對象添加DNS A記錄(從域名解析 IP 的記錄)app

對於 ClusterIP 模式的 Service 來講,它的 A 記錄的格式是:

..svc.cluster.local,當你訪問這條 A 記錄的時候,它解析到的就是該 Service 的 VIP 地址。

對於指定了 clusterIP=None 的 Headless Service來講,它的A記錄的格式跟上面同樣,可是訪問記錄後返回的是Pod的IP地址集合。Pod 也會被分配對應的 DNS A 記錄,格式爲: ...svc.cluster.local

咱們會在後面的實踐練習裏經過nslookup印證DNS記錄是否符合這裏說的格式

建立和使用Service

跟其餘Kubernetes裏的API對象,Service也是經過YAML文件定義而後提交給Kubernetes後由ApiManager建立完成。一個典型的NodePort類型的Service的定義以下所示:

apiVersion: v1
kind: Service
metadata:
  name: app-service
spec:
  type: NodePort
  selector:
    app: go-app
  ports:
    - name: http
      protocol: TCP
      nodePort: 30080
      port: 80
      targetPort: 3000
複製代碼

這裏定義的Service對象會去管控咱們在以前的文章《K8s上的Go服務怎麼擴容、發版更新、回滾、平滑重啓?教你用Deployment全搞定!》裏用Deployment建立的Go應用的三個Pod副本。

➜ kubectl get pods -l app=go-app 
NAME                         READY   STATUS    RESTARTS   AGE
my-go-app-864496b67b-6hm7r   1/1     Running   1          16d
my-go-app-864496b67b-d87kl   1/1     Running   1          16d
my-go-app-864496b67b-qxrsr   1/1     Running   1          16d
➜ 
複製代碼

咱們用**kubectl apply -f service.yaml **命令把定義好的Service提交給Kubernetes

➜ kubectl apply -f service.yaml 
service/app-service created
複製代碼

Serviceselector選中的Pod,就稱爲ServiceEndpoints,可使用 kubectl get ep 命令看到它們,以下所示:

➜  kubectl get ep app-service
NAME          ENDPOINTS                                         AGE
app-service   172.17.0.6:3000,172.17.0.7:3000,172.17.0.8:3000   8m38s
複製代碼

須要注意的是,只有處於Running狀態,且 readinessProbe 檢查經過的Pod,纔會出如今ServiceEndpoints 列表裏。當某一個Pod出現問題時,Kubernetes 會自動把它從 Service 裏摘除掉。

使用 kubectl get svc能夠查看到剛纔看到的Service的信息和狀態。

➜ kubectl get svc
NAME          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
app-service   NodePort    10.108.26.155   <none>        80:30080/TCP   116m
kubernetes    ClusterIP   10.96.0.1       <none>        443/TCP        89d

複製代碼

nodePort 、port、targetPort都是啥

上面咱們建立了一個NodePort類型的Service,在下面的端口映射spec.ports配置裏,每一個端口映射裏出現了三種port:nodePort、port、targetPort。那這三種port都表明的什麼意思呢?

  • port:指定在集羣內部暴露Service 所使用的端口,集羣內部使用**:**訪問ServiceEndPointsService選中的Pod)。
  • nodePort:指定向集羣外部暴露Service 所使用的端口,從集羣外部使用**:**訪問ServiceEndPoints。若是不指nodePort,會默認使用port字段指定的端口。若是你不顯式地聲明 nodePort 字段,會隨機分配可用端口來設置代理。這個端口的範圍默認是 30000-32767。
  • targetPorttargetPort是後面的Pod監聽的端口,容器裏的應用也應該監聽這個端口,Service會把請求發送到這個端口。

因此結合剛纔咱們建立的app-service這個Service的信息,在集羣內部使用10.108.26.155:80 訪問Pod裏的應用。由於咱們試驗使用的minikube是個單節點的集羣,NodeIP能夠經過 minikube ip命令得到。

➜ minikube ip

192.168.64.4
複製代碼

因此從集羣外部,經過192.168.64.4:30080訪問Pod裏的應用。

➜ curl 192.168.64.4:30080
Hello World
Hostname: my-go-app-75d6d768ff-mlqnh%                                                                                                                    ➜ curl 192.168.64.4:30080
Hello World
Hostname: my-go-app-75d6d768ff-4x8p8%                                                                                                                    ➜  curl 192.168.64.4:30080
Hello World
Hostname: my-go-app-75d6d768ff-vt7dx%                                                                                                                    
複製代碼

經過屢次訪問,咱們能夠看到請求會經過Service發給不一樣的應用PodPod裏的應用就是在原來的文章裏一直使用的例子的基礎上加了一行獲取系統Hostname的代碼:

...

func index(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Hello World")
	hostname, _ := os.Hostname()
	fmt.Fprintf(w, "Hostname: %s", hostname)
}

...
複製代碼

最後咱們進到Pod裏看一下Service建立後kube-dns組件在集羣裏爲app-service這個Service對象建立的DNS A記錄,由於Service定義裏指定的名字是app-service,命名空間的話由於沒有指定就是默認的default命名空間,因此咱們使用nslookup app-service.default.svc.cluster.local 查看一下這條DNS記錄,進入到其中一個Pod裏,執行上述查詢的結果以下:

/app # nslookup app-service.default.svc.cluster.local
Server:         10.96.0.10
Address:        10.96.0.10:53

Name:   app-service.default.svc.cluster.local
Address: 10.108.26.155
複製代碼

對於ServiceEndPoints 也是有DNS記錄的,由於不是Headless Service,因此須要用**nslookup .app-service.default.svc.cluster.local* 查詢DNS記錄。

/app # nslookup *.app-service.default.svc.cluster.local
Server:         10.96.0.10
Address:        10.96.0.10:53

Name:   *.app-service.default.svc.cluster.local
Address: 172.17.0.8
Name:   *.app-service.default.svc.cluster.local
Address: 172.17.0.6
Name:   *.app-service.default.svc.cluster.local
Address: 172.17.0.7
複製代碼

上面查詢出來三條DNS記錄,正好跟Service管控的Pod數量可以對上。

總結

今天的文章裏我結合實例講述了KubernetesService對象的基本使用方法和對象自己的一些原理,其實須要計算機網絡知識掌握的好才能從更深層次瞭解各類模式的Service的實現原理,這方面的內容推薦極客時間裏的專欄文章深刻剖析Kubernetes Service

到這裏若是你認真看了我寫的關於Kubernetes的這幾篇文章,再回看我以前的文章Kubernetes入門實踐--部署運行Go項目,就會以爲文章裏的例子很好理解了。Kubernetes的確是學習曲線比較陡峭,我也是在邊學邊練。但願個人這些入門文章能幫助到想學Kubernetes的後端程序員們,你們一塊兒進步一塊兒漲薪。

看到這裏了,若是喜歡個人文章能夠幫我點個贊,我會每週經過技術文章分享個人所學所見,感謝你的支持。微信搜索關注公衆號「網管叨bi叨」第一時間獲取個人文章推送。回覆「k8s」獲取文章裏所用實踐練習的源碼和yaml文件。

相關文章
相關標籤/搜索