0%

k8s教程04-Service和Ingress使用

1. Service 基础

⽔平伸缩意味着多个pod可能会提供相同的服务——每个pod都有⾃⼰的IP地址,客户端⽆须关⼼后端提供服务pod的数量,以及各⾃对应的IP地址。它们⽆须记录每个pod的IP地址。相反,所有的pod可以通过⼀个单⼀的IP地址进⾏访问。为了解决上述问题,Kubernetes提供了⼀种资源类型——服务(service)

1.1 创建 service

创建服务的最简单的⽅法是通过kubectl expose。另外一种就是yaml文件。

  • 定义
1714529197848

创建了⼀个名叫kubia的服务,它将在端⼜80接收请求并将连接路由到具有标签选择器是app=kubia的pod的8080端⼜上。

  • 查看service

1714529248276

列表显⽰分配给服务的IP地址是10.111.249.153。因为只是集群的 IP地址,只能在集群内部可以被访问。服务的主要⽬标就是使集群内部的其他pod可以访问当前这组pod,但通常也希望对外暴露服务。

1.2 会话亲和性【网络层级别】

如果多次执⾏同样的命令,每次调⽤执⾏应该在不同的pod上。因为服务代理通常将每个连接随机指向选中的后端pod中的⼀个,即使连接来⾃于同⼀个客户端。

另⼀⽅⾯,如果希望特定客户端产⽣的所有请求每次都指向同⼀个 pod , 可 以 设 置 服 务 的 sessionAffinity 属 性 为 ClientIP 。

Kubernetes 仅 仅 ⽀ 持 两 种 形 式 的 会 话 亲 和 性 服 务 : None 和ClientIP。你或许惊讶竟然不⽀持基于cookie的会话亲和性的选项,但是你要了解Kubernetes 服务不是在HTTP层⾯上⼯作。服务处理TCP和UDP包,并不关⼼其中的载荷内容。因为cookie是HTTP协议中的⼀部分,服务并不知道它们,这就解释了为什么会话亲和性不能基于 cookie。

1.3 使⽤命名的端口

1714529671586

可以在服务spec中按名称引⽤这些端口,如下⾯的代码清单所⽰。好处就是即使更换端口号也⽆须更改服务spec。

1714529672795

1.4 服务发现和全限定域名(FQDN)

环境变量

但客户端pod如何知道服务的IP和端口?是否需要先创建服务,然后⼿动查找其IP地址并将IP传递给客户端pod的配置选项?当然不是。Kubernetes还为客户端提供了通过环境变量发现服务。

image-217

KUBIA_SERVICE_HOST和KUBIA_SERVICE_PORT,分别代表了kubia服务的IP地址和端口号。

通过DNS发现服务(FQDN)

运⾏在pod上的进程DNS查询都会被Kubernetes⾃⾝的DNS 服务器响应,该服务器知道系统中运⾏的所有服务。

每个服务从内部DNS 服务器中获得⼀个DNS条⽬,客户端的pod在知道服务名称的情况下可以通过全限定域名(FQDN)来访问,⽽不是诉诸于环境变量。

1714529909602

  1. backend-database对应于服务名称
  2. default表⽰服务在其中定义的名称空间
  3. svc.cluster.local是在所有集群本地服务名称中使⽤的可配置集群域后缀。

image-220

Ping的问题

curl这个服务是⼯作的,但是却ping不通。这是因为服务的集群IP是⼀个虚拟IP,并且只有在与服务端口结合时才有意义。

image-223

2. Service 指向外部服务

不让服务将连接重定向到集群中的pod,⽽是让它重定向到外部IP和端口。这样在集群中运⾏的客户端pod可以像连接到内部服务⼀样连接到外部服务。

2.1 Endpoint

服务并不是和pod直接相连的。相反,有⼀种资源介于两者之间——它就是Endpoint资源。

1714530396955

2.2 关联 Endpoint 和 Service

qq579726-1

Endpoint是⼀个单独的资源并不是服务的⼀个属性。由于创建的资源中并不包含选择器,相关的Endpoints资源并没有⾃动创建,所以必须⼿动创建。

qq579726-2

显⽰了三个pod连接到具有外部endpoint的服务。

1714530653879

2.3 为外部服务创建别名

要创建⼀个具有别名的外部服务的服务时,要将创建服务资源的⼀ 个 type 字 段 设 置 为 ExternalName 。

例如, 设想⼀下 在api.somecompany.com上有公共可⽤的API,可以定义⼀个指向它的服务,如下⾯的代码清单所⽰。

1714530813919

服务创建完成后, 可以通过 external-service.default.svc.cluster.local域名(甚⾄是external-service)连接到外部服务,⽽不是使⽤服务的实际FQDN(api.somecompany.com)。【相当于中转了一层】

这隐藏了实际的服务名称及其使⽤该服务的pod的位置,允许修改服务定义,并且在以后如果将其指向不同的服务,只需简单地修改externalName属性。

3. Service 暴露给外部客户端

  1. Service Type: NodePort——每个集群节点都会在节点上打开⼀个端口,对于NodePort服务,每个集群节点在节点本⾝(因此得名叫NodePort)上打开⼀个端口,并将在该端⼜上接收到的流量重定向到基础服务。
  2. Service Type: LoadBalance。NodePort类型的⼀种扩展,这是由Kubernetes中正在运⾏的云基础设施提供的。负载均衡器将流量重定向到跨所有节点的节点端口。客户端通过负载均衡器的IP连接到服务。
  3. Ingress 资源:这是⼀个完全不同的机制,通过⼀个IP地址公开多个服务——它运⾏在HTTP层(⽹络协议第7层)上,因此可以提供⽐⼯作在第4层的服务更多的功能。

3.1 NodePort (任一节点)

1714533864600

将类型设置为NodePort并指定该服务应该绑定到的所有集群节点的节点端口。指定端⼜不是强制性的。如果忽略它,Kubernetes将选择⼀个随机端口。

1714533866811

访问流程

看看EXTERNAL-IP列。它显⽰nodes,表明服务可通过任何集群节点的IP地址访问。PORT(S)列显⽰集群 IP(80)的内部端口和节点端口(30123),可以通过以下地址访问该服务:

  • 10.11.254.223:80(这个IP是这个集群的IP)

  • <1stnode'sIP>:30123,<2ndnode'sIP>:30123,等等

服务暴露在两个集群节点的端口30123上,到达任何⼀个端口的传⼊连接将被重定向到⼀个随机选择的pod,该pod是否位于接收到连接的节点上是不确定的。

1714533868664

现在整个互联⽹可以通过任何节点上的30123端⼜访问到你的pod。如果只将客户端指向第⼀个节点,那么当该节点发⽣故障时,客户端⽆法再访问该服务。

这就是为什么将负载均衡器放在节点前⾯以确保发送的请求传播到所有健康节点,并且从不将它们发送到当时处于脱机状态的节点的原因。

3.2 LoadBalancer (云提供商)

在云提供商上运⾏的Kubernetes集群通常⽀持从云基础架构⾃动提供负载平衡器。负载均衡器拥有⾃⼰独⼀⽆⼆的可公开访问的IP地址,并将所有连接重定向到服务。可以通过负载均衡器的IP地址访问服务。

1714535657075

创建服务后,云基础架构需要⼀段时间才能创建负载均衡器并将其IP地址写⼊服务对象。⼀旦这样做了,IP地址将被列为服务的外部IP地址:

1714535659133

1714535991229

外部客户端(可以使⽤curl)连接到负载均衡器的80端口,并路由到其中⼀个节点上的隐式分配节点端口。之后该连接被转发到⼀个pod实例。

1714536063294

记住客户端IP是不记录的

通常,当集群内的客户端连接到服务时,⽀持服务的pod可以获取客户端的IP地址。但是,当通过节点端口接收到连接时,由于对数据包执⾏了源⽹络地址转换(SNAT),因此数据包的源IP将发⽣更改。

后端的pod⽆法看到实际的客户端IP,这对于某些需要了解客户端 IP的应⽤程序来说可能是个问题。例如,对于Web服务器,这意味着访问⽇志⽆法显⽰浏览器的IP。

4. Ingress暴露服务(ingress->service)

为什么需要Ingress? ⼀个重要的原因是每个LoadBalancer 服务都需要⾃⼰的负载均衡器,以及独有的公有IP地址。

⽽Ingress只需要⼀个公⽹IP就能为许多服务提供访问。当客户端向Ingress发送HTTP请求时,Ingress会根据请求的主机名和路径决定请求转发到的服务。

Ingress在⽹络栈(HTTP)的应⽤层操作,并且可以提供⼀些服务不能实现的功能,诸如基于cookie的会话亲和性(session affinity)等功能。⽬前仅⽀持L7(⽹络第7层)(HTTP/HTTPS)负载平衡,但也计划⽀持L4(⽹络第4层)负载平衡。

1714536577259

4.1 创建

image-20240502102458946

Ingress控制器收到的所有请求主机kubia.example.com的HTTP请求,将被发送到端口80上的kubia-nodeport服务。

4.2 访问

1714537165419

要通过http://kubia.example.com访问服务,需要确保域名解析为 Ingress控制器的IP。

1714537232972

4.3 原理

  1. 客户端⾸先对kubia.example.com执⾏DNS查找,DNS服务器(或本地操作系统)返回了Ingress控制器的IP。
  2. 客户端然后向Ingress控制器发送 HTTP请求,并在Host头中指定kubia.example.com。控制器从该头部确定客户端尝试访问哪个服务。
  3. 通过与该服务关联的Endpoint对象查看 pod IP,并将客户端的请求转发给其中⼀个pod。
1714537397658

如你所见,Ingress控制器不会将请求转发给该服务,只⽤它来选择⼀个pod。⼤多数(即使不是全部)控制器都是这样⼯作的。

4.4 转发多个服务

1714537455697

5. 参考资料

  • 《k8s in action》
给作者打赏,可以加首页微信,咨询作者相关问题!