Kubernetes(k8s)中文文件 名詞解釋 Services_Kubernetes中文社群
Overview(概述)
Kubernetes Pod是平凡的,它門會被建立,也會死掉(生老病死),並且他們是不可復活的。 ReplicationControllers動態的建立和銷燬Pods(比如規模擴大或者縮小,或者執行動態更新)。每個pod都由自己的ip,這些IP也隨著時間的變化也不能持續依賴。這樣就引發了一個問題:如果一些Pods(讓我們叫它作後臺,後端)提供了一些功能供其它的Pod使用(讓我們叫作前臺),在kubernete叢集中是如何實現讓這些前臺能夠持續的追蹤到這些後臺的?
答案是:Service
Kubernete Service 是一個定義了一組Pod的策略的抽象,我們也有時候叫做巨集觀服務。這些被服務標記的Pod都是(一般)通過label Selector決定的(下面我們會講到我們為什麼需要一個沒有label selector的服務)
舉個例子,我們假設後臺是一個圖形處理的後臺,並且由3個副本。這些副本是可以相互替代的,並且前臺並需要關心使用的哪一個後臺Pod,當這個承載前臺請求的pod發生變化時,前臺並不需要直到這些變化,或者追蹤後臺的這些副本,服務是這些去耦
對於Kubernete原生的應用,Kubernete提供了一個簡單的Endpoints API,這個Endpoints api的作用就是當一個服務中的pod發生變化時,Endpoints API隨之變化,對於哪些不是原生的程式,Kubernetes提供了一個基於虛擬IP的網橋的服務,這個服務會將請求轉發到對應的後臺pod
Defining a service(定義一個服務)
一個Kubernete服務是一個最小的物件,類似pod,和其它的終端物件一樣,我們可以朝paiserver傳送請求來建立一個新的例項,比如,假設你擁有一些Pod,每個pod都開放了9376埠,並且均帶有一個標籤app=MyApp
{
“kind”: “Service”,
“apiVersion”: “v1”,
“metadata”: {
“name”: “my-service”
},
“spec”: {
“selector”: {
“app”: “MyApp”
},
“ports”: [
{
“protocol”: “TCP”,
“port”: 80,
“targetPort”: 9376
}
]
}
}
這段程式碼會建立一個新的服務物件,名稱為:my-service,並且會連線目標埠9376,並且帶有label app=MyApp的pod,這個服務會被分配一個ip地址,這個ip是給服務代理使用的(下面我們會看到),服務的選擇器會持續的評估,並且結果會被髮送到一個Endpoints 物件,這個Endpoints的物件的名字也叫“my-service”.
服務可以將一個“入埠”轉發到任何“目標埠”,預設情況下targetPort的值會和port的值相同,更有趣的是,targetPort可以是字串,可以指定到一個name,這個name是pod的一個埠。並且實際指派給這個name的埠可以是不同在不同的後臺pod中,這樣讓我們能更加靈活的部署我們的服務,比如;我們可以在下一個更新版本中修改後臺pod暴露的埠而不會影響客戶的使用(更新過程不會打斷)
- 服務支援tcp和UDP,但是預設的是TCP
- Services without selectors(沒有選擇器的服務)
服務總體上抽象了對Pod的訪問,但是服務也抽象了其它的內容,比如:
1:比如你希望有一個額外的資料庫雲在生產環境中,但是在測試的時候,我們希望使用自己的資料庫
2:我們希望將服務指向其它的服務或者其它名稱空間或者其它的雲平臺上的服務
3:我們正在向kubernete遷移,並且我們後臺並沒有在Kubernete中
如上的情況下,我們可以定義一個服務沒有選擇器
{
“kind”: “Service”,
“apiVersion”: “v1″,
“metadata”: {
“name”: “my-service”
},
“spec”: {
“ports”: [
{
“protocol”: “TCP”,
“port”: 80,
“targetPort”: 9376
}
]
}
}
因為沒有選擇器,所以相應的Endpoints物件就不會被建立,但是我們手動把我們的服務和Endpoints對應起來
{
“kind”: “Endpoints”,
“apiVersion”: “v1″,
“metadata”: {
“name”: “my-service”
},
“subsets”: [
{
“addresses”: [
{ “IP”: “1.2.3.4” }
],
“ports”: [
{ “port”: 80 }
]
}
]
}
這樣的話,這個服務雖然沒有selector,但是卻可以正常工作,所有的請求都會被轉發到1.2.3.4:80
Virtual IPs and service proxies(虛擬IP和服務代理)
每一個節點上都運行了一個kube-proxy,這個應用監控著Kubermaster增加和刪除服務,對於每一個服務,kube-proxy會隨機開啟一個本機埠,任何發向這個埠的請求都會被轉發到一個後臺的Pod當中,而如何選擇是哪一個後臺的pod的是基於SessionAffinity進行的分配。kube-proxy會增加iptables rules來實現捕捉這個服務的Ip和埠來並重定向到前面提到的埠。
最終的結果就是所有的對於這個服務的請求都會被轉發到後臺的Pod中,這一過程使用者根本察覺不到
預設的,後臺的選擇是隨機的,基於使用者session機制的策略可以通過修改service.spec.sessionAffinity 的值從NONE到ClientIP
Multi-Port Services(多埠服務)
可能很多服務需要開發不止一個埠,為了滿足這樣的情況,Kubernetes允許在定義時候指定多個埠,當我們使用多個埠的時候,我們需要指定所有埠的名稱,這樣endpoints才能清楚,例如
{
“kind”: “Service”,
“apiVersion”: “v1”,
“metadata”: {
“name”: “my-service”
},
“spec”: {
“selector”: {
“app”: “MyApp”
},
“ports”: [
{
“name”: “http”,
“protocol”: “TCP”,
“port”: 80,
“targetPort”: 9376
},
{
“name”: “https”,
“protocol”: “TCP”,
“port”: 443,
“targetPort”: 9377
}
]
}
}
選擇自己的IP地址
我們可以在建立服務的時候指定IP地址,將spec.clusterIP的值設定為我們想要的IP地址即可。例如,我們已經有一個DNS域我們希望用來替換,或者遺留系統只能對指定IP提供服務,並且這些都非常難修改,使用者選擇的IP地址必須是一個有效的IP地址,並且要在API server分配的IP範圍內,如果這個IP地址是不可用的,apiserver會返回422http錯誤程式碼來告知是IP地址不可用
為什麼不使用迴圈的DNS
一個問題持續的被提出來,這個問題就是我們為什麼不使用標準的迴圈DNS而使用虛擬IP,我們主要有如下幾個原因
1:DNS不遵循TTL查詢和快取name查詢的問題由來已久(這個還真不知道,就是DNS更新的問題估計)
2:許多的應用的DNS查詢查詢一次後就快取起來
3:即使如上亮點被解決了,但是不停的進行DNS進行查詢,大量的請求也是很難被管理的
我們希望阻止使用者使用這些可能會“傷害”他們的事情,但是如果足夠多的人要求這麼作,那麼我們將對此提供支援,來作為一個可選項.
Discovering services(服務的發現)
Kubernetes 支援兩種方式的來發現服務 ,環境變數和 DNS
環境變數
當一個Pod在一個node上執行時,kubelet 會針對執行的服務增加一系列的環境變數,它支援Docker links compatible 和普通環境變數
舉例子來說:
redis-master服務暴露了 TCP 6379埠,並且被分配了10.0.0.11 IP地址
那麼我們就會有如下的環境變數
REDIS_MASTER_SERVICE_HOST=10.0.0.11
REDIS_MASTER_SERVICE_PORT=6379
REDIS_MASTER_PORT=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP_PROTO=tcp
REDIS_MASTER_PORT_6379_TCP_PORT=6379
REDIS_MASTER_PORT_6379_TCP_ADDR=10.0.0.11
這樣的話,對系統有一個要求:所有的想要被POD訪問的服務,必須在POD建立之前建立,否則這個環境變數不會被填充,使用DNS則沒有這個問題
DNS
一個可選擇的雲平臺外掛就是DNS,DNS 伺服器監控著API SERVER ,當有服務被建立的時候,DNS 伺服器會為之建立相應的記錄,如果DNS這個服務被添加了,那麼Pod應該是可以自動解析服務的。
舉個例子來說:如果我們在my-ns名稱空間下有一個服務叫做“my-service”,這個時候DNS就會建立一個my-service.my-ns的記錄,所有my-ns名稱空間下的Pod,都可以通過域名my-service查詢找到對應的ip地址,不同名稱空間下的Pod在查詢是必須使用my-sesrvice.my-ns才可以。
Kubernete 同樣支援埠的解析,如果my-service有一個提供http的TCP主持的埠,那麼我們可以通過查詢“_http._tcp.my-service.my-ns”來查詢這個埠
Headless services
有時候我們可能不需要一個固定的IP和分發,這個時候我們只需要將spec.clusterIP的值設定為none就可以了
對於這樣的服務來說,叢集IP沒有分配,這個時候當你查詢服務的名稱的時候,DNS會返回多個A記錄,這些記錄都是指向後端Pod的。Kube 代理不會處理這個服務,在服務的前端也沒有負載均衡器。但是endpoints controller還是會建立Endpoints
(好吧,這個好處貌似我還理解好)
This option allows developers to reduce coupling to the Kubernetes system, if they desire, but leaves them freedom to do discovery in their own way. Applications can still use a self-registration pattern and adapters for other discovery systems could easily be built upon this API.
External services(外部服務)
對於我們的應用程式來說,我們可能有一部分是放在Kubernete外部的(比如我們有單獨的物理機來承擔資料庫的角色),Kubernetes支援兩種方式:NodePorts,LoadBalancers
每一個服務都會有一個欄位定義了該服務如何被呼叫(發現),這個欄位的值可以為:
- ClusterIP:使用一個叢集固定IP,這個是預設選項
- NodePort:使用一個叢集固定IP,但是額外在每個POD上均暴露這個服務,埠
- LoadBalancer:使用叢集固定IP,和NODEPord,額外還會申請申請一個負載均衡器來轉發到服務(load balancer )
注意:NodePort 支援TCP和UDN,但是LoadBalancers在1.0版本只支援TCP
Type NodePort
如果你選擇了“NodePort”,那麼 Kubernetes master 會分配一個區域範圍內,(預設是30000-32767),並且,每一個node,都會代理(proxy)這個埠到你的服務中,我們可以在spec.ports[*].nodePort 找到具體的值
如果我們向指定一個埠,我們可以直接寫在nodePort上,系統就會給你指派指定埠,但是這個值必須是指定範圍內的。
這樣的話就能夠讓開發者搭配自己的負載均衡器,去撘建一個kubernete不是完全支援的系統,又或者是直接暴露一個node的ip地址
Type LoadBalancer
在支援額外的負載均衡器的的平臺上,將值設定為LoadBalancer會提供一個負載均衡器給你的服務,負載均衡器的建立其實是非同步的。下面的例子
{
“kind”: “Service”,
“apiVersion”: “v1″,
“metadata”: {
“name”: “my-service”
},
“spec”: {
“selector”: {
“app”: “MyApp”
},
“ports”: [
{
“protocol”: “TCP”,
“port”: 80,
“targetPort”: 9376,
“nodePort”: 30061
}
],
“clusterIP”: “10.0.171.239”,
“type”: “LoadBalancer”
},
“status”: {
“loadBalancer”: {
“ingress”: [
{
“ip”: “146.148.47.155”
}
]
}
}
}
所有服務的請求均會直接到到Pod,具體是如何工作的是由平臺決定的
缺點
我們希望使用IPTABLES和使用者名稱空間來代理虛擬IP能在中小型規模的平臺上正常執行,但是可能出現問題在比較大的平臺上當應對成千上萬的服務的時候。
這個時候,使用kube-proxy來封裝服務的請求,這使得這些變成可能
LoadBalancers 只支援TCP,不支援UDP
Type 的值是設定好的,不同的值代表不同的功能,並不是所有的平臺都需要的,但是是所有API需要的
Future work
在將來,我們預想proxy的策略能夠更加細緻,不再是單純的轉發,比如master-elected or sharded,我們預想將來服務會擁有真正的負載均衡器,到時候虛擬IP直接轉發到負載均衡器
將來有傾向與將所的工作均通過iptables來進行,從而小從使用者名稱空間代理,這樣的話會有更好的效能和消除一些原地值IP的問題,儘管這樣的會減少一些靈活性.
更多討論:QQ交流群 513817976 入群暗號: kubernetes.org.cn
更多請參考:http://kubernetes.io/docs/user-guide/services/#future-work