Service
概念介紹
雖然每個Pod都會被分配一個單獨的IP地址,但這個IP地址會隨著Pod的銷毀而消失悲关。引出的一個問題是:如果有一組Pod組成一個應(yīng)用集群來提供服務(wù),那么該如何訪問它們呢娄柳?
Service
就是用來解決這個問題的,一個Service可以看作一組提供相同服務(wù)的Pod的對外接口艘绍,Service是通過LabelSelector選擇一組Pod作為后端服務(wù)提供者赤拒。
定義
一個 Service
在 Kubernetes 中是一個 REST 對象,和 Pod
類似。
例如挎挖,假定有一組 Pod
这敬,它們對外暴露了 3000 端口,同時還被打上 app=jaymz
標(biāo)簽蕉朵。
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: jaymz
ports:
- protocol: TCP
port: 80
targetPort: 3000
上述配置創(chuàng)建一個名稱為 “my-service” 的 Service
對象崔涂,它會將請求代理到使用 TCP 端口 3000,并且具有標(biāo)簽 "app=jaymz"
的 Pod
上始衅。 Kubernetes 為該服務(wù)分配一個 IP 地址(有時稱為 “集群IP” )冷蚂。
多端口 Service
對于某些服務(wù),需要公開多個端口汛闸。 Kubernetes允許在Service對象上配置多個端口定義蝙茶。 當(dāng)使用多個端口時,必須提供所有端口名稱诸老,以使它們無歧義隆夯。 例如:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 3000
- name: https
protocol: TCP
port: 443
targetPort: 9000
服務(wù)代理
在 Kubernetes 集群中,每個 Node 運行一個 kube-proxy
進(jìn)程。kube-proxy
負(fù)責(zé)為 Service
實現(xiàn)了一種 VIP(虛擬 IP)的形式睬关,而不是 ExternalName
的形式赠尾。
為什么不使用 DNS 輪詢?
時不時會有人問道宦芦,就是為什么 Kubernetes 依賴代理將入站流量轉(zhuǎn)發(fā)到后端。 那其他方法呢轴脐? 例如调卑,是否可以配置具有多個A值(或IPv6為AAAA)的DNS記錄,并依靠輪詢名稱解析大咱?
使用服務(wù)代理有以下幾個原因:
- DNS 實現(xiàn)的歷史由來已久恬涧,它不遵守記錄 TTL,并且在名稱查找結(jié)果到期后對其進(jìn)行緩存碴巾。
- 有些應(yīng)用程序僅執(zhí)行一次 DNS 查找溯捆,并無限期地緩存結(jié)果。
- 即使應(yīng)用和庫進(jìn)行了適當(dāng)?shù)闹匦陆馕鱿闷埃珼NS 記錄上的 TTL 值低或為零也可能會給 DNS 帶來高負(fù)載提揍,從而使管理變得困難。
版本兼容性
從Kubernetes v1.0開始煮仇,您已經(jīng)可以使用 用戶空間代理模式劳跃。 Kubernetes v1.1添加了 iptables 模式代理,在 Kubernetes v1.2 中浙垫,kube-proxy 的 iptables 模式成為默認(rèn)設(shè)置刨仑。 Kubernetes v1.8添加了 ipvs 代理模式郑诺。
虛擬IP
Service
一般會提供一個虛擬IP(Virtual IP),它在Service
創(chuàng)建之后就保持不變杉武,并且能夠被同一集群中的Pod資源所訪問辙诞。Service端口用于接收客戶端請求并將其轉(zhuǎn)發(fā)給后端的Pod應(yīng)用程序,這種代理機(jī)制稱為“端口代理”或四層代理轻抱,工作于TCP/IP協(xié)議棧的傳輸層飞涂。
API Server會持續(xù)監(jiān)聽(watch)Pod的變化(增加、刪除)祈搜,并實時更新Endpoint较店,Endpoint是一個由Pod的IP地址和端口組成的列表,Service會從Endpoint列表中選擇一條記錄進(jìn)行流量調(diào)度夭问。
服務(wù)代理 - userspace(用戶空間)泽西、iptables、ipvs
在眾多的Pod對象中缰趋,Service資源是如何選擇正確的Pod進(jìn)行流量調(diào)度的呢捧杉?
這就是我們要具體介紹的,Kubernetes提供的“端口代理”的幾種實現(xiàn)方式秘血。
簡單來說味抖,一個Service對象就是Node上的一些iptables或ipvs規(guī)則,用于將到達(dá)Service對象的流量調(diào)度到相應(yīng)的Pod上灰粮。Kubernetes集群中的每個Node上都會有產(chǎn)生一個Kube-proxy程序仔涩,它通過API-Server持續(xù)監(jiān)控各Service及其關(guān)聯(lián)的Pod對象,并將其變化實時反映至當(dāng)前節(jié)點的iptables或ipvs規(guī)則上粘舟。
kube-Proxy將請求代理至相應(yīng)端點的方式有三種:userspace(用戶空間)熔脂、iptables、ipvs
userspace代理模型
這里的userspace是指Linux操作系統(tǒng)的用戶空間柑肴。對于每個Service對象它會打開一個本地端口(運行于用戶空間的kube-proxy進(jìn)程負(fù)責(zé)監(jiān)聽此端口)霞揉,任何到達(dá)此代理端口的連接請求都會被代理至當(dāng)前Service資源后端的各Pod對象上,至于會挑選哪個Pod取決于Service資源的調(diào)度方式晰骑,默認(rèn)為輪詢(roundrobin)
這種代理模式請求流量到達(dá)內(nèi)核空間后經(jīng)由套接字送往用戶空間的kube-proxy适秩,而后由它送回內(nèi)核空間,并調(diào)度至后端pod硕舆。這種方式請求會在內(nèi)核和用戶空間之間來回轉(zhuǎn)發(fā)導(dǎo)致效率不高秽荞。
iptables代理模型
iptables代理模式和前一種代理模式是類似的,都是由kube-proxy來跟蹤監(jiān)聽API-server上的service和Endpoints的變更抚官。但是有一點不同的是iptables規(guī)則直接捕獲到達(dá)cluster IP和port的流量扬跋,并將其重定向至當(dāng)前Service的后端,對于每個Endpoints對象耗式,Service資源會為其創(chuàng)建iptables規(guī)則并關(guān)聯(lián)至挑選的后端pod資源胁住,默認(rèn)算法是隨機(jī)調(diào)度(random)趁猴。iptables代理模式在Kubernetes1.1版本引入刊咳,并在1.2版開始成為默認(rèn)類型彪见。
在創(chuàng)建service資源時,集群中每個節(jié)點上的kube-proxy都會接受到通知并將其定義為當(dāng)前節(jié)點上的iptables規(guī)則娱挨,用于轉(zhuǎn)發(fā)接收到的iptables余指,進(jìn)行調(diào)度和目標(biāo)地址轉(zhuǎn)換(DNAT)后再轉(zhuǎn)發(fā)至集群內(nèi)部的pod對象上。
默認(rèn)的策略是跷坝,kube-proxy 在 iptables 模式下隨機(jī)選擇一個 backend酵镜。
相對于用戶空間來講,iptables模型無須將流量在用戶空間和內(nèi)核空間來回切換柴钻,因更加高效和可靠淮韭。
ipvs代理模型
Kubernetes從1.9版本引入ipvs代理模型,且從1.11版本起成為默認(rèn)設(shè)置贴届。它和iptables模型很類似靠粪,唯一一點不同的是在其請求流量的調(diào)度功能由ipvs實現(xiàn),余下的功能仍由iptables完成毫蚓。
ipvs是建立在netfilter的鉤子函數(shù)上占键,但它使用hash表作為底層數(shù)據(jù)結(jié)構(gòu)并工作于內(nèi)核空間,因此流量轉(zhuǎn)發(fā)速度特別快元潘、規(guī)則同步性很好畔乙,而且它支持眾多調(diào)度算法,rr(輪詢)翩概、lc(最小連接數(shù))牲距、dh(目標(biāo)哈希)、sh(源哈希)钥庇、sed(最短期望延遲)牍鞠、nq(不排隊調(diào)度)。
服務(wù)發(fā)現(xiàn)
Kubernetes 支持2種基本的服務(wù)發(fā)現(xiàn)模式 —— 環(huán)境變量和 DNS上沐。
環(huán)境變量
當(dāng) Pod
運行在 Node
上皮服,kubelet 會為每個活躍的 Service
添加一組環(huán)境變量, {SVCNAME}_SERVICE_HOST
和 {SVCNAME}_SERVICE_PORT
變量参咙,這里 Service
的名稱需大寫龄广,橫線被轉(zhuǎn)換成下劃線。
舉例蕴侧,一個名稱為 "redis-master"
的 Service 暴露了 TCP 端口 6379择同,同時給它分配了 Cluster IP 地址 10.0.0.11,這個 Service 生成了如下環(huán)境變量:
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出現(xiàn) 之前 創(chuàng)建Service净宵, 否則敲才,這些Pod將不會設(shè)定其環(huán)境變量裹纳。 如果僅使用DNS查找Service的群集IP,則無需擔(dān)心環(huán)境變量問題紧武。
DNS
您可以使用附加組件為Kubernetes集群設(shè)置DNS服務(wù)剃氧。
支持群集的DNS服務(wù)器(例如CoreDNS)監(jiān)視 Kubernetes API 中的新Service
,并為每個Service
創(chuàng)建一組 DNS 記錄阻星。 如果在整個集群中都啟用了 DNS朋鞍,則所有 Pod 都應(yīng)該能夠通過其 DNS 名稱自動解析出Service
對應(yīng)的IP地址。
Headless Services
有時不需要或不想要負(fù)載均衡妥箕,以及單獨的 Service IP滥酥。 遇到這種情況,可以通過指定 Cluster IP(spec.clusterIP
)的值為 "None"
來創(chuàng)建 Headless
Service畦幢。
您可以使用 headless Service 與其他服務(wù)發(fā)現(xiàn)機(jī)制進(jìn)行接口坎吻,而不必與 Kubernetes 的實現(xiàn)捆綁在一起。
對這 headless Service
并不會分配 Cluster IP宇葱,kube-proxy 不會處理它們瘦真,而且平臺也不會為它們進(jìn)行負(fù)載均衡和路由。 DNS 如何實現(xiàn)自動配置贝搁,依賴于 Service
是否定義了 selector吗氏。
配置 Selector
對定義了 selector 的 Headless Service,Endpoint 控制器在 會創(chuàng)建 Endpoints
記錄雷逆,并且修改 DNS 配置返回 A 記錄(地址)弦讽,通過這個地址直接到達(dá) Service
的后端 Pod
上。
配置 Selector的Headless Services應(yīng)用場景
自主選擇權(quán)膀哲,有時候
client
想自己來決定使用哪個Endpoints
記錄往产,可以通過查詢DNS
來獲取Endpoints
的信息。Headless Services
還有一個用處(PS:也就是我們需要的那個特性)某宪。Headless Service
的對應(yīng)的每一個Endpoints
仿村,即每一個Pod
,都會有對應(yīng)的DNS
域名兴喂;這樣Pod
之間就可以互相訪問蔼囊。
不配置 Selector
對沒有定義 selector 的 Headless Service,Endpoint 控制器不會創(chuàng)建 Endpoints
記錄衣迷。 然而 DNS 系統(tǒng)會查找和配置畏鼓,無論是:
-
ExternalName
類型 Service 的 CNAME 記錄 - 記錄:與 Service 共享一個名稱的任何
Endpoints
,以及所有其它類型
不配置 Selector的Headless Services應(yīng)用場景
Service
最常見的應(yīng)用是作為Pod的負(fù)載均衡器(反向代理)使用壶谒,抽象化對 Kubernetes Pod 的訪問云矫,但是Service
也可以作為其他應(yīng)用程序的反向代理來使用。 實例:
- 希望在生產(chǎn)環(huán)境中使用外部的數(shù)據(jù)庫集群汗菜,但測試環(huán)境使用自己的數(shù)據(jù)庫让禀。
- 希望
Service
指向另一個Namespace
中或其它集群中的服務(wù)挑社。 - 您正在將工作負(fù)載遷移到 Kubernetes。 在評估該方法時巡揍,您僅在 Kubernetes 中運行一部分后端痛阻。
服務(wù)沒有選擇器,因此不會自動創(chuàng)建相應(yīng)的 Endpoint 對象吼肥。 您可以通過手動添加 Endpoint 對象录平,將服務(wù)手動映射到運行該服務(wù)的網(wǎng)絡(luò)地址和端口:
apiVersion: v1
kind: Endpoints
metadata:
name: my-service
subsets:
- addresses:
- ip: 192.0.2.42
ports:
- port: 9376
注意: 端點 IPs 必須不可以 : 環(huán)回( IPv4 的 127.0.0.0/8 , IPv6 的 ::1/128 )或本地鏈接(IPv4 的 169.254.0.0/16 和 224.0.0.0/24麻车,IPv6 的 fe80::/64)缀皱。 端點 IP 地址不能是其他 Kubernetes Services 的群集 IP,因為 kube-proxy 不支持將虛擬 IP 作為目標(biāo)动猬。
訪問沒有 selector 的 Service
啤斗,與有 selector 的 Service
的原理相同。 請求將被路由到用戶定義的 Endpoint赁咙, YAML中為: 192.0.2.42:9376
(TCP)钮莲。
ExternalName Service
是 Service
的特例,它沒有 selector彼水,也沒有使用 DNS 名稱代替崔拥。
類型ExternalName
類型為 ExternalName 的服務(wù)將Service
映射到 DNS 名稱,而不是典型的選擇器凤覆,例如 my-service
链瓦, 您可以使用 spec.externalName
參數(shù)指定這些服務(wù)。
例如盯桦,以下 Service 定義將 prod
名稱空間中的 my-service
服務(wù)映射到 my.database.example.com
:
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: prod
spec:
type: ExternalName
externalName: my.database.example.com
注意: ExternalName 接受 IPv4 地址字符串慈俯,但作為包含數(shù)字的 DNS 名稱,而不是 IP 地址拥峦。 類似于 IPv4 地址的外部名稱不能由 CoreDNS 或 ingress-nginx 解析贴膘,因為外部名稱旨在指定規(guī)范的 DNS 名稱。 要對 IP 地址進(jìn)行硬編碼略号,請考慮使用 headless Services刑峡。
當(dāng)查找主機(jī) my-service.prod.svc.cluster.local
時,群集DNS服務(wù)返回 CNAME
記錄玄柠,其值為 my.database.example.com
突梦。 訪問 my-service
的方式與其他服務(wù)的方式相同,但主要區(qū)別在于重定向發(fā)生在 DNS 級別随闪,而不是通過代理或轉(zhuǎn)發(fā)阳似。 如果以后您決定將數(shù)據(jù)庫移到群集中,則可以啟動其 Pod铐伴,添加適當(dāng)?shù)倪x擇器或端點以及更改Service的類型撮奏。
外部 IP
如果外部的 IP 路由到集群中一個或多個 Node 上俏讹,Kubernetes Service
會被暴露給這些 externalIPs
。 通過外部 IP(作為目的 IP 地址)進(jìn)入到集群畜吊,打到 Service
的端口上的流量泽疆,將會被路由到 Service
的 Endpoint 上。 externalIPs
不會被 Kubernetes 管理玲献,它屬于集群管理員的職責(zé)范疇殉疼。
發(fā)布服務(wù) —— 服務(wù)類型
有一些Pod可能希望將服務(wù)暴露給外部應(yīng)用調(diào)用,可以通過Service捌年,選擇合適的類型來實現(xiàn)瓢娜。
Service的默認(rèn)值是 ClusterIP
類型。
Type
的可選值以及行為如下:
-
ClusterIP
:通過集群的內(nèi)部 IP 暴露服務(wù)礼预,選擇該值眠砾,服務(wù)只能夠在集群內(nèi)部可以訪問,這也是默認(rèn)的ServiceType
托酸。 -
NodePort
:通過每個 Node 上的 IP 和靜態(tài)端口(NodePort
)暴露服務(wù)褒颈。NodePort
服務(wù)會路由到ClusterIP
服務(wù),這個ClusterIP
服務(wù)會自動創(chuàng)建励堡。通過請求<NodeIP>:<NodePort>
谷丸,可以從集群的外部訪問一個NodePort
服務(wù)。 -
LoadBalancer
:使用云提供商的負(fù)載局衡器应结,可以向外部暴露服務(wù)刨疼。外部的負(fù)載均衡器可以路由到NodePort
服務(wù)和ClusterIP
服務(wù)。 - ExternalName:通過返回 CNAME 和它的值摊趾,可以將服務(wù)映射到 externalName 字段的內(nèi)容(例如币狠, foo.bar.example.com)。 沒有任何類型代理被創(chuàng)建砾层。
注意: 您需要 CoreDNS 1.7 或更高版本才能使用 ExternalName
類型漩绵。
您也可以使用 Ingress 來暴露自己的服務(wù)。 Ingress 不是服務(wù)類型肛炮,但它充當(dāng)集群的入口點止吐。 它可以將路由規(guī)則整合到一個資源中,因為它可以在同一IP地址下公開多個服務(wù)侨糟。
保留源 IP
各種類型的 Service 對源 IP 的處理方法不同:
使用 userspace 代理碍扔,隱藏了訪問
Service
的數(shù)據(jù)包的源 IP 地址。 這使得一些類型的防火墻無法起作用秕重。ClusterIP Service:使用 iptables 模式不同,集群內(nèi)部的源 IP 會保留(不做 SNAT)。如果 client 和 server pod 在同一個 Node 上,那源 IP 就是 client pod 的 IP 地址二拐;如果在不同的 Node 上服鹅,源 IP 則取決于網(wǎng)絡(luò)插件是如何處理的,比如使用 flannel 時百新,源 IP 是 node flannel IP 地址企软。
NodePort Service:默認(rèn)情況下,源 IP 會做 SNAT饭望,server pod 看到的源 IP 是 Node IP仗哨。為了避免這種情況,可以給 service 設(shè)置
spec.ExternalTrafficPolicy=Local
(1.6-1.7 版本設(shè)置 Annotationservice.beta.kubernetes.io/external-traffic=OnlyLocal
)铅辞,讓 service 只代理本地 endpoint 的請求(如果沒有本地 endpoint 則直接丟包)厌漂,從而保留源 IP。LoadBalancer Service:默認(rèn)情況下巷挥,源 IP 會做 SNAT桩卵,server pod 看到的源 IP 是 Node IP。設(shè)置
service.spec.ExternalTrafficPolicy=Local
后可以自動從云平臺負(fù)載均衡器中刪除沒有本地 endpoint 的 Node倍宾,從而保留源 IP。