使用 nginx 反向代理的 Docker 容器内的闪亮服务器不会遵循 301 重定向

Posted

技术标签:

【中文标题】使用 nginx 反向代理的 Docker 容器内的闪亮服务器不会遵循 301 重定向【英文标题】:Shiny server inside Docker container reverse proxied with nginx will not follow 301 redirects 【发布时间】:2016-10-13 19:43:23 【问题描述】:

这个问题有3个要素:

    Docker 容器:我有一个部署在 EC2 实例上的 Docker 容器。更具体地说,我有rocker/shiny 图像,我使用它运行:

    sudo docker run -d -v /home/ubuntu/projects/shiny_example:/srv/shiny-server -p 3838:3838 rocker/shiny
    

    Shiny 服务器:标准的 Shiny 服务器配置文件保持不变,设置为在端口 3838 上为 /srv/shiny-server 文件夹中的所有内容提供服务,并且映射了我本地 ~/projects/shiny_example 的内容到容器的/srv/shiny-server/

    在我本地的~/projects/shiny_example,我克隆了一个随机的 Shiny 应用程序:

    git clone https://github.com/rstudio/shiny_example 
    

    nginx:我已将nginx设置为反向代理,here是/etc/nginx/nginx.conf的全部内容。


问题在于,使用此设置时,当我尝试检索 http://<ip-address>/shiny/shiny_example 时,我得到一个 404。关于可能出错的主要线索是,当我执行以下操作时:

 wget http://localhost:3838/shiny_example

从我的 EC2 实例的命令行中,我得到:

--2016-06-13 11:05:08-- http://localhost:3838/shiny_example 正在解析本地主机(localhost)... 127.0.0.1

正在连接到本地主机 (localhost)|127.0.0.1|:3838... 已连接。

HTTP 请求已发送,正在等待响应...301 已永久移动

位置:/shiny_example/ [关注]

--2016-06-13 11:05:08-- http://localhost:3838/shiny_example/

重用与 localhost:3838 的现有连接。

HTTP 请求已发送,等待响应... 200 OK

长度:3136 (3.1K) [文本/html]

保存到:'shiny_example.3'

100%[============================================= ==================================================== =================================>] 3,136 --.-K/s in 0.04s

2016-06-13 11:05:09 (79.6 KB/s) - “shiny_example.3”已保存 [3136/3136]

重点是我的

我认为我的 nginx 配置没有考虑到在请求 Docker 映射端口时存在 301 重定向的事实。我认为解决方案涉及proxy_next_upstream,但我希望能在我的上下文中尝试设置它时提供一些帮助。

我也认为这个问题可以忽略 Docker 上下文,但是如果从 Docker 容器中的 Shiny 服务器请求资源时如何防止 301 重定向,以及这种行为是否是预期的,那就太好了.

【问题讨论】:

您是在与http://<ip-address>/shiny/shiny_examplehttp://<ip-address>:3838/shiny/shiny_example/ 进行外部连接吗?如果您也显示了失败的 wget 命令,这将有所帮助。请注意,重定向只是在您的 url 中添加一个斜杠,这不是 Docker 执行 301。 @BMitch nginx 正在监听 80 和反向代理 localhost:3838。另请注意,wget 没有失败,但正在优雅地处理 301。 @BMitch 因此,为了明确起见,我正在与http://<ip-address>/shiny/shiny_example/ 进行外部连接。 wget 的输出包含在问题中。 正确,您只向我们展示了成功运行的东西,但暗示还有其他问题。如果您包括故障以及如何将其缩小到 nginx 未处理 301 与任何其他可能的问题,这将有所帮助。 【参考方案1】:

如果没有更多输出,我无法确定,但怀疑您的错误出现在您的 proxy_redirect 行中:

    location /shiny/ 
      rewrite ^/shiny/(.*)$ /$1 break;
      proxy_pass http://localhost:3838;
      proxy_redirect http://localhost:3838/ $scheme://$host/shiny_example;
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection $connection_upgrade;
      proxy_read_timeout 20d;
    

尝试将其更改为:

    location /shiny/ 
      rewrite ^/shiny/(.*)$ /$1 break;
      proxy_pass http://localhost:3838;
      proxy_redirect http://localhost:3838/ $scheme://$host/shiny/;
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection $connection_upgrade;
      proxy_read_timeout 20d;
    

原因是当 301 标头从“http://localhost:3838”返回以添加尾部斜杠时,它被重写为 nginx 配置中不存在的“http://localhost/shiny_example”,而且它也可能会删除路径中的斜线。这意味着从“http://localhost:3838/shiny_example”到“http://localhost:3838/shiny_example/”的 301 将被重写为“http://localhost/shiny_exampleshiny_example/”,此时您会得到 404。

【讨论】:

感谢您的回答 BMitch。我理解你的推理。但是,我觉得http_redirect 行之所以这样写,是因为服务器实现了 websockets,而scheme(即ws:)处理起来很重要。 Here 是对此的参考。 另外,您能否列出您希望我尝试的内容并包含其输出?从您的评论中我不清楚。 我正在寻找的输出是来自 localhost/shiny/shiny_example 的失败 wget 行。您说您看到了一个您认为与 301 相关的 404,但没有提供任何输出、日志或其他详细信息。我已经更新了答案以包含 $scheme 重定向,从提供的 URL 中,我没有看到 $host/shiny_example,只有 $host/shiny/【参考方案2】:

没有任何问题。基本上,/etc/nginx/nginx.conf 中的一行是include /etc/nginx/sites-enabled/*,它为启用的站点拉入默认文件,其中包含以下几行:

 server 
        listen 80 default_server;
        listen [::]:80 default_server ipv6only=on;

        root /usr/share/nginx/html;
        index index.html index.htm;

        # Make site accessible from http://localhost/
        server_name localhost;

        location / 
                # First attempt to serve request as file, then
                # as directory, then fall back to displaying a 404.
                try_files $uri $uri/ =404;
                # Uncomment to enable naxsi on this location
                # include /etc/nginx/naxsi.rules
        

这覆盖了我对端口 80 和位置 / 的监听指令。注释掉 /etc/nginx/nginx.conf 文件中启用站点的默认 conf 文件的 include 指令为我解决了所有问题。

【讨论】:

【参考方案3】:

不确定这是否仍然相关,但我在这里有一个最小的例子:https://github.com/mRcSchwering/butterbirne

服务shinyserver(基于rocker/shiny)通过服务webserver(基于nginx:latest)启动:

version: '2'

services:

  shinyserver:
    build: shinyserver/

  webserver:
    build: webserver/
    ports:
      - 80:80

我配置了 ngin,这样它就可以直接重定向到闪亮的服务器根目录。就我而言,我添加了应用程序(此处称为 myapp)作为 shinyserver 的根(因此不需要 /myapp)。这是整个nginx.conf

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;


    # apparently this is needed for shiny server
    map $http_upgrade $connection_upgrade 
        default upgrade;
        ''      close;
      

    # proxy shinyserver
    server 
        listen 80;

        location / 
          proxy_pass http://shinyserver:3838;
          proxy_redirect http://shinyserver:3838/ $scheme://$host/;
          proxy_http_version 1.1;
          proxy_set_header Upgrade $http_upgrade;
          proxy_set_header Connection $connection_upgrade;
          proxy_read_timeout 20d;
          proxy_buffering off;
        
    

【讨论】:

以上是关于使用 nginx 反向代理的 Docker 容器内的闪亮服务器不会遵循 301 重定向的主要内容,如果未能解决你的问题,请参考以下文章

docker 安装 nginx 并配置反向代理

对 docker 容器使用 nginx 反向代理

nginx 反向代理可以访问多个 docker 容器

为Docker创建自动化nginx反向代理

Nginx反向代理docker容器局域网

docker使用nginx实现ssl(https)反向代理其他容器应用