小哥之哥 小哥之哥
首页
    • Prometheus
    • Kubertenes
    • Docker
    • MySQL
  • Go
  • Python
  • Vue
  • Jenkins
  • ELK
  • LDAP
  • 随笔
  • 最佳实践
  • 博客搭建
  • 问题杂谈
关于
友链
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

小哥之哥

运维扫地僧
首页
    • Prometheus
    • Kubertenes
    • Docker
    • MySQL
  • Go
  • Python
  • Vue
  • Jenkins
  • ELK
  • LDAP
  • 随笔
  • 最佳实践
  • 博客搭建
  • 问题杂谈
关于
友链
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Kubertenes

    • 安装部署

    • 技术杂谈

      • k8s-创建serviceAccount
      • k8s-serviceAccount最佳实践
      • k8s网络概述-容器网络
      • k8s网络概述-Service集群网络
        • Service
          • 一、概述
          • 二、 service创建
          • 三、Service工作机制
          • 3.1 service负载均衡
          • 3.2 service 工作机制过程
          • 四、服务发现DNS
          • 4.1 coredns服务
          • 4.2 pod默认dns
          • 4.3 DNS创建规则
          • 4.3.1 POD
          • 4.3.2 SERVICE
          • 4.4 POD的DNS策略
          • 4.5 DNS解析流程
          • 4.5.1 cluster.local 取值
          • 4.5.2 options ndots
          • 4.5.3 总结
      • k8s网络概述-pod抓包
      • k8s资源管理问题
    • 最佳实践

  • Prometheus

  • Docker

  • 数据库

  • 运维利器

  • 运维
  • Kubertenes
  • 技术杂谈
tchua
2023-03-08
目录

k8s网络概述-Service集群网络

# Service

Service是集群重要的核心服务之一,主要是为pod分配固定的、基于iptables(ipvs)的访问入口。

# 一、概述

当我们创建一个service对象时,会同步的创建endpoint对象,endpoint是用来做容器转发,将一组容器与service关联,然后由kube-proxy组件进行路由转发。

# 二、 service创建

# serve_dep.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: serve-demo
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: mirrorgooglecontainers/serve_hostname
        ports:
        - containerPort: 9376
          protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
  name: serve-svc
spec:
  selector:
    app: nginx
  ports:
  - name: tcp-80
    protocol: TCP
    port: 80
    targetPort: 9376
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
  • 当service创建后,我们可以查看下相关资源信息
# 查看pod状态
# ## 可以看到三个pod 有一个处于未就绪状态
[root@k8s-master01 ~]# kubectl get pods -o wide
NAME                          READY   STATUS              RESTARTS   AGE   IP              NODE         NOMINATED NODE   READINESS GATES
serve-demo-5c4f45c7fc-psr42   1/1     Running             0          82m   172.41.56.197   k8s-node01   <none>           <none>
serve-demo-5c4f45c7fc-psr42   0/1     ContainerCreating   0          82m   <none>          k8s-node03   <none>           <none>
serve-demo-5c4f45c7fc-v2rlx   1/1     Running             0          82m   172.41.70.132   k8s-node02   <none>           <none>

# service对象
[root@k8s-master01 demo]# kubectl get svc
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP   60d
serve-svc    ClusterIP   10.96.201.52   <none>        80/TCP    4m7s
# endpoint
# ## endpoint里面的两个IP就是上面已经就绪的pod IP
[root@k8s-master01 demo]# kubectl get endpoints serve-svc 
NAME        ENDPOINTS                                                  AGE
serve-svc   172.41.10.193:9376,172.41.56.197:9376,172.41.70.132:9376   3m17s
# 访问service ip查看
# ## 该pod是k8s官方提供的debug镜像,一个返回hostname的web server
[root@k8s-master01 demo]# kubectl get svc serve-svc
NAME        TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
serve-svc   ClusterIP   10.96.201.52   <none>        80/TCP    3m3s
[root@k8s-master01 demo]# curl 10.96.201.52
serve-demo-5c4f45c7fc-v2rlx
[root@k8s-master01 demo]# curl 10.96.201.52
serve-demo-5c4f45c7fc-psr42
[root@k8s-master01 demo]# curl 10.96.201.52
serve-demo-5c4f45c7fc-gtvzm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

注意

Endpoints列表里面的pod是处于Running以及状态是就绪行性的才会在该列表里面,如果pod出现故障,则k8s会自动把该pod从列表中移除。

  • service与pod之间关系

image-20230306151810777

# 三、Service工作机制

通过上面 例子,我们知道了service对于选择后端pod的方式是通过借助endpoint实现。其实对于创建service对象来说,不仅是生产endpoint对象,当kube-proxy组件对象监听到集群中创建了service对象后,会根据service对象关联的pod,在对应的pod宿主机创建一系列的iptables规则,来进行service到pod的流量转发管理。

# 3.1 service负载均衡

  • k8s从1.11之后,默认使用ipvs代理模式进行流量管理机相关规则创建,当创建service对象时,kube-proxy首先会在service关联的pod所在宿主机创建一个虚拟网卡kube-ipvs0,并把service IP分配给改地址。

image-20230306161833281

  • 然后kube-proxy通过linux的ipvs模块,为该IP添加三个ipvs虚拟主机,采用轮询的方式进行访问.

image-20230306162225835

注意

不过需要注意的有两点:

1)IPVS 模块只负责上述的负载均衡和代理功能。而一个完整的 Service 流程正常工作所需要的包过滤、SNAT 等操作,ipvs依然使用iptables进行管理。

2)kube-proxy创建的service IP(ClusterIP)是一个虚拟IP,该IP在整个集群中根本不存在,当然也就无法通过IP协议栈无法路由,底层underlay设备更无法感知这个IP的存在,因此service IP只能是单主机(Host Only)作用域可见,这个IP在其他节点以及集群外均无法访问,Kubernetes为了实现在集群所有的节点都能够访问Service,kube-proxy默认会在所有的Node节点都创建这个VIP并且实现负载。

# 3.2 service 工作机制过程

image-20230307164753972

# 四、服务发现DNS

k8s集群默认使用coredns提供集群中内部域名解析。

# 4.1 coredns服务

[root@k8s-master01 ~]# kubectl get pods -n kube-system |grep coredns
coredns-5bbd96d687-8gfw7                  1/1     Running   1 (8d ago)   62d
coredns-5bbd96d687-s6kk4                  1/1     Running   1 (8d ago)   62d
# service
[root@k8s-master01 ~]# kubectl get svc -n kube-system -owide
NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                        AGE   SELECTOR
kube-dns   ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP,9153/TCP         62d   k8s-app=kube-dns

1
2
3
4
5
6
7
8

# 4.2 pod默认dns

# 我们随便进入一个容器
# ## 可以看到pod指定的dns地址就是coredns的service地址
[root@k8s-master01 demo]# kubectl exec -it busybox-7dcdb7fd4b-2rzgt -- /bin/sh
/ # cat /etc/resolv.conf 
search default.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.96.0.10
options ndots:5
1
2
3
4
5
6
7

# 4.3 DNS创建规则

对于coredns,当集群中有svc创建时,k8s会为该svc以及svc下关联的pod创建DNS记录

# 4.3.1 POD
  • pod资源创建
# pod资源
[root@k8s-master01 demo]# kubectl get pods -owide
NAME                                READY   STATUS    RESTARTS   AGE   IP              NODE         NOMINATED NODE   READINESS GATES
alpine-78c7f9955c-ngzqf             1/1     Running   0          64m   172.41.10.197   k8s-node03   <none>           <none>
nginx-deployment-7695fcb5b5-j8chs   1/1     Running   0          46m   172.41.10.200   k8s-node03   <none>           <none>
serve-demo-5c4f45c7fc-gtvzm         1/1     Running   0          44h   172.41.10.193   k8s-node03   <none>           <none>
serve-demo-5c4f45c7fc-psr42         1/1     Running   0          44h   172.41.56.197   k8s-node01   <none>           <none>
serve-demo-5c4f45c7fc-v2rlx         1/1     Running   0          44h   172.41.70.132   k8s-node02   <none>           <none>
# pod对应svc
[root@k8s-master01 demo]# kubectl get svc nginx-svc -owide
NAME        TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE   SELECTOR
nginx-svc   ClusterIP   10.101.112.166   <none>        80/TCP    46m   app=nginx-v1

1
2
3
4
5
6
7
8
9
10
11
12
13
  • dns记录

这里使用nslook工具进行域名解析的查询

pod 域名格式: ${POD_IP}.${service_name}.${namespace_name}.svc.cluster.local.

# 从上面看 nginx对应的pod IP为172.41.10.200,dns svc IP为10.96.0.10
# ## 可以看到pod的域名解析是以172-41-10-200.nginx-svc.default.svc.cluster.local.
[root@k8s-master01 demo]# nslookup 172.41.10.200 10.96.0.10
200.10.41.172.in-addr.arpa	name = 172-41-10-200.nginx-svc.default.svc.cluster.local.
1
2
3
4
# 4.3.2 SERVICE

上面我们看到nginx对应的svc地址为10.101.112.166

[root@k8s-masternslookup 10.101.112.166  10.96.0.10
166.112.101.10.in-addr.arpa	name = nginx-svc.default.svc.cluster.local.
1
2

注意

1)因为pod IP及名称在集群中会随着pod的销毁重建而改变,所以一般情况下,无需关注pod在集群中对应的解析记录,只有在一些特殊场景需要点对点直接直接访问pod IP时,可以使用pod域名。

2)对于Service资源对象创建时生成的IP,虽然service不会像pod那样经常销毁重建,但是也不会说一直不变,所以最好的方式就是直接使用service对应的域名进行访问链接。

3)通过Service域名访问时,分为以下两个方式:

  • 同命名空间: 可以直接使用${service_name}进行访问,无需拼接全域名,这种解析速度最快,例如: nginx-svc

  • 非同命名空间: 可以直接使用${service_name}.${namespace_name}进行访问,例如: nginx-svc.default

# 4.4 POD的DNS策略

DNS 策略可以逐个 Pod 来设定,通过dnsPolicy字段设置,支持的字段:

  • Default: 继承pod所在宿主机dns解析配置。
  • ClusterFirst:k8s默认配置,先从集群coreDNS进行域名解析,解析不到的则通过coreDNS内部自定义的配置/etc/resolv.conf进行解析。
  • ClusterFirstWithHostNet: 主要用于以 hostNetwork 方式运行的 Pod,如果这些pod想要使用K8S集群内的DNS服务,则可以配置为这个字段.
  • None:忽略 Kubernetes 环境中的 DNS 设置,使用pod的dnsConfig字段进行解析。

# 4.5 DNS解析流程

在集群中,kubelet会为每一个pod创建一个/etc/resolv.conf文件,DNS解析会按照文件内容进行查询:

[root@k8s-master01 demo]# kubectl exec -it nginx-deployment-7695fcb5b5-j8chs -- /bin/bash
root@nginx-deployment-7695fcb5b5-j8chs:/# cat /etc/resolv.conf 
search default.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.96.0.10
options ndots:5
1
2
3
4
5

说明

  • search: 搜索的域,默认情况下会从该pod所属的namespace开始逐级补充。
  • nameserver: 集群中的DNS服务器IP,默认就是coredns的ClusterIP。
  • options: 是否触发上的search,k8s集群中默认ndots为5,一般系统默认为1
# 4.5.1 cluster.local 取值

cluster.local 其实是根据coredns中的定义

# 在/etc/resolv.conf文件中 search搜索的域 后面都是以cluster.local结尾,这里对应的是coredns中的配置
# ## coredns的configmap
[root@k8s-master01 demo]# kubectl get configmaps -n kube-system coredns -oyaml
apiVersion: v1
data:
  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
    }
kind: ConfigMap
metadata:
  creationTimestamp: "2023-01-04T09:12:11Z"
  name: coredns
  namespace: kube-system
  resourceVersion: "229"
  uid: e718af62-1cf3-4c8a-9d98-c5506cd98b94
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 4.5.2 options ndots

上面我们看到在pod中的dns文件/etc/resolv.conf,下面针对search与ndots探究下DNS的解析流程。

search与ndots

在介绍ndots域search之前,这里需要引入一个概念FQDN,FQDN是指完整域名,一般来说,域名最终以.结束表示是FQDN,例如baidu.com.是FQDN,但baidu.com不是。如果域名是FQDN则直接走DNS服务,否则就需要用到search和nodts了。

ndots简单来说,就是请求一个域名的时候,域名中点的个数,如果域名中点的个数大于ndots,则会直接按照域名进行解析;如果小于ndots,则会按照search中指定的域,依次添加后缀进行解析,直到可以支持获取域名的地址,如果添加后缀都解析不到,则会按照原域名进行解析。

示例-小于ndots

  • 外部域名
请求域名: www.baidu.com
请求域名不是完整域名且点数小于5,则会根据搜索域进行如下4次解析:
www.baidu.com.default.svc.cluster.local
www.baidu.com.svc.cluster.local
www.baidu.com.cluster.local
www.baidu.com. 

# 抓包数据 tcpdump -nt -i eth0 port 53(ping www.baidu.com)
IP 172.41.10.200.36537 > 10.96.0.10.53: 60071+ A? www.baidu.com.default.svc.cluster.local. (57)
IP 172.41.10.200.36537 > 10.96.0.10.53: 30392+ AAAA? www.baidu.com.default.svc.cluster.local. (57)
IP 10.96.0.10.53 > 172.41.10.200.36537: 60071 NXDomain*- 0/1/0 (150)
IP 10.96.0.10.53 > 172.41.10.200.36537: 30392 NXDomain*- 0/1/0 (150)
IP 172.41.10.200.37517 > 10.96.0.10.53: 55016+ A? www.baidu.com.svc.cluster.local. (49)
IP 172.41.10.200.37517 > 10.96.0.10.53: 12017+ AAAA? www.baidu.com.svc.cluster.local. (49)
IP 10.96.0.10.53 > 172.41.10.200.37517: 12017 NXDomain*- 0/1/0 (142)
IP 10.96.0.10.53 > 172.41.10.200.37517: 55016 NXDomain*- 0/1/0 (142)
IP 172.41.10.200.48853 > 10.96.0.10.53: 61152+ A? www.baidu.com.cluster.local. (45)
IP 172.41.10.200.48853 > 10.96.0.10.53: 60648+ AAAA? www.baidu.com.cluster.local. (45)
IP 10.96.0.10.53 > 172.41.10.200.48853: 60648 NXDomain*- 0/1/0 (138)
IP 10.96.0.10.53 > 172.41.10.200.48853: 61152 NXDomain*- 0/1/0 (138)
IP 172.41.10.200.40127 > 10.96.0.10.53: 22685+ A? www.baidu.com. (31)
IP 172.41.10.200.40127 > 10.96.0.10.53: 54947+ AAAA? www.baidu.com. (31)
IP 10.96.0.10.53 > 172.41.10.200.40127: 22685 3/0/0 CNAME www.a.shifen.com., A 112.80.248.76, A 112.80.248.75 (138)
IP 10.96.0.10.53 > 172.41.10.200.40127: 54947 1/1/0 CNAME www.a.shifen.com. (164)
IP 172.41.10.200.60493 > 10.96.0.10.53: 10365+ PTR? 76.248.80.112.in-addr.arpa. (44)
IP 10.96.0.10.53 > 172.41.10.200.60493: 10365 NXDomain 0/1/0 (130)

请求域名: www.baidu.com.
请求域名点数小于5,但是是完整域名,则会进行如下一次解析:
www.baidu.com. 

# 抓包数据 tcpdump -nt -i eth0 port 53(ping www.baidu.com.)
IP 172.41.10.200.51292 > 10.96.0.10.53: 12976+ A? www.baidu.com. (31)
IP 10.96.0.10.53 > 172.41.10.200.51292: 12976* 3/0/0 CNAME www.a.shifen.com., A 112.80.248.76, A 112.80.248.75 (138)
IP 172.41.10.200.51292 > 10.96.0.10.53: 19645+ AAAA? www.baidu.com. (31)
IP 10.96.0.10.53 > 172.41.10.200.51292: 19645* 1/1/0 CNAME www.a.shifen.com. (164)
IP 172.41.10.200.35968 > 10.96.0.10.53: 14896+ PTR? 76.248.80.112.in-addr.arpa. (44)
IP 10.96.0.10.53 > 172.41.10.200.35968: 14896 NXDomain* 0/1/0 (130)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
  • 内部service域名
# 同命名空间访问(default)
请求域名: nginx-svc
请求域名点数小于5,则会进行如下一次解析:
nginx-svc.default.svc.cluster.local.

# 抓包数据tcpdump -nt -i eth0 port 53(nslookup nginx-svc)
IP 172.41.10.200.33807 > 10.96.0.10.53: 22119+ A? nginx-svc.default.svc.cluster.local. (53)
IP 10.96.0.10.53 > 172.41.10.200.33807: 22119*- 1/0/0 A 10.101.112.166 (104)
IP 172.41.10.200.44417 > 10.96.0.10.53: 47328+ AAAA? nginx-svc.default.svc.cluster.local. (53)
IP 10.96.0.10.53 > 172.41.10.200.44417: 47328*- 0/1/0 (146)

# 不同命名空间访问(default-->monitoring)
# ## 根据搜索域规则,不同命名空间必须加上namespace,否则无法支持解析
请求域名: prometheus-k8s.monitoring
请求域名点数小于5,则会进行如下2次解析:
prometheus-k8s.monitoring.default.svc.cluster.local.
prometheus-k8s.monitoring.svc.cluster.local.


# 抓包数据 tcpdump -nt -i eth0 port 53(nslookup prometheus-k8s.monitoring)
IP 172.41.10.200.44501 > 10.96.0.10.53: 25510+ A? prometheus-k8s.monitoring.default.svc.cluster.local. (69)
IP 10.96.0.10.53 > 172.41.10.200.44501: 25510 NXDomain*- 0/1/0 (162)
IP 172.41.10.200.44017 > 10.96.0.10.53: 2945+ A? prometheus-k8s.monitoring.svc.cluster.local. (61)
IP 10.96.0.10.53 > 172.41.10.200.44017: 2945*- 1/0/0 A 10.99.54.68 (120)
IP 172.41.10.200.59528 > 10.96.0.10.53: 63858+ AAAA? prometheus-k8s.monitoring.svc.cluster.local. (61)
IP 10.96.0.10.53 > 172.41.10.200.59528: 63858*- 0/1/0 (154)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

示例-大于ndots

  • 外部域名
请求域名: sls.doc.dc.wiki.tbchip.com
请求域名不是完整域名点数等于5,则会根据搜索域进行如下1次解析:
sls.doc.dc.wiki.tbchip.com.

# 抓包数据tcpdump -nt -i eth0 port 53(nslookup sls.doc.dc.wiki.tbchip.com)
IP 172.41.10.200.52474 > 10.96.0.10.53: 61735+ A? sls.doc.dc.wiki.tbchip.com. (44)
IP 10.96.0.10.53 > 172.41.10.200.52474: 61735 1/0/0 A 39.103.233.101 (86)
IP 172.41.10.200.60240 > 10.96.0.10.53: 45547+ AAAA? sls.doc.dc.wiki.tbchip.com. (44)
IP 10.96.0.10.53 > 172.41.10.200.60240: 45547 0/1/0 (129)

1
2
3
4
5
6
7
8
9
10
  • 内部域名
请求域名:  prometheus-k8s.monitoring.svc.cluster.local.
请求域名点数等于5,则会进行如下一次解析:
# 抓包数据 tcpdump -nt -i eth0 port 53(nslookup prometheus-k8s.monitoring.svc.cluster.local.)
IP 172.41.10.200.36073 > 10.96.0.10.53: 9472+ A? prometheus-k8s.monitoring.svc.cluster.local. (61)
IP 10.96.0.10.53 > 172.41.10.200.36073: 9472*- 1/0/0 A 10.99.54.68 (120)
IP 172.41.10.200.52819 > 10.96.0.10.53: 18400+ AAAA? prometheus-k8s.monitoring.svc.cluster.local. (61)
IP 10.96.0.10.53 > 172.41.10.200.52819: 18400*- 0/1/0 (154)
1
2
3
4
5
6
7
# 4.5.3 总结

通过以上测试级search搜索域我们可以得出以下结论:

  • 同命名空间: 如果是内部域名,直接访问service即可请求到对应的服务,并且解析性能最好。
  • 不同命名空间,如果是内部域名:
    • 如果域名中点数小于5,则使用${service_name}.${name_space}访问,解析性能最好,会进行2次查询。
    • 如果域名中点数大于5,则会直接解析并返回结果,不会再对搜索域进行拼接。
  • 对于外部域名:
    • 如果是全域名(FQDN),则无论是否域名中点数大于等于5,都会直接解析,不会再拼接搜索域
    • 如果是非全域名,域名点数大于等于5,直接解析;域名中点数小于5,触发search搜索域拼接,依次进行解析。

因此,在k8s中,如果应用需要访问外部域名,则最好使用域名的FQDN,这样保证可以直接解析

编辑 (opens new window)
#k8s
上次更新: 2024/01/02, 18:20:30
k8s网络概述-容器网络
k8s网络概述-pod抓包

← k8s网络概述-容器网络 k8s网络概述-pod抓包→

最近更新
01
cert-manager自动签发Lets Encrypt
09-05
02
Docker构建多架构镜像
08-02
03
Prometheus数据迁移至VMstorage
08-01
更多文章>
Theme by Vdoing | Copyright © 2023-2024 |豫ICP备2021026650号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式