Nginx启动期DNS死锁

Nginx启动期DNS死锁

Sean 1 2026-04-15

踩坑

今天遇到一个nginx配置的坑,上午突然发现用用nginx部署的静态服务挂掉了,然后看了下nginx状态,发现nginx直接挂掉了,查看日志报错[emerg] host not found,查了下报错是因为DNS域名解析失败导致的,下面我们就来分析一下。

分析

当我们在nginx配置文件中的server块中对api进行配置时,常常会进行下面的配置:

location /BASEAPI/ {
    proxy_pass https://example.com;
    include proxy_params;
}

看上去没什么问题,为什么会造成Nginx报错呢?

  • 当我们在配置中写死这种硬编码的域名时,Nginx在服务启动的一瞬间,就会立刻向操作系统的DNS查这个域名对应的IP是多少。

  • 但是如果服务器刚开机,网卡还没连网或者本地DNS服务还没启动,Nginx就查不到IP,它就会认为这是致命的配置错误,服务不可用,抛出[emerg] host not found报错。

  • 并且即使当时查到了IP成功启动,Nginx也会把这个IP持久化的写在内存中,如果以后api地址更新了,Nginx依然会请求老IP,除非进行手动重启Nginx

解决方案

所以为了长久的解决这个报错,并且优化,提高健壮性。我们可以修改成下面这样去配置:

location /BASEAPI/ {
    # 强制 Nginx 使用公共 DNS,而不是依赖系统启动时的状态
    resolver 8.8.8.8 114.114.114.114 valid=300s;
    
    # 把它变成变量,Nginx 启动时就不会去死磕域名,而是有人访问时才去解析
    set $backend "https://example.com";
    
    proxy_pass $backend;
    include proxy_params;
}

这样做就能完美解决。下面来解析一下原因:

  • Nginx在启动时,看到的不再是一个硬编码地址,而是一个$backend的变量。所以它只会做常规的语法检查,不会检查这个变量的地址是否存在,哪怕服务器断网,Nginx也能无报错的成功启动。

  • 这样配置,只有当真正发起请求时,Nginx才会去访问变量,获得域名,然后再用DNS去解析到IP

  • 由于是临时访问变量去查询,所以需要手动在location块中配置resolver 8.8.8.8valid=300s的作用是将DNS 解析出的 IP 地址缓存5分钟,假设某次业务调整换了api地址,Nginx最多只会有 5 分钟的短暂寻址错误,5分钟后会自动解析到新的IP,无需人工手动重启Nginx。这样提高了性能,同时降低了延迟。

总结

  1. 解决了开机Nginx崩溃问题:解耦了Nginx启动时与网络状态的绑定,不管服务器网络如何,Nginx都能正常运行。

  2. 兼顾性能和健壮性:配置DNS并加上valid=300s,可以防止像之前把域名持久化存储,又能防止每次请求都需要获取变量去做DNS解析。