nginx

关注公众号 jb51net

关闭
首页 > 网站技巧 > 服务器 > nginx > Nginx upstream域名解析失效问题

Nginx中动态upstream域名解析失效问题解决的几种方法

作者:舞夢輝影

本文主要介绍了Nginx中实现动态解析upstream域名的方法,强调了使用resolver指令与变量触发、作用域正确、配置兼容三要素的重要性,并提供了四种解决方案,下面就来详细的介绍一下

要让 Nginx 对 upstream 中的域名做运行时动态解析(而非仅启动时静态解析),关键不是简单加个 resolver,而是必须满足“变量触发 + 作用域正确 + 配置兼容”三要素。直接写 proxy_pass http://api.example.com; 或在 upstream 块里写 server api.example.com;,无论有没有 resolver,都只会解析一次。、

问题根源

# ❌ 错误示例:仅在启动时解析一次
upstream backend {
    server api.example.com:8080;  # 启动时解析,之后永远不变
}
location / {
    proxy_pass http://backend;
}

当后端 IP 变化时,Nginx 仍指向旧 IP,导致请求失败。

解决方案一:使用 resolver 指令(推荐)

基础配置

http {
    # 1. 配置 DNS 解析器(必须放在 http/server/location 层级)
    resolver 8.8.8.8 8.8.4.4 valid=10s;  # Google DNS,TTL 10秒
    resolver_timeout 5s;
    server {
        listen 80;
        location /api/ {
            # 2. 使用变量强制动态解析
            set $backend "api.example.com";
            proxy_pass http://$backend:8080;
            # 3. 关键:配合 resolver 实现每次请求都解析(或按 TTL 缓存)
            proxy_http_version 1.1;
            proxy_set_header Host $host;
        }
    }
}

进阶:Consul/内部 DNS 服务发现

http {
    # 针对 Consul 或 Kubernetes CoreDNS 优化
    resolver 127.0.0.1:8600 valid=5s;  # Consul DNS 端口
    resolver_timeout 2s;
    # 可选:指定 DNS 搜索域
    resolver 10.96.0.10 valid=5s ipv6=off;  # K8s CoreDNS,禁用 IPv6 避免延迟
    server {
        location / {
            set $svc "user-service.service.consul";
            proxy_pass http://$svc:8080;
            # 连接池优化:避免频繁 DNS 查询影响性能
            proxy_connect_timeout 2s;
            proxy_send_timeout 5s;
            proxy_read_timeout 10s;
        }
    }
}

解决方案二:upstream + resolver 组合(高可用)

需要负载均衡时,结合 upstream 动态解析:

http {
    resolver 10.0.0.2 valid=10s;
    # 定义 upstream 但使用域名变量
    upstream dynamic_backend {
        server 0.0.0.0;  # 占位,实际在 location 中解析
        # 健康检查(需要 nginx_upstream_check_module 或商业版)
        check interval=3000 rise=2 fall=3 timeout=1000 type=http;
        check_http_send "HEAD /health HTTP/1.0\r\n\r\n";
        check_http_expect_alive http_2xx http_3xx;
    }
    server {
        location / {
            # 关键:使用变量触发 resolver
            set $backend_nodes "app.service.consul";
            # 通过 Lua 或 split_clients 实现动态选择(见方案三)
            proxy_pass http://$backend_nodes;
        }
    }
}

解决方案三:Lua 动态解析(OpenResty)

最灵活的方式,完全控制解析逻辑:

http {
    resolver 127.0.0.1:8600;
    server {
        location / {
            access_by_lua_block {
                local resolver = require "resty.dns.resolver"
                -- 1. 创建 DNS 客户端
                local dns, err = resolver:new{
                    nameservers = {"127.0.0.1", {"127.0.0.1", 8600}},
                    retrans = 2,
                    timeout = 1000,
                }
                -- 2. 查询 SRV 记录(Consul 推荐)
                local answers, err = dns:query("user-service.service.consul", 
                                               {qtype = dns.TYPE_SRV})
                -- 3. 动态选择节点(加权随机)
                if answers then
                    local target = answers[math.random(1, #answers)]
                    ngx.var.target_host = target.target
                    ngx.var.target_port = target.port
                else
                    -- 降级到静态配置
                    ngx.var.target_host = "127.0.0.1"
                    ngx.var.target_port = "8080"
                end
            }
            set $target_host "";
            set $target_port "";
            proxy_pass http://$target_host:$target_port;
        }
    }
}

解决方案四:Nginx Plus(商业版)

如果预算允许,Nginx Plus 提供原生动态解析:

upstream backend {
    zone upstream_backend 64k;
    # 关键:resolve 参数启用动态 DNS
    server api.example.com:8080 resolve;
    # 健康检查
    health_check interval=5s fails=3 passes=2;
}
server {
    location / {
        proxy_pass http://backend;
        health_check;
    }
}

关键配置参数详解

参数说明推荐值
valid=10sDNS 缓存 TTL微服务环境 5-10s,稳定服务 60s
ipv6=off禁用 IPv6 解析纯 IPv4 环境建议关闭,避免延迟
resolver_timeout单次 DNS 查询超时2-5s
resolver 位置必须在 http{}、server{} 或 location{}通常放 http{} 全局

常见问题排查

1. 解析不生效(仍使用旧 IP)

# ❌ 错误:直接写死域名,不会触发 resolver
proxy_pass http://api.example.com:8080;
# ✅ 正确:使用变量强制动态解析
set $backend "api.example.com";
proxy_pass http://$backend:8080;

2. 日志中出现 no resolver defined

# 错误原因:location 中使用了变量,但上级未定义 resolver
# 解决:确保 resolver 在 http 或 server 层级定义
http {
    resolver 8.8.8.8;  # 必须在这里定义,不能只在 location
}

3. DNS 查询延迟高

# 优化:本地 DNS 缓存 + 连接池
resolver 127.0.0.1 valid=5s;  # 指向本地 dnsmasq/unbound
upstream backend {
    keepalive 100;  # 连接池,避免每次新建连接
    keepalive_timeout 60s;
    keepalive_requests 1000;
}

生产环境完整配置模板

http {
    # DNS 配置(多可用区冗余)
    resolver 10.0.0.2 10.0.0.3 valid=10s ipv6=off;
    resolver_timeout 3s;
    # 日志记录解析详情(调试用)
    log_format dns_log '$remote_addr - $upstream_addr [$time_local] '
                      '"$request" $status $body_bytes_sent '
                      'upstream: "$upstream_addr" '
                      'resolve: "$upstream_http_host"';
    upstream app_servers {
        zone app_zone 64k;
        keepalive 100;
        # 占位 server,实际在 location 中动态解析
        server 127.0.0.1:1 backup;  # 备用,防止启动失败
    }
    server {
        listen 80;
        access_log /var/log/nginx/access.log dns_log;
        location / {
            # 动态解析核心配置
            set $backend_host "app.service.consul";
            set $backend_port "8080";
            proxy_pass http://$backend_host:$backend_port;
            # 错误降级
            proxy_intercept_errors on;
            error_page 502 503 504 = @fallback;
            # 连接优化
            proxy_connect_timeout 3s;
            proxy_next_upstream error timeout http_502 http_503 http_504;
            proxy_next_upstream_tries 2;
        }
        location @fallback {
            internal;
            return 503 '{"error":"Service Unavailable","retry_after":10}';
        }
    }
}

核心要点:resolver + 变量 是实现动态 DNS 的黄金组合,务必确保使用 $variable 形式编写 proxy_pass,否则 Nginx 会优化为启动时静态解析。

到此这篇关于Nginx resolver指令解决动态upstream域名解析失效问题的文章就介绍到这了,更多相关Nginx upstream域名解析失效问题内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

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