什么是service
pod不是永恒存在的怠肋,pod會(huì)隨時(shí)被創(chuàng)建,同時(shí)pod也會(huì)被隨時(shí)銷毀(運(yùn)行出錯(cuò)宣决,超過(guò)資源限制等原因)穿挨。pod不可能被復(fù)活,因此pod被銷毀和重建之后的IP地址是不同的胧后。
其他依賴該pod的服務(wù)不可能去維護(hù)這些pod的IP地址信息芋浮。這里我們引入的service,service保存了我們?nèi)绾卧L問(wèn)一組pod的方式壳快。這一組pod通過(guò)pod selector來(lái)確定纸巷。無(wú)論這些pod怎樣創(chuàng)建和銷毀镇草,他們的label不變。service可以動(dòng)態(tài)維護(hù)這些pod的真實(shí)IP(通過(guò)endpoint對(duì)象)瘤旨。所以說(shuō)梯啤,service解耦了pod和依賴這些pod的應(yīng)用。
定義一個(gè)service
創(chuàng)建service的描述文件:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
這里我們定義了一個(gè)service的描述文件存哲。service的名字為my-service因宇。service指向的pod需要具有app: MyApp標(biāo)簽∷钔担可以使用80端口訪問(wèn)每個(gè)pod的9376端口察滑。這個(gè)service擁有一個(gè)cluster IP。這個(gè)IP只能在kubernetes集群內(nèi)部訪問(wèn)肩袍。
具有selector的service在創(chuàng)建的時(shí)候會(huì)自動(dòng)創(chuàng)建一個(gè)同名的endpoint杭棵。endpoint維護(hù)了具有這些標(biāo)簽的一組pod的真正的IP。如果這些pod的IP發(fā)生變化氛赐,endpoint中的IP也會(huì)隨著變化魂爪。
沒(méi)有pod selector的service
如下情況我們需要使用沒(méi)有pod selector的service:
- 使用集群外部的數(shù)據(jù)庫(kù)
- 要?jiǎng)?chuàng)建的service指向位于另一個(gè)namespace,或者是另一個(gè)kubernetes集群的service
編寫(xiě)描述文件:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
只有這樣是不夠的艰管,service需要有一個(gè)對(duì)應(yīng)的endpoint對(duì)象滓侍。創(chuàng)建沒(méi)有selector的service并不會(huì)隨之創(chuàng)建一個(gè)endpoint。這時(shí)候我們需要手工創(chuàng)建endpoint牲芋。endpoint的描述文件如下:
apiVersion: v1
kind: Endpoints
metadata:
name: my-service
subsets:
- addresses:
- ip: 192.0.2.42
ports:
- port: 9376
service proxy
service使用kube-proxy實(shí)現(xiàn)
不使用輪詢DNS方式的原因
- 不少DNS的實(shí)現(xiàn)不支持TTL(Time To Live)撩笆。無(wú)法在pod IP發(fā)生變化的時(shí)候及時(shí)改變緩存。
- 一部分應(yīng)用在DNS查詢之后便永久保存缸浦,再也不會(huì)更新夕冲。
- 即便是支持DNS再次解析,我們需要降低DNS的TTL裂逐。越低的TTL會(huì)導(dǎo)致DNS解析服務(wù)的頻繁運(yùn)行歹鱼,增加系統(tǒng)的負(fù)載。
user space proxy 模式
kube-proxy 負(fù)責(zé)監(jiān)視service和endpoint的創(chuàng)建和刪除卜高。對(duì)于每個(gè)service弥姻,在本地節(jié)點(diǎn)上隨機(jī)開(kāi)啟一個(gè)端口(代理端口)。任何連接到該端口的連接會(huì)被代理到service后端一組pod中的一個(gè)掺涛。kube-proxy使用SessionAffinity
配置項(xiàng)來(lái)決定使用哪一個(gè)后端pod庭敦。
user space模式使用iptables規(guī)則,把目標(biāo)為service的cluster IP和端口的請(qǐng)求發(fā)送到代理端口薪缆。
iptables proxy 模式
iptables使用規(guī)則配置秧廉,將訪問(wèn)service的cluster IP和port的連接重定向到某一個(gè)后端pod。默認(rèn)來(lái)說(shuō),kube-proxy隨機(jī)選擇后端pod提供服務(wù)定血。
iptables模式系統(tǒng)開(kāi)銷較小赔癌。
如果連接到的第一個(gè)被選定的pod沒(méi)有響應(yīng),此連接會(huì)失敗澜沟。這個(gè)和userspace模式不同灾票。userspace模式中,如果連接到第一個(gè)pod失敗茫虽,系統(tǒng)會(huì)自動(dòng)重試連接另一個(gè)后端pod刊苍。
可以使用readness probe
來(lái)確保pod是否正常工作。如果某個(gè)podreadness probe
失敗濒析,這個(gè)pod會(huì)從endpoint中移除正什,網(wǎng)絡(luò)請(qǐng)求不會(huì)再轉(zhuǎn)發(fā)到這個(gè)pod中,直到readness probe
恢復(fù)到success狀態(tài)号杏。
IPVS proxy 模式
ipvs 模式kube-proxy使用netlink
接口創(chuàng)建IPVS規(guī)則婴氮。IPVS基于鉤子函數(shù),使用內(nèi)核空間的hash表作為底層數(shù)據(jù)結(jié)構(gòu)盾致。因此IPVS相比iptables具有更低的延遲主经,吞吐量更高。同時(shí)同步代理規(guī)則的時(shí)候也更快庭惜。
IPVS重定向網(wǎng)絡(luò)通信具有如下策略:
- rr: round-robin 輪詢
- lc: least connection (smallest number of open connections) 優(yōu)先連接數(shù)最少
- dh: destination hashing 目標(biāo)hash
- sh: source hashing 源hash
- sed: shortest expected delay 延遲最小
- nq: never queue 不排隊(duì)
如果需要相同客戶端的請(qǐng)求被重定向到相同的pod罩驻,需要配置service.spec.sessionAffinity
為ClientIP
(默認(rèn)為None
)。我們還可以設(shè)置pod和client的最大粘性時(shí)間service.spec.sessionAffinityConfig.clientIP.timeoutSeconds
护赊。默認(rèn)為10800惠遏,即3小時(shí)。如果3小時(shí)內(nèi)client沒(méi)有訪問(wèn)pod骏啰,那么該client下次訪問(wèn)service的時(shí)候?qū)⒅匦轮付ㄐ碌膒od节吮,原先的session會(huì)失效。
多端口服務(wù)
service可以暴露多個(gè)port判耕。如果使用多個(gè)port透绩,必須為每一個(gè)port設(shè)置name。描述文件如下所示:
apiVersion: v1
kind: Service
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
顯式指定service的IP
可以設(shè)定spec.clusterIP
配置項(xiàng)祈秕。但是IP必須位于API server的service-cluster-ip-range
CIDR范圍中。
service發(fā)現(xiàn)
環(huán)境變量方式
Kubernetes自動(dòng)為每一個(gè)運(yùn)行的pod中加入各個(gè)service環(huán)境變量雏胃。這些service的環(huán)境變量的格式為
- {SVCNAME}_SERVICE_HOST
- {SVCNAME}_SERVICE_PORT
以redis-master服務(wù)為例请毛,對(duì)應(yīng)的環(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需要使用環(huán)境變量的方式訪問(wèn)service,一定要在創(chuàng)建pod之前創(chuàng)建service瞭亮。否則pod中將沒(méi)有這個(gè)service的環(huán)境變量方仿。如果使用DNS方式的話不需要擔(dān)心這個(gè)創(chuàng)建順序問(wèn)題。
DNS方式
集群的DNS服務(wù),例如CoreDNS會(huì)監(jiān)聽(tīng)service仙蚜,創(chuàng)建service對(duì)應(yīng)的DNS入口此洲。
如果在命名空間my-ns定義了一個(gè)服務(wù)my-service,自動(dòng)創(chuàng)建的DNS入口為my-service.my-ns委粉。集群內(nèi)任何pod可以通過(guò)此DNS訪問(wèn)這個(gè)服務(wù)呜师。如果是和該服務(wù)位于同一個(gè)命名空間的pod,可以省略DNS中的命名空間部分贾节,使用my-service這個(gè)DNS入口訪問(wèn)my-service服務(wù)汁汗。
DNS支持命名端口的方式。如果一個(gè)端口命名為http栗涂,使用的是TCP協(xié)議知牌,可以通過(guò)_http._tcp.my-service.my-ns
這條DNS解析到它的IP和端口號(hào)。
Headless Service
特點(diǎn):
- 禁用掉了service的負(fù)載均衡功能斤程。
- 沒(méi)有指定service的clusterIP角寸。
- service不使用kube-proxy代理。
適用場(chǎng)景:
- 需要不經(jīng)過(guò)代理直接訪問(wèn)pod忿墅。
- 使用服務(wù)發(fā)現(xiàn)功能扁藕,例如部署zookeeper等。
配置方式:
設(shè)置spec.clusterIP
為None
DNS自動(dòng)配置的方式和是否配置了pod selector有關(guān)球匕。
- 配置了pod selector:endpoints會(huì)被創(chuàng)建纹磺,DNS直接指向后臺(tái)pod的IP
- 沒(méi)有配置pod selector:endpoints不會(huì)被創(chuàng)建。DNS服務(wù)會(huì)查找: 1. CNAME記錄(ExternalName-type Services)2. 和service同名的endpoints
公開(kāi)service(允許service在集群外訪問(wèn))
通過(guò)設(shè)置服務(wù)類型(ServiceType)來(lái)實(shí)現(xiàn)亮曹。
service type有如下類型:
- clusterIP:一個(gè)只有在集群內(nèi)部可訪問(wèn)的虛擬的IP橄杨。這個(gè)是Service Type的默認(rèn)值。
- NodePort:使用每個(gè)節(jié)點(diǎn)的一個(gè)靜態(tài)端口來(lái)訪問(wèn)service照卦。集群外可使用<NodeIP>:<NodePort>形式訪問(wèn)式矫。
- LoadBalancer:負(fù)載均衡器,使用云提供商的負(fù)載均衡器訪問(wèn)服務(wù)役耕。
- ExternalName:映射為一個(gè)自定義DNS名稱采转,適用于訪問(wèn)外部服務(wù)。
除此之外還可以使用Ingress方式瞬痘,但是Ingress不是ServiceType故慈。
NodePort
NodePort范圍從--service-node-port-range
flag中隨機(jī)選擇(默認(rèn)為30000-32767)。分配的端口號(hào)在.spec.ports[*].nodePort
字段中框全。
可以使用kube-proxy的--nodeport-addresses
配置指定nodePort IP的范圍察绷。
可以通過(guò)指定nodePort字段的內(nèi)容,來(lái)使得NodePort使用一個(gè)固定的IP津辩。
NodePort的使用例子:
apiVersion: v1
kind: Service
metadata:
name: kubia-nodeport
spec:
type: NodePort
ports:
- port: 80
targetPort: 8080
nodePort: 30123
selector:
app: kubia
LoadBalancer
自己部署的集群中無(wú)LoadBalancer拆撼,此部分省略容劳。
ExternalName
映射service到一個(gè)DNS名稱。
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: prod
spec:
type: ExternalName
externalName: my.database.example.com
External IPs
使用外部IP訪問(wèn)服務(wù)闸度。要求可以通過(guò)外部IP訪問(wèn)到集群節(jié)點(diǎn)竭贩。
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
externalIPs:
- 80.11.12.10
Service 使用Session Affinity
session affinity,同一個(gè)client發(fā)出的請(qǐng)求會(huì)發(fā)送給同一個(gè)pod處理莺禁。配置方法如下:
apiVersion: v1
kind: Service
spec:
sessionAffinity: ClientIP
...
使用命名端口
可以在Pod中為端口命令留量,在service中使用。
步驟如下所示:
- 在pod中給端口命名
kind: Pod
spec:
containers:
- name: kubia
ports:
- name: http
containerPort: 8080
- name: https
containerPort: 8443
- 創(chuàng)建service時(shí)使用命名端口(targetPort中使用)
apiVersion: v1
kind: Service
spec:
ports:
- name: http
port: 80
targetPort: http
- name: https
port: 443
targetPort: https
Service關(guān)聯(lián)k8s集群外部的服務(wù)
把集群外部的某一服務(wù)的入口做成k8s的endpoint睁宰。創(chuàng)建service的時(shí)候如果不指定label selector肪获,endpoints就不會(huì)自動(dòng)創(chuàng)建。
下面舉一個(gè)例子柒傻。我們需要在k8s集群中訪問(wèn)集群外部11.11.11.11和22.22.22.22的80端口這個(gè)服務(wù)孝赫。
這里我們需要手工創(chuàng)建service和endpoint:
apiVersion: v1
kind: Service
metadata:
name: external-service
spec:
ports:
- port: 80
apiVersion: v1
kind: Endpoints
metadata:
name: external-service
subsets:
- addresses:
- ip: 11.11.11.11
- ip: 22.22.22.22
ports:
- port: 80
完成之后,集群內(nèi)部可以通過(guò)訪問(wèn)external-service來(lái)實(shí)現(xiàn)訪問(wèn)集群外部11.11.11.11
和22.22.22.22
红符。
減少網(wǎng)絡(luò)連接的hop數(shù)量
可以通過(guò)配置service青柄,只把連接重定向到接收這個(gè)連接的node上運(yùn)行的pod。
方法如下:
spec:
externalTrafficPolicy: Local
...
缺點(diǎn):如果這個(gè)node上沒(méi)有運(yùn)行該service對(duì)應(yīng)的pod预侯,連接會(huì)掛起致开。