Kubernetes-Service資源詳解


service的三種工作模式:(userstats(效率低)、iptables、ipvs)

 

service可以自動實現負載均衡service自動實現了負載均衡,service通過selector標簽選擇器匹配了后面多個pod!后端多個pod提供底層服務。

 

Service版本介紹

userspace: 1.1之前版本

iptabls: 1.10之前版本

ipvs: 1.11之后版本

service依賴core-dns

 

四種service類型:

ExternalName(把外部集群服務引入到集群內部使用)、ClusterIP(默認這個。僅僅用於集群內部通信)、NodePort(和集群外部通信)、LoadBalance(把k8s部署在雲環境上面,雲環境支持LBAS(負載均衡即服務一鍵調用),創建軟負載均衡器使用。自動外部觸發創建一個負載均衡,例如阿里LB-service)。

 

資源記錄

SVC_NAME.NS_NAME.DOMAIN.LTD. #提供service服務域名格式

svc.clusqter.local #集群的默認名字域名

redis.default.svc.clustel.local. #每一個服務生產的域名格式,默認格式


 

Service用標簽selector和后端pod建立關系,所以pod要先跑起來。

0、kube-proxy時刻watch監控着api-server,只要service發生變化,轉換為當前節點實現調度的資源規則(有可能是iptables或者ipvs,取決於service的實現方式(userspace、iptables、ipvs))

1、service依賴dns服務,較新版本默認用的coredns,1.11之前版本用的kube-dns。service的名稱解析強依賴於dns附件。所有dns必須部署。

2、k8s向客戶提供網絡功能,依賴第三方方案,可以用cni容器網絡插件標准接口,另外還有flanael、calico插件也支持。

3、k8s有三類ip地址,節點ip網絡、pod網絡、集群地址cluster network(virtual ip虛擬ip)。

 

service工作。每個節點至少,node節點工作有一個組件kube-proxy,通過master上的api-server始終監視着有關service的變動信息。這種行為方式成為watch,一旦有service變化,kube-proxy轉換為當前節點之上,能夠實現service資源調度的規則,這種規則有可能是iptables,或者ipVS。規則取決於service的實現方式。

 

service三種工作代理模式(userstats(效率低)、iptables、ipvs)

第一種方式userstats:下面是用戶空間模型解析。內部客戶端用戶請求clientPod,請求某個服務時,到達當前節點的內核空間iptables規則(其實就是service的規則)。service的工作方式是請求到達service以后,由service轉換為本地監聽在某個套接字的用戶空間的kuber-proxy,kube-proxy處理完成再轉給serviceip。最終代理這個這個service相關聯的各個pod。最終實現調度。這個效率很低下。

第二種方式 iptables:客戶端ip直接請求service ip。這個請求報文在本地內核空間的service的規則所截取。進而直接調度,service相關聯的pod。區別是:直接工作在內核空間。由iptables規則直接負責調度。而第一種是由工作在內核空間的kube-proxy負責調度,所有第一種叫userstats。第二種叫iptabls。

第三種方式 ipvs。 client的請求到達內核空間以后,直接由ipvs規則來調度,直接調度給pod網絡訪問的相關資源。

總結:所以設定service工作在什么模型,就運行在什么模式。1.1及之前用iptables。1.11默認用的ipvsq。ipvs沒有被激活,自動降級為iptables。


 

實戰(pod刪除增加原理過程):#例子:假設Service的lable適配版本多了一個pod。這個pod立刻反饋到apiserver(因為pod的信息要保存在apiserver的etcd里面)。而后kuber-proxy檢查watch到這種變化。將其轉換為iptables規則。所以轉換是動態的而且是實時的。刪除也是同樣的邏輯。

總結:pod變化會存儲到apiserver的etcd里面。kube-proxy負責watch。watch信息會轉為iptables規則

 


 

例子1-ClusterIP集群(使用yaml文件創建基於ClusterIP集群的service)

#查看當前service服務

[root@node-001 manifests]# kubectl get service

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE

kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 18d

#10.96.0.1是apiserver的地址。千萬不能刪除。

#這個千萬不能刪除,集群所有的pod需要10.96.0.1和集群的apiserver聯系,這個地址是集群內的。

#apiserver有兩個地址。一個面向集群外部,一個內部。

myapp NodePort 10.111.150.166 <none> 80:32489/TCP 16d

 

#刪除之前創建的redis服務

[root@node-001 manifests]# kubectl delete svc redis

service "redis" deleted

 

#查看service的幫助文檔

[root@node-001 manifests]# kubectl explain svc.spec

ports #把哪個端口和后端容器建立關系

selector #關聯哪些pod資源

clusterIP #動態分配屬於192端,假設使用固定,需要這個字段。創建沒法改變。

 

#創建service的yaml文件

[root@node-001 manifests]# vim redis-service.yaml

apiVersion: v1

kind: Service

metadata:

name: redis

namespace: default #注意service和pod最好同一名稱空間!

spec:

selector: #關聯到哪些后端pod資源上面,只要pod打標簽了,就會自動關聯!

app: redis

role: logstor #打的標簽是role,角色定義是logstorsdad

clusterIP: 10.97.97.97 #可以動態分配,不指定。建議不自己定義。或者自己定義。只要在10.96.0.0/12網段就可以。

type: ClusterIP #service集群類型

ports

- port: 6379 #service提供服務暴露的端口、service端口。

targetPort: 6379 #service后端容器的端口 NodePort 外網訪問端口、容器端口

 

#生成service服務

[root@node-001 manifests]# kubectl apply -f redis-service.yaml

service/redis created

[root@node-001 manifests]# kubectl get service

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE

kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 18d

myapp NodePort 10.111.150.166 <none> 80:32489/TCP 17d

redis ClusterIP 10.97.97.97 <none> 6379/TCP 8m18s

#6379就是service提供的端口

 

#service創建好了,會在的core-dns自動生成記錄svc、a等記錄

資源記錄格式:

SVC_NAME.NS_NAME.DOMAIN.LTD. #提供service服務域名格式

svc.clusqter.local #集群的默認名字域名

redis.default.svc.clustel.local. #例如redis在dns的記錄格式顯示。

 

#查看redis的service服務詳細信息。

[root@node-001 manifests]# kubectl describe service redis

Name: redis

Namespace: default

Labels: <none>

Annotations: kubectl.kubernetes.io/last-applied-configuration:

{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"redis","namespace":"default"},"spec":{"clusterIP":"10.97.97.97","...

Selector: app=redis,role=logstor #被selector匹配到的pod資源的地址。

Type: ClusterIP

IP: 10.97.97.97

Port: <unset> 6379/TCP

TargetPort: 6379/TCP

Endpoints: 10.244.1.73:6379 #service后端提供真正pods地址是這個。

小知識:為什么沒有直接到顯示pods?因為service到pod之間是有個中間層的。service不會直接到pod。先到Endpoints,再有Endpoints關聯到pod。endpoints其實也可以手動創建,我們可以忽略這一層面。

Session Affinity: None

Events: <none>

小知識:服務添加完成,會自動動態在dns添加記錄,不止一個,保護svc,A記錄等,此時可以解析記錄格式。


 

例子2-service---NodePort集群(使用yaml文件創建基於NodePort集群的service)

#通過訪問集群端口NodePort集群來定義。nodeport類型的service。集群外部可以直接訪問!!!

#先創建pods,打標簽,然后創建service關聯這個標簽的后端pod。

#創建pods、打標簽

[root@node-001 manifests]# cat rs-demo.yaml

apiVersion: apps/v1

kind: ReplicaSet

metadata:

name: myapp

namespace: default

spec:

replicas: 2

selector:

matchLabels:

app: myapp

release: canary

template:

metadata:

name: myapp-pod

labels:

app: myapp

release: canary

environment: qa

spec:

containers:

- name: myapp-container

image: ikubernetes/myapp:v1

ports:

- name: http

containerPort: 80

[root@node-001 manifests]# kubectl apply -f rs-demo.yaml

[root@node-001 manifests]# kubectl get pods -o wide --show-labels

NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS

myapp-bv2ff 1/1 Running 0 17s 10.244.1.90 node-002 <none> <none> app=myapp,environment=qa,release=canary

myapp-zf2c6 1/1 Running 0 17s 10.244.1.89 node-002 <none> <none> app=myapp,environment=qa,release=canary

 

#創建service、創建基於NodePort-service集群的yaml文件

[root@node-001 manifests]# vim myapp-nodeport-service.yaml

apiVersion: v1

kind: Service

metadata:

name: myapp

namespace: default

spec:

selector:

app: myapp

role: canary #打的標簽是role,角色定義是logstorsdad

clusterIP: 10.99.99.99 #可以動態分配,不指定。建議不自己定義。或者自己定義。只要在10.96.0.0/12網段就可以。

type: NodePort #service類型為nodeport類型!!

ports:

- port: 80 #service端口

targetPort: 80 #pod端口 NodePort 外網訪問端口

nodePort: 30080 #節點端口,這樣外部直接訪問宿主機ip:30080就可以訪問了,注意這個30080在所有的node都會啟動!端口在30000~32767范圍內!不指定也可以,隨機分配.

#每一個節點nodePort端口,不能被占用,不指定隨機分配。!一旦被占用!!!錯誤設置會導致節點這個端口服務不能訪問!!!因為相當於在iptables設置重定向。做成了dnat了。

#端口從30000 到32767之間隨機分配。

 

#創建NortPort的service集群

[root@node-001 manifests]# kubectl create -f myapp-nodeport-service.yaml

service/myapp created

 

#查看service服務

[root@node-001 manifests]# kubectl get service

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE

kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 19d

myapp NodePort 10.99.99.99 <none> 80:30080/TCP 3m59s

service名字是myapp 集群類型是nodeport service集群ip serviceip端口是80 映射為node主機的端口30080

[root@node-001 manifests]#

 

#查看service詳細信息

[root@node-001 manifests]# kubectl describe service myapp

Name: myapp

Namespace: default

Labels: <none>

Annotations: <none>

Selector: app=myapp,role=canary

Type: NodePort

IP: 10.99.99.99

Port: <unset> 80/TCP

TargetPort: 80/TCP

NodePort: <unset> 30080/TCP #這邊好像有點問題,應該前面pod關停的原因導致的。

Endpoints: <none>

Session Affinity: None

External Traffic Policy: Cluster

Events: <none>

[root@node-001 manifests]#

 

#在每一個node宿主機查看端口

[root@node-001 manifests]# netstat -tunlp|grep 32489

tcp6 0 0 :::30080 :::* LISTEN 3313/kube-proxy

#每一個node節點都開啟了30080端口

 

#從集群外機器訪問myapp的service的nodeport集群的應用

[root@k8s-master mainfests]# while true;do curl http://192.168.100.181:30080/hostname.html;sleep 1;done

#自動實現負載均衡。因為service自動實現了負載均衡,爽快。因為selector后面匹配了多個pod!多個pod提供服務。

#原理:經過好幾層轉換,先訪問nodeport先轉為service port,再轉換為pod port。

#此時前面可以再加個nginx,或者lvs hapoxcy做反向代理。

#下面做下回滾,或者更新(前面有做實驗),利用命令set 或者 patch 打補丁方式!

 


 例子3-LoadBalance集群(使用yaml文件創建基於LoadBalance集群的service)

#LoadBalance這邊沒法做,假設購買阿里雲6台ecs主機,又購買LBAS服務。ecs虛擬機部署了k8s,k8s可以與共有雲交互IAAS的api,純軟件方式。請求創建一個外部的負載均衡器(0:31:00)。但是需要兩級調度,先調度代理給nodeport服務,再由nodeport代碼內部的servcie‘,service代理到真正的pod port。


 

例子4-LoadBalance集群

#ExternalName(把外部集群服務引入到集群內部使用)

#核心知識點:用於實現假設訪問的服務在集群外,我們希望集群內可以訪問到集群外服務。從而讓集群內部服務訪問外部服務像訪問內部服務一樣簡單。

#建立一個service。這個service關聯到外部服務。當內部的service服務訪問時候,外部服務先轉給node,node轉給service。service轉給port。

#查看幫助文檔:[root@node-001 manifests]# kubectl explain svc.spec.externalName


 

Service調度機制(默認隨機調度)

Pod會話保持(Session affinity)

核心知識:支持兩種: ClientIP、None(默認),所以默認隨機調度。配置為clientip,把來自同一ip客戶端,始終調度到統一個pod里面。可通過patch打補丁方式修改# kubectl patch svc myapp -p '{"spec":{"sessionAffinity":"ClientIP"}}',改好不用重啟pod,立即生效,強大到沒有朋友!

   Service資源還支持Session affinity(粘性會話)機制,可以將來自同一個客戶端的請求始終轉發至同一個后端的Pod對象,這意味着它會影響調度算法的流量分發功用,進而降低其負載均衡的效果。因此,當客戶端訪問Pod中的應用程序時,如果有基於客戶端身份保存某些私有信息,並基於這些私有信息追蹤用戶的活動等一類的需求時,那么應該啟用session affinity機制。

  Service affinity的效果僅僅在一段時間內生效,默認值為10800秒,超出時長,客戶端再次訪問會重新調度。該機制僅能基於客戶端IP地址識別客戶端身份,它會將經由同一個NAT服務器進行原地址轉換的所有客戶端識別為同一個客戶端,由此可知,其調度的效果並不理想。Service 資源 通過. spec. sessionAffinity 和. spec. sessionAffinityConfig 兩個字段配置粘性會話。 spec. sessionAffinity 字段用於定義要使用的粘性會話的類型,它僅支持使用“ None” 和“ ClientIP” 兩種屬性值。如下:


 

例子(配置pod會話保持)

#用Session affinity配置調度的時候調度后端的同一個pod。

 #通過patch打補丁方式對之前myapp這個service設置會話保持,格式為ClientIP.

[root@node-001 manifests]# kubectl get svc

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE

kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 19d

myapp NodePort 10.99.99.99 <none> 80:30080/TCP 57m

redis ClusterIP 10.97.97.97 <none> 6379/TCP 36m

[root@node-001 manifests]# kubectl patch svc myapp -p '{"spec":{"sessionAffinity":"ClientIP"}}'

service/myapp patched

 

#查看service是否pod會話保持是否設置為ClientIP。

[root@node-001 manifests]# kubectl describe svc myapp

Name: myapp

Namespace: default

Labels: <none>

Annotations: kubectl.kubernetes.io/last-applied-configuration:

{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"myapp","namespace":"default"},"spec":{"clusterIP":"10.99.99.99","...

Selector: app=myapp,role=canary

Type: NodePort

IP: 10.99.99.99

Port: <unset> 80/TCP

TargetPort: 80/TCP

NodePort: <unset> 30080/TCP

Endpoints: <none>

Session Affinity: ClientIP #這邊已經顯示pod會話保持設置為ClientIP

External Traffic Policy: Cluster

Events: <none>

[root@node-001 manifests]#

 

#查看pod會話保持情況。改完是動態生效!不用重啟!從集群外機器訪問myapp的service的nodeport集群的應用

[root@k8s-master mainfests]# while true;do curl http://192.168.100.181:30080/hostname.html;sleep 1;done


 Headless Service(沒有cluster ip):

#headless service表示沒有service沒有cluster ip。訪問直達pod。kubcetl get svc的之后clueter ip沒有數值

#有時不需要或不想要負載均衡,以及單獨的 Service IP。 遇到這種情況,可以通過指定 Cluster IP(spec.clusterIP)的值為 "None " 來創建 Headless Service。

#無頭service,每一個service應該名字。並且這個service名字可以解析成對應的cluster ip。通常cluster ip只有一個,由cluster ip調度到后端的多個pod。假設解析service名字的時候。沒有cluster ip,#去掉service ip,這樣解析service名字因為不能解析到clusterip,從而解析到pod ip。pod ip可能多個.沒有cluster ip的就是無頭service,而是直接達到pod。

配置Headless Service:只要定義字段clusterIP的值為:None


例子(定義hedless service)

#創建yaml文件。注意不能定義nodeport。

[root@node-001 manifests]# vim myapp-service-headless.yaml

apiVersion: v1

kind: Service

metadata:

name: myapp-svc

namespace: default

spec:

selector:

app: myapp #挑選后端有myapp的標簽的pod

release: canary #打的標簽是role,角色定義是logstorsdad

clusterIP: "None" #定義service類型為無頭service。定義None。沒有clusterip。只有service。訪問的時候直接訪問后端pod。

ports:

- port: 80 #service暴露的端口

targetPort: 80 #service后端提供的pod端口

 

#創建headless-service服務

[root@node-001 manifests]# kubectl apply -f myapp-service-headless.yaml

service/myapp-svc created

 

#查看clusterip沒有數值!

[root@node-001 manifests]# kubectl get svc

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE

kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 19d

myapp NodePort 10.99.99.99 <none> 80:30080/TCP 96m

myapp-svc ClusterIP None <none> 80/TCP 2s #none表示無頭service

redis ClusterIP 10.97.97.97 <none> 6379/TCP 74m

 

#service創建完成,會自動在core-dns添加記錄!

#用dig進行名稱解析。查看服務解析情況!非常實用!!

[root@node-001 manifests]# kubectl get svc -n kube-system

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE

myapp-svc ClusterIP None <none> 80/TCP 2s #none表示無頭service

[root@mater01 ~]# kubectl get svc -n kube-system

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE

kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 4d4h

[root@node-001 manifests]# dig -t A myapp-svc.defautl.svc.cluster.local. @10.96.0.10

# @表示不使用本地配置的dns解析,而是使用指向目標dns服務器去進行解析!目標dns是k8s集群內部的dns地址。

;; ANSWER SECTION:

. 30 IN A 10.244.1.27

. 30 IN A 10.244.1.27

#dig總結:這邊會顯示所有A記錄。后面直接顯示pod的地址,由這些pod地址提供服務!但是如果是有頭的service。dig出來的后面的ip顯示的是service-ip地址!


總結(有頭service和無頭service區別):

#無頭service:這邊可以看到,無頭service,解析的直接就是pod的ip地址

#有頭service:解析的就是自己service的cluster ip地址。

 

service缺點

#service無法解決https訪問問題。定義service以后,尤其是nodeport集群訪問,需要經過2級轉換調度,而且是4層調度,無論是iptables還是ipvs。4調度自身無法實現卸載https會話。

#ingress----k8s還有一種引入集群外部流量的方式,叫ingress。基於7層調度器。利用7層pod,將外部流量引入到內部。


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

------------恢復內容結束------------


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM