漫畫:小黃人學 Kubernetes Service

------3小時後------

Service 是kubernetes中一個很重要的,也是很有用的概念,可以通過Service來將pod進行分組,並提供外網的訪問endpoint。在這個過程中還有比如kube-proxy提供了對Service的訪問。

但pod是一個短暫存在的東西,很可能突然掛了然後重啟,這時候ip地址就會改變,所以pod的ip地址並不是靜態的。比如說:

用戶在這張圖裡面通過ip地址訪問到了4個pod,突然其中有一個pod掛了,然後controller又起了一個pod:

這時候用戶就訪問不到了,因為用戶不知道新的ip地址是多少。

Kubernetes為了解決這個問題,提供了一個高層的抽象,叫做Service。Service從邏輯上把pod進行分組,並且設置訪問的策略。一般我們是通過label和selector來達到分組的目的的。

通過selector(app=frontend和app=db),我們就可以把這些pod分為兩個邏輯組了。

這個時候,我們再給這兩個邏輯組加上一個名稱,比如frontend-svc和db-svc,就是Service了(如下圖):

kind: ServicenapiVersion: v1nmetadata:n name: frontend-svcnspec:n selector:n app: frontendn ports:n - protocol: TCPn port: 80n targetPort: 5000 n

在這個對象模型中,我們創建了一個叫做frontend-svc的Service,這個Service選擇了所有的app=frontend的pod。在默認情況下,每個Service都會有一個cluster內部可以訪問到的ip地址,也被稱為ClusterIP:

當轉發請求的時候,我們可以選擇pod上的目標埠,比如在我們的例子裡面,frontend-svc通過80埠來接受用戶的請求,然後轉發到pod的5000埠。如果目標埠沒有被顯式聲明,那麼會默認轉發到Service接受請求的埠(和service埠一樣)。

一個pod、ip地址和目標埠的元組代表了一個Service的endpoint,比如在這個例子裡面,frontend-svc有3個endpoints,分別是10.0.1.3:5000, 10.0.1.4:5000和10.0.1.5:5000。

所有的worker node都有一個後台任務,叫做kube-proxy。這個kube-proxy會檢測API Server上對於Service和endpoint的新增或者移除。對於每個新的Service,在每個node上,kube-proxy都會設置相應的iptables的規則來記錄應該轉發的地址。當一個service被刪除的時候,kube-proxy會在所有的pod上移除這些iptables的規則。

我現在已經知道,Service是和kubernetes進行溝通的主要方式,那麼我們就需要有一個辦法來在運行的時候能夠對已有的服務進行發現。請問Kubernetes是如何實現?

方法一: 每個pod在worker node上啟動的時候,kubelet都會通過環境變數把所有目前可用的Service的信息傳進去。舉個例子,我們有一個叫做redis-master的Service,這個service expose了6379的埠,並且ClusterIP是172.17.0.6,那麼在一個新創建的pod上,我們可以看到以下環境變數:

REDIS_MASTER_SERVICE_HOST=172.17.0.6nREDIS_MASTER_SERVICE_PORT=6379nREDIS_MASTER_PORT=tcp://172.17.0.6:6379nREDIS_MASTER_PORT_6379_TCP=tcp://172.17.0.6:6379nREDIS_MASTER_PORT_6379_TCP_PROTO=tcpnREDIS_MASTER_PORT_6379_TCP_PORT=6379nREDIS_MASTER_PORT_6379_TCP_ADDR=172.17.0.6n

如果使用這個解決方案,我們必須非常小心啟動服務的順序,因為pod不會獲得自己啟動之後的service的env。

方法二: Kubernetes有一些dns的addon,這些addon會自動為所有Service創建一個類似my-svc.my-namespace.svc.cluster.local的dns解析,並且在同一個Namespace裡面的Service 可以直接用Service name進行訪問。這是最為推薦的方法。

  • 是否只能在cluster內部訪問
  • 是否同時可以被cluster內部和外部訪問
  • 是否是映射到一個集群外的entity上

可訪問的範圍由Service 的類型決定,Service 的類型可以在創建Service 的時候聲明。

ClusterIP 和 NodePort

ClusterIP是默認的Service type,一個Service 通過ClusterIP來獲取自己的Virtual IP,這個IP是用來和別的service通信的,只能在集群內部被訪問。

NodePort的Service type除了會創建一個ClusterIP之外,還會把所有worker node上的一個30000-32767之間的埠映射到這個Service ,比如假設32233埠映射到了frontend-svc,那麼不管我們連接到哪個worker node,我們都會被轉發到Service 分配的ClusterIP——172.17.0.4。

默認情況下,當expose到有一個nodeport的時候,kubernetes master會自動隨機選擇一個30000-32767之間的port,當然,我們自己也可以手動指定這個port。

NodePort的這個Service type在我們想要讓外網訪問我們服務的時候非常有用,用戶通過訪問node上指定的port就可以訪問到這個Service 。管理員可以在Kubernetes集群外再搭一個反向代理就可以更方便地進行訪問了。

  • NodePort和ClusterIP會被自動創建,外部的load balancer會自動路由上去
  • Service 會在一個靜態的埠上被暴露
  • 通過底層的cloud provider提供的load balancer來暴露到外網

LoadBalancer這個Service type只有在底層的基礎架構支持了自動創建load balancer的時候kubernetes才支持,比如Google Cloud Platform和aws。

ExternalName

ExternalName是一個特定的Service type,這種Service type沒有任何的selector也沒有任何聲明的endpoint。當在集群中訪問到這個Service 的時候,會返回一個外部服務的CNAME。

這個service一般是用來讓一個外部的服務在集群內部可以訪問到的,比如我們有一個外部服務叫做http://my-database.example.com,那麼我們可以通過設置ExternalName類型的Service,讓內部的其它Service 通過my-database之類的名字訪問到這個服務。

ExternalIP

如果一個Service 可以路由到一個或者多個worker node上,那麼它可以被映射到一個ExternalIP地址。通過這個ExternalIP進入到集群的流量會被路由到其中一個endpoint上。

需要注意的是,ExternalIP並不是由k8s自動管理的,是由管理員手動設置路由到其中的一個node上的,ExternalIP可以和任意Service type來一起指定。

----1小時後----

本文作者:吳迪 個人博客, 插畫來源 K8S中文社區 / ID:k8schina ,微信公眾號;

推薦閱讀:

梁勝關於容器的年終總結,沒再提Docker
使用Heapster和Splunk監控Kubernetes運行性能
docker 編排工具 2017最佳選擇是 swarm/kubernetes/Mesos ?
有人在嘗試使用Kubernetes嗎?
現在做雲計算的出路到底在哪?

TAG:Kubernetes | Docker | 容器 |