K8s中服务发现Ingress与Ingress Controller应用实践

前言

在 Kubernetes 集群中,Service 已经为 Pod 提供了稳定的访问入口和基础的负载均衡能力。然而,Service 默认工作在四层(TCP/UDP),无法实现基于域名、URL 路径等七层路由的高级流量管理。当我们需要将多个服务通过同一个入口暴露到外部,并且希望根据不同的域名或路径转发到不同的后端时,传统的 Service(尤其是 NodePort 类型)会暴露出明显的不足:每个服务需要占用一个节点端口,端口管理混乱;随着服务增多,iptables 规则急剧膨胀,维护和排错变得困难。为了解决这一问题,Kubernetes 引入了 Ingress 资源及其控制器(Ingress Controller)。Ingress 充当集群的"智能路由器",能够提供七层负载均衡、虚拟主机、SSL 终结等高级功能。本文将详细介绍 Ingress 的原理、架构以及两种主流的部署方式(NodePort 模式和 HostNetwork 模式),并通过实际案例演示如何配置和使用 Ingress。


一、Service 机制的局限性

Service 提供了 Pod 的稳定访问和四层负载均衡,但在对外暴露服务时存在以下不足:

  1. 仅支持四层负载均衡:无法根据 HTTP 头部、域名、URL 路径等七层信息进行路由。

  2. NodePort 类型的端口管理困难:每个 Service 需要在每个节点上占用一个端口(范围 30000-32767)。当服务数量增多时,端口分配和记忆变得混乱,且容易冲突。

  3. iptables 规则膨胀:每个 NodePort 和 ClusterIP Service 都会在节点上生成大量 iptables 规则。大规模集群中,规则数量会导致性能下降和排错困难。

  4. 对外依赖负载均衡器:使用 LoadBalancer 类型的 Service 需要云厂商支持,且通常成本较高。

下图展示了 NodePort 模式的工作方式:外部请求先访问节点 IP 的某个端口,再经过 Service 转发到后端 Pod。这种方式每增加一个服务就需要新增一个端口映射。

复制代码
Node1:30577  ---> Service ---> Pod
Node2:30577  ---> Service ---> Pod
Node3:30577  ---> Service ---> Pod

二、Ingress 与 Ingress Controller 简介

Ingress 是 Kubernetes 的一种 API 资源,用于定义七层路由规则(如域名、URL 路径与后端 Service 的对应关系)。它本身只是一个规则配置,不具备路由能力。真正实现七层流量转发的是 Ingress Controller

一个典型的 Ingress 架构包含三个部分:

  • Ingress 资源:用 YAML 定义的规则集合,描述"哪个域名/路径的请求转发到哪个 Service"。

  • Ingress Controller:核心组件,通常以 Pod 形式运行在集群中。它通过 Watch 机制监听 API Server 中 Ingress 资源的变化,动态生成负载均衡器(如 Nginx、HAProxy)的配置文件,并 reload 使其生效。

  • Ingress-Controller-Service:一个四层 Service(通常是 NodePort 或 LoadBalancer 类型),用于将外部流量接入到 Ingress Controller 的 Pod 中。

整个工作流程如下:

  1. 外部请求到达集群边缘(如 DNS 解析到某个负载均衡器)。

  2. 流量被转发到 Ingress Controller 对应的 Service(NodePort 或 LoadBalancer)。

  3. Service 将请求分发到 Ingress Controller 的 Pod。

  4. Ingress Controller 解析请求的域名和路径,根据 Ingress 规则将请求转发到对应的后端 Service,最终到达 Pod。

下图示意了 Ingress 的整体逻辑:

复制代码
外部负载均衡器
    ↓
Ingress Controller Service(四层)
    ↓
Ingress Controller Pod(Nginx/HAProxy)
    ↓(根据域名/路径)
Service-A  ←→ Pods
Service-B  ←→ Pods

三、Ingress Controller 的工作流程(以 Nginx 为例)

以社区广泛使用的 ingress-nginx 为例,它的工作流程如下:

  1. Ingress Controller Pod 中运行着一个 Nginx 进程和一个 Controller 进程。

  2. Controller 通过 Kubernetes API 持续监听 Ingress、Service、Endpoint 等资源的变化。

  3. 当用户创建或更新 Ingress 对象时,Controller 读取这些规则,按照内置的模板生成新的 nginx.conf 配置文件。

  4. Controller 向 Nginx 进程发送 nginx -s reload 信号,使新配置生效,无需重启 Pod。

  5. 外部流量经由 Ingress Controller Service 进入 Nginx,Nginx 根据配置进行七层路由,将请求代理到对应的后端 Service(进而到 Pod)。

这种动态配置机制使得用户只需管理 Ingress YAML 文件,无需手动操作 Nginx。


四、部署 Ingress-Nginx 的三种常见方式

根据实际环境的需求,ingress-nginx 有多种部署和暴露方式。以下介绍三种主流方案。

部署方式 适用场景 优点 缺点
Deployment + LoadBalancer 公有云(AWS、GCP、Azure) 自动分配公网负载均衡器,配置简单 依赖云厂商,会产生额外费用
Deployment + NodePort 裸金属、私有云,节点 IP 固定 不依赖云服务,无需额外 LB 多一层 NAT,性能略差;需要管理 NodePort 端口
DaemonSet + HostNetwork + nodeSelector 高并发生产环境 性能最好,链路短,直接使用宿主机端口 每个节点只能运行一个实例,需配合外部 LB

下面重点演示后两种方式:NodePort 模式HostNetwork 模式


五、实战:Deployment + NodePort 模式部署 Ingress-Nginx

5.1 下载并修改部署文件

从官方仓库下载 deploy.yaml(本例使用 v0.34.1 版本,适用于 baremetal 环境):

复制代码
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.34.1/deploy/static/provider/baremetal/deploy.yaml

由于国内网络访问 Google 镜像可能较慢,需要修改三处镜像地址:

  • Deployment 中的 nginx-ingress-controller 容器

    复制代码
    image: registry.cn-beijing.aliyuncs.com/iivey/nginx-ingress-controller:v0.34.1
  • Job ingress-nginx-admission-create 中的容器

    复制代码
    image: registry.cn-beijing.aliyuncs.com/iivey/kube-webhook-certgen:v1.2.2

    Job ingress-nginx-admission-patch 中的容器

复制代码
  image: registry.cn-beijing.aliyuncs.com/iivey/kube-webhook-certgen:v1.2.2

保留文件中 Service 的类型为 NodePort(默认即为 NodePort)。

5.2 部署资源

复制代码
[root@master ingress]# kubectl apply -f deploy.yaml
namespace/ingress-nginx created
...
[root@master ingress]# kubectl -n ingress-nginx get pod
NAME                                            READY   STATUS      RESTARTS   AGE
ingress-nginx-admission-create-9xxgq           0/1     Completed   0          28s
ingress-nginx-admission-patch-f9dvq            0/1     Completed   0          28s
ingress-nginx-controller-68b999bc64-llhbx      1/1     Running     0          38s

[root@master ingress]# kubectl -n ingress-nginx get svc
NAME                                 TYPE       CLUSTER-IP       PORT(S)                      AGE
ingress-nginx-controller             NodePort   10.106.244.107   80:30577/TCP,443:30687/TCP   2m
ingress-nginx-controller-admission   ClusterIP  10.98.8.94       443/TCP                      2m

可以看到,Ingress Controller 被暴露为 NodePort Service,HTTP 端口 80 映射到节点的 30577 端口。

5.3 创建后端 Service 和 Pod

准备两个示例 Service 和 Deployment,例如 service-nginx(nginx 服务)和 service-httpd(httpd 服务),它们分别对应不同的标签选择器。这里假设已经存在。

5.4 创建 Ingress 规则

编写 ingress1.yml,定义两个虚拟主机 www.best.orgwww.best1.org,分别指向不同的后端 Service:

复制代码
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: ingress-demo
spec:
  rules:
  - host: www.best.org
    http:
      paths:
      - path: /
        backend:
          serviceName: service-nginx
          servicePort: 80
  - host: www.best1.org
    http:
      paths:
      - path: /
        backend:
          serviceName: service-httpd
          servicePort: 80

注意:networking.k8s.io/v1beta1 在较新版本中已废弃,可改用 v1,语法略有不同,但原理一致。

创建 Ingress:

复制代码
[root@master ingress]# kubectl apply -f ingress1.yml
[root@master ingress]# kubectl get ingress
NAME           CLASS    HOSTS                         ADDRESS   PORTS   AGE
ingress-demo   <none>   www.best.org,www.best1.org             80      5s

5.5 测试访问

在客户端主机上修改 /etc/hosts,将域名解析到任意一个集群节点的 IP(因为 NodePort 模式下每个节点都监听了 30577 端口):

复制代码
172.16.213.222   www.best.org
172.16.213.223   www.best1.org

然后通过浏览器或 curl 访问:

复制代码
curl http://www.best.org:30577/
# 应返回 service-nginx 的页面
curl http://www.best1.org:30577/
# 应返回 service-httpd 的页面

六、实战:DaemonSet + HostNetwork + nodeSelector 模式部署

该方案使用主机网络(HostNetwork),Ingress Controller Pod 直接监听节点的 80 和 443 端口,性能最佳,适合高并发场景。

6.1 修改部署文件

依然使用上述 deploy.yaml 文件,但需要进行以下调整:

  1. 将 Deployment 改为 DaemonSet :在文件中找到 kind: Deployment,修改为 kind: DaemonSet

  2. 添加 hostNetwork: true :在 spec.template.spec 下加入:

    复制代码
    hostNetwork: true
  3. 添加 nodeSelector:确保 Ingress Controller 只运行在带有特定标签的节点上,便于前端负载均衡器固定后端。

    复制代码
    nodeSelector:
      ingress: nginx-server
  4. 调整 Service 类型:由于使用了 HostNetwork,实际上可以不依赖 Service 暴露端口,但为了 admission webhook 等功能仍保留 ClusterIP Service 即可。

6.2 给节点打标签

选择一个节点(例如 node1)打上标签:

复制代码
[root@master ingress]# kubectl label nodes node1 ingress=nginx-server
[root@master ingress]# kubectl get nodes --show-labels | grep nginx-server
node1   ...   ingress=nginx-server

6.3 部署 DaemonSet 版本

复制代码
[root@master ingress]# kubectl apply -f deploy.yaml
[root@master ingress]# kubectl -n ingress-nginx get daemonset
NAME                       DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR
ingress-nginx-controller   1         1         1       1            1           ingress=nginx-server

[root@master ingress]# kubectl -n ingress-nginx get pod -o wide
NAME                             READY   STATUS    IP             NODE
ingress-nginx-controller-xxxxx   1/1     Running   172.16.213.222 node1

注意 Pod 的 IP 就是 node1 的物理 IP(如 172.16.213.222),且 Pod 直接使用了宿主机的网络栈,因此可以通过该节点的 80 端口直接访问。

6.4 创建相同的 Ingress 资源

使用与 NodePort 模式相同的 ingress1.yml 文件创建 Ingress。

6.5 测试访问

修改客户端 /etc/hosts,将域名解析到 node1 的 IP(本例 172.16.213.222):

复制代码
172.16.213.222   www.best.org
172.16.213.222   www.best1.org

由于 Ingress Controller 监听了宿主机的 80 端口,可以直接使用 http 协议访问,无需指定端口:

复制代码
curl http://www.best.org/
# 返回 service-nginx 内容
curl http://www.best1.org/
# 返回 service-httpd 内容

这种模式下,如果需要在集群外部提供高可用,通常会在多个节点上部署 Ingress Controller(通过 nodeSelector 分散到多台节点),然后在前面挂载一个硬件负载均衡器或 HAProxy 来对这几个节点的 80/443 端口进行流量分发。


七、总结与最佳实践

Ingress 和 Ingress Controller 的结合为 Kubernetes 提供了强大的七层流量管理能力,使得基于域名和 URL 路径的路由变得简单、动态。本文通过实际案例演示了两种常用的部署方式:

  • NodePort 模式:配置简单,适用于测试或节点 IP 固定的环境,但多了一层 NAT,性能略低且需管理随机端口。

  • HostNetwork 模式:性能最优,适合生产环境,但需配合节点标签和外部负载均衡器使用,且每个节点只能运行一个 Ingress Controller Pod。

掌握 Ingress 的使用,将大幅提升 Kubernetes 集群对外服务的能力和灵活性。希望本文能够帮助你顺利部署和应用 Ingress。