蛮荆

Kubernetes 为什么需要 Ingress

2023-10-13

概述

默认情况下,Kubernetes 集群中的 Service 和 Pod 只能在集群中访问,集群外部需要通过 NodePort 类型的 Service 进行访问, 而且一般不是直接访问 NodePort 暴露的 Service, 而是通过 LoadBalancer 类型的 Service 转发到 NodePort。

虽然 LoadBalancer 类型的 Service 可以解决服务发现和负载均衡问题,但是应用时还是会存在一些限制,例如:

  • LoadBalancer 仅支持四层负载均衡,不支持七层负载均衡,如果一个应用需要对外开放 N 个服务时,需要为每一个服务都创建一个 LoadBalancer,这会增加开销和管理成本
  • LoadBalancer 要求 Kubernetes 集群必要运行在支持 LoadBalancer Cloud Provider 的云计算服务商的环境中 (增加成本)
  • 直接将应用服务暴露会影响后端架构的灵活性

为了解决上面提到的限制和问题,可以通过引入 Ingress 来统一应用流量入口,将集群内服务暴露到集群外部,并且可以自定义服务的访问策略。

Ingress 是用于管理 Kubernetes 集群外部访问集群内部的服务时采用的资源对象,可以对流量进行更灵活和更细粒度的控制,典型的访问方式是 HTTP, Ingress 的核心价值在于通过重用单个外部负载均衡器和 IP, 可以为多个 Service 提供服务,简化系统结构的同时降低了基础架构成本。

除此之外,Ingress 还可以提供一些附加功能,例如授权、限流、SSL 等七层协议对应的应用功能。

Ingress 拓扑结构

路由方式

Ingress 提供七层负载均衡,作为一种资源,Ingress 包含所有入站请求的匹配规则列表,当客户端流量达到时,Ingress 仅支持用于转发 HTTP(S) 流量的规则。

常见的 HTTP 路由方式分为基于 Host 和基于 URL, 示例图如下:

基于 Host 的路由方式

基于 URL 的路由方式

Ingress Controller

Ingress 资源本身不会自动创建负载均衡器,必须要借助 Ingress Controller 才能控制 Kubernetes 集群的进出口流量,所以集群中必须有一个运行的 Ingress Controller, Ingress Controller 会根据 Ingress 的定义来管理负载均衡器。

默认的 Ingress 实现为 Nginx Ingress Controller, 简单来说,可以将其理解为运行在 Pod 的 Nginx 服务器,负责将请求重定向到集群内部的其他 Service, 作为 “入口”,这个 Nginx 服务器本身也需要暴露在集群之外,典型的方案是通过 Service 中的 LoadBalancer 类型来实现的,如果使用的是云服务提供商, 直接使用内置的负载均衡方案即可。和 Nginx Ingress Controller 类似,其他类型的 Ingress Controller 实现也是这样实现的。

以笔者使用的社区版的 Nginx Ingress Controller 来说,其 Nginx 守护进程容器和 Ingress Controller 容器运行在同一 Pod 中。 其中 Nginx 负责处理请求,Ingress Controller 负责从 Informer 订阅 Service, EndPoint 资源的变化,并生成对应的 nginx.conf 配置文件, 最后执行 nginx -s relaod 通知 Nginx 重新加载配置。

Ingress Controller 运行时结构

除此之外,几乎主流的几个云服务厂商都提供了自己的开源版本 Ingress Controller, 除此之外,开源社区中比较火的是基于 openresty 的实现的 Kong Ingress Controller 和 Apache APISIX Ingress Controller, 还有基于 Traefik 实现的 Ingress Controller。

Ingress Controller 拓扑结构

示例

下面是一个 Kubernetes Ingress 资源配置示例 yaml 文件:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: simple-fanout-example
spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - path: /foo
        pathType: Prefix
        backend:
          service:
            name: service1
            port:
              number: 4200
      - path: /bar
        pathType: Prefix
        backend:
          service:
            name: service2
            port:
              number: 8080

yaml 声明文件对应的拓扑图

这里以 Nginx Ingress Controller 为例,最终生成的 nginx.conf 配置文件内容如下:

# 代码中的 IP 均为虚拟 IP

...

http {
    ...

    upstream service1 {
        server 10.244.0.2:4200;
        keepalive 32;
    }

    upstream service2 {
        server 10.244.0.3:8080;
        keepalive 32;
    }

    server {
        listen 80;
        server_name example.com;

        location /foo {
            proxy_pass http://service1;
        }

        location /bar {
            proxy_pass http://service2;
        }
    }
}

当然,真实业务中的场景和接口和比这个示例复杂得多,笔者曾经在生产中的某条业务线观察到生成的 nginx.conf 配置文件,15 个服务和 100+ 条 Ingress 路由规则, 生成的 nginx.conf 配置文件大小为 4.3 MB, 这也提示我们要合理地划分微服务以及构建对应的 Ingress 结构。

扩展阅读

转载申请

本作品采用 知识共享署名 4.0 国际许可协议 进行许可,转载时请注明原文链接,图片在使用时请保留全部内容,商业转载请联系作者获得授权。