28、K8S-服务发现-CoreDNS、sessionAffinity、headless
1、基础知识
1.1、需求
在传统的系统部署中,服务运行在一个固定的已知的 IP 和端口上,如果一个服务需要调用另外一个服
务,可以通过地址直接调用,但是,在虚拟化或容器话的环境中,以我们的k8s集群为例,如果存在个位数个
service我们可以很快的找到对应的clusterip地址,进而找到指定的资源,虽然ip地址不容易记住,因为
service在创建的时候会为每个clusterip分配一个名称,我们同样可以根据这个名称找到对应的服务。但
是,如果我们的集群中有1000个Service,我们如何找到指定的service呢?
尤其是在k8s集群中,服务实例的启动和销毁是很频繁的,服务地址在动态的变化,所以传统的方式配置
DNS解析记录就不太好实现了。所以针对于这种场景,我们如果需要将请求发送到动态变化的服务实例上,可
以通过一下两个步骤来实现:
服务注册 — 创建服务实例后,主动将当前服务实例的信息,存储到一个集中式的服务管理中心。
服务发现 — 当A服务需要找未知的B服务时,先去服务管理中心查找B服务地址,然后根据该地址找到B服务
1.2、服务注册和发现的解决方案
专用于kubernetes集群中的服务注册和发现的解决方案就是KubeDNS。kubeDNS自从k8s诞生以来,其方案 的具体实现样式前后经历了三代,分别是 SkyDNS、KubeDNS、CoreDNS(目前默认的)。 对于k8s的容器环境来说,之前用的是Docker,那么对于docker本身来说,我们可以基于 手工环境变量 + --link参数方式实现有限的发现功能,所以环境变量也是一种服务发现方案。 所以,对于k8s环境来说,它主要有两种服务发现机制: DNS解析 环境变量
2、方案解析
2.1、环境变量类型
2.1.1、Kubernetes Service环境变量
Kubernetes为每个Service资源生成包括以下形式的环境变量在内一系列环境变量,在同一名称空间中创建的Pod对象都会自动拥有这些变量: {SVCNAME}_SERVICE_HOST、{SVCNAME}_SERVICE_PORT 举例:default名称空间,创建名为demoapp的Service,意味着default名称空间下的每个Pod内部会被自动注入 DEMOAPP_SERVICE_HOST:ClusterIP, DEMOAPP_SERVICE_PORT=80 注意:如果先创建pod然后关联到service是不生效的。
2.1.2、Docker Link形式的环境变量
Docker使用--link选项实现容器连接时所设置的环境变量形式,具体使用方式请参考Docker的相关文档。在创建Pod对象时,kubernetes也会把与此形式兼容的一系列环境变量注入到Pod对象中。
由于k8s准备下一个版本禁用dockershim,所以后续的k8s环境中,可能不再支持这种样式了。
2.2、环境变量-实践
2.2.1、创建service环境
cat >service-test.yml<<'EOF' kind: Service apiVersion: v1 metadata: name: service-test spec: selector: app: my-nginx ports: - name: http protocol: TCP port: 80 targetPort: 80 EOF
2.2.2、创建pod对象,通过标签将其添加到service环境
master1 ]# kubectl apply -f service-test.yml service/service-test created master1 ]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6d17h service-test ClusterIP 10.102.251.155 <none> 80/TCP 2s master1 ~]# kubectl create deployment my-nginx --image=192.168.10.33:80/k8s/pod_test:v0.1 --replicas=3 注意: 必须先创建service,后创建deployment,否则的话,无法演示后面的效果
2.2.3、分析环境变量
master1 ~]# kubectl get pod NAME READY STATUS RESTARTS AGE my-nginx-7c7dc654f6-7kqwf 1/1 Running 0 30s my-nginx-7c7dc654f6-dnmnf 1/1 Running 0 30s my-nginx-7c7dc654f6-qv6mr 1/1 Running 0 30s master1 ~]# kubectl exec my-nginx-7c7dc654f6-qv6mr -- printenv PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin HOSTNAME=my-nginx-7c7dc654f6-qv6mr DEPLOYENV=Production RELEASE=Stable PS1=[\u@\h \w]\$ SERVICE_TEST_PORT=tcp://10.102.251.155:80 SERVICE_TEST_PORT_80_TCP_PORT=80 SERVICE_TEST_PORT_80_TCP_ADDR=10.102.251.155 KUBERNETES_PORT=tcp://10.96.0.1:443 KUBERNETES_PORT_443_TCP_PROTO=tcp KUBERNETES_PORT_443_TCP_PORT=443 KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443 KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1 SERVICE_TEST_SERVICE_HOST=10.102.251.155 SERVICE_TEST_SERVICE_PORT=80 SERVICE_TEST_PORT_80_TCP=tcp://10.102.251.155:80 SERVICE_TEST_PORT_80_TCP_PROTO=tcp KUBERNETES_SERVICE_HOST=10.96.0.1 KUBERNETES_SERVICE_PORT=443 KUBERNETES_SERVICE_PORT_HTTPS=443 SERVICE_TEST_SERVICE_PORT_HTTP=80 HOME=/root # 在容器内部,可以直接使用变量名来进行服务的访问,访问环境变量. [root@my-nginx-7c7dc654f6-qv6mr /]# curl ${SERVICE_TEST_SERVICE_HOST}:${SERVICE_TEST_SERVICE_PORT} kubernetes pod-test v0.1!! ClientIP: 10.244.3.216, ServerName: my-nginx-7c7dc654f6-7kqwf, ServerIP: 10.244.4.94!
2.2、DNS解析-实践
2.2.1、实现方式
Kubelet会为创建的每一个容器于/etc/resolv.conf配置文件中生成DNS查询客户端依赖到的必要配置, 相关的配置信息源自于kubelet的配置参数,各容器的DNS服务器由clusterDNS参数的值设定,它的取值为 kube-system名称空间中的Service对象kube-dns的ClusterIP,默认为10.96.0.10,而DNS搜索域的 值由clusterDomain参数的值设定,若部署Kubernetes集群时未特别指定,其值将为cluster.local、 svc.cluster.local和NAMESPACENAME.svc.cluster.local,下面的示例取自集群上的一个随机选择的Pod中的容器。 [root@my-nginx-7c7dc654f6-qv6mr /]# cat /etc/resolv.conf search default.svc.cluster.local svc.cluster.local cluster.local nameserver 10.96.0.10 options ndots:5 上述search参数中指定的DNS各搜索域,是以次序指定的几个域名后缀,它们各自的如下所示。 <ns>.svc.<zone>:附带有特定名称空间的域名,例如default.svc.cluster.local; svc. <zone>:附带了Kubernetes标识Service专用子域svc的域名,例如svc.cluster.local; <zone>:集群本地域名,例如cluster.local。 ndots:5,表示:如果手工查询时候给的域名包含的点“.”,不到5个,那么进行DNS查找,将使用非完全限定名称 即 <手工输入域名> 或者 <手工输入域名>.<search 部分给定的域名后缀> 如果你查询的域名包含点数大于等于5,那么DNS查询,默认会使用绝对域名进行查询。 即 <手工输入域名>
2.2.3、基于DNS的服务发现-3个类型的DNS资源记录
基于DNS的服务发现,对于每个Service对象,都会具有以下3个类型的DNS资源记录。 1)、根据ClusterIP的地址类型,为IPv4生成固定格式的 A记录,为IPv6生成AAAA记录; <service>.<ns>.svc.<zone>. <ttl> IN A <cluster-ip> <service>.<ns>.svc.<zone>. <ttl> IN AAAA <cluster-ip> 举例: demoapp, demoapp.default.svc.cluster.local. 注意: cluster.local 是我们在初始化k8s集群中,自己通过dnsDomain属性定制的。 2)、为每个定义了名称的端口生成一个SRV记录,未命名的端口号则不具有该记录; _<port>._<proto>.<service>.<ns>.svc.<zone>. <ttl> IN SRV <weight> <priority> <port-number> <service>.<ns>.svc.<zone>. 3)、对于每个给定的A记录或AAAA记录都要生成PTR记录,它们各自的格式如下所示: <d>.<c>.<b>.<a>.in-addr.arpa. <ttl> IN PTR <service>.<ns>.svc.<zone>. h4.h3.h2.h1.g4.g3.g2.g1.f4.f3.f2.f1.e4.e3.e2.e1.d4.d3.d2.d1.c4.c3.c2.c1.b4.b3.b2 .b1.a4.a3.a2.a1.ip6.arpa <ttl> IN PTR <service>.<ns>.svc.<zone>. 例如,前面在default名称空间中创建Service对象demoapp-svc的地址为10.97.72.1,且为TCP协议的 80端口取名http,对于默认的cluster.local域名来说,此它会拥有如下3个DNS资源记录。 A记录:demoapp-svc.default.svc.cluster.local. 30 IN A 10.97.72.1; SRV记录:_http._tcp.demoapp-svc.default.svc.cluster.local. 30 IN SRV 0 100 80 demoapp-svc.default.svc.cluster.local. PTR记录:1.72.97.10.in-addr.arpa. 30 IN PTR demoappsvc.default.svc.cluster.local.
2.2.4、实践
master1 ~]# kubectl -n kube-system describe svc kube-dns ... Port: dns 53/UDP TargetPort: 53/UDP Endpoints: 10.244.0.12:53,10.244.1.6:53 Port: dns-tcp 53/TCP TargetPort: 53/TCP Endpoints: 10.244.0.12:53,10.244.1.6:53 Port: metrics 9153/TCP TargetPort: 9153/TCP Endpoints: 10.244.0.12:9153,10.244.1.6:9153 ... master1 ~]# kubectl -n kube-system get pod -l k8s-app=kube-dns -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES coredns-75c9cf9bd7-qcwcf 1/1 Running 0 6d17h 10.244.1.6 master2 <none> <none> coredns-75c9cf9bd7-sdsq5 1/1 Running 0 6d17h 10.244.0.12 master1 <none> <none> ------ # pod里面查看相关的配置信息 [root@my-nginx-7c7dc654f6-qv6mr /]# cat /etc/hosts # Kubernetes-managed hosts file. ... 10.244.3.216 my-nginx-7c7dc654f6-qv6mr [root@my-nginx-7c7dc654f6-qv6mr /]# cat /etc/resolv.conf search default.svc.cluster.local svc.cluster.local cluster.local nameserver 10.96.0.10 options ndots:5 [root@my-nginx-7c7dc654f6-qv6mr /]# curl service-test kubernetes pod-test v0.1!! ClientIP: 10.244.3.1, ServerName: my-nginx-7c7dc654f6-qv6mr, ServerIP: 10.244.3.216! [root@my-nginx-7c7dc654f6-qv6mr /]# curl service-test.default.svc.cluster.local kubernetes pod-test v0.1!! ClientIP: 10.244.3.216, ServerName: my-nginx-7c7dc654f6-dnmnf, ServerIP: 10.244.3.215! [root@my-nginx-7c7dc654f6-qv6mr /]# curl service-test.default.svc.cluster.local. kubernetes pod-test v0.1!! ClientIP: 10.244.3.216, ServerName: my-nginx-7c7dc654f6-7kqwf, ServerIP: 10.244.4.94! 注意: 一旦我们将service删除后,就无法正常删除了,
3、DNS记录解析-实践
3.1、正向解析记录
master1 ~]# kubectl exec -it my-nginx-7c7dc654f6-qv6mr -- /bin/sh [root@my-nginx-7c7dc654f6-qv6mr /]# nslookup > service-test.default.svc.cluster.local Server: 10.96.0.10 Address: 10.96.0.10#53 Name: service-test.default.svc.cluster.local Address: 10.102.251.155 > kube-dns.kube-system.svc.cluster.local Server: 10.96.0.10 Address: 10.96.0.10#53 Name: kube-dns.kube-system.svc.cluster.local Address: 10.96.0.10
3.2、反向解析记录
# 反向解析 [root@master1 ~]# kubectl exec -it my-nginx-7c7dc654f6-qv6mr -- /bin/sh [root@my-nginx-7c7dc654f6-qv6mr /]# nslookup > 10.244.3.215 215.3.244.10.in-addr.arpa name = 10-244-3-215.service-test.default.svc.cluster.local. > 10.244.3.216 216.3.244.10.in-addr.arpa name = 10-244-3-216.service-test.default.svc.cluster.local. > 10.244.4.94 94.4.244.10.in-addr.arpa name = 10-244-4-94.service-test.default.svc.cluster.local.
3.3、查询域名的A记录
# 查询域名的A记录,对应SVC IP地址 [root@my-nginx-7c7dc654f6-qv6mr /]# nslookup -query=A service-test.default.svc.cluster.local Server: 10.96.0.10 Address: 10.96.0.10#53 Name: service-test.default.svc.cluster.local Address: 10.102.251.155 master2 ~]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6d18h service-test ClusterIP 10.102.251.155 <none> 80/TCP 44m
4、CoreDNS配置
4.1、配置解析
4.1.1、查询configmap的coredns
# coredns的配置依然是存放在 configmap master2 ~]# kubectl -n kube-system get cm | grep coredns coredns 1 6d18h
4.1.2、查看coredns配置
# 查询CoreDNS配置 master2 ~]# kubectl -n kube-system describe configmap coredns ... ==== Corefile: ---- .:53 { errors health { # 健康检测 lameduck 5s } ready kubernetes cluster.local in-addr.arpa ip6.arpa { # 解析配置 pods insecure fallthrough in-addr.arpa ip6.arpa ttl 30 } prometheus :9153 forward . /etc/resolv.conf { max_concurrent 1000 # 转发配置,如果集群内部无法解析的话,交由宿主机文件解析 } cache 30 loop reload # 自动加载 loadbalance # dns记录负载 } 对于我们之前所说的企业级的dns解决方案,我们可以通过 forward 来实现,格式如下 forward <域名> <转发后的地址> { # 转发配置,如果集群内部无法解析 的话,交由宿主机文件解析 max_concurrent 最大连接配置 except 排除的域名 } 注意: 如果仅仅对某个域名进行转发的话,只需要将 <域名> 部分设置为指定的域名即可。 生产中不推荐直接将 "." 的转发地址使用公网的dns地址,推荐在当前主机 的/etc/resolv.conf中配置外网,实现间接的效果 # 添加dns解析 hosts { 192.168.8.100 www.example.com fallthrough }
4.2、实践:不使用默认的转发策略,使用自定义的转发策略
4.2.1、编辑coredns配置文件
master1 ~]# kubectl edit configmap coredns -n kube-system ... prometheus :9153 forward . /etc/resolv.conf { max_concurrent 1000 except www.baidu.com. } hosts { 192.168.10.33 harbor.test.com fallthrough } cache 30 ... 注意: 多个dns地址间用空格隔开 排除的域名最好在末尾添加 “.”,对于之前的旧版本来说可能会出现无法保存的现象。
4.2.2、同步dns的配置信息
master1 ~]# kubectl -n kube-system delete pod -l k8s-app=kube-dns
4.2.3、测试自定义DNS解析是否正常
# 删除svc,重新创建 master1 ]# kubectl delete service service-test cat >service-test.yml<<'EOF' kind: Service apiVersion: v1 metadata: name: service-test spec: selector: app: my-nginx ports: - name: http protocol: TCP port: 80 targetPort: 80 EOF kubectl apply -f service-test.yml kubectl create deployment my-nginx --image=192.168.10.33:80/k8s/pod_test:v0.1 --replicas=3 master1 ]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6d19h service-test ClusterIP 10.104.128.244 <none> 80/TCP 4s [root@master1 service]# kubectl get deployments.apps NAME READY UP-TO-DATE AVAILABLE AGE my-nginx 3/3 3 3 7s master1 ]# kubectl get pod NAME READY STATUS RESTARTS AGE my-nginx-768865db6b-fbpk9 1/1 Running 0 13s my-nginx-768865db6b-lftcm 1/1 Running 0 13s my-nginx-768865db6b-n6mcb 1/1 Running 0 13s # 验证域名配置是否生效 [root@my-nginx-768865db6b-fbpk9 /]# ping www.baidu.com ping: bad address 'www.baidu.com' [root@my-nginx-768865db6b-fbpk9 /]# ping harbor.test.com PING harbor.test.com (192.168.10.33): 56 data bytes 64 bytes from 192.168.10.33: seq=0 ttl=63 time=1.279 ms
5、会话粘滞
5.1、需求
kubernetes的Service功能可以通过多种方式将pod对象提供的服务对外开放,但是当pod多的时候, 用户访问service的时候,service默认是按照轮训的情况进行转发,如果我们的用户请求由一定的会话要 求,即希望同一个客户能访问一个pod的时候,我们可以使用service的affinity机制来实现,它能将同一 个客户端的请求始终转发至同一个后端Pod对象,它是由kube-proxy的ipvs机制来实现的。 默认情况下,service所提供的会话粘性效果默认在10800s(3小时)后会重新调度,而且仅能基于客户端IP进行识别,调度粒度较粗,不推荐使用
5.2、属性解析
5.2.1、属性信息
spec.sessionAffinity,定义粘性会话的类型,可为 None 和 ClientIP spec.sessionAffinityConfig.clientIP,配置会话保持时长,默认10800s 范围 1-86400
5.2.2、配置样式
sessionAffinity: ClientIP sessionAffinityConfig: clientIP: timeoutSeconds: 10800
5.3、默认没有使用会话粘滞
5.3.1、删除原来的配置
# 删除原来的配置 kubectl delete deployments.apps my-nginx kubectl delete service service-test
5.3.2、重新创建service和deployment
# 重新创建service和 deployment cat >service-test.yml<<'EOF' kind: Service apiVersion: v1 metadata: name: service-test spec: selector: app: my-nginx ports: - name: http protocol: TCP port: 80 targetPort: 80 EOF kubectl apply -f service-test.yml kubectl create deployment my-nginx --image=192.168.10.33:80/k8s/pod_test:v0.1 --replicas=3
5.3.3、访问测试
# 可以看到,每次都是不一样的IP地址 master1 ]# for i in {1..3}; do curl 10.104.82.193; done kubernetes pod-test v0.1!! ClientIP: 10.244.0.0, ServerName: my-nginx-5fc6bb8b-7z4fk, ServerIP: 10.244.3.223! kubernetes pod-test v0.1!! ClientIP: 10.244.0.0, ServerName: my-nginx-5fc6bb8b-6mrn2, ServerIP: 10.244.3.222! kubernetes pod-test v0.1!! ClientIP: 10.244.0.0, ServerName: my-nginx-5fc6bb8b-45bdt, ServerIP: 10.244.4.98!
5.4、修改会话配置-实现会话粘滞
5.4.1、定义资源配置清单
cat >nginx-service.yml<<'EOF' apiVersion: v1 kind: Service metadata: name: nginx-service spec: type: LoadBalancer ports: - port: 80 selector: app: my-nginx sessionAffinity: ClientIP EOF
5.4.2、应用资源配置清单
master1 ]# kubectl apply -f nginx-service.yml service/nginx-service created master1 ]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6d19h nginx-service LoadBalancer 10.101.186.100 <pending> 80:30140/TCP 5s
5.4.3、验证是否生效
master1 ]# kubectl apply -f nginx-service.yml service/nginx-service created master1 ]# kubectl get service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6d19h nginx-service ClusterIP 10.101.164.147 <none> 80/TCP 8s master1 ]# for i in {1..3};do curl 10.101.164.147;done kubernetes pod-test v0.1!! ClientIP: 10.244.0.0, ServerName: my-nginx-5fc6bb8b-45bdt, ServerIP: 10.244.4.98! kubernetes pod-test v0.1!! ClientIP: 10.244.0.0, ServerName: my-nginx-5fc6bb8b-45bdt, ServerIP: 10.244.4.98! kubernetes pod-test v0.1!! ClientIP: 10.244.0.0, ServerName: my-nginx-5fc6bb8b-45bdt, ServerIP: 10.244.4.98!
6、Headless Service-无头服务
6.1、需求
到现在为止,我们所遇到的用户访问,用户将请求交给service对象的流程是,kube-proxy其实是将
用户访问的域名解析给了service对象所对应的cluster_ip,然后再由cluster_ip将请求转发给后端的
pod,然后在交给pod内部的容器,这样一个请求经过了两层转发才到容器里面的服务,流程有些繁琐。
目前我们集群的资源调度主要是针对的是无状态的服务,但是对于一些特定场景状态服务,我们虽然可以
基于会话粘滞来实现,但是毕竟需要经过两层跳转,如果转发量太大的话,效果不是太好。
对于某些特殊的负载均衡场景,既然pod有专门的ip地址,那么我可以无需经过cluster_ip,直接定向
到Pod_ip就行了,那么和普通Service相比,这种没有配置ClusterIP项的场景,我们称之为Headless
Service。
6.2、简介
无头服务场景下,k8s会将一个集群内部的所有成员提供唯一的DNS域名来作为每个成员的网络标识,集 群内部成员之间使用域名通信,这个时候,就特别依赖service的selector属性配置了。 无头服务管理的域名是如下的格式: $(service_name).$(k8s_namespace).svc.cluster.local。 dns解析记录 A记录 <a>-<b>-<c>-<d>.<service>.<ns>.svc.<zone> A PodIP PodIP的PTR反解析记录 <d>.<c>.<b>.<a>.in-addr.arpa IN PTR <hostname>.<service>.<ns>.svc.<zone>
关键点: svc_name的解析结果从常规Service的ClusterIP,转为各个Pod的IP地址; 反解,则从常规的clusterip解析为service name,转为从podip到hostname, <a>-<b>-<c>-<d>.<service>.<ns>.svc.<zone> <hostname>指的是a-b-c-d格式,而非Pod自己的主机名;
6.3、实践
6.3.1、方法1:命令行的创建
master1 ]# kubectl create service clusterip service-headless-cmd --clusterip="None" service/service-headless created master1 ]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6d19h service-headless-cmd ClusterIP None <none> <none> 3s
6.3.2、方法2:资源配置清单的创建
cat >service-headless.yml<<'EOF' apiVersion: v1 kind: Service metadata: name: service-headless spec: selector: app: my-nginx clusterIP: "None" EOF kubectl apply -f service-headless.yml master1 ]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6d19h service-headless ClusterIP None <none> <none> 3s
6.3.3、创建deployment
kubectl create deployment my-nginx --image=192.168.10.33:80/k8s/pod_test:v0.1 --replicas=3
6.3.4、查询定义的service详细信息
master1 ]# kubectl describe service service-headless Name: service-headless Namespace: default Labels: <none> Annotations: <none> Selector: app=my-nginx Type: ClusterIP IP Family Policy: SingleStack IP Families: IPv4 IP: None IPs: None Session Affinity: None Events: <none> master1 ]# kubectl describe ep service-headless Name: service-headless Namespace: default Labels: service.kubernetes.io/headless= Annotations: endpoints.kubernetes.io/last-change-trigger-time: 2023-03-23T07:53:08Z Subsets: Addresses: 10.244.3.224,10.244.3.225,10.244.4.99 NotReadyAddresses: <none> 注意: 有可能因为dns的原因,这里面的无头服务可能无法显示对应的subnets相关的信息,但是不影响无头服 务的正常使用可以正常的进入到pod内部进行测试.
6.3.5、验证是否生效
master1 ]# kubectl exec -it my-nginx-b5fb867f7-dcsr6 -- /bin/sh [root@my-nginx-b5fb867f7-dcsr6 /]# nslookup 10.244.4.99 99.4.244.10.in-addr.arpa name = 10-244-4-99.service-headless.default.svc.cluster.local. master1 ]# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES my-nginx-b5fb867f7-dcsr6 1/1 Running 0 107m 10.244.4.99 node2 <none> <none> my-nginx-b5fb867f7-mkp2h 1/1 Running 0 107m 10.244.3.224 node1 <none> <none> my-nginx-b5fb867f7-t9s6t 1/1 Running 0 107m 10.244.3.225 node1 <none> <none> [root@my-nginx-b5fb867f7-dcsr6 /]# nslookup service-headless.default.svc.cluster.local Server: 10.96.0.10 Address: 10.96.0.10#53 Name: service-headless.default.svc.cluster.local Address: 10.244.3.225 Name: service-headless.default.svc.cluster.local Address: 10.244.4.99 Name: service-headless.default.svc.cluster.local Address: 10.244.3.224 结总: 从上面解析结果发现,解析出来的是pod的三个IP地址,说明headless生效,,不是通过ClusterIp中转,直接把请求直接转发到pod地址处理。
6.3.6、注意事项
headless service 在statefulSet资源对象中会重点使用 headless service是一个四层调度,因为iptables/ipvs都是四层的,所以我们如果要建立一个https 服务的话,每一个服务都必须配置一个https的主机,因为四层调度是无法卸载https回话的。
7、Service是如何做到服务发现的
7.1、查询
7.1.1、查询svc资源Endpoints信息
]# kubectl describe svc deployment-service | grep -i Endpoints Endpoints: 10.244.3.85:80,10.244.3.86:80,10.244.4.120:80 + 1 more... # 发现Endpoint资源对象,关于pod的IP地址
7.1.2、查询Endpoints资源对象
]# kubectl get endpoints deployment-service NAME ENDPOINTS AGE deployment-service 10.244.3.85:80,10.244.3.86:80,10.244.4.120:80 + 1 more... 5m47s 可以查询得到:Kubernetes正是通过Endpoints监控到Pod的IP,从而让Service能够发现Pod。
7.2、Endpoints、svc、pod关系图
如果删除一个pod,deployment控制器会重新创建pod,获取到新的ip地址,endpoints也会相应更新IP地址信息。
7.3、服务发现的原理
7.3.1、Pod X访问Service流程图
7.3.2、pod、service流程说明
在Kubernetes集群架构中介绍过Node节点上的kube-proxy,实际上Service相关的事情都由节点上的kube-proxy处理。
在Service创建时Kubernetes会分配IP给Service,同时通过API Server通知所有kube-proxy有新Service创建了,
kube-proxy收到通知后通过iptables记录Service和IP/端口对的关系,从而让Service在节点上可以被查询到。 上图是一个实际访问Service的图示,Pod X访问Service(10.247.124.252:8080),在往外发数据包时,在节点上根据iptables规则目的IP:Port被随机替换为Pod1的IP:Port,从而通过Service访问到实际的Pod。 除了记录Service和IP/端口对的关系,kube-proxy还会监控Service和Endpoint的变化,从而保证Pod重建后仍然能通过Service访问到Pod。