介绍
- 官网地址:
- Ingress-Nginx Deploy
- releases page: ingress-nginx releases
-
artifactHub: ingress-nginx helm chart
-
azure文档:
- Create an unmanaged ingress controller
-
因为K8s中Pod是易变的, Pod IP在更新中会自动修改, 使用Service能使访问入口相对固定, 但是Service IP在集群外不能访问, 要对外提供访问, 只能把Service以NodePort, LoadBalancer这些方式Expose出去, 但是NodePort会与每一个Node主机绑定, 而LoadBalancer要云服务商提供相应的服务 (或自己安装)
- Ingress 启动一个独立的Pod来运行七层代理, 可以是 Nginx, Traefik 或者是 Envoy. Ingress Pod会直接代理后端提供服务的Pod, 为了能监听后端Pod的变化, 需要一个 Headless Service 通过Selector选择指定的Pod, 并收集到Pod对应的IP. 一旦后端Pod产生变化, Headless Service 会自动根据变化更改配置文件并重载。如果使用的是 Nginx 类型的 Ingress Pod, 则每次变化后通过reload修改过的配置文件实现规则更新.
- 集群内节点上运行web七层代理nginx所对应的Pod, 由nginx Pod代理到集群内部的Service, Service再把流量转发给集群内部对应的Pod, 这就叫做 Ingress Controller。
下载
# curl -LO https://github.com/kubernetes/ingress-nginx/releases/download/helm-chart-$VERSION/ingress-nginx-$VERSION.tgz #4.10.1
helm repo add --force-update ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update ingress-nginx
helm pull ingress-nginx/ingress-nginx --version "${INGRESS_NGINX_VERSION#helm-chart-}" #4.10.1
配置hostnetwork模式
Bare-metal considerations - Ingress-Nginx Controller
hostnetwork
controller.service.type:cloud上用的是LoadBalancer,本地虚拟机集群上External IP会创建不出来;所以disable掉暂时不需要service。controller.hostNetwork=true,controller.hostPort.enabled=true,ingress-nginx pod用宿主机网络栈,并且开启节点宿主机80、443端口,否则nginx会报404
DNS policy
DNS for Services and Pods | Kubernetes
controller.dnsPolicy: ClusterFirstWithHostNet,让ingress-nginx pod可以用kube-dns解析集群内的svc- 虚拟机和虚拟机外面的hosts文件还是配置任意工作节点IP
注意
-
hosts文件里面写哪个节点的IP取决于ingress-controller的pod调度到了哪个节点上面,保险起见可以这样做:
-
controller.sutoscaling里面设置至少3个replica, -
或者replicaCount设置为3。
-
或者干脆直接把controller type设为daemonset
这样每个host都有pod可以接收80端口的请求。
-
当 Pod 设置 hostNetwork: true 时:
-
Pod 会使用宿主机的网络命名空间
-
默认情况下,Pod 会使用宿主机的 DNS 配置(/etc/resolv.conf)
这意味着 Pod 无法解析 Kubernetes 集群内部的服务名称,如:
kubernetes.default.svc.cluster.local
oauth2-proxy.oauth2-proxy.svc.cluster.local
-
ClusterFirstWithHostNet 的解决方案:设置 dnsPolicy: ClusterFirstWithHostNet 后:
- 优先使用 CoreDNS(集群内部 DNS)来解析域名
- 能够解析 Kubernetes 集群内部服务
- 如果集群 DNS 解析失败,则回退到宿主机的 DNS
controller:
name: controller
kind: DaemonSet
image:
image: ingress-nginx/controller
tag: "v1.12.1"
digest: ""
enableAnnotationValidations: false
config:
proxy-buffer-size: "16k"
enable-modsecurity: "false"
enable-owasp-modsecurity-crs: "false"
modsecurity-snippet: |
# Enable prevention mode. Can be any of: DetectionOnly,On,Off (default is DetectionOnly)
SecRuleEngine DetectionOnly
# Enable scanning of the request body
SecRequestBodyAccess On
# Max request sizes in bytes (with/without files)
# Note NGINX Ingress has its own annotations, keep in sync!
SecRequestBodyLimit 20971520 # 20Mb (default is 12.5Mb)
SecRequestBodyNoFilesLimit 262144 # 250Kb (default is 128Kb)
SecRequestBodyLimitAction Reject # Reject if larger (we could also let it pass with ProcessPartial)
# Update config to include PUT/PATCH/DELETE in the allowed HTTP methods (instead of fully disabling 911100)
SecAction "id:900200,phase:1,nolog,pass,t:none,\
setvar:tx.allowed_methods=GET HEAD POST OPTIONS PUT PATCH DELETE"
# Send ModSecurity audit logs to the stdout (only for rejected requests)
SecAuditLog /dev/stdout
SecAuditLogFormat JSON
SecAuditEngine RelevantOnly # could be On/Off/RelevantOnly
# Enable XML and JSON parsing
SecRule REQUEST_HEADERS:Content-Type "(?:application(?:/soap\+|/)|text/)xml" \
"id:200000,phase:1,t:none,t:lowercase,pass,nolog,\
ctl:requestBodyProcessor=XML"
SecRule REQUEST_HEADERS:Content-Type "application/json" \
"id:200001,phase:1,t:none,t:lowercase,pass,nolog,\
ctl:requestBodyProcessor=JSON"
# Rule syntax https://malware.expert/tutorial/writing-modsecurity-rules/
# Important that the new ids created (1000x) are new
# Remove rule that checks for connecting to raw ip, very spammy and not useful
SecRuleRemoveById 920350
# Remove php injection attack against prometheus endpoint, not php and not an attack
SecRule REQUEST_URI "@beginsWith /api/ds/query" \
"id:10000,phase:1,pass,nolog,ctl:ruleRemoveById=933210"
# Remove php injection attack against prometheus endpoint, not php and not an attack
SecRule REQUEST_URI "@beginsWith /api/ds/query" \
"id:10001,phase:1,pass,nolog,ctl:ruleRemoveById=949110"
admissionWebhooks:
patch:
image:
image: ingress-nginx/kube-webhook-certgen
tag: v1.5.2
digest: ""
watchIngressWithoutClass: true
ingressClassByName: true
# Pods configured with hostNetwork: true do not use the internal DNS resolver (i.e. kube-dns or CoreDNS)
# unless their dnsPolicy spec field is set to ClusterFirstWithHostNet.
# Consider using this setting if NGINX is expected to resolve internal names for any reason.
# https://kubernetes.github.io/ingress-nginx/deploy/baremetal/#via-the-host-network
# https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
hostPort:
enabled: true
ports:
http: 80
https: 443
# Explicitly set the DNS configuration for the controller pods.
# If not, the ingress pod's /etc/resolv.conf will only include CoreDNS, no host DNS.
# dnsConfig:
# nameservers:
# - "10.96.0.10" # CoreDNS
# - "192.168.40.2" # host DNS
# searches:
# - "ingress-nginx.svc.cluster.local"
# - "svc.cluster.local"
# - "cluster.local"
# options:
# - name: ndots
# value: "5"
# - name: timeout
# value: "2"
ingressClassResource:
name: nginx-default
# -- Create the IngressClass or not
enabled: true
# -- If true, Ingresses without `ingressClassName` get assigned to this IngressClass on creation.
# Ingress creation gets rejected if there are multiple default IngressClasses.
# Ref: https://kubernetes.io/docs/concepts/services-networking/ingress/#default-ingress-class
default: true
ingressClass: nginx-default
tolerations:
- key: "node-role.kubernetes.io/control-plane"
operator: "Equal"
effect: "NoSchedule"
# replicaCount: 3
# autoscaling:
# enabled: false
# minReplicas: 3
# maxReplicas: 3
# targetCPUUtilizationPercentage: 80
# targetMemoryUtilizationPercentage: 80
resources:
limits:
cpu: 300m
memory: 300Mi
requests:
cpu: 100m
memory: 90Mi
service:
enabled: false
type: NodePort
metrics:
enabled: true
service:
enabled: true
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "10254"
serviceMonitor:
enabled: true
安装
helm upgrade -i ingress-nginx -n ingress-nginx . -f values.yaml --create-namespace
配置HTTPS访问
自签证书
-
以grafana为例。
-
首先部署出certmanager --> 创建clusterissuer --> 创建给grafana ingress https的secret --> helm values.yaml的grafana ingress tls部分配置secret、host
-
创建secret
tee certificate-grafana.yaml <<'EOF'
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: cert-grafana
namespace: monitoring
spec:
secretName: grafana-tls-cert-secret
privateKey:
rotationPolicy: Always
commonName: grafana.hanxux.local
dnsNames:
- grafana.hanxux.local
usages:
- digital signature
- key encipherment
- server auth
issuerRef:
name: selfsigned
kind: ClusterIssuer
EOF
- kube-prometheus-stack的values配置
## TLS configuration for grafana Ingress
## Secret must be manually created in the namespace
##
tls:
- secretName: cert-grafana
hosts:
- grafana.hanxux.local
- https访问hostname即可,由于lab用的是自签证书,所以浏览器会报连接不安全。
Letsencrypt证书
- 参考azure文档:AKS Ingress TLS
hostnetwork的流量代理过程
基于当前的 ingress-nginx 配置,从 Windows 电脑访问 grafana.hanxux.local 到达 K8s 后端 Pod 的完整请求流程:
- DNS 解析阶段
Windows 电脑 → DNS 查询 grafana.hanxux.local
- Windows 电脑上的浏览器发起对
grafana.hanxux.local的 DNS 查询 - 需要在 Windows 的
hosts文件或 DNS 服务器中配置该域名指向 VMware 虚拟机的 IP 地址 -
例如:
192.168.x.x grafana.hanxux.local(虚拟机的 IP) -
网络路由阶段
Windows 电脑 → VMware 虚拟网络 → K8s 节点
- 请求通过 VMware 的虚拟网络接口发送到虚拟机
-
由于您配置了
hostNetwork: true,ingress-nginx Pod 直接使用宿主机的网络栈 -
Ingress Controller 接收阶段
请求处理流程:
Windows:浏览器 → VMware虚拟网络 → K8s节点:80端口 → ingress-nginx Pod
-
Ingress 规则匹配阶段
-
ingress-nginx Controller 接收到请求后,检查 HTTP Host 头部(
grafana.hanxux.local) - 根据 Ingress 资源中定义的规则进行匹配
-
找到对应的后端服务配置
-
DNS 解析后端服务阶段
由于配置了:
dnsPolicy: ClusterFirstWithHostNet
- ingress-nginx Pod 使用 K8s 集群的 DNS(CoreDNS)来解析后端服务名
-
例如解析
grafana-service.monitoring.svc.cluster.local到对应的 Cluster IP -
负载均衡转发阶段
ingress-nginx → Grafana Service → Grafana Pod
- ingress-nginx 根据 Ingress 规则将请求转发给对应的 Service
- Service 通过 iptables/ipvs 规则进行负载均衡
- 最终请求到达 Grafana Pod
完整的数据流路径
[Windows 电脑:浏览器]
↓ HTTP请求 grafana.hanxux.local
[VMware 虚拟网络层]
↓ 网络包转发
[K8s节点:80端口] (hostNetwork模式)
↓ 端口绑定
[ingress-nginx Pod] (DaemonSet)
↓ Host头匹配 + 路由规则
[Grafana Service] (ClusterIP)
↓ 负载均衡
[Grafana Pod] (目标容器)
关键配置的作用
hostNetwork: true- 让 ingress-nginx 直接使用宿主机网络,避免额外的网络层转发hostPort.enabled: true- 直接绑定宿主机的 80/443 端口kind: DaemonSet- 确保每个节点都有 ingress controllerservice.enabled: false- 不需要 Service,因为使用了 hostNetwork 模式dnsPolicy: ClusterFirstWithHostNet- 确保能正确解析集群内服务名
这种配置特别适合单节点或裸机部署,能够提供最直接的网络路径和最佳性能。
Nodeport模式的流量代理过程
上面是采用hostNetwork模式,请求直接到达宿主机80/443端口,被ingress-controller pod接收。还可以给ingress controller开一个NodePort service,请求先到NodePort Service再给ingress-controller。
需要将 values.yaml 修改为:
controller:
# ...existing code...
# 禁用 hostNetwork 模式
hostNetwork: false
# 移除 hostPort 配置
# hostPort:
# enabled: true
# ports:
# http: 80
# https: 443
# 启用 Service 并配置为 NodePort
service:
enabled: true
type: NodePort
ports:
http: 80
https: 443
nodePorts:
http: 30080 # 指定 NodePort 端口
https: 30443 # 指定 NodePort 端口
# DNS 策略可以改回默认值
dnsPolicy: ClusterFirst
# ...existing code...
原有 hostNetwork 模式流程:
Windows电脑 → VMware虚拟网络 → K8s节点:80 → ingress-nginx Pod
NodePort 模式流程:
Windows电脑 → VMware虚拟网络 → K8s节点:30080 → NodePort Service → ingress-nginx Pod:80
详细实现步骤:
- DNS 配置调整
在 Windows 的 hosts 文件中,需要指定端口:
# 方式1:在 hosts 文件中仍然指向虚拟机IP(需要端口转发)
192.168.x.x grafana.hanxux.local
# 方式2:直接在浏览器中访问带端口的URL
http://grafana.hanxux.local:30080
- 端口转发解决方案
由于域名访问默认使用 80/443 端口,有几种方案:
方案A:使用端口转发(推荐)
在 VMware 虚拟机或宿主机上配置端口转发:
# 在 Linux 虚拟机上使用 iptables
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 30080
sudo iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 30443
方案B:在 Windows 上使用 netsh
# 在 Windows 上配置端口转发
netsh interface portproxy add v4tov4 listenport=80 listenaddress=0.0.0.0 connectport=30080 connectaddress=192.168.x.x
netsh interface portproxy add v4tov4 listenport=443 listenaddress=0.0.0.0 connectport=30443 connectaddress=192.168.x.x
- 网络流量路径
[Windows 电脑:浏览器]
↓ HTTP请求 grafana.hanxux.local:80
[端口转发规则]
↓ 80 → 30080 转发
[VMware 虚拟网络层]
↓ 网络包转发到 30080
[K8s节点:30080] (NodePort)
↓ iptables/kube-proxy 规则
[ingress-nginx Service] (ClusterIP)
↓ 负载均衡
[ingress-nginx Pod:80] (容器端口)
↓ Host头匹配 + 路由规则
[Grafana Service] (ClusterIP)
↓ 负载均衡
[Grafana Pod] (目标容器)
NodePort 模式的优缺点
优点:
- 标准 K8s 模式 - 更符合 Kubernetes 最佳实践
- 端口隔离 - 避免与宿主机其他服务的端口冲突
- 更好的可移植性 - 可以轻松迁移到云环境
- 支持多副本 - 不受 hostNetwork 的单节点限制
缺点:
- 额外的网络跳转 - 性能略有损失
- 端口映射复杂性 - 需要配置端口转发
- 端口范围限制 - NodePort 默认范围 30000-32767
特点:
- 保持现有的 hostNetwork 模式 - 对于单节点开发环境,性能和简洁性更佳
- 如果需要 NodePort 模式 - 使用方案A的 iptables 端口转发,配置简单且稳定
集成oauth2proxy
- 给ingress添加annotations:
annotations:
nginx.ingress.kubernetes.io/auth-url: "http://oauth2-proxy.oauth2-proxy.svc.cluster.local/oauth2/auth"
nginx.ingress.kubernetes.io/auth-signin: "https://oauth2proxy.hanxux.local/oauth2/start?rd=https%3A%2F%2Fgrafana.hanxux.local"
注意
这里的auth-url写的是oauth2proxy的svc地址。而不是oauth的ingress url(oauth2proxy.hanxux.local),是因为这个auth-url是在ingress pod内部去请求的,来找oauth获取是否认证的信息。如果写oauth2proxy.hanxux.local,pod内部用的是coreDNS,解析不了。写svc地址就可以解析了。
- ingress可以配置的annotations:Nginx Ingress Annotations,可以实现其他流量控制等功能。
实战--流量复制/流量镜像
流量镜像(Traffic Mirror)功能应用于以下两个场景:
-
在系统进行重大重构或者发布新功能时,可以通过将线上的应用流量镜像到指定的线下环境来对新系统进行仿真测试。
-
线上系统遇到性能瓶颈,要快速地定位出问题时,可以采用流量镜像的方式来将应用的真实流量引导到线下环境来进行问题定位。

准备步骤
- 在prod集群部署应用,在staging集群部署相同应用
- 获取应用域名信息
- prod:www.product-nginx.com
- staging:www.staging-nginx.com
流量镜像配置
说明
-
将K8s Product Cluster中应用100%的访问流量镜像到K8s Stage Cluster中应用服务上,即将所有访问域名"www.product-nginx.com"的请求复制一份转发到"www.stage-nginx.com"
-
K8s Stage Cluster Ingress仅作为复制流量的接收方,不做任何配置修改
-
在将K8s Product Cluster中应用的访问流量镜像到K8s Stage Cluster中对应的应用服务后,客户端只会收到K8s Product Cluster中的请求响应,K8s Stage Cluster中的请求响应会被丢弃。
步骤1-配置nging-ingress configMap
在nginx-ingress-controller configmap中增加以下内容,配置多个流量接收目标:
kubectl get configmap -n kube-system
kubectl edit configmap nginx-ingress-controller -n kube-system
#追加以下配置
data:
http-snippet: |
split_clients "$date_gmt" $mirror_servers1 {
100% www.stage-nginx1.com;
}
split_clients "$date_gmt" $mirror_servers2 {
100% www.stage-nginx2.com;
}
配置多个接收目标并且每个目标流量都是100%:
- 流量百分比取值范围:(0, 100],百分比总和必须不大于100%。
- 支持同时配置多个不同的复制流量接收目标应用。
Q:这里是在哪个环境的ingress操作的??
步骤2-配置prod的ingress
通过configuration-snippet和server-snippet修改源Ingress,增加应用的流量镜像配置。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-ingress
annotations:
nginx.ingress.kubernetes.io/configuration-snippet: |
# 配置多个接收目标
mirror /mirror1;
mirror /mirror2;
nginx.ingress.kubernetes.io/server-snippet: |
# 配置第1个接收复制的集群
location = /mirror1 {
internal;
# 不打印mirror请求日志
#access_log off;
# 设置proxy_upstream_name,格式必须为[Namespace]-[BackendServiceName]-[BackendServicePort]
set $proxy_upstream_name "default-nginx-service-80";
# 自定义字符串,会作为请求头X-Shadow-Service值传给mirror server
set $shadow_service_name "nginx-product-service";
proxy_set_header X-Shadow-Service $shadow_service_name;
proxy_set_header Host $mirror_servers1;
proxy_pass http://$mirror_servers1$request_uri;
}
# 配置第2个接收复制的集群
location = /mirror2 {
internal;
# 不打印mirror请求日志
#access_log off;
# 设置proxy_upstream_name,格式必须为[Namespace]-[BackendServiceName]-[BackendServicePort]
set $proxy_upstream_name "default-nginx-service-80";
# 自定义字符串,会作为请求头X-Shadow-Service值传给mirror server
set $shadow_service_name "nginx-product-service";
proxy_set_header X-Shadow-Service $shadow_service_name;
proxy_set_header Host $mirror_servers2;
proxy_pass http://$mirror_servers2$request_uri;
}
spec:
rules:
- host: www.product-nginx.com
http:
paths:
- path: /
backend:
service:
name: nginx-product
port:
number: 80
pathType: ImplementationSpecific
修改coredns hosts配置
在K8s Product Cluster集群coredns插件中添加需要发送流量镜像的域名解析
kubectl get configmap -n kube-system
kubectl edit configmap coredns -n kube-system
data:
Corefile: |-
.:5353 {
bind {$POD_IP}
# 添加服务域名解析
hosts {
192.168.4.16 www.stage-nginx1.com
192.168.4.53 www.stage-nginx2.com
fallthrough
}
...