在 Kubernetes (K8s) 世界中,Ingress 是管理集群外部访问内部服务(主要是 HTTP 和 HTTPS)的核心组件。它允许你定义路由规则,将外部请求转发到集群内的不同服务。Ingress-Nginx 是一个基于 Nginx 的 Ingress 控制器实现,因其高性能、稳定性和丰富的功能而广受欢迎。
Helm 作为 Kubernetes 的包管理器,极大地简化了复杂应用的部署和管理过程。本文将详细介绍如何使用 Helm 将 Ingress-Nginx 部署到你的 K8s 集群,重点采用 DaemonSet + HostNetwork 模式,这种模式在追求高性能和简化网络路径的场景下具有优势。
Ingress-Nginx 部署模式简介
在开始之前,我们先简单回顾一下 Ingress-Nginx 常见的几种部署模式:
-
Deployment + LoadBalancer Service:
- 原理: Ingress Controller Pods 由 Deployment 管理。创建一个
type: LoadBalancer的 Service 指向这些 Pods。云厂商(如 AWS, GCP, Azure)会自动创建并关联一个外部负载均衡器(ELB/GLB/ALB)及公网 IP。 - 优点: 易于与公有云集成,自动获取公网 IP 和负载均衡。
- 缺点: 依赖云厂商支持,可能产生额外费用,网络路径相对较长。
- 适用场景: 公有云环境。
- 原理: Ingress Controller Pods 由 Deployment 管理。创建一个
-
Deployment + NodePort Service:
- 原理: Ingress Controller Pods 由 Deployment 管理。创建一个
type: NodePort的 Service 指向这些 Pods。Ingress Controller 会暴露在集群每个节点的一个静态高位端口上。 - 优点: 不依赖特定云厂商,部署相对简单。
- 缺点: NodePort 端口通常在高位范围 (30000-32767),需要外部负载均衡器(如 HAProxy, F5)或 DNS 轮询将 80/443 端口的流量转发到节点的 NodePort。增加了一层转发,可能影响性能。
- 适用场景: 自建机房或需要手动控制负载均衡器的环境。
- 原理: Ingress Controller Pods 由 Deployment 管理。创建一个
-
DaemonSet + HostNetwork:
- 原理: Ingress Controller Pods 由 DaemonSet 管理,确保在指定的每个节点上都运行一个 Pod。Pod 配置
hostNetwork: true,直接使用宿主机的网络命名空间。这意味着 Pod 直接监听宿主机的 80/443 端口。 - 优点: 网络路径最短(请求 -> Node IP:80/443 -> Pod),性能通常最优。无需额外的 Service 暴露端口(虽然可以保留 Service 用于内部发现)。
- 缺点: Pod 直接占用宿主机端口,可能与宿主机上其他服务冲突。每个节点只能运行一个监听相同端口(如 80/443)的 Ingress Controller Pod。需要通过
nodeSelector或affinity精确控制部署节点。 - 适用场景: 对性能要求高、网络延迟敏感的生产环境,且有专用节点承载 Ingress 流量。
- 原理: Ingress Controller Pods 由 DaemonSet 管理,确保在指定的每个节点上都运行一个 Pod。Pod 配置
本文重点实践 DaemonSet + HostNetwork 模式。
准备工作
- 一个可用的 Kubernetes 集群。
kubectl命令行工具已配置并连接到集群。helmv3 命令行工具已安装,如果没有安装,可以参考Kubernetes 包管理利器 helm 入门指南来进行安装。- (可选)
docker命令行工具,用于处理镜像(如果需要手动拉取和加载)。
安装步骤
1. 添加 Ingress-Nginx Helm 仓库
首先,我们需要将官方的 Ingress-Nginx Helm chart 仓库添加到本地 Helm 配置中,并更新仓库信息。
# 添加仓库
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
# 更新本地仓库缓存
helm repo update
# (可选) 搜索可用的 chart 版本
helm search repo ingress-nginx/ingress-nginx -l
2. 下载并解压 Chart 包
为了方便修改配置,我们将下载 Chart 包到本地。我们将使用 v4.12.2 版本作为示例(你可以根据 helm search 的结果选择需要的版本)。
# 指定版本下载 Chart 包
helm pull ingress-nginx/ingress-nginx --version 4.12.2
如果在执行 helm pull 时遇到网络问题,例如 connection reset by peer 错误,这通常是由于网络限制或 GFW 干扰导致的无法访问 GitHub Releases。你可以尝试直接使用 wget 或浏览器下载:
# 备选下载方式
wget https://github.com/kubernetes/ingress-nginx/releases/download/helm-chart-4.12.2/ingress-nginx-4.12.2.tgz
下载完成后,解压 Chart 包:
# 解压缩
tar -zxvf ingress-nginx-4.12.2.tgz
# 进入 chart 目录
cd ingress-nginx
接下来的修改都将在 ingress-nginx 目录下的 values.yaml 文件中进行。
3. 镜像准备(应对镜像拉取困难)
官方 Ingress-Nginx 使用的镜像托管在 registry.k8s.io。在中国大陆或其他网络受限的环境中,可能无法直接拉取。我们需要使用国内或其他可访问的镜像源进行替代,并重新打标签。
重要提示: 以下操作需要在所有你计划部署 Ingress Controller Pod 的节点上执行,或者在一个节点操作后将镜像文件拷贝到其他目标节点。
以 controller:v1.11.3 和 kube-webhook-certgen:v1.4.4 (对应 Chart v4.12.2 中的默认版本)为例,假设你找到了华为云或其他镜像仓库的替代镜像:
# === 处理 controller 镜像 ===
# 1. 从替代源拉取镜像
docker pull swr.cn-north-4.myhuaweicloud.com/ddn-k8s/registry.k8s.io/ingress-nginx/controller:v1.11.3
# 2. 将镜像重新打标签,使其与官方名称一致
docker tag swr.cn-north-4.myhuaweicloud.com/ddn-k8s/registry.k8s.io/ingress-nginx/controller:v1.11.3 registry.k8s.io/ingress-nginx/controller:v1.11.3
# 3. (可选) 保存镜像为 tar 文件,用于分发
docker save -o ncontroller.tar registry.k8s.io/ingress-nginx/controller:v1.11.3
# === 处理 kube-webhook-certgen 镜像 ===
# 1. 从替代源拉取镜像
docker pull swr.cn-north-4.myhuaweicloud.com/ddn-k8s/registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.4.4
# 2. 重新打标签
docker tag swr.cn-north-4.myhuaweicloud.com/ddn-k8s/registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.4.4 registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.4.4
# 3. (可选) 保存镜像为 tar 文件
docker save -o certgen.tar registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.4.4
# === 在其他目标节点上 ===
# 4. (如果使用了 .tar 文件) 将 tar 文件拷贝到其他节点
# scp ncontroller.tar root@<other-node-ip>:/root
# scp certgen.tar root@<other-node-ip>:/root
# 5. (如果使用了 .tar 文件) 在其他节点加载镜像
# docker load -i ncontroller.tar
# docker load -i certgen.tar
确保在执行 Helm 安装之前,所有目标节点上都存在 registry.k8s.io/ingress-nginx/controller:v1.11.3 和 registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.4.4 这两个本地镜像。
4. 修改 values.yaml 文件
现在,打开 ingress-nginx 目录下的 values.yaml 文件,进行以下修改以适配 DaemonSet + HostNetwork 模式:
-
注释掉 Controller 镜像的
digest: 这样 K8s 会根据tag来拉取镜像,避免因我们手动 re-tag 导致的 sha256 摘要不匹配问题。controller: image: registry: registry.k8s.io image: ingress-nginx/controller tag: "v1.11.3" # digest: sha256:d56f135b6462cfc476447cfe564b83a45e8bb7da2774963b00d12161112270b7 # 注释掉或删除此行 # ... 其他 image 配置 ... -
启用
hostNetwork: 这是实现 HostNetwork 模式的关键。controller: # ... 省略其他 controller 配置 ... hostNetwork: true # 将 false 修改为 true # ... 省略其他 controller 配置 ... -
修改
dnsPolicy: 使用hostNetwork: true时,需要将 DNS 策略设置为ClusterFirstWithHostNet,以便 Pod 能正确解析集群内部服务名,同时也能继承宿主机的 DNS 配置。controller: # ... 省略其他 controller 配置 ... dnsPolicy: ClusterFirstWithHostNet # 将 ClusterFirst 修改为 ClusterFirstWithHostNet # ... 省略其他 controller 配置 ... -
添加
nodeSelector: 我们希望 Ingress Controller 只运行在特定的节点上(例如,专门用于处理外部流量的边缘节点)。这里我们添加一个标签选择器ingress: "true"。稍后需要给目标节点打上这个标签。controller: # ... 省略其他 controller 配置 ... nodeSelector: kubernetes.io/os: linux ingress: "true" # 添加此行 # ... 省略其他 controller 配置 ... -
修改部署类型
kind为DaemonSet: 确保 Controller 以 DaemonSet 方式部署。controller: # ... 省略其他 controller 配置 ... kind: DaemonSet # 将 Deployment 修改为 DaemonSet # ... 省略其他 controller 配置 ... -
修改 Admission Webhooks 的
image配置 (注释digest) Admission Webhooks 用于验证 Ingress 资源的配置。同样,如果手动处理了kube-webhook-certgen镜像,需要注释掉其digest。controller: # ... 省略其他 controller 配置 ... admissionWebhooks: # ... 省略其他 admissionWebhooks 配置 ... patch: image: registry: registry.k8s.io image: ingress-nginx/kube-webhook-certgen tag: v1.4.4 # digest: sha256:a9f03b34a3cbfbb26d103a14046ab2c5130a80c3d69d526ff8063d2b37b9fd3f # 注释掉或删除此行 # ...注意:在较新版本的 Chart 中,
kube-webhook-certgen的配置路径可能在defaultBackend或单独的admissionWebhooks顶级键下,请根据你的values.yaml结构调整。此处路径基于原文档描述。 -
修改 Service 类型:
controller: # ... 省略其他 controller 配置 ... service: enabled: true # 确保 service 是启用的 # ... 省略其他 service 配置 ... type: NodePort # 将 LoadBalancer 或 ClusterIP 修改为 NodePort nodePorts: http: "30080" # (可选) 指定 HTTP 的 NodePort https: "30443" # (可选) 指定 HTTPS 的 NodePort tcp: {} udp: {} # ... 省略其他 controller 配置 ...效果: 这将创建一个
NodePort类型的 Service,除了可以通过NodeIP:80/443(HostNetwork)访问 Ingress 外,还可以通过NodeIP:30080和NodeIP:30443访问。
5. 处理 helm install 错误 ( ne comparison error)
在某些 Helm 或 Go 模板版本组合下,直接安装可能会遇到类似以下的错误:
Error: INSTALLATION FAILED: template: ingress-nginx/templates/controller-role.yaml:48:9: executing "ingress-nginx/templates/controller-role.yaml" at <ne (index .Values.controller.extraArgs "update-status") "false">: error calling ne: incompatible types for comparison
这是因为模板中试图比较 extraArgs 字典中的 update-status 值(可能不存在,即 nil)与字符串 "false"。我们需要显式地在 values.yaml 中设置这个参数。
-
修改
controller.extraArgs:controller: # ... 省略其他 controller 配置 ... extraArgs: update-status: "false" # 添加此行或修改{}为这个 # ... 省略其他 controller 配置 ...将
extraArgs: {}修改为此内容。注意update-status的值是字符串"false"。
6. 安装 Ingress-Nginx
现在,所有配置都已修改完毕,可以执行安装了。
# 1. (如果不存在) 创建目标命名空间
kubectl create ns ingress-nginx
# 2. 使用修改后的 values.yaml 文件执行 Helm 安装
# 确保当前目录在 ingress-nginx chart 目录下 (包含 Chart.yaml 和 values.yaml)
helm install ingress-nginx -n ingress-nginx .
7. 处理 helm install 错误 (Request entity too large)
如果遇到 Error: INSTALLATION FAILED: create: failed to create: Request entity too large: limit is 3145728 错误,这通常发生在 Helm 将 release 信息存储为 K8s Secret 或 ConfigMap 时,内容超出了 K8s API Server 的请求体大小限制(默认为 3MB)。
这可能是因为 values.yaml 文件过大,或者 Chart 本身包含大量模板文件。
解决方法:
- 清理之前的失败尝试 (如果适用):
helm uninstall ingress-nginx -n ingress-nginx # 可能需要手动删除 Helm 的 Secret/ConfigMap 记录,请谨慎操作 # kubectl delete secret -n ingress-nginx -l owner=helm,name=ingress-nginx - 检查
values.yaml大小: 确保没有包含异常大的内容(例如,嵌入的大型证书或配置片段)。 - 考虑 K8s API Server 配置: 在某些情况下,可能需要调整 K8s API Server 的
--max-request-bytes参数,但这通常不是首选,需要集群管理员权限。 - 尝试分步安装或减小 Chart 复杂度 (高级): 对于非常复杂的 Chart,有时会考虑将 Chart 拆分。
- 重新尝试: 有时简单地清理并重试即可。原文档建议删除本地 Chart 目录后重新解压修改,这主要确保本地文件没有损坏,但通常不直接解决 "Request entity too large" 问题。
8. 处理端口冲突(例如 K3s 内置 Traefik)
如果你使用的是 K3s 等自带 Ingress Controller(如 Traefik)的 K8s 发行版,并且配置了 hostNetwork: true,Ingress-Nginx Pod 可能会因为 80/443 端口已被占用而无法启动,状态卡在 Pending。
使用 kubectl describe pod <ingress-nginx-pod-name> -n ingress-nginx 查看 Pod 事件,可能会看到类似 FailedScheduling ... node(s) didn't have free ports for the requested pod ports 的错误。
解决方法: 卸载或禁用内置的 Ingress Controller。以 K3s 的 Traefik 为例:
# 1. 查找 Traefik相关的 Helm release (通常在 kube-system 命名空间)
helm list -n kube-system | grep traefik
# 2. 卸载 Traefik (release 名称可能不同,例如 traefik 或 traefik-crd)
helm uninstall traefik -n kube-system
helm uninstall traefik-crd -n kube-system # 如果有单独的 CRD release
# 3. (或者) 如果是通过 K3s 配置文件禁用的,请参考 K3s 文档,在启动参数中添加 --disable traefik 并重启 K3s 服务。
卸载冲突的服务后,Ingress-Nginx Pod 应该能够被正确调度并启动。
Post-Installation: 部署后配置
1. 验证部署
检查 Ingress-Nginx 相关资源的状态:
kubectl get pods,svc,ds -n ingress-nginx -o wide
你应该能看到 ingress-nginx-controller 的 DaemonSet 管理的 Pods 处于 Running 状态,并且它们运行在你期望打标签的节点上。同时,也能看到对应的 Service (如果启用了)。
2. 为节点打标签
根据之前在 values.yaml 中设置的 nodeSelector (ingress: "true"),需要为你希望运行 Ingress Controller Pod 的节点添加标签。
# 1. 查看节点列表
kubectl get nodes
# 2. 为目标节点打标签 (替换 k8s-worker01, k8s-worker02 为你的实际节点名)
kubectl label node k8s-worker01 ingress=true
kubectl label node k8s-worker02 ingress=true
# ... 为所有需要的节点打标签 ...
# (可选) 查看节点标签确认
kubectl get node --show-labels
如果标签打好后 Pod 仍未调度,请检查 kubectl describe pod ... 的事件。
3. 处理 Master 节点 Taints (测试环境)
默认情况下,Kubernetes Master 节点通常带有 NoSchedule 或 NoExecute 污点 (Taints),阻止普通工作负载调度上去。如果你希望在 Master 节点上也运行 Ingress Controller(通常不推荐在生产环境这样做,但在资源有限的测试环境中可能需要),你需要移除相应的污点。
# 查看 Master 节点的 Taints
kubectl describe node <your-master-node-name> | grep Taints
# 假设 Master 节点名为 k8s-master01,且有污点 node-role.kubernetes.io/master:NoSchedule
# 移除污点 (注意最后的 -)
kubectl taint node k8s-master01 node-role.kubernetes.io/master-
# 如果污点是 node-role.kubernetes.io/control-plane:NoSchedule (或者类似), 命令相应调整
# kubectl taint node k8s-master01 node-role.kubernetes.io/control-plane-
移除污点后,如果 Master 节点也打了 ingress=true 标签,DaemonSet 就会在该节点上创建一个 Pod。
进阶配置 (可选)
1. 配置 TCP/UDP 代理
Ingress 资源主要处理 HTTP/HTTPS,但 Ingress-Nginx Controller 也可以通过 ConfigMap 配置来代理 TCP 或 UDP 流量。
-
修改
values.yaml: 在controller.tcp或controller.udp下添加端口和服务映射。controller: # ... tcp: # <external-port>: "<namespace>/<service-name>:<service-port>" "3306": "default/mysql-service:3306" "5432": "infra/postgres-service:5432" udp: # <external-port>: "<namespace>/<service-name>:<service-port>" "53": "kube-system/kube-dns:53" # ... -
更新 Helm Release: 修改
values.yaml后,使用helm upgrade应用更改。helm upgrade ingress-nginx -n ingress-nginx .
2. 修改 Controller 监听端口 (Host Ports)
默认情况下,hostNetwork: true 使 Controller 直接监听宿主机的 80 (HTTP) 和 443 (HTTPS) 端口。如果需要修改这些端口(例如,避免与宿主机其他服务冲突),最佳实践是通过 values.yaml 配置并使用 helm upgrade,而不是直接 kubectl edit ds (因为 Helm upgrade 会覆盖手动修改)。
-
修改
values.yaml: 使用controller.extraArgs来传递 Nginx Ingress Controller 的命令行参数--http-port和--https-port。controller: # ... # 确保 extraArgs 中有这几项 extraArgs: update-status: "false" # 保留之前的修改 http-port: "8880" # 设置新的 HTTP 端口 https-port: "8881" # 设置新的 HTTPS 端口 # ... -
更新 Helm Release:
helm upgrade ingress-nginx -n ingress-nginx .更新后,Ingress Controller Pod 将在宿主机上监听 8880 (HTTP) 和 8881 (HTTPS) 端口。
总结
通过本文的步骤,你应该能够使用 Helm 成功地将 Ingress-Nginx 以 DaemonSet + HostNetwork 模式部署到你的 Kubernetes 集群中。这种模式提供了优异的网络性能,特别适合对延迟敏感的应用。我们还涵盖了镜像处理、常见错误排查、节点标签管理以及一些进阶配置。
请记住,选择哪种部署模式取决于你的具体环境(公有云 vs 私有云)、性能需求、运维复杂度和成本考虑。希望这篇博文能帮助你更好地理解和应用 Ingress-Nginx。