1. Service 為何存在
Pod的生命是有限的,如果Pod重啟IP很有可能會發(fā)生變化际起。
如果我們的服務(wù)都是將Pod的IP地址寫死,Pod的掛掉或者重啟,后端其他服務(wù)也將會不可用脑漫,當然我們可以通過手動修改如nginx的upstream配置來改變后端的服務(wù)IP髓抑。
現(xiàn)在我們可以通過,Consul优幸,ZooKeeper還有我們熟悉的etcd等工具吨拍,有了這些工具過后我們就可以只需要把我們的服務(wù)注冊到這些服務(wù)發(fā)現(xiàn)中心去就可以,然后讓這些工具動態(tài)的去更新Nginx的配置就可以了网杆,我們完全不用去手工的操作了羹饰。
Kubernetes集群就為我們提供了這樣的一個對象 - Service,Service是一種抽象的對象碳却,它定義了一組Pod的邏輯集合和一個用于訪問它們的策略队秩,其實這個概念和微服務(wù)非常類似。一個Serivce下面包含的Pod集合一般是由Label Selector來決定的昼浦。
我們這樣就可以不用去管后端的Pod如何變化馍资,只需要指定Service的地址就可以了,因為我們在中間添加了一層服務(wù)發(fā)現(xiàn)的中間件关噪,Pod銷毀或者重啟后鸟蟹,把這個Pod的地址注冊到這個服務(wù)發(fā)現(xiàn)中心去。Service的這種抽象就可以幫我們達到這種解耦的目的色洞。
2. Kubernetes的三種IP
- Node IP: Node節(jié)點的IP地址
- Cluster IP: Service的IP地址
- Pod IP: Pod的IP地址
Node IP是Kubernetes集群中節(jié)點的物理網(wǎng)卡IP地址(一般為內(nèi)網(wǎng))戏锹,屬于這個網(wǎng)絡(luò)的服務(wù)器之間都可以直接通信,所以Kubernetes集群外要想訪問Kubernetes集群內(nèi)部的某個節(jié)點或者服務(wù)火诸,肯定得通過Node IP進行通信(這個時候一般是通過外網(wǎng)IP了)
Pod IP是每個Pod的IP地址锦针,它是Docker Engine根據(jù)docker0網(wǎng)橋的IP地址段進行分配的(我們這里使用的是flannel這種網(wǎng)絡(luò)插件保證所有節(jié)點的Pod IP不會沖突)
Cluster IP是一個虛擬的IP,僅僅作用于Kubernetes Service這個對象置蜀,由Kubernetes自己來進行管理和分配地址奈搜,當然我們也無法ping這個地址,他沒有一個真正的實體對象來響應(yīng)盯荤,他只能結(jié)合Service Port來組成一個可以通信的服務(wù),相當于VIP地址馋吗,來代理后端服務(wù)。
3. 創(chuàng)建Service
現(xiàn)在我們先創(chuàng)建一組Pod
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: httpd
labels:
app: httpd-demo
spec:
replicas: 3
template:
metadata:
labels:
app: httpd
spec:
containers:
- name: httpd
image: httpd
ports:
- containerPort: 80
上面這個Pod 我們定義了三個副本秋秤,標簽為app=httpd
,下面我們來創(chuàng)建這個Pod的Service宏粤,通過spec.selector來指定標簽為httpd的Pod。
其中被selector 選中的 Pod灼卢,就稱為 Service 的Endpoints绍哎,可以使用kubectl get ep
命令查看。
[root@master testyml]# kubectl get ep
NAME ENDPOINTS AGE
fuseim.pri-ifs <none> 21d
httpd-svc 10.244.0.24:80,10.244.1.112:80,10.244.2.2:80 19h
我們可以看到httpd-svc
的后端端點IP10.244.0.24:80,10.244.1.112:80,10.244.2.2:80
鞋真,也就是httpd應(yīng)用Pod的IP地址崇堰。
需要注意的是,只有處于Running狀態(tài)且readlinessProbe檢查通過的Pod,才會出現(xiàn)在Service的Endpoints列表里海诲,同樣當某個Pod出現(xiàn)問題時繁莹,Service也會把這個Pod從Endpoints中摘掉。
現(xiàn)在我們創(chuàng)建上面Pod集合所對應(yīng)的Service
apiVersion: v1
kind: Service
metadata:
name: httpd-svc
spec:
selector:
app: httpd
ports:
- protocol: TCP
port: 8080
targetPort: 80
[root@master testyml]# kubectl create -f svc.yml
service "httpd-svc" created
[root@master testyml]# kubectl get svc --all-namespaces
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
blog mysql ClusterIP 10.101.134.172 <none> 3306/TCP 30d
blog wordpress NodePort 10.107.173.113 <none> 80:32255/TCP 30d
default httpd-svc ClusterIP 10.108.195.47 <none> 8080/TCP 3m
default kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 114d
default mysql-production ClusterIP 10.102.208.69 <none> 3306/TCP 13d
default order NodePort 10.99.99.88 <none> 8080:30080/TCP 16d
kube-ops jenkins2 NodePort 10.111.112.3 <none> 8080:30005/TCP,50000:30340/TCP 17d
kube-system kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP 114d
kube-system kubernetes-dashboard NodePort 10.108.149.176 <none> 443:30002/TCP 111d
[root@master testyml]#
現(xiàn)在我們看到已經(jīng)創(chuàng)建了名為httpd-svc
的Service特幔,分配的Cluster IP 為 10.108.195.47
咨演,該Service還會持續(xù)的監(jiān)聽selector下面的Pod,會把這些Pod信息更新到一個名為httpd-svc的Endpoints對象上去蚯斯,這個對象就類似于我們上面說的Pod集合了雪标。
需要注意的是,Service能夠?qū)⒁粋€接收端口映射到任意的targetPort溉跃。 默認情況下,targetPort將被設(shè)置為與port字段相同的值告抄。 可能更有趣的是撰茎,targetPort 可以是一個字符串,引用了 backend Pod 的一個端口的名稱打洼。 因?qū)嶋H指派給該端口名稱的端口號龄糊,在每個 backend Pod 中可能并不相同,所以對于部署和設(shè)計 Service 募疮,這種方式會提供更大的靈活性炫惩。
另外Service能夠支持 TCP 和 UDP 協(xié)議,默認是 TCP 協(xié)議阿浓。
4. kube-proxy
在Kubernetes集群中他嚷,每個Node會運行一個kube-proxy進程, 負責為Service實現(xiàn)一種 VIP(虛擬 IP,就是clusterIP)的代理形式芭毙。
Service其實是由kube-proxy組件和Iptables來實現(xiàn)的筋蓖。
現(xiàn)在的Kubernetes中默認是使用的iptables這種模式來代理
這種模式,kube-proxy會監(jiān)視Kubernetes master對 Service 對象和 Endpoints (端點)對象的添加和移除退敦。 對每個 Service粘咖,它會添加上 iptables 規(guī)則,從而捕獲到達該 Service 的 clusterIP(虛擬 IP)和端口的請求侈百,進而將請求重定向到 Service 的一組 backend(后端) 中的某一個個上面瓮下。 對于每個 Endpoints 對象,它也會安裝 iptables 規(guī)則钝域,這個規(guī)則會選擇一個 backend Pod讽坏。
默認的策略是,隨機選擇一個 backend网梢。 我們也可以實現(xiàn)基于客戶端 IP 的會話親和性震缭,可以將 service.spec.sessionAffinity 的值設(shè)置為 "ClientIP" (默認值為 "None")。
另外需要了解的是如果最開始選擇的 Pod 沒有響應(yīng)战虏,iptables 代理能夠自動地重試另一個 Pod拣宰,所以它需要依賴 readiness probes(準備就緒探測器)党涕。
5. Service 的類型
ClusterIP:通過集群的內(nèi)部 IP 暴露服務(wù),選擇該值巡社,服務(wù)只能夠在集群內(nèi)部可以訪問膛堤,這也是默認的ServiceType。
NodePort:通過每個 Node 節(jié)點上的 IP 和靜態(tài)端口(NodePort)暴露服務(wù)晌该。NodePort 服務(wù)會路由到 ClusterIP 服務(wù)肥荔,這個 ClusterIP 服務(wù)會自動創(chuàng)建。通過請求 :朝群,可以從集群的外部訪問一個 NodePort 服務(wù)燕耿。
LoadBalancer:使用云提供商的負載局衡器,可以向外部暴露服務(wù)姜胖。外部的負載均衡器可以路由到 NodePort 服務(wù)和 ClusterIP 服務(wù)誉帅,這個需要結(jié)合具體的云廠商進行操作。
ExternalName:通過返回 CNAME 和它的值右莱,可以將服務(wù)映射到 externalName 字段的內(nèi)容(例如蚜锨, foo.bar.example.com)。沒有任何類型代理被創(chuàng)建慢蜓,這只有 Kubernetes 1.7 或更高版本的 kube-dns 才支持亚再。
NodePort
如果設(shè)置 type 的值為 "NodePort",端口可以通過 Service 的 spec.ports[*].nodePort 字段來指定晨抡,如果不指定的話會自動生成一個端口氛悬,Kubernetes master 將從給定的配置范圍內(nèi)(默認:30000-32767)分配端口,每個 Node 將從該端口(每個 Node 上的同一端口)代理到 Service凄诞。
需要注意的是圆雁,Service 將能夠通過 :spec.ports[].nodePort 和 spec.clusterIp:spec.ports[].port 而對外可見。
現(xiàn)在我們修改上面的Service帆谍,將Cluster IP 改為NodePort伪朽,指定nodeport為30088
apiVersion: v1
kind: Service
metadata:
name: httpd-svc
spec:
selector:
app: httpd
type: NodePort
ports:
- protocol: TCP
port: 80
targetPort: 80
nodePort: 30088
現(xiàn)在我們更新這個Service
[root@master testyml]# kubectl apply -f svc.yml
Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply
service "httpd-svc" configured
[root@master testyml]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
httpd-svc NodePort 10.108.195.47 <none> 8080:30088/TCP 16h
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 115d
mysql-production ClusterIP 10.102.208.69 <none> 3306/TCP 14d
order NodePort 10.99.99.88 <none> 8080:30080/TCP 17d
可以看到httpd-svc
的TYPE類型已經(jīng)變成了NodePort,并且自動分配了30088
的端口映射,現(xiàn)在我們可以通過訪問Node IP
和30088
端口訪問這個httpd服務(wù)
ExternalName
ExternalName 是 Service 的特例汛蝙,它沒有 selector烈涮,也沒有定義任何的端口和 Endpoint。 對于運行在集群外部的服務(wù)窖剑,它通過返回該外部服務(wù)的別名這種方式來提供服務(wù)坚洽。
kind: Service
apiVersion: v1
metadata:
name: my-service
namespace: prod
spec:
type: ExternalName
externalName: my.database.example.com
當查詢主機 my-service.prod.svc.cluster.local ,集群的 DNS 服務(wù)將返回一個值為 my.database.example.com 的 CNAME 記錄西土。 訪問這個服務(wù)的工作方式與其它的相同讶舰,唯一不同的是重定向發(fā)生在 DNS 層,而且不會進行代理或轉(zhuǎn)發(fā)。 如果后續(xù)決定要將數(shù)據(jù)庫遷移到 Kubernetes 集群中跳昼,可以啟動對應(yīng)的 Pod般甲,增加合適的 Selector 或 Endpoint,修改 Service 的 type鹅颊,完全不需要修改調(diào)用的代碼敷存,這樣就完全解耦了。