k8s 获取真实ip地址
说明:自建的的k8s环境是 一主二从 的架构。主节点 k8s-master 从节点 k8s-node1 k8s-node2 (主节点是污节点,默认是不会调度到污节点的)。控制面板使用 Kuboard
externalTrafficPolicy Local Cluster 区别
在 Kubernetes 中,Service 的 externalTrafficPolicy
字段用于控制外部流量(来自集群外的请求)如何路由到后端 Pod,主要有 Cluster
(默认值)和 Local
两种模式
1. 流量转发机制
Cluster
模式(默认)
集群中的所有节点(包括没有运行目标 Pod 的节点)都可以接收外部流量。
当请求到达任意节点时,节点会跨节点转发流量到集群内任意健康的目标 Pod(无论 Pod 是否在当前节点上)。Local
模式
节点只处理本地运行的目标 Pod 的流量。
当请求到达某个节点时,如果该节点上没有目标 Pod,请求会被直接丢弃(不会转发到其他节点)。
2. 源 IP 保留
Cluster
模式
由于可能存在跨节点转发,请求的源 IP 会被中间节点替换为节点自身的 IP,因此后端 Pod 无法获取真实客户端 IP(只能拿到转发节点的 IP)。Local
模式
流量不会跨节点转发,请求直接发送到本地 Pod,因此后端 Pod 可以保留并获取真实的客户端源 IP。
3. 负载均衡与可用性
Cluster
模式- 优点:负载均衡更均衡,可充分利用集群资源(所有节点都能接收流量并转发)。
- 缺点:无法获取真实客户端 IP;跨节点转发会增加网络开销。
Local
模式- 优点:保留真实客户端 IP;减少跨节点网络开销。
- 缺点:负载均衡可能不均衡(只有运行 Pod 的节点能处理流量);若节点上的 Pod 故障,该节点接收的请求会被丢弃(需依赖外部负载均衡器健康检查规避)。
情况一
- nginx 作为流量的入口
第一步:服务选择 nodePort 模式
第二步: 修改 externalTrafficPolicy 的值为 Local (默认都是Cluster)
第三步:节点调度策列 选择根据【节点名称】选择节点,选择 master 节点
第四步:容忍 key选择污点--选择exists--选择NoSchedule
第三点和第四点 对应的yaml配置内容(也可以通过修改yaml文件实现)
spec:
template:
spec:
nodeName: k8s-master
tolerations:
- effect: NoSchedule
key: node-role.kubernetes.io/master
operator: Exists
- nginx.conf 的主要配置
location /api/ {
#proxy_set_header Host $host;
#proxy_set_header X-Real-IP $remote_addr;
#proxy_set_header X-Forwarded-Proto $scheme;
#proxy_set_header X-Forwarded-Host $host;
#proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://网关地址:端口/;
}
- gateway服务
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.server.reactive.ServerHttpRequest;
import reactor.core.publisher.Mono;
@Configuration
public class RealIpConfig {
@Bean
public GlobalFilter realIpFilter() {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
// 1. 优先从 X-Real-IP 获取(通常由 ingress-nginx 直接写入真实 IP)
String realIp = request.getHeaders().getFirst("X-Real-IP");
// 2. 若 X-Real-IP 不存在,从 X-Forwarded-For 取第一个 IP(格式:客户端IP, 代理1IP, 代理2IP...)
if (realIp == null || realIp.isEmpty()) {
String xForwardedFor = request.getHeaders().getFirst("X-Forwarded-For");
if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
realIp = xForwardedFor.split(",")[0].trim();
}
}
// 3. 若以上都不存在, fallback 到直接连接的 IP(可能是代理 IP)
if (realIp == null || realIp.isEmpty()) {
realIp = request.getRemoteAddress().getHostString();
}
// 将真实 IP 存入上下文,供后续使用(如日志、业务逻辑)
exchange.getAttributes().put("REAL_CLIENT_IP", realIp);
return chain.filter(exchange);
};
}
}
情况二
ingress-nginx作为流量入口
ingress-nginx部署文件下载 wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.47.0/deploy/static/provider/baremetal/deploy.yaml
修改deploy.yaml内容
# Source: ingress-nginx/templates/controller-configmap.yaml apiVersion: v1 kind: ConfigMap metadata: labels: helm.sh/chart: ingress-nginx-3.33.0 app.kubernetes.io/name: ingress-nginx app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/version: 0.47.0 app.kubernetes.io/managed-by: Helm app.kubernetes.io/component: controller name: ingress-nginx-controller namespace: ingress-nginx data: # 添加以下配置 compute-full-forwarded-for: "true" # 启用完整的X-Forwarded-For头计算 forwarded-for-header: "X-Forwarded-For" use-forwarded-headers: "true" # 启用对转发头的处理(但仅信任直接连接的源 IP) use-proxy-protocol: "false" # 如果前端有代理(如负载均衡器)使用 proxy protocol,需开启 real-ip-header: "X-Forwarded-For" # 从 X-Forwarded-For 头获取真实 IP spec: template: spec: # 添加节点选择器,指定部署到 master 节点 nodeSelector: node-role.kubernetes.io/master: "" # 添加容忍度,允许调度到有污点的 master 节点 tolerations: - key: "node-role.kubernetes.io/master" operator: "Exists" effect: "NoSchedule" 删掉原来的这个配置 nodeSelector: kubernetes.io/os: linux
nginx.conf 的主要配置
#从哪个头中获取真实 IP(与 ingress-nginx 配置对应) real_ip_header X-Forwarded-For; #信任来自 ingress-nginx 的 IP(填写 ingress-nginx 的 service 网段或具体 IP) set_real_ip_from 10.96.0.0/16; #信任来自 ingress-nginx 的 IP(填写 ingress-nginx 的 Pod 网段或具体 IP) set_real_ip_from 178.38.0.0/16; #如果 X-Forwarded-For 中有多个 IP(如多级代理),取第一个作为真实 IP real_ip_recursive on; location /api/ { #proxy_set_header Host $host; #proxy_set_header X-Real-IP $remote_addr; #proxy_set_header X-Forwarded-Proto $scheme; #proxy_set_header X-Forwarded-Host $host; #proxy_set_header X-Forwarded-Port $server_port; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://网关地址:端口/; }
网关服务
import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.server.reactive.ServerHttpRequest; import reactor.core.publisher.Mono; @Configuration public class RealIpConfig { @Bean public GlobalFilter realIpFilter() { return (exchange, chain) -> { ServerHttpRequest request = exchange.getRequest(); // 1. 优先从 X-Real-IP 获取(通常由 ingress-nginx 直接写入真实 IP) String realIp = request.getHeaders().getFirst("X-Real-IP"); // 2. 若 X-Real-IP 不存在,从 X-Forwarded-For 取第一个 IP(格式:客户端IP, 代理1IP, 代理2IP...) if (realIp == null || realIp.isEmpty()) { String xForwardedFor = request.getHeaders().getFirst("X-Forwarded-For"); if (xForwardedFor != null && !xForwardedFor.isEmpty()) { realIp = xForwardedFor.split(",")[0].trim(); } } // 3. 若以上都不存在, fallback 到直接连接的 IP(可能是代理 IP) if (realIp == null || realIp.isEmpty()) { realIp = request.getRemoteAddress().getHostString(); } // 将真实 IP 存入上下文,供后续使用(如日志、业务逻辑) exchange.getAttributes().put("REAL_CLIENT_IP", realIp); return chain.filter(exchange); }; } }
k8s 查看污点
1. 查看所有节点的污点
kubectl describe nodes | grep Taint -A 10
2. 查看指定节点的污点
kubectl describe node <节点名称> | grep Taint
3. 更简洁的方式(推荐)
kubectl get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints