首页 > 解决方案 > Docker 中的 NGINX 缓存 IP 地址并提供错误的 Content-Type

问题描述

摘要:
我想让 NGINX(不是 NGINX Plus)通过使用变量 in 重新解析 DNS 名称中的 IP 地址(如这篇官方 Nginx 文章中“在变量中设置域名”部分中proxy_pass所建议的那样)。但是当我这样做时,它不会设置/转发正确的标题,而是始终使用、 for等文件。Content-Typetext/html.css.js

我的设置:
我有一个前端和一个后端服务在单独的 Docker 容器中运行(在生产中部署到 OpenShift)。还有第三个容器在运行nginx:latest(截至今天的 v1.19.9)并充当反向代理,/my-app将对前端和/my-app/api后端容器的调用转发。NGINX 反向代理使用它们的 DNS 名称将它们设置为上游服务器。所有三个容器都在同一个自定义 Docker 网络中运行,因此解析本身可以正常工作 - 但仅限于(重新)启动 NGINX 时。

问题:
当前端或后端容器重新启动时,它可能会获得一个新的 IP 地址。由于 NGINX 会缓存 IP 地址,因此我在502调用它们时会得到一个。我希望 NGINX 更频繁地重新解析 IP 地址,就像 NGINX Plus 一样。Content-Type当我试图让 NGINX 重新解析 DNS 名称时,这就是问题的出现方式。

配置:
这是我的 NGINX 配置(仅简化为相关内容):

  index    index.html index.htm;

  upstream upstream_frontend {
    server frontend:8080;
  }

  upstream upstream_backend {
    server backend:8000;
  }

  server {
    listen       8080;
    root         /usr/share/nginx/html;

    try_files    $uri$args $uri$args/ $uri $uri/ /index.html =404;
    
    rewrite ^/my-app$ $scheme://$http_host/my-app/ permanent;

    location /my-app {
      resolver 127.0.0.11 ipv6=off valid=1s;

      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto  $scheme;
      proxy_set_header X-NginX-Proxy true;
      proxy_ssl_session_reuse off;
      proxy_set_header Host $http_host;
      proxy_redirect off;

      proxy_http_version 1.1;
      proxy_pass_request_headers on;
      proxy_pass_header Content-Type;

#     Attempt #1, NOT working: using a variable and an upstream server setup
      set $frontend_var "http://upstream_frontend/";
      proxy_pass http://$frontend_var;

#     Attempt #2, NOT working: using a variable and directly using the container name
#      set $frontend_var "http://frontend:8080/";
#      proxy_pass $frontend_var;

#     Attempt #3, working fine:  using NO variable and an upstream server setup, , but no DNS re-resolving happening :-(
#      proxy_pass http://upstream_frontend/;

#     Attempt #4, working fine:  using NO variable and directly using the container name, but no DNS re-resolving happening :-(
#      proxy_pass http://frontend:8080/;
    }

    location /my-app/api {
      resolver 127.0.0.11 ipv6=off valid=1s;

      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto  $scheme;
      proxy_set_header X-NginX-Proxy true;
      proxy_ssl_session_reuse off;
      proxy_set_header Host $http_host;
      proxy_redirect off;

#     Attempt #1, NOT working: using a variable and an upstream server setup
      set $backend_var "http://upstream_backend/api";
      proxy_pass http://$backend_var;

#     Attempt #2, NOT working:  using a variable and directly using the container name
#      set $backend_var "http://backend:8000/api";
#      proxy_pass http://$backend_var;

#     Attempt #3, working fine:  using NO variable and an upstream server setup, , but no DNS re-resolving happening :-(
#      proxy_pass http://upstream_backend/api;

#     Attempt #4, working fine:  using NO variable and directly using the container name, but no DNS re-resolving happening :-(
#      proxy_pass http://backend:8000/api;
    }
  }

(请注意location /my-app {...}/末尾的proxy_pass,因为前端是从 提供的/my-app,而前端容器本身(也从 NGINX 提供)直接在 上运行...:8080/,没有/my-app上下文路径。我们对后端容器有相同的设置那是在监听:8000,但是当呼叫被转发到它时,我们会/my-app从它们中删除它,以便它们直接到达容器...:8000/api。)

NGINX 在端口上运行,当我使用上面的尝试 #1尝试 #29000打开 http://localhost:9000/my-app时,、、 图像文件等都与 一起提供,这会阻止浏览器正确呈现页面,并在调试器窗格中显示如下消息:.css.jsContent-Type: text/html

The stylesheet http://localhost:9000/my-app/static/css/main.df1d2133.chunk.css  was not loaded because its MIME type, ”text/html“ is not ”text/css"

起初我以为proxy_pass_header Content-Type;会解决这个问题,但这也无济于事。

然后我还从这个 NGINX 的 bug 票中了解到“在 proxy_pass 中使用变量时,如果指定了 URI,则按原样传递给服务器,替换原始请求 URI。” . 这似乎是Content-Type我在这里遇到的问题的一个可能原因。

此外,正如您在配置中看到的那样,我设置了 Docker DNS IP: resolver 127.0.0.11 ipv6=off valid=1s;。我也尝试将它设置在location块外,这也没有帮助。

问题:
那么我如何在 NGINX(开源)中重新解析 DNS 并设置正确的Content-Type仍然设置?不必使用上游服务器,所以我可以摆脱它们,如果这是可能的修复的一部分。

PS:
我不能,正如其他 StackOverflow 评论中所建议的那样,添加单独location的块来“追溯”修复Content-Type依赖的文件夹、文件名等,因为这是一个不断发展的项目,我担心它需要我继续添加这样的修补程序定期到 NGINX 配置。

编辑:
我发布的配置包含在 NGINX 的 Docker 容器的配置文件中,如果您想知道一些“缺失”的设置:

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;

    keepalive_timeout  65;

    # This line includes my configuration above
    include /etc/nginx/conf.d/*.conf;
}

标签: dockernginxdnswebserveropenshift

解决方案


这不是问题Content-Type。您使用proxy_pass不正确。正如您已经指出的:

在 proxy_pass 中使用变量时,如果指定了 URI,则按原样传递给服务器,替换原来的请求 URI。

您当前具有以下工作配置,但您想将其替换为变量以强制 DNS 解析:

location /my-app {
    proxy_pass http://frontend:8080/;
}

最简单的解决方案是使用rewrite...break对 URI 进行更改,并/从变量中删除尾随。

例如:

location /my-app {
    rewrite ^/my-app/?(.*)$ /$1 break;
    set $frontend_var "frontend:8080";
    proxy_pass http://$frontend_var;
}

或者,使用正则表达式 location捕获原始请求的其余部分。

例如:

location ~ ^/my-app/?(.*)$ {
    proxy_pass http://frontend:8080/$1;
}

推荐阅读