28、K8S-服务发现-CoreDNS、sessionAffinity、headless

Kubernetes学习目录

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。

 

posted @ 2023-03-23 17:47  小粉优化大师  阅读(1060)  评论(0编辑  收藏  举报