nginx

关注公众号 jb51net

关闭
首页 > 网站技巧 > 服务器 > nginx > Nginx 客户端 IP动态后端路由

Nginx map实现基于客户端 IP 段的动态后端路由的几种方法实现

作者:舞夢輝影

这篇文章主要介绍了Nginx map实现基于客户端 IP 段的动态后端路由的几种方法实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

Nginx 的 map 模块结合 Geo 模块可以实现基于客户端 IP 段的动态后端路由,常用于灰度发布、A/B 测试、多租户隔离等场景。以下是完整的进阶配置方案:

基础原理

客户端 IP → Geo/Map 匹配 → 变量赋值 → proxy_pass 动态路由

方案一:基于 IP 段的简单分流

http {
    # 定义 IP 段到后端组的映射
    geo $backend_pool {
        default         backend_default;
        192.168.1.0/24  backend_vip;      # 内网 VIP 段
        10.0.0.0/8      backend_internal; # 企业内网
        172.16.0.0/16   backend_partner;  # 合作伙伴
    }
    upstream backend_default {
        server 10.0.1.10:8080;
        server 10.0.1.11:8080;
    }
    upstream backend_vip {
        server 10.0.2.10:8080 weight=5;   # 高配机器
        server 10.0.2.11:8080 weight=5;
    }
    upstream backend_internal {
        server 10.0.3.10:8080;
    }
    upstream backend_partner {
        server 10.0.4.10:8080;
    }
    server {
        listen 80;
        location / {
            # 直接使用 geo 变量路由
            proxy_pass http://$backend_pool;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}

方案二:多级 Map 实现复杂策略

http {
    # 第一级:识别 IP 段类型
    geo $ip_segment {
        default              unknown;
        192.168.0.0/16       corp;
        10.0.0.0/8          dc;
        172.16.0.0/12       vpn;
        include /etc/nginx/geo_allow.conf;  # 外部文件维护白名单
    }
    # 第二级:基于 IP 段 + 请求特征决定版本
    map "$ip_segment:$http_x_feature_flag" $app_version {
        # 格式: "段类型:特征标志"  → 后端版本
        default                    "v1-stable";
        "~^corp:experiment$"      "v2-beta";      # 内网用户 + 实验头
        "~^corp:preview$"          "v3-preview";   # 内网用户 + 预览头
        "dc:"                      "v1-stable";    # 数据中心强制稳定版
        "vpn:"                     "v1-stable";    # VPN 用户安全版本
        "unknown:"                 "v1-stable";    # 未知来源保守策略
    }
    # 第三级:版本到 upstream 的映射
    map $app_version $upstream_group {
        v1-stable   backend_prod;
        v2-beta     backend_canary;
        v3-preview  backend_preview;
    }
    upstream backend_prod {
        server 10.0.1.10:8080 max_fails=3;
        server 10.0.1.11:8080 backup;
    }
    upstream backend_canary {
        server 10.0.2.10:8080;  # 金丝雀环境
    }
    upstream backend_preview {
        server 10.0.3.10:8080;  # 预览环境
    }
    server {
        listen 80;
        location / {
            proxy_pass http://$upstream_group;
            # 透传版本信息给后端
            proxy_set_header X-App-Version $app_version;
            proxy_set_header X-IP-Segment $ip_segment;
        }
        # 调试端点:查看路由决策
        location /_route_debug {
            default_type application/json;
            return 200 '{
                "remote_addr": "$remote_addr",
                "ip_segment": "$ip_segment",
                "feature_flag": "$http_x_feature_flag",
                "app_version": "$app_version",
                "upstream": "$upstream_group"
            }';
        }
    }
}

方案三:CIDR 精确匹配 + 权重灰度

http {
    # 使用 geo 的复杂匹配(支持 CIDR 和范围)
    geo $gray_level {
        default         0;   # 0 = 稳定版
        # 办公网段:10% 流量进入灰度
        192.168.10.0/24 10;
        # 测试网段:100% 灰度
        192.168.50.0/24 100;
        # 特定 IP:强制灰度
        203.0.113.5     100;
        # 排除名单(从文件中加载)
        include /etc/nginx/geo_deny.conf;
    }
    # 基于灰度级别计算实际后端
    map $gray_level $target_backend {
        0       "stable";
        ~^1-50$ "weighted";   # 1-50 需要二次计算
        ~^51-99$ "canary";
        100     "canary";
    }
    # 对加权段进行随机分流(split_clients 实现百分比)
    split_clients "${remote_addr}${http_user_agent}" $canary_decision {
        10%     canary;
        *       stable;
    }
    # 最终 upstream 选择
    map "$target_backend:$canary_decision" $final_upstream {
        "stable:"       backend_stable;
        "stable:stable" backend_stable;
        "weighted:canary"   backend_canary;
        "weighted:stable"   backend_stable;
        "canary:"       backend_canary;
        "canary:canary" backend_canary;
    }
    upstream backend_stable {
        server 10.0.1.10:8080;
        server 10.0.1.11:8080;
    }
    upstream backend_canary {
        server 10.0.2.10:8080;
    }
    server {
        location / {
            proxy_pass http://$final_upstream;
            # 设置灰度 Cookie 保持会话一致性
            add_header Set-Cookie "gray_route=$final_upstream; Path=/; Max-Age=3600" always;
        }
    }
}

方案四:外部数据源动态更新(Lua 增强)

http {
    # 基础 geo 配置
    geo $base_segment {
        default unknown;
        include /etc/nginx/geo_base.conf;
    }
    server {
        location / {
            access_by_lua_block {
                local ipmatcher = require "resty.ipmatcher"
                -- 1. 从 Redis/Consul 加载动态 IP 段配置
                local redis = require "resty.redis"
                local red = redis:new()
                red:connect("127.0.0.1", 6379)
                -- 2. 获取实时灰度配置
                local gray_ips = red:smembers("gray:ip_segments")
                local vip_ips = red:smembers("vip:ip_segments")
                -- 3. 构建 IP 匹配器
                local gray_matcher = ipmatcher.new(gray_ips)
                local vip_matcher = ipmatcher.new(vip_ips)
                -- 4. 精确匹配决策
                local client_ip = ngx.var.remote_addr
                if vip_matcher:match(client_ip) then
                    ngx.var.target_group = "backend_vip"
                    ngx.var.priority = "100"
                elseif gray_matcher:match(client_ip) then
                    ngx.var.target_group = "backend_gray"
                    ngx.var.priority = "50"
                else
                    ngx.var.target_group = "backend_default"
                    ngx.var.priority = "0"
                end
            }
            set $target_group "";
            set $priority "";
            proxy_pass http://$target_group;
            proxy_set_header X-Router-Priority $priority;
        }
    }
}

关键配置技巧

1. 外部文件管理 IP 段

# /etc/nginx/geo_segments.conf
geo $segment {
    default default;
    # 通过 include 分离配置,支持热更新(nginx -s reload)
    include /etc/nginx/ip_segments/*.conf;
}
# /etc/nginx/ip_segments/corp.conf
192.168.0.0/16    corp;
10.0.0.0/8        corp;
# /etc/nginx/ip_segments/whitelist.conf
203.0.113.0/24    trusted;
198.51.100.0/24   trusted;

2. 多级回退策略

map "$geo_segment:$cookie_user_tier:$arg_version" $route_target {
    # 优先级:URL 参数 > Cookie > IP 段 > 默认
    default                                 "prod";
    "~*:(vip|enterprise):.*"                "vip";      # Cookie 标记 VIP
    "~*:.*:preview"                          "preview";  # URL 参数强制预览版
    "partner:.*:.*"                          "partner";
    "internal:.*:.*"                         "internal";
}

3. 与 DNS 动态解析结合

http {
    resolver 10.0.0.2 valid=10s;
    geo $region_code {
        default ap;
        1.0.0.0/8   us;   # Cloudflare 美国段示例
        103.21.244.0/22 ap; # Cloudflare 亚太段
    }
    map $region_code $backend_host {
        us  "us-west.internal";
        ap  "ap-southeast.internal";
        eu  "eu-central.internal";
    }
    server {
        location / {
            # 动态解析 + 地域路由
            set $target "http://$backend_host:8080";
            proxy_pass $target;
        }
    }
}

性能优化要点

优化项配置说明
Geo 数据库geoip_module 或 geo超过 1000 条 CIDR 使用 geo 模块的 radix tree
缓存匹配结果map 变量持久化同连接复用变量值(HTTP/2 多路复用注意)
异步更新lua-resty-core避免 reload 频繁,使用共享内存热更新
监控埋点log_format记录 $geo_segment 用于分析路由准确性
# 路由决策日志
log_format routing '$remote_addr - $geo_segment -> $route_target '
                  '[$time_local] "$request" '
                  'upstream=$upstream_addr '
                  'decision_time=$request_time';
access_log /var/log/nginx/routing.log routing;

核心要点:geo 负责 IP→分类的匹配,map 负责分类→行为的映射,两者组合实现声明式路由策略,比 if 指令性能高且可维护性强。

到此这篇关于Nginx map实现基于客户端 IP 段的动态后端路由的几种方法实现的文章就介绍到这了,更多相关Nginx 客户端 IP动态后端路由内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文