前言
在 Kubernetes 集群中,Service 已经为 Pod 提供了稳定的访问入口和基础的负载均衡能力。然而,Service 默认工作在四层(TCP/UDP),无法实现基于域名、URL 路径等七层路由的高级流量管理。当我们需要将多个服务通过同一个入口暴露到外部,并且希望根据不同的域名或路径转发到不同的后端时,传统的 Service(尤其是 NodePort 类型)会暴露出明显的不足:每个服务需要占用一个节点端口,端口管理混乱;随着服务增多,iptables 规则急剧膨胀,维护和排错变得困难。为了解决这一问题,Kubernetes 引入了 Ingress 资源及其控制器(Ingress Controller)。Ingress 充当集群的"智能路由器",能够提供七层负载均衡、虚拟主机、SSL 终结等高级功能。本文将详细介绍 Ingress 的原理、架构以及两种主流的部署方式(NodePort 模式和 HostNetwork 模式),并通过实际案例演示如何配置和使用 Ingress。
一、Service 机制的局限性
Service 提供了 Pod 的稳定访问和四层负载均衡,但在对外暴露服务时存在以下不足:
-
仅支持四层负载均衡:无法根据 HTTP 头部、域名、URL 路径等七层信息进行路由。
-
NodePort 类型的端口管理困难:每个 Service 需要在每个节点上占用一个端口(范围 30000-32767)。当服务数量增多时,端口分配和记忆变得混乱,且容易冲突。
-
iptables 规则膨胀:每个 NodePort 和 ClusterIP Service 都会在节点上生成大量 iptables 规则。大规模集群中,规则数量会导致性能下降和排错困难。
-
对外依赖负载均衡器:使用 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 中。
整个工作流程如下:
-
外部请求到达集群边缘(如 DNS 解析到某个负载均衡器)。
-
流量被转发到 Ingress Controller 对应的 Service(NodePort 或 LoadBalancer)。
-
Service 将请求分发到 Ingress Controller 的 Pod。
-
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 为例,它的工作流程如下:
-
Ingress Controller Pod 中运行着一个 Nginx 进程和一个 Controller 进程。
-
Controller 通过 Kubernetes API 持续监听 Ingress、Service、Endpoint 等资源的变化。
-
当用户创建或更新 Ingress 对象时,Controller 读取这些规则,按照内置的模板生成新的
nginx.conf配置文件。 -
Controller 向 Nginx 进程发送
nginx -s reload信号,使新配置生效,无需重启 Pod。 -
外部流量经由 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.2Job
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.org 和 www.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 文件,但需要进行以下调整:
-
将 Deployment 改为 DaemonSet :在文件中找到
kind: Deployment,修改为kind: DaemonSet。 -
添加 hostNetwork: true :在
spec.template.spec下加入:hostNetwork: true -
添加 nodeSelector:确保 Ingress Controller 只运行在带有特定标签的节点上,便于前端负载均衡器固定后端。
nodeSelector: ingress: nginx-server -
调整 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。