灵魂拷问x8:关于K8SPod通信和DNS那些事儿!

在一些项目和技术交流中,发现很多朋友对OCP pod通信机制和OCP中的DNS犯懵。我专门写一篇进行分析。

创新互联公司专注于企业全网营销推广、网站重做改版、左权网站定制设计、自适应品牌网站建设、H5建站商城网站建设、集团公司官网建设、成都外贸网站建设公司、高端网站制作、响应式网页设计等建站业务,价格优惠性价比高,为左权等各大城市提供网站开发制作服务。

本文中大量内容和K8S是相通的,文中也直接大量使用了K8S的概念,因此本文题目起的是K8S。OCP=OpenShift Container Platform,OCP内部的SDN用的是OVS。

我们以反问的方式进行“灵魂拷问”

拷问1. OCP中pod之间通讯是否一定需要service?

不需要。

没有service,pod能通信,而且好好的,如下图所示。

在一个namespace里(因为有networkpolicy,因此先做这个假设),只要指定的端口是pod开放的端口,在一个pod中,curl另外一个pod,就绝对能通。pod之间能不能通和俩pod跨不跨service,有没有service,没半毛钱关系。

拷问2. 从一个pod curl另外一个pod+端口号能通这件事,背后的链路机制是什么?

(1)俩pod在一个节点上,链路如下,流量没有绕出本宿主机的ovs:

Pod 1的eth0 → vethxx → br0 → vethyy → Pod 2的eth0

(2).俩pod在不同node上,链路如下,pod之间的通讯经过了vxlan:

Pod 1的eth0 → vethxx → br0 → vxlan0 → host1 eth0(192.168.1.101)→ network → host2 eth0(192.168.1.102)→ vxlan0 → br0 → vethmm → Pod 3的eth0

也就是说,截止到目前,我们把俩pod之间的通讯机制,简单介绍了。这里面没service啥事,这点记住。还需要记住的是,这是OCP集群中,pod之间通讯的实际数据链路(和后面service寻址对应)。

拷问3:我们都知道K8S service的基本概念,说说为啥K8S需要它?

我们首先要明确:Service是对一组提供相同功能的Pods的抽象,并为它们提供一个统一的内部访问入口。它主要解决:

1.负载均衡(这个大家会最先想到)。

2.服务注册与发现:解决不同服务之间的通信问题。在OpenShift中的创建应用后,需要提供访问应用的地址供其他服务调用,这个地址就是由Service提供。

每创建一个Service会分配一个ServiceIP地址,称为ClusterIP,这个IP地址是一个虚拟的地址,无法执行ping操作。同时自动在内部DNS注册一条对应的A记录(A记录很重要,后面展开说),这就完成了服务注册,注册信息全部保存在Etcd中。

拷问4:详细说说服务注册到底向etcd里注册了啥?

注册到etcd的,实际上是service yaml的先关内容。

我们查看etcd中router service 的内容。我们看到了什么?

service name、namespace、service ip、port number的,以及对应的endpoints等很多信息。

也就是说,etcd里有namespace,service name,service ip等,

通过这三个信息就可组成DNS A记录,也就是,service的FQDN和service ip之间的对应关系。但需要说明的是,etcd不是DNS!DNS A记录是通过查询生成的!OCP的DNS是由SkyDNS/CoreDNS实现的,这个后面说。

我们再说说服务发现。

服务发现这个词经常被妖魔化。我们把字换个顺序,就好理解:发现服务。也就说,service A要和serviceB通讯,我得知道serviceB是谁、在哪、service IP、pod ip都是啥?这就叫服务发现。

拷问5:有了servcice以后,pod之间的通讯和没有service有啥区别?

在数据通讯层,没区别!因为service只是逻辑层面的东西。

但是,没有service,K8S是无法控制pod之间的通讯的,也无法为pod之间进行寻址。也就是说,没有service,pod之间的通讯,就不会和K8S发生任何关系,和自己在笔记本linux中装起两容器ping着玩,没太大区别(在网络层)。

有了service以后,pod之间怎么寻址?

回答这个问题,我们要站在开发者角度。如果一个程序员,要写微服务,微服务之间要相互调用,怎么写?写 pod IP和service ip都不靠谱吧,你都不知道IP地址会是啥。

如果程序员决定用k8s做服务发现的前提下写服务之间的调用(如果使用spring cloud,那就用它的服务注册中心做解析,也就不必用K8S的service了),那么就得写K8S的service名称!

因为service名称我们是可以固定的。

K8S中service有短名和长名。

以下图为例,jws-app就是service的短名,service的长名是:..svc.cluser.local,也就是jws-app.web.svc.cluser.local。service短名可以自动补充成长名,这是OCP中的DNS做的,这个后面说。

那么,这时候大魏也有个疑问了。如果在两个不同的namespace中,有两个相同的service短名,微服务调用不是会出现混乱?程序员的代码里是不是要写service全名?

不能说想法不对,但一般不这样干。

首先,站在OCP集群cluster-admin的角度,我们看所有的项目,有几十个或者而更多,会觉得在不同namespaces中起相同的service短名是可能(比如namespace A中有个acat的service,namespace B中也有个acat的service)。但站在程序员角度,他只是OCP的使用者,他有自己的namespace,他能访问的namespace很有限,可能就1个。绝大多数情况下,同一个业务项目的微服务一般会运行在同一个namespace中,默认如果使用短名称(只写service name),则会自动补全成当前namespace的FQDN,只有在跨namespace调用的时候才必须写全名FQDN。

所以,程序员写的程序,用到了K8S service name,那么,真正跑应用的pod之间的通讯,也必然会以service name去找。通过service名称找到service ip,然后最终找到pod ip(一个service可能多个pod,从service ip到pod ip的负载均衡实现后面讲)。找到pod的ip以后,接下来实际的数据交换,就和拷问2讲述机制就接上头了。

拷问6. 我们知道Service的作用了,那从service ip到pod ip这段的负载均衡怎么实现的?

K8S中通过service name做服务发现,选短名会自动自动扩展成FQDN,然后解析成service ip,然后再走kube-proxy负载均衡倒pod ip。

Service的负载均衡可以由很多的实现方式,目前Kubernetes官方提供了三种代理模式:userspace、iptables、ipvs。目前版本OpenShift默认的代理模式是iptables

从图中可以看出,当客户端访问Servcie的ClusterIP时,由Iptables实现负载均衡,选择一个后端处理请求,默认的负载均衡策略是轮询。在这种模式下,每创建一个Service,会自动匹配后端实例Pod记录在Endpoints对象中,并在所有Node节点上添加相应的iptables规则,将访问该Service的ClusterIP与Port的连接重定向到Endpoints中的某一个后端Pod,由于篇幅有限,关于负载均衡实现的细节不再赘述。

这种模式有两个缺点需要关注:第一,不支持复杂的负载均衡算法;第二,当选择的某个后端Pod没有响应时,无法自动重新连接到另一个Pod,用户必须利用Pod的健康监测来保证Endpoints列表中Pod都是存活的(也就是说,生产上你好把liveness和rediness配上。)。

对于OCP而言,每个节点上都有iptables。而iptables之前的通讯和同步,是通过每个节点的kube-proxy实现的(这个进程在sdn pod中运行。想详细研究的可以看我前两天写的文章: 深度解析:kube-proxy在OpenShift上的实现)

拷问7. 我记得iptables不是做防火墙的么?

OCP中的iptables,主要目的做的是service ip和pod ip链路的事情。一般不涉及常规意义上的防火墙规则的INPUT/OUTPUT ACCEPT/DENY。也就是说,不要尝试自己ssh到OCP4节点上通过iptables做安全规则,不是这样玩的。

拷问8. 前面7个问题清楚了,说说OCP的DNS机制吧。

首先,OCP3和OCP4的DNS机制是不同的。

OpenShift 3内置的是SkyDNS,SkyDNS会监测Kubernetes API,当新创建一个Service,SkyDNS中就会提供..svc.cluster.local域名的解析。除了解析Service,还可以通过..endpoints.cluster.local解析endpoints。

例如,如果 myproject 服务中存在 myapi 服务,则整个OpenShift集群中的所有Pod都可以解析 myapi.myproject.svc.cluster.local 主机名以获取Service ClusterIP地址。除此之外,OpenShift DNS还提供以下两种短域名:

在简单了解了SkyDNS的机制之后,我们来看看OpenShift3是如何使用和配置DNS的。为了便于理解,我们用下图来进行说明。

上图表示了OpenShift 3中DNS解析流程:

以上就是OpenShift 3中DNS的解析流程,核心是通过每个节点上运行的Dnsmasq进程做了SkyDNS和上游DNS的代理。

在OpenShift 3的DNS里面需要注意以下几点:

OpenShift 4的DNS

OpenShift 4使用CoreDNS替换了OpenShift 3使用的SkyDNS,起到的作用是一样的,同样是提供OpenShift内部的域名解析服务。

在OpenShift 4中CoreDNS使用Operator实现部署,最终会创建出DaemonSet部署CoreDNS,也就是在每个节点会启动一个CoreDNS容器。在Kubelet将--cluster-dns设定为CoreDNS的ServiceClusterIP,这样Pod中就可以使用CoreDNS进行域名解析。

在安装OpenShift 4时,通过名为dns的Clusteroperator创建整个DNS堆栈,最终会在项目openshift-dns-operator下实例化一个dns pod完成具体的部署配置操作。

Cluster Domain定义了集群中Pod和Service域名的基本DNS域,默认为cluster.local,域名服务的地址是CoreDNS的ClusterIP,是配置的Service IP CIDR网段中的第10个地址,默认网段为172.30.0.0/16,第十个地址为172.30.0.10。DNS解析流程如下图所示:

上图表示了OpenShift 4的DNS解析流程

说简单点,在OCP中,随便创建一个pod,这个pod中的name server都会指向到172.30.0.10,这是coredns pod的service ip。

我们查看coredns的pod和service ip:

我们访问prometheus-k8s-0 这个pod进行查看。

查看这个规则:

sh-4.2$ cat /etc/resolv.conf

search openshift-monitoring.svc.cluster.local svc.cluster.local cluster.local

nameserver 172.30.0.10

options ndots:5

我们查看宿主机的dns。

OCP宿主机的nameserver可以是数据中心内部的,也可以自行构建。

举例说,如果我要在pod中nslookup baidu.com:

1.如果coredns pod中有缓存,直接返回

2. coredns pod中没有缓存,coredns一看这是外部域名,不归它管,他就会转到宿主机指向的192.168.91.8去解析,如果这192.168.91.8也解析不了,那就看还有没有上级的DNS了。总之,得有dns把baidu.com能解析出来。如果在OCP在纯离线的环境,baidu.com八成就解析失败了。(数据中心内部应该没人给baidu.com自己配解析)

好了,灵魂拷问结束,这种拷问方式是不是感觉比直接叙述看着爽一些?

本文标题:灵魂拷问x8:关于K8SPod通信和DNS那些事儿!
网页路径:https://www.cdcxhl.com/article12/sddigc.html

成都网站建设公司_创新互联,为您提供网页设计公司软件开发网站内链用户体验手机网站建设搜索引擎优化

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联

成都seo排名网站优化