今天,我们正在发布《 Kubernetes网络指南》第三部分的翻译。
第一部分是有关Pod的内容,
第二部分是有关服务的内容,今天我们将讨论Ingress类型的负载平衡和Kubernetes资源。
路由不平衡负载
在本系列的
上一篇文章中,我们考虑了由一对炉床和一个服务组成的配置,该服务分配有一个称为“集群IP”的IP地址。 针对炉膛的查询已发送到该地址。 从上次毕业的地方开始,我们将在这里继续研究我们的培训系统。 回想一下,该服务的群集IP地址
10.3.241.152
属于一个IP地址范围,该IP地址范围不同于在炉床网络中使用的IP地址和在节点所在的网络中使用的IP地址。 我将由该地址空间定义的网络称为“服务网络”,尽管几乎没有一个特别的名字,因为没有设备连接到该网络,并且其地址空间实际上完全由路由规则组成。 先前已演示了如何在称为
kube-proxy的Kubernetes组件的基础上实现该网络
,并与Linux内核模块
netfilter交互以拦截和重定向发送到IP群集的流量以在其下工作。
网络图到目前为止,我们讨论了“连接”和“请求”,甚至使用了难以理解的“流量”概念,但是为了理解Kubernetes Ingress机制的特征,我们需要使用更精确的术语。 因此,连接和请求可以在
OSI模型的第4级(tcp)或第7级(http,rpc等)上工作。 Netfilter规则是路由规则,它们与第三级的IP数据包一起使用。 所有路由器,包括netfilter,都仅根据数据包中包含的信息来或多或少地做出决策。 通常,他们对数据包的来源和去向感兴趣。 因此,为了用OSI模型的第三级来描述此行为,必须说到达
eth0
节点接口的,位于
10.3.241.152:80
的服务的每个数据包都由netfilter处理,并根据为我们的服务设置的规则将重定向到可行的壁炉的IP地址。
显然,我们用来允许外部客户端访问Pod的任何机制都应使用相同的路由基础结构。 结果,这些外部客户端将访问群集的IP地址和端口,因为它们是到目前为止我们所讨论的所有机制的“访问点”。 它们使我们不必担心在特定时间点将在何处准确执行它。 但是,如何使它们全部工作还不是很明显。
群集IP服务仅可通过节点的以太网接口访问。 群集外的任何人都不知道该地址所属范围内的地址该怎么做。 仅当数据包已经到达主机时,如何才能将流量从公共IP地址重定向到可访问的地址?
如果我们尝试找到此问题的解决方案,那么寻找解决方案的过程中可以做的一件事情就是使用
iptables实用工具研究netfilter规则。 如果这样做,乍看之下可能会发现一些不寻常的发现:服务规则不仅限于特定的源网络。 这意味着,在节点的以太网接口上到达任何地方且目的地地址为
10.3.241.152:80
任何数据包都将被视为符合规则,并将被重定向到子节点。 我们是否可以给客户一个IP群集,也许是通过将它绑定到一个合适的域名,然后建立一条路由,使我们能够将这些数据包组织到一个节点上呢?
外部客户端和集群如果一切都以这种方式进行设置,那么这样的设计将可以正常工作。 客户端访问群集IP,数据包遵循通向主机的路由,然后将它们重定向到底部。 目前,您似乎可以限制这种解决方案,但是却遇到了一些严重的问题。 首先是节点,实际上是短暂的概念,它们在这方面与炉膛没有特别的区别。 当然,它们比Pod更加接近于物料世界,但是它们可以迁移到新的虚拟机,集群可以向上或向下扩展,依此类推。 路由器在OSI模型的第三层上工作,数据包无法区分正常工作的服务和不能正常工作的服务。 他们希望路线中的下一个过渡路段容易到达且稳定。 如果该节点不可达,则该路由将无法使用,并且在大多数情况下将保持大量时间。 即使路由能够抵抗故障,这种方案也会导致所有外部流量都流经单个节点这一事实,这可能不是最佳选择。
无论我们如何将客户端流量带入系统,我们都需要这样做,以使其不依赖于任何单个群集节点的状态。 而且,实际上,没有可靠的方法仅使用路由来执行此操作,而没有一些主动管理路由器的方法。 实际上,正是kube-proxy相对于netfilter扮演着这个角色,即控制系统的角色。 对于系统架构师而言,将Kubernetes的职责扩展到管理外部路由器可能没有多大意义,尤其是因为我们已经拥有成熟的工具来在多台服务器之间分配客户端流量。 它们被称为负载平衡器,毫不奇怪,它们是Kubernetes Ingress真正可靠的解决方案。 为了确切地了解这是如何发生的,我们需要从OSI的第三级起步并再次讨论连接。
为了使用负载平衡器在群集节点之间分配客户端流量,我们需要客户端可以连接到的公共IP地址,还需要负载平衡器可以将请求重定向到的节点本身的地址。 由于上述原因,我们不能简单地使用基于服务的网络(IP群集)在网关路由器和节点之间创建稳定的静态路由。
在您可以使用的其他地址中,只有节点的以太网接口所连接的网络地址,即本例中的
10.100.0.0/24
,可以被记录
10.100.0.0/24
。 路由器已经知道如何将数据包转发到这些接口,并且从负载平衡器发送到路由器的连接将到达它们应该到达的位置。 但是,如果客户端要通过端口80连接到我们的服务,则我们不能只是将数据包发送到节点的网络接口上的该端口。
负载均衡器,尝试访问主机网络接口的端口80失败无法做到这一点的原因是完全显而易见的。 也就是说,我们正在谈论的事实是,没有进程在
10.100.0.3:80
等待连接(如果存在,则肯定不是同一进程),并且netfilter规则(正如我们希望的那样)将拦截请求并他们会将其发送给他,但无法在该目标地址使用。 它们仅响应基于服务的群集IP网络,即地址
10.3.241.152:80
。 结果,这些数据包一到达,就无法传递到目标地址,内核将发出
ECONNREFUSED
响应。 这使我们处于一个混乱的境地:在将数据从网关重定向到节点时,很难与网络一起工作以将数据包重定向到netfilter配置到的网络,并且易于配置路由的网络不是netfilter将数据包重定向到的网络。 为了解决此问题,您可以在这些网络之间建立桥梁。 这正是Kubernetes使用NodePort之类的服务所做的事情。
像NodePort这样的服务
例如,我们在上一篇文章中创建的服务未分配类型,因此它采用了默认类型
ClusterIP
。 还有两种类型的服务在附加功能方面有所不同,而我们现在感兴趣的一种是
NodePort
。 这是对此类型的服务的描述示例:
kind: Service apiVersion: v1 metadata: name: service-test spec: type: NodePort selector: app: service_test_pod ports: - port: 80 targetPort: http
NodePort
类型的服务是具有额外机会的
ClusterIP
类型的服务:可以通过分配给主机的IP地址和服务网络中分配给集群的地址来访问它们。 这可以通过一种非常简单的方式来实现:当Kubernetes创建NodePort服务时,kube-proxy会在30000-32767范围内分配一个端口,并在每个节点的
eth0
接口上打开该端口(因此,服务类型的名称为
NodePort
)。 与此端口(我们称为此类
NodePort
端口)建立的连接将重定向到服务的群集IP。 如果我们创建上述服务并运行
kubectl get svc service-test
命令,我们可以看到为其分配的端口。
$ kubectl get svc service-test NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE service-test 10.3.241.152 <none> 80:32213/TCP 1m
在这种情况下,将为该服务分配NodePort
32213
。 这意味着我们现在可以通过实验集群中位于
10.100.0.2:32213
或
10.100.0.3:32213
任何节点连接到服务。 在这种情况下,流量将被重定向到服务。
在系统的这一部分取代之后,我们就拥有了管道的所有片段,用于平衡客户端请求对集群所有节点产生的负载。
NodePort服务在上图中,客户端通过公共IP地址连接到负载均衡器,负载均衡器选择节点并以
10.100.0.3:32213
连接到该节点,kube-proxy接受此连接并将其重定向到可通过群集IP
10.3.241.152:80
访问的服务。 。 在此,根据netfilter设置的规则成功处理了请求,并将其重定向到地址为
10.0.2.2:8080
的服务器pod。 也许所有这些看起来都有些复杂,并且在某种程度上看起来有些复杂,但是要想找到一个简单的解决方案来支持我们的Pod和基于服务的网络的所有重要功能,这并不容易。
但是,这种机制并非没有其自身的问题。 使用诸如
NodePort
服务,可使客户使用非标准端口访问服务。 通常这不是问题,因为负载平衡器可以为它们提供常规端口,
NodePort
最终用户隐藏
NodePort
。 但是在某些情况下,例如,当使用外部Google Cloud平台负载平衡器时,可能有必要将
NodePort
部署
NodePort
客户端。 应该注意的是,尽管即使对于最大的群集来说,2768个端口也可能足够,但此类端口还代表有限的资源。 在大多数情况下,您可以让Kubernetes随机选择端口号,但必要时可以自行设置。 另一个问题是有关请求中源IP地址存储的一些限制。 为了找到解决这些问题的方法,您可以从Kubernetes文档中参考
该材料。
端口
NodePorts
是所有外部流量进入Kubernetes集群的基本机制。 但是,他们自己并没有为我们提供现成的解决方案。 由于上述原因,在群集之前,无论客户端是位于公共网络中的内部还是外部实体,始终都需要具有某种负载平衡器。
平台架构师意识到了这一点,提供了两种从Kubernetes平台本身配置负载均衡器的方法。 让我们讨论一下。
诸如LoadBalancer之类的服务和Ingress类型的资源
诸如
LoadBalancer
服务和
Ingress
类型的资源是一些最复杂的Kubernetes机制。 但是,我们不会在它们上花费太多时间,因为到目前为止,我们所谈论的一切并不会导致根本改变。 与以前一样,所有外部流量都通过
NodePort
进入集群。
架构师可以在这里停下来,让创建群集的人员只关心公共IP地址和负载平衡器。 实际上,在某些情况下,例如在常规服务器上或在家中启动集群,这正是它们所做的。 但是在支持API控制的网络资源配置的环境中,Kubernetes允许您在一处配置所需的一切。
最简单的解决此问题的方法是使用Kubernetes服务,例如
LoadBalancer
。 此类服务具有
NodePort
等服务的所有功能,此外还具有基于群集运行在通过API支持网络资源配置的GCP或AWS等环境中的假设来为传入流量创建完整路径的能力。
kind: Service apiVersion: v1 metadata: name: service-test spec: type: LoadBalancer selector: app: service_test_pod ports: - port: 80 targetPort: http
如果我们从Google Kubernetes Engine中的示例中删除并重新创建该服务,则此后不久,使用
kubectl get svc service-test
命令,我们可以验证是否已分配了外部IP。
$ kubectl get svc service-test NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE openvpn 10.3.241.52 35.184.97.156 80:32213/TCP 5m
上面已经说过,尽管需要将外部IP地址分配到健康状态,但分配外部IP可能要花费几分钟,因此我们将能够验证“很快”分配外部IP地址的事实。 例如,在GCP平台上,这要求系统创建外部IP地址,流量重定向规则,目标代理服务器,后端服务以及可能的组实例。 分配外部IP地址后,您可以通过该地址连接到服务,为其分配域名并通知客户端。 在销毁并重新创建服务之前(为了这样做,很少有充分的理由),IP地址不会更改。
诸如
LoadBalancer
服务有一些限制。 无法将此类服务配置为解密HTTPS通信。 您无法创建虚拟主机或配置基于路径的路由,因此,不能通过实际配置将单个负载均衡器与许多服务一起使用。 这些限制导致引入了Kubernetes 1.1。 用于配置负载均衡器的特殊资源。 这是
Ingress类型的资源。 诸如
LoadBalancer
类的服务旨在扩展单个服务的功能以支持外部客户端。 相反,
Ingress
资源是特殊资源,可让您灵活地配置负载均衡器。 Ingress API支持TLS流量,虚拟主机和基于路径的路由的解密。 使用此API,可以轻松配置负载平衡器以与多个后端服务一起使用。
Ingress
类型的资源API太大,无法在此处讨论其功能;此外,它不会特别影响Ingress资源在网络级别的工作方式。 该资源的实现遵循通常的Kubernetes模式:存在一个资源类型和一个控制该类型的控制器。 在这种情况下,资源是
Ingress
资源,它描述对网络资源的请求。 这是
Ingress
资源的描述可能会是什么样的。
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: test-ingress annotations: kubernetes.io/ingress.class: "gce" spec: tls: - secretName: my-ssl-secret rules: - host: testhost.com http: paths: - path: /* backend: serviceName: service-test servicePort: 80
入口控制器负责通过将其他资源置于所需状态来执行这些请求。 使用Ingress时,会创建诸如
NodePort
类的服务,然后允许Ingress控制器决定如何将流量定向到节点。 对于GCE负载平衡器,AWS平衡器,流行的代理服务器(例如nginx和haproxy),有一个Ingress控制器实现。 请注意,在某些环境中,混合使用Ingress资源和服务(例如
LoadBalancer
可能会引起较小的问题。 它们易于处理,但是通常,即使是简单的服务,也最好只使用Ingress。
HostPort和HostNetwork
我们现在要谈论的内容,即
HostPort
和
HostNetwork
,可以归因于有趣的稀有类别,而不是有用的工具。 实际上,我承诺断言,在99.99%的情况下,可以将它们的使用视为反模式,并且使用它们的任何系统都必须对其结构进行强制检查。
我认为根本不值得讨论它们,但是它们就像Ingress资源用来处理传入流量的工具一样,因此我认为至少值得一提。
首先,
HostPort
谈谈
HostPort
。 这是一个容器属性(在
ContainerPort
结构中声明)。 当在其中写入端口号时,这将导致该端口在节点上打开并直接重定向到容器。 没有代理机制,并且端口仅在运行容器的节点上打开。 在该平台的早期,在出现
DaemonSet和
StatefulSet机制之前,
HostPort
是一种技巧,它使得在任何节点上仅启动一个特定类型的容器成为可能。 例如,我曾经通过将
HostPort
设置为
9200
并指定与节点数一样多的副本来创建Elasticsearch集群。 , Kubernetes, -
HostPort
.
NostNetwork
, , Kubernetes ,
HostPort
.
true
, -
network=host
docker run
. , .
eth0
. , . , , , Kubernetes, - .
总结
Kubernetes, , Ingress. , , , Kubernetes.
亲爱的读者们! Ingress?
