1. 快速安装
1.1. 使用 Helm 安装
在线安装
helm upgrade --install ingress-nginx ingress-nginx \
--repo https://kubernetes.github.io/ingress-nginx \
--namespace ingress-nginx --create-namespace
离线安装
wget https://github.com/kubernetes/ingress-nginx/releases/download/helm-chart-4.13.3/ingress-nginx-4.13.3.tgz
helm install ingress-nginx ingress-nginx-4.13.3.tgz --version 4.13.3 --namespace ingress-nginx --create-namespace
1.2. 使用 yaml 安装
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.13.3/deploy/static/provider/cloud/deploy.yaml
kubectl apply -f deploy.yaml
1.3. 安装日志
NAME: ingress-nginx
LAST DEPLOYED: Mon Oct 6 13:55:42 2025
NAMESPACE: ingress-nginx
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
The ingress-nginx controller has been installed.
It may take a few minutes for the load balancer IP to be available.
You can watch the status by running 'kubectl get service --namespace ingress-nginx ingress-nginx-controller --output wide --watch'
# 省略 ……
1.4. 查看服务
$ kubectl get svc ingress-nginx-controller -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
ingress-nginx-controller LoadBalancer 10.99.75.212 <pending> 80:31647/TCP,443:32648/TCP
ingress-nginx-controller 对外曝露了 2 个端口:http 为 31647;https 为 32648。
2. 应用示例
这一节,我将先部署 my-nginx 服务,然后创建 ingress 规则和配置 TLS 证书,将对 https://www.igeeksky.com 的请求转发给 my-nginx 进行处理。
2.1. 部署 nginx
创建 my-nginx-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
namespace: dev
spec:
selector:
matchLabels:
app: my-nginx
replicas: 2
template:
metadata:
labels:
app: my-nginx
spec:
containers:
- name: my-nginx
image: nginx:1.29.1
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: my-nginx
namespace: dev
spec:
selector:
app: my-nginx
type: ClusterIP
ports:
- port: 8080
targetPort: 80
部署服务:
kubectl apply -f my-nginx-deploy.yaml
2.2. 创建 secret
如果想要实现 HTTPS 通信,需先创建 tls 类型的 secret 配置,然后在 Ingress 配置中指定使用此 secret 即可。
我这里已提前准备好了证书和私钥,因此直接创建 secret 即可。
kubectl create secret tls igeeksky-com-tls \
--cert=./igeeksky.com.crt \
--key=./igeeksky.com.key \
--namespace=dev
2.3. 配置 Ingress 规则
创建 igeeksky-com-ingress.yaml:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: igeeksky-com-ingress
namespace: dev
annotations:
# Use this annotation to enforce a redirect from HTTP to HTTPS
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
spec:
ingressClassName: nginx
rules:
- host: www.igeeksky.com
http:
paths:
- pathType: Prefix
path: /
backend:
service:
name: my-nginx
port:
number: 8080
tls:
- hosts:
- www.igeeksky.com
secretName: igeeksky-com-tls
应用 Ingress 规则:
kubectl apply -f igeeksky-com-ingress.yaml
查看 Ingress:
$ kubectl get ingress -A
NAMESPACE NAME CLASS HOSTS ADDRESS PORTS AGE
dev igeeksky-com-ingress nginx www.igeeksky.com 80, 443 2d
我们可以看到,关于 www.igeeksky.com 的规则已经成功应用。
2.4. 外部访问
先在集群外部主机添加 DNS 记录:
C:\Windows\System32\drivers\etc\hosts
192.168.50.130 www.igeeksky.com
192.168.50.135 www.igeeksky.com
192.168.50.136 www.igeeksky.com
然后,在该集群外部主机打开浏览器,输入 https://www.igeeksky.com:32648 即可通过 Ingress 访问后端的 my-nginx 服务。
3. Service 配置
默认情况下,Service: ingress-nginx-controller 的部分配置如下:
apiVersion: v1
kind: Service
metadata:
name: ingress-nginx-controller
spec:
externalIPs:
type: LoadBalancer # NodePort, ClusterIP
externalTrafficPolicy: Cluster # Local
接下来,我们来测试不同的配置项,并重点关注以下问题:
① 是否可以使用标准的 HTTP 端口 80/443?
② 是否任意节点均可接受请求?
③ 是否可以获取真实的源 IP?
3.1. LoadBalancer or NodePort
spec:
externalIPs:
type: LoadBalancer # NodePort
externalTrafficPolicy: Cluster # Local
示意图:

这个示意图中,仅有 192.168.50.135 节点存在 Pod: ingress-nginx-controller。
流量路径:Client → Service(Ingress-nginx:31647/32648) → kube-proxy → ClusterIP → Pod(Ingress-nginx:80/443) → App
3.1.1. type
spec:type 为 LoadBalancer 或 NodePort 时,会在所有节点曝露服务端口(示例中的 31647/32648)。
当 spec: type 为 NodePort 时,服务信息如下:
# 查看服务
$ kubectl get svc ingress-nginx-controller -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
ingress-nginx-controller NodePort 10.99.75.212 <none> 80:31647/TCP,443:32648/TCP
当 spec:type 为 LoadBalancer 时,服务信息如下:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
ingress-nginx-controller LoadBalancer 10.99.75.212 <pending> 80:31647/TCP,443:32648/TCP
LoadBalancer 相当于是 NodePort 的云环境增强版,还会期望云厂商的外部负载均衡器提供公网 IP。
因为这里是用 VMware 搭建的本地虚拟机集群,无云厂商的负载均衡器分配公网 IP,所以 EXTERNAL-IP 会一直显示 pending 状态。
因此,如果未使用云平台的负载均衡器,将 spec:type 设为 NodePort 即可。
关于端口:
根据 Kubernetes 的端口规则, service 默认曝露的端口范围为 30000-32767。
因此,一般情况下,我们无法使用标准的 80 和 443 端口,即无法直接使用 https://www.igeeksky.com 来访问后端服务,而是必须附加端口 32648。
3.1.2. externalTrafficPolicy
当 spec:externalTrafficPolicy 为 Cluster 时,客户端可访问任意集群节点,最终都会转发到 192.168.50.135 节点去处理。
当 spec:externalTrafficPolicy 为 Local 时,客户端仅能访问 192.168.50.135 节点,其它节点接收到的请求会被丢弃。
关于 源 IP:
对于后端应用,可能会需要根据客户端 IP 来执行类似于白名单、黑名单这样的逻辑。
所以,我们来测试下如何配置才能获取真实的客户端 IP。
测试之前,先添加 DNS 记录,将 www.igeeksky.com 指向 192.168.50.135。
# 查看 App(my-nginx) 日志
$ kubectl logs my-nginx-788998658d-24hk6 -n dev -f
# 测试 spec:externalTrafficPolicy:Cluster
curl -k -v https://www.igeeksky.com:32648
# 192.168.50.135 为接收请求的集群节点 IP,日志如下:
172.30.230.33 - - [14/Oct/2025:08:20:30 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/8.13.0" "192.168.50.135"
# 测试 spec:externalTrafficPolicy:Local
curl -k -v https://www.igeeksky.com:32648
# 192.168.50.218 为客户端 IP,日志如下:
172.30.230.33 - - [14/Oct/2025:08:58:53 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/8.13.0" "192.168.50.218"
当 externalTrafficPolicy 为 Cluster 时,所有节点均可接收请求,但在转发请求过程中需执行 NAT 地址转换,所以源IP 地址将变成 Kubernetes 集群中接收请求的节点 IP。
所以,如果想获取真实的客户端 IP 地址,那么 externalTrafficPolicy 必须设为 Local。
3.1.3. 小结
spec:type:当使用云厂商的负载均衡器时,设为 LoadBalancer;否则设为 NodePort 。
spec:externalTrafficPolicy: 当设为 Cluster 时,所有节点都可以接受请求,但无法保留源IP;当设为 Local 时,仅仅部署有 Pod: ingress-nginx-controller 的节点能处理请求,但可以保留源 IP。
3.2. External IPs
如果希望使用标准的 80/443 端口来接收请求,一种可行的办法是配置 externalIPs。
示例配置:
spec:
externalIPs:
- 192.168.50.135
- 192.168.50.136
type: ClusterIP # NodePort, LoadBalancer
externalTrafficPolicy: Cluster # Local
示意图:

流量路径:Client → Service(Ingress-nginx:80/443) → kube-proxy → ClusterIP → Pod(Ingress-nginx:80/443) → App
查看服务:
# 查看服务
kubectget svc ingress-nginx-controller -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
ingress-nginx-controller ClusterIP 10.99.75.212 192.168.50.135,192.168.50.136 80/TCP,443/TCP
可以看到 EXTERNAL-IP 的状态显示为手动设定的 192.168.50.135,192.168.50.136。
测试不同配置:
关于源 IP、可接受请求节点、可接受请求端口等的测试结果如下:
spec:externalTrafficPolicy: Local:仅 135 节点可接受请求;可以获取真实的源 IP。
spec:externalTrafficPolicy: Cluster:135 和 136 节点均可接受请求;无法获取真实的源 IP。
spec:type: ClusterIP:仅 80/443 可接受请求。
spec:type: NodePort 或 spec:type: LoadBalancer :80/443 和31647/32648 均可接受请求。
注:Ingress-nginx 的官方文档说,当配置
externalIPs以提供外部服务时,无论如何都无法保留真实源 IP。经测试,这种说法是错误的,只要配置是
spec:externalTrafficPolicy:Local,同样可以获取真实源 IP。
4. HostNetwork
上一节的示例配置中,对外提供服务的都是 Service 层。
但对于 Ingress 这种对外曝露接口的应用网关而言,我们完全可以直接使用 Pod 来提供对外服务。
这样,既可以少一层网络转发,还可以直接曝露标准的 80/443 端口。
示意图:

流量路径:Client → Pod(Ingress-nginx:80/443) → App
4.1. 删除资源
这一节中,我们已不再需要 Service,并且需将 Deployment 改为 DaemonSet,所以先删除。
# 删除 Service
kubectl delete svc ingress-nginx-controller -n ingress-nginx
# 删除 Deployment
kubectl delete deploy ingress-nginx-controller -n ingress-nginx
4.2. DaemonSet
增加节点标签(用以选择部署的节点):
kubectl label nodes k8s-worker-1 ingress-ready=true
kubectl label nodes k8s-worker-2 ingress-ready=true
修改清单:
为了将 Deployment 改为 DaemonSet,我从 Ingress-nginx 官网下载的 deploy.yaml 中仅复制了 kind: Deployment 部分的内容进行修改,并命名为 ingress-daemonset.yaml。
其它的诸如 Role 、RoleBinding…… 之类的其它资源因为之前已经部署并且未删除,所以无需再重复部署。
关键修改如下:
# kind: Deployment
kind: DaemonSet
# dnsPolicy: ClusterFirst
dnsPolicy: ClusterFirstWithHostNet
# hostNetwork: false
hostNetwork: true
修改后完整的 ingress-daemonset.yaml 配置内容:
apiVersion: apps/v1
kind: DaemonSet
metadata:
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.13.3
name: ingress-nginx-controller
namespace: ingress-nginx
spec:
minReadySeconds: 0
revisionHistoryLimit: 10
selector:
matchLabels:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
#strategy:
# rollingUpdate:
# maxUnavailable: 1
# type: RollingUpdate
template:
metadata:
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.13.3
spec:
automountServiceAccountToken: true
containers:
- args:
- /nginx-ingress-controller
#- --publish-service=$(POD_NAMESPACE)/ingress-nginx-controller
- --election-id=ingress-nginx-leader
- --controller-class=k8s.io/ingress-nginx
- --ingress-class=nginx
- --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
- --validating-webhook=:8443
- --validating-webhook-certificate=/usr/local/certificates/cert
- --validating-webhook-key=/usr/local/certificates/key
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: LD_PRELOAD
value: /usr/local/lib/libmimalloc.so
image: registry.k8s.io/ingress-nginx/controller:v1.13.3@sha256:1b044f6dcac3afbb59e05d98463f1dec6f3d3fb99940bc12ca5d80270358e3bd
imagePullPolicy: IfNotPresent
lifecycle:
preStop:
exec:
command:
- /wait-shutdown
livenessProbe:
failureThreshold: 5
httpGet:
path: /healthz
port: 10254
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
name: controller
ports:
- containerPort: 80
name: http
protocol: TCP
- containerPort: 443
name: https
protocol: TCP
- containerPort: 8443
name: webhook
protocol: TCP
readinessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 10254
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
resources:
requests:
cpu: 100m
memory: 90Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
add:
- NET_BIND_SERVICE
drop:
- ALL
readOnlyRootFilesystem: false
runAsGroup: 82
runAsNonRoot: true
runAsUser: 101
seccompProfile:
type: RuntimeDefault
volumeMounts:
- mountPath: /usr/local/certificates/
name: webhook-cert
readOnly: true
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
nodeSelector:
# kubernetes.io/os: linux
# 仅选择有 ingress-ready 标签的节点
ingress-ready: "true"
serviceAccountName: ingress-nginx
terminationGracePeriodSeconds: 300
volumes:
- name: webhook-cert
secret:
secretName: ingress-nginx-admission
应用配置:
kubectl apply -f ingress-daemonset.yaml
4.3. 测试
现在,我们已经删除了原有的 service 和 deployment,并以 DaemonSet 方式在 135 和 136 节点重新部署了 Pod:Ingress-nginx-controller。
查看 pod 及 ingress:
kubectl get pod -n ingress-nginx -o wide
NAME READY STATUS IP NODE
ingress-nginx-controller-6b9lh 1/1 Running 192.168.50.136 k8s-worker-2
ingress-nginx-controller-s254t 1/1 Running 192.168.50.135 k8s-worker-1
kubectl get ingress -n dev
NAME CLASS HOSTS ADDRESS PORTS
igeeksky-com-ingress nginx www.igeeksky.com 192.168.50.135,192.168.50.136 80, 443
测试:
# 查看 App(my-nginx) 日志
$ kubectl logs my-nginx-788998658d-24hk6 -n dev -f
curl -k -v https://www.igeeksky.com
# 192.168.50.218 为客户端 IP,192.168.50.136 为接受请求的节点 IP,日志如下:
192.168.50.136 - - [22/Oct/2025:15:32:32 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36" "192.168.50.218"
😊 可以顺利访问后端的 my-nginx,并且显示真实的客户端 IP,测试成功!
5. 负载均衡器
在此之前,我们都假设客户端与 K8s 集群位于同一局域网,客户端可以直接访问 K8s 集群。
但更一般的情况是,我们会再增加一层负载均衡器,DNS 记录中添加负载均衡器的外部 IP,客户端需经由负载均衡器来访问 K8s 集群。
如此一来,则可以较好地屏蔽 K8s 集群的节点变化。
5.1. 外部负载均衡器
关于外部负载均衡器,有硬件解决方案和软件解决方案,如果是云平台,还可以选择云厂商的负载均衡器。
软件解决方案中,又有 LVS 、Nginx、HAProxy 等,总之就是方案众多,需根据自身网络环境和业务需求而定。
因此,这里仅简单介绍下,并给出简化的网络架构示意图。

当使用外部负载均衡器时:
1、Ingress 的曝露端口:如使用 LVS 的 DR 模式时需曝露 80/443 端口,而 NAT 模式则随意。
2、Ingress 同样可以有两种方案:有 service 层;无 service 层。
3、源 IP:如使用 Nginx 的 Strem 模块作为负载均衡,应用获取到的源 IP 将是负载均衡器的 IP。
4、K8s 集群的网络参数:如使用 LVS 的 DR 模式,需设置 K8s 集群节点的虚 IP、ARP 抑制等,并注意不能与 K8s 集群的网络插件配置发生冲突。
5、限定 Ingress-nginx 部署节点:为了让负载均衡器能将请求转发到正确节点,当配置为使用 HostNetwork 或 spec:externalTrafficPolicy: Local 时,需配合节点亲和度,并使用 DaemonSet 方式将 Ingress-nginx 部署到指定节点。
5.2. 内部负载均衡器
当使用外部负载均衡器时,如 K8s 集群节点发生变更,可能还需调整外部负载均衡器的下游节点信息。
那么,有没有可能在 K8s 集群内部部署一个负载均衡器呢?这样,当 Ingress-nginx 的部署发生变化时就无需再进行额外的操作。
答案是有的,MetalLB 正是这样的一个项目。
但因为当前 MetalLB 仍处于 beta 阶段,所以这里不再过多赘述,如有兴趣可仔细阅读项目成熟度并自行作出评估。
5.3. 小结
关于负载均衡器的选择和使用,这是一个稍显复杂的话题。
技术决策时,通常需考虑部署复杂度、配置灵活度、性能、网络环境、是否能获取源 IP 等诸多因素。
另外,如果要实现高可用,可能还需搭配类似于 Keepalived 这样的组件。
再有,还可以在 DNS 服务器中为同一域名配置多个 IP,以实现 DNS 负载均衡和故障转移。
当然,如果只是实验性使用,【Nginx-Stream 模块】 + 【K8s-HostNetwork】是我认为最最简单的解决方案。
后续如果有时间的话,再来详细介绍 LVS 各种模式下与 K8s 集群的搭配。
6.总结
这篇文章首先介绍了 Ingress-nginx 的基本安装和应用示例。
然后,重点介绍了如何曝露 HTTP 标准端口,如何才能获取真实源 IP,以及不同配置下可接受请求的节点变化;
最后,概略介绍了负载均衡器的主要类型和注意事项。
另外,Ingress API 自 Kubernetes v1.19 开始已是冻结状态,而 Gateway API 是其后继者。
因此,如果是新搭建的 Kubernetes 集群,更推荐直接使用 Gateway API。
7. 参考文档
https://github.com/kubernetes/ingress-nginx
https://kubernetes.github.io/ingress-nginx/deploy/
https://kubernetes.io/docs/concepts/services-networking/ingress/