一、Service對應組件關系
1、在kubernetes平台之上,pod是有生命周期的,所以為了能夠給對應的客戶端提供一個固定的訪問端點,因此我們在客戶端和服務Pod之間添加一個固定的中間層,這個中間層我們稱之為Service,這個Service的真正工作還要嚴重依賴於我們k8s之上部署的附件。稱之為kubernetes的dns服務,不同的k8s版本實現可能不同,較新版本中默認使用的CoreDNS,1.11版本之前版本用的kube-dns,Service名稱解析是強依賴於DNS附件的,因此我們部署完k8s以后必須要去部署一個CoreDNS或者kube-dns。
2、k8s要想能夠向客戶端提供網絡功能,它需要依賴於第三方的方案,這種第三方方案可通過(至少較新版本中) cni(容器網絡插件標准的接口)來進行接入任何遵循這種插件標准的第三方方案。當然,這里面的方案有很多個,像我們之前部署的flannel,canal等。
3、在k8s中有三類網絡地址,分別是節點網絡(node network),pod網絡(pod network),集群網絡(cluster network或Service network)前兩種網絡都是實實在在的存在純硬件設備或軟件模擬的,都是存在的。后一種集群網絡 的IP稱之為virtual IP,因為這些IP沒有實實在在配置在某個接口上。它僅是出現在Service的規則中。
4、那么Service是什么呢?在每一個節點上我們工作了一個組件叫kube-proxy,此組件將始終監視着master上的api server中有關Service的資源變動信息,這種是通過k8s中的固有的一種請求方法watch(監視)來實現的。一旦有Service的資源的變動,包括創建,kube-proxy都要將其轉換為當前節點之上的能夠實現service資源調度,包括將用戶請求調度到后端特定pod資源之上的規則中,這個規則有可能是iptables,也有可能是ipvs,取決於Service的實現方式
5、Service實現方式在k8s上有三種模型
a、userspace,來自內部的請求client pod請求某個服務時一定先到達當前節點內核空間的iptables規則,也就是service的規則。這個service的工作方式是請求到達service后由service先把它轉為本地監聽在某個套接字上的用戶空間的kube-proxy,它來負責處理,處理完后再轉給service IP,最終代理至於service相關聯的各個pod實現調度。可以發現請求由client pod發給service,service還要回到監聽在這個端口上的kube-proxy,由kube-proxy來進行分發,所以kube-proxy是工作在用戶空間的進程。所以其被稱之為userspace。這種方式效率很低,原因在於先要到內核空間然后再到當前主機的用戶空間kube-proxy,由kube-proxy封裝其報文代理完以后再回到內核空間然后由iptables規則進行分發。
b、iptables,后來就到了第二種方式,方法是,客戶端ip請求時直接請求service的ip,這個請求報文被本地內核空間中的service規則所截取,進而直接調度給server pod,這種方式直接工作在內核空間由iptables規則直接負責調度。
c、ipvs,client pod請求到達內核空間后直接由ipvs規則來調度,直接調度給pod網絡地址范圍內的相關Pod資源。
6、我們在安裝並配置k8s的時候設定service工作在什么模式下他就會生成對應的什么模式的規則。1.10及之前的版本用的是iptables,再往前是1.1之前用的是userspace,1.11默認使用的是ipvs,若ipvs未被激活則默認為iptables。如果某個服務背后的Pod資源發生改變,比如service的標簽選擇器適用的版本又多一個那么這個pod適用的信息會立即反應到apiserver上,因為多個pod信息是會存在apiserver的etcd中,而后kube-proxy能夠檢測到這種變化並將其立即轉化為service規則。所以他的轉化是動態實時的,如果刪除了一個pod並且這個pod沒有被重構,這個狀態結果會反饋至apiserver的etcd中,這種變化被kube-proxy watch到了,然后立即將其轉換成iptables規則。
7、service到pod是有一個中間層的,service不會直接到pod,service會先到endpoints ,endpoints也是一個標准的k8s對象。其相當於是pod 地址 + 端口,然后再由endpoint關聯至后端的pod,但是我們理解的話可以直接理解為從service直接到pod就行,但事實上我們可以為service手動創建endpoints資源。
二、service創建
1、使用清單創建service資源,以前創建service都是通過expose命令來創建,現在通過使用清單來創建。
[root@k8smaster ~]# kubectl explain svc KIND: Service VERSION: v1 #核心資源v1 DESCRIPTION: Service is a named abstraction of software service (for example, mysql) consisting of local port (for example 3306) that the proxy listens on, and the selector that determines which pods will answer requests sent through the proxy. FIELDS: apiVersion <string> APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources kind <string> Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds metadata <Object> Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata spec <Object> Spec defines the behavior of a service. https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status status <Object> Most recently observed status of the service. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status [root@k8smaster ~]# kubectl explain svc.spec KIND: Service VERSION: v1 RESOURCE: spec <Object> DESCRIPTION: Spec defines the behavior of a service. https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status ServiceSpec describes the attributes that a user creates on a service. FIELDS: clusterIP <string> #默認是自動分配,也可自己指定 clusterIP is the IP address of the service and is usually assigned randomly by the master. If an address is specified manually and is not in use by others, it will be allocated to the service; otherwise, creation of the service will fail. This field can not be changed through updates. Valid values are "None", empty string (""), or a valid IP address. "None" can be specified for headless services when proxying is not required. Only applies to types ClusterIP, NodePort, and LoadBalancer. Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies externalIPs <[]string> externalIPs is a list of IP addresses for which nodes in the cluster will also accept traffic for this service. These IPs are not managed by Kubernetes. The user is responsible for ensuring that traffic arrives at a node with this IP. A common example is external load-balancers that are not part of the Kubernetes system. externalName <string> externalName is the external reference that kubedns or equivalent will return as a CNAME record for this service. No proxying will be involved. Must be a valid RFC-1123 hostname (https://tools.ietf.org/html/rfc1123) and requires Type to be ExternalName. externalTrafficPolicy <string> externalTrafficPolicy denotes if this Service desires to route external traffic to node-local or cluster-wide endpoints. "Local" preserves the client source IP and avoids a second hop for LoadBalancer and Nodeport type services, but risks potentially imbalanced traffic spreading. "Cluster" obscures the client source IP and may cause a second hop to another node, but should have good overall load-spreading. healthCheckNodePort <integer> healthCheckNodePort specifies the healthcheck nodePort for the service. If not specified, HealthCheckNodePort is created by the service api backend with the allocated nodePort. Will use user-specified nodePort value if specified by the client. Only effects when Type is set to LoadBalancer and ExternalTrafficPolicy is set to Local. loadBalancerIP <string> Only applies to Service Type: LoadBalancer LoadBalancer will get created with the IP specified in this field. This feature depends on whether the underlying cloud-provider supports specifying the loadBalancerIP when a load balancer is created. This field will be ignored if the cloud-provider does not support the feature. loadBalancerSourceRanges <[]string> If specified and supported by the platform, this will restrict traffic through the cloud-provider load-balancer will be restricted to the specified client IPs. This field will be ignored if the cloud-provider does not support the feature." More info: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/ ports <[]Object> #我們打算把哪個端口與后端容器端口建立關聯關系 The list of ports that are exposed by this service. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies publishNotReadyAddresses <boolean> publishNotReadyAddresses, when set to true, indicates that DNS implementations must publish the notReadyAddresses of subsets for the Endpoints associated with the Service. The default value is false. The primary use case for setting this field is to use a StatefulSet's Headless Service to propagate SRV records for its Pods without respect to their readiness for purpose of peer discovery. selector <map[string]string> #關聯到哪些pod資源上 Route service traffic to pods with label keys and values matching this selector. If empty or not present, the service is assumed to have an external process managing its endpoints, which Kubernetes will not modify. Only applies to types ClusterIP, NodePort, and LoadBalancer. Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/ sessionAffinity <string> Supports "ClientIP" and "None". Used to maintain session affinity. Enable client IP based session affinity. Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies sessionAffinityConfig <Object> sessionAffinityConfig contains the configurations of session affinity. type <string> type determines how the Service is exposed. Defaults to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, and LoadBalancer. "ExternalName" maps to the specified externalName. "ClusterIP" allocates a cluster-internal IP address for load-balancing to endpoints. Endpoints are determined by the selector or if that is not specified, by manual construction of an Endpoints object. If clusterIP is "None", no virtual IP is allocated and the endpoints are published as a set of endpoints rather than a stable IP. "NodePort" builds on ClusterIP and allocates a port on every node which routes to the clusterIP. "LoadBalancer" builds on NodePort and creates an external load-balancer (if supported in the current cloud) which routes to the clusterIP. More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services---service-types
[root@k8smaster ~]# kubectl explain svc.spec.ports KIND: Service VERSION: v1 RESOURCE: ports <[]Object> DESCRIPTION: The list of ports that are exposed by this service. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies ServicePort contains information on service's port. FIELDS: name <string> #ports的名稱 The name of this port within the service. This must be a DNS_LABEL. All ports within a ServiceSpec must have unique names. This maps to the 'Name' field in EndpointPort objects. Optional if only one ServicePort is defined on this service. nodePort <integer> #指定節點上的端口,只有類型為NodePort時才生效。 The port on each node on which this service is exposed when type=NodePort or LoadBalancer. Usually assigned by the system. If specified, it will be allocated to the service if unused or else creation of the service will fail. Default is to auto-allocate a port if the ServiceType of this Service requires one. More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport port <integer> -required- #這個服務對外提供服務的端口 The port that will be exposed by this service. protocol <string> #協議,默認tcp The IP protocol for this port. Supports "TCP" and "UDP". Default is TCP. targetPort <string> #容器的端口 Number or name of the port to access on the pods targeted by the service. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. If this is a string, it will be looked up as a named port in the target Pod's container ports. If this is not specified, the value of the 'port' field is used (an identity map). This field is ignored for services with clusterIP=None, and should be omitted or set equal to the 'port' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service
2、kubectl explain svc.spec 中的type屬性有四種
a、默認為ClusterIP表示給其分配一個集群ip地址僅用於集群內通信,使用此類型時只有兩個端口有用
1)、port,service地址上的端口
2)、targetPort,pod ip上的端口
[root@k8smaster manifests]# cat redis-svc.yaml apiVersion: v1 kind: Service metadata: name: redis namespace: default spec: slector: app: redis role: logstor clusterIP: 10.97.97.97 #指定固定ClusterIP type: ClusterIP ports: - port: 6379 #Service上的端口 targetPort: 6379 #pod IP上的端口 [root@k8smaster manifests]# kubectl apply -f redis-svc.yaml service/redis created [root@k8smaster manifests]# kubectl get svc -o wide --show-labels NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR LABELS kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 11d <none> component=apiserver,provider=kubernetes redis ClusterIP 10.97.97.97 <none> 6379/TCP 14s app=redis,role=logstor <none> [root@k8smaster manifests]# kubectl describe svc 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","ports":[{"por...Selector: app=redis,role=logstor Type: ClusterIP IP: 10.97.97.97 #集群ip Port: <unset> 6379/TCP TargetPort: 6379/TCP Endpoints: 10.244.1.45:6379 #匹配到的pod IP Session Affinity: None Events: <none>
service創建完只要k8s上的集群的dns服務是存在的那么我們在這兒就可以直接解析他的服務名,服務名的解析方式為每一個服務創建完以后都會在集群的dns中自動動態添加一個資源記錄。不止一個,還會包含服務層svc記錄,A記錄等,添加完后就可以解析,資源記錄的格式為 SVC_NAME(服務名).NS_NAME(名稱空間名).DOMAIN.LTD.(域名后綴),而集群的默認域名后綴是svc.cluster.local. 因此如果我們沒改域名后綴,那么我們每一個服務創建完就是這種域名格式的,比如上面的資源記錄為 redis.default.svc.cluster.local
b、NodePort 接入集群外部的流量,只有使用這種類型時才能使用nodePort,否則是沒用的
[root@k8smaster manifests]# kubectl get pods -o wide --show-labels NAME READY STATUS RESTARTS AGE IP NODE LABELS filebeat-ds-f5drs 1/1 Running 1 3d 10.244.1.44 k8snode1 app=filebeat,controller-revision-hash=2004607620,pod-template-generation=2,release=stable filebeat-ds-n9hgz 1/1 Running 1 3d 10.244.2.34 k8snode2 app=filebeat,controller-revision-hash=2004607620,pod-template-generation=2,release=stable liveness-httpget-pod 1/1 Running 2 6d 10.244.2.38 k8snode2 <none> myapp-deploy-69b47bc96d-6x987 1/1 Running 1 4d 10.244.2.35 k8snode2 app=myapp,pod-template-hash=2560367528,release=canary myapp-deploy-69b47bc96d-f2cjq 1/1 Running 1 4d 10.244.1.47 k8snode1 app=myapp,pod-template-hash=2560367528,release=canary myapp-deploy-69b47bc96d-tlq6v 1/1 Running 1 4d 10.244.1.48 k8snode1 app=myapp,pod-template-hash=2560367528,release=canary myapp-deploy-69b47bc96d-vx46z 1/1 Running 1 4d 10.244.1.46 k8snode1 app=myapp,pod-template-hash=2560367528,release=canary myapp-deploy-69b47bc96d-vzdpt 1/1 Running 1 4d 10.244.2.36 k8snode2 app=myapp,pod-template-hash=2560367528,release=canary nginx-deploy-5b595999-zgjgz 1/1 Running 0 3d 10.244.1.52 k8snode1 pod-template-hash=16151555,run=nginx-deploy poststart-pod 1/1 Running 30 6d 10.244.2.37 k8snode2 <none> readiness-httpget-pod 1/1 Running 1 6d 10.244.2.39 k8snode2 <none> redis-5b5d6fbbbd-kk782 1/1 Running 1 3d 10.244.1.45 k8snode1 app=redis,pod-template-hash=1618296668,role=logstor
[root@k8smaster manifests]# cat myapp.svc.yaml apiVersion: v1 kind: Service metadata: name: myapp namespace: default spec: selector: app: myapp release: canary clusterIP: 10.99.99.99 #指定固定ClusterIP type: NodePort ports: - port: 80 #Service上的端口 targetPort: 80 #pod IP上的端口 nodePort: 30080 #節點端口,也可以不指定讓系統動態分配 [root@k8smaster manifests]# kubectl apply -f myapp.svc.yaml service/myapp configured [root@k8smaster manifests]# kubectl get svc -o wide --show-labels NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR LABELS kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 12d <none> component=apiserver,provider=kubernetes myapp NodePort 10.99.99.99 <none> 80:30080/TCP 13m app=myapp,release=canary <none> redis ClusterIP 10.97.97.97 <none> 6379/TCP 22h app=redis,role=logstor <none> [root@k8smaster 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":{"cl usterIP":"10.99.99.99","ports":[{"nod...Selector: app=myapp,release=canary Type: NodePort IP: 10.99.99.99 Port: <unset> 80/TCP TargetPort: 80/TCP NodePort: <unset> 30080/TCP Endpoints: 10.244.1.46:80,10.244.1.47:80,10.244.1.48:80 + 2 more... Session Affinity: None External Traffic Policy: Cluster Events: <none>
重新打開一個shell訪問集群任意一個節點,可以看到其還有負載均衡的效果,並且流量是經過了好幾級轉換,首先由nodeport轉換為service port,再由service轉成pod port。
[root@k8smaster ~]# while true; do curl http://192.168.10.10:30080/hostname.html; sleep 5; done myapp-deploy-69b47bc96d-6x987 myapp-deploy-69b47bc96d-6x987 myapp-deploy-69b47bc96d-vx46z myapp-deploy-69b47bc96d-f2cjq myapp-deploy-69b47bc96d-6x987 myapp-deploy-69b47bc96d-vzdpt myapp-deploy-69b47bc96d-vzdpt ^C
c、LoadBalance(負載均衡及服務(LBaas)的一鍵調用):表示我們把k8s部署在虛擬機上而虛擬機是工作在雲環境中,而我們雲環境支持lb負載均衡器時使用。自動觸發在外部創建一個負載均衡器。 比如在阿里雲上買了四個虛擬主機,同時又買了阿里雲的LBaas的服務,在這四個vps上部署了k8s集群,然后這個k8s集群可以與其底層的公有雲IaaS公有雲的api相交互。其自身有這個能力,其能去調底層的IAAS雲計算層中的API,調的時候其能夠請求去創建一個外置的負載均衡器,比如我們有四個節點,一個master,正常工作的是三個節點,在這三個節點上都使用的是同一個nodePort對集群外提供服務,它會自動請求底層IAAS用純軟件的方式做一個負載均衡器並且為這個負載均衡器提供的配置信息是我們本機這三個節點的(注意是節點IP)節點端口上提供的相應服務,可以自動通過底層IAAS的api創建這個軟負載均衡器的時候提供后端有哪幾個節點,因此,回頭用戶通過雲計算環境之外的客戶端來訪問阿里雲的內部的LBAAS生成的負載均衡器時由該負載均衡器來調度到后端幾個節點的nodePort上,然后由nodeport轉發給service,再由service在集群內部負載均衡至pod上,因此可以發現其有兩級負載均衡,第一級是將用戶請求負載給多個node中的某一個,再由node通過service反代給集群內部的多個pod中的某一個。
d、ExternalName :將集群外部的服務引入至集群內部在集群內部直接使用。假如我們有個k8s集群,有三個工作節點,三個節點上有一些節點上的pod資源是作為客戶端使用的,當此客戶端訪問某一服務時,此服務應該是由其它pod提供的,但有這種可能性,我們pod訪問服務集群中沒有,但是集群外有個服務,比如在我們的本地局域網環境中,但是是在k8s集群之外,或者在互聯網上有一個服務,比如dns等,我們期望這個服務讓集群內能夠訪問到,集群內一般用的都是私網地址,就算我們能夠將請求報文路由出去離開本地網絡到外部去那么外部響應報文也回不來,這樣干是沒法正常通信的,因此,ExternalName就是用來實現我們在集群中建一個服務(service),這個service的端點不是本地port而是service關聯到外部服務上去了因此我們客戶端訪問service時由service通過層級轉換,包括nodePort轉換請求到外部的服務中,外部服務先回給nodeIP,再由nodeIP轉交給service,再由service轉交給pod client,從而讓pod能夠訪問集群外部的服務。這樣就能讓我們集群內的pod像使用集群內部的服務一樣來使用集群外部的服務。對此種服務來講我們的cluster IP作用在於pod client內部解析時使用,更重要的是ExternalName此時很關鍵,因為ExternalName確實應該是一個name而不是一個IP,並且此name還必須要被我們dns服務所解析才能夠被訪問,所以ExternalName引入時有這么一個基本限制(了解一下就好),在svc.spec.中有如下字段
[root@k8smaster /]# kubectl explain svc.spec KIND: Service VERSION: v1 RESOURCE: spec <Object> DESCRIPTION: Spec defines the behavior of a service. https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status ServiceSpec describes the attributes that a user creates on a service. FIELDS: clusterIP <string> clusterIP is the IP address of the service and is usually assigned randomly by the master. If an address is specified manually and is not in use by others, it will be allocated to the service; otherwise, creation of the service will fail. This field can not be changed through updates. Valid values are "None", empty string (""), or a valid IP address. "None" can be specified for headless services when proxying is not required. Only applies to types ClusterIP, NodePort, and LoadBalancer. Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies externalIPs <[]string> externalIPs is a list of IP addresses for which nodes in the cluster will also accept traffic for this service. These IPs are not managed by Kubernetes. The user is responsible for ensuring that traffic arrives at a node with this IP. A common example is external load-balancers that are not part of the Kubernetes system. externalName <string> #這個類型也只有類型為externalName時才有用,這個name解析出來應該是一個記錄,這個CNAME能夠被我們真正的互聯網上的dns服務器或者能被我們本地服務器在根域解析為A記錄進行互相通信 externalName is the external reference that kubedns or equivalent will return as a CNAME record for this service. No proxying will be involved. Must be a valid RFC-1123 hostname (https://tools.ietf.org/html/rfc1123) and requires Type to be ExternalName. externalTrafficPolicy <string> externalTrafficPolicy denotes if this Service desires to route external traffic to node-local or cluster-wide endpoints. "Local" preserves the client source IP and avoids a second hop for LoadBalancer and Nodeport type services, but risks potentially imbalanced traffic spreading. "Cluster" obscures the client source IP and may cause a second hop to another node, but should have good overall load-spreading. healthCheckNodePort <integer> healthCheckNodePort specifies the healthcheck nodePort for the service. If not specified, HealthCheckNodePort is created by the service api backend with the allocated nodePort. Will use user-specified nodePort value if specified by the client. Only effects when Type is set to LoadBalancer and ExternalTrafficPolicy is set to Local. loadBalancerIP <string> Only applies to Service Type: LoadBalancer LoadBalancer will get created with the IP specified in this field. This feature depends on whether the underlying cloud-provider supports specifying the loadBalancerIP when a load balancer is created. This field will be ignored if the cloud-provider does not support the feature. loadBalancerSourceRanges <[]string> If specified and supported by the platform, this will restrict traffic through the cloud-provider load-balancer will be restricted to the specified client IPs. This field will be ignored if the cloud-provider does not support the feature." More info: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/ ports <[]Object> The list of ports that are exposed by this service. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies publishNotReadyAddresses <boolean> publishNotReadyAddresses, when set to true, indicates that DNS implementations must publish the notReadyAddresses of subsets for the Endpoints associated with the Service. The default value is false. The primary use case for setting this field is to use a StatefulSet's Headless Service to propagate SRV records for its Pods without respect to their readiness for purpose of peer discovery. selector <map[string]string> Route service traffic to pods with label keys and values matching this selector. If empty or not present, the service is assumed to have an external process managing its endpoints, which Kubernetes will not modify. Only applies to types ClusterIP, NodePort, and LoadBalancer. Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/ sessionAffinity <string> Supports "ClientIP" and "None". Used to maintain session affinity. Enable client IP based session affinity. Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies sessionAffinityConfig <Object> sessionAffinityConfig contains the configurations of session affinity. type <string> type determines how the Service is exposed. Defaults to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, and LoadBalancer. "ExternalName" maps to the specified externalName. "ClusterIP" allocates a cluster-internal IP address for load-balancing to endpoints. Endpoints are determined by the selector or if that is not specified, by manual construction of an Endpoints object. If clusterIP is "None", no virtual IP is allocated and the endpoints are published as a set of endpoints rather than a stable IP. "NodePort" builds on ClusterIP and allocates a port on every node which routes to the clusterIP. "LoadBalancer" builds on NodePort and creates an external load-balancer (if supported in the current cloud) which routes to the clusterIP. More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services---service-types
另外,在我們svc實現負載均衡時還支持sessionAffinity(會話聯系,上述explain中有),默認值為None,因此其是隨機基於iptables來調度的,若我們將其值定義成ClientIP則表示把來自同一個客戶端IP的請求始終調度到同一個后端pod上去。
[root@k8smaster manifests]# kubectl patch svc myapp -p '{"spec":{"sessionAffinity":"ClientIP"}}' service/myapp patched [root@k8smaster ~]# while true; do curl http://192.168.10.10:30080/hostname.html; sleep 3; done myapp-deploy-67f6f6b4dc-tf2zm myapp-deploy-67f6f6b4dc-g694w myapp-deploy-67f6f6b4dc-g694w myapp-deploy-67f6f6b4dc-tf2zm myapp-deploy-67f6f6b4dc-tr4sn myapp-deploy-67f6f6b4dc-lqpxm myapp-deploy-67f6f6b4dc-lqpxm #這是打補丁后后的結果 myapp-deploy-67f6f6b4dc-g694w myapp-deploy-67f6f6b4dc-g694w myapp-deploy-67f6f6b4dc-g694w myapp-deploy-67f6f6b4dc-g694w myapp-deploy-67f6f6b4dc-g694w myapp-deploy-67f6f6b4dc-g694w myapp-deploy-67f6f6b4dc-g694w ^C
[root@k8smaster 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":{"cl usterIP":"10.99.99.99","ports":[{"nod...Selector: app=myapp,release=canary Type: NodePort IP: 10.99.99.99 Port: <unset> 80/TCP TargetPort: 80/TCP NodePort: <unset> 30080/TCP Endpoints: 10.244.1.53:80,10.244.1.54:80,10.244.1.55:80 + 2 more... Session Affinity: ClientIP #可以看到我們打的補丁已經生效 External Traffic Policy: Cluster Events: <none>
三、還有一種service叫無頭service(headless),我們此前使用service一直是客戶端pod訪問service時解析的應該是service的名稱,每一個service應該有其名稱,並且其解析結果應該是其ClusterIP,一般解析ClusterIP一般只有一個。但是我們也可以這樣干,把中間層去掉,每一個pod也有其自己名稱,我們可以在解析service IP時,將其解析給后端的pod IP,這種service就叫無頭service。創建這種service時我們一樣只需要指定明確定義clusterIP,並且指定其值為None。
[root@k8smaster manifests]# cat myapp-svc-headless.yaml apiVersion: v1 kind: Service metadata: name: myapp-svc namespace: default spec: selector: app: myapp release: canary clusterIP: "None" ports: - port: 80 #Service上的端口 targetPort: 80 #pod IP上的端口 [root@k8smaster manifests]# kubectl apply -f myapp-svc-headless.yaml service/myapp-svc created [root@k8smaster manifests]# kubectl get svc -o wide --show-labels NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR LABELS kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 16d <none> component=apiserver,provider=kubernetes myapp NodePort 10.99.99.99 <none> 80:30080/TCP 3d app=myapp,release=canary <none> myapp-svc ClusterIP None <none> 80/TCP 16s app=myapp,release=canary <none> redis ClusterIP 10.97.97.97 <none> 6379/TCP 4d app=redis,role=logstor <none> #首先我們查看我們pod 的ip [root@k8smaster manifests]# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE filebeat-ds-f5drs 1/1 Running 1 7d 10.244.1.44 k8snode1 filebeat-ds-n9hgz 1/1 Running 1 7d 10.244.2.34 k8snode2 liveness-httpget-pod 1/1 Running 2 10d 10.244.2.38 k8snode2 myapp-deploy-67f6f6b4dc-g694w 1/1 Running 0 49m 10.244.1.55 k8snode1 myapp-deploy-67f6f6b4dc-k6rbp 1/1 Running 0 51m 10.244.1.54 k8snode1 myapp-deploy-67f6f6b4dc-lqpxm 1/1 Running 0 51m 10.244.2.40 k8snode2 myapp-deploy-67f6f6b4dc-tf2zm 1/1 Running 0 49m 10.244.2.41 k8snode2 myapp-deploy-67f6f6b4dc-tr4sn 1/1 Running 0 51m 10.244.1.53 k8snode1 nginx-deploy-5b595999-zgjgz 1/1 Running 0 7d 10.244.1.52 k8snode1 poststart-pod 1/1 Running 54 10d 10.244.2.37 k8snode2 readiness-httpget-pod 1/1 Running 1 10d 10.244.2.39 k8snode2 redis-5b5d6fbbbd-kk782 1/1 Running 1 7d 10.244.1.45 k8snode1 #我們通過dig解析 [root@k8smaster ~]# dig -t A myapp-svc.default.svc.cluster.local. #這是我們svc的名字 @10.96.0.10 #這是我們coredns pod 的IP ; <<>> DiG 9.9.4-RedHat-9.9.4-50.el7 <<>> -t A myapp-svc.default.svc.cluster.local. @10.96.0.10 ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 41276 ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 5, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 4096 ;; QUESTION SECTION: ;myapp-svc.default.svc.cluster.local. IN A ;; ANSWER SECTION: #可以看到解析出了5條A記錄 myapp-svc.default.svc.cluster.local. 5 IN A 10.244.1.53 myapp-svc.default.svc.cluster.local. 5 IN A 10.244.1.54 myapp-svc.default.svc.cluster.local. 5 IN A 10.244.1.55 myapp-svc.default.svc.cluster.local. 5 IN A 10.244.2.40 myapp-svc.default.svc.cluster.local. 5 IN A 10.244.2.41 ;; Query time: 75 msec ;; SERVER: 10.96.0.10#53(10.96.0.10) ;; WHEN: Sat May 25 10:26:14 CST 2019 ;; MSG SIZE rcvd: 319
但是可以發現service有個問題,當我們定義完service后,我們要訪問service后端的pod需要多級調度或代理,因此如果我們要建一個https服務的話我們會發現我們每一個myapp都要配置為https的主機,事實上k8s還有一種引入集群外部流量的方式叫做 ingress ,我們service是4層調度,但是ingress是七層調度器,它利用一種七層pod實現將外部流量引入到內部來,但是事實上他也脫離不了service的工作。作為ingress作為七層調度時我們必須要用Pod中的運行的七層服務功能的應用來調度,可以用nginx,haproxy等等