使用 nginx 反向代理的 Docker 容器内的闪亮服务器将不会遵循 301 重定向
Shiny server inside Docker container reverse proxied with nginx will not follow 301 redirects
这个问题有 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是[=20=的内容] 完整。
问题是,使用此设置,当我尝试检索 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
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:3838... connected.
HTTP request sent, awaiting response... 301 Moved Permanently
Location: /shiny_example/ [following]
--2016-06-13 11:05:08-- http://localhost:3838/shiny_example/
Reusing existing connection to localhost:3838.
HTTP request sent, awaiting response... 200 OK
Length: 3136 (3.1K) [text/html]
Saving to: ‘shiny_example.3’
100%[==============================================================================================================================>] 3,136 --.-K/s in 0.04s
2016-06-13 11:05:09 (79.6 KB/s) - ‘shiny_example.3’ saved [3136/3136]
重点是我的。
我认为我的 nginx 配置没有考虑到在请求 Docker 映射端口时存在 301 重定向这一事实。我认为该解决方案涉及 proxy_next_upstream
,但如果您能帮助我尝试在我的上下文中进行设置,我将不胜感激。
我也认为这个问题可以从 Docker 上下文中剔除,但是当从 [=64] 中的闪亮服务器请求资源时,了解如何防止 301 重定向会很好=] 容器,以及这种行为是否是预期的。
如果没有更多输出我无法确定,但怀疑你的错误在你的 proxy_redirect 行:
location /shiny/ {
rewrite ^/shiny/(.*)$ / 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/(.*)$ / 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 header 从“http://localhost:3838" to add the trailing slash, it gets rewritten to "http://localhost/shiny_example" which doesn't exist in your nginx config, plus it may also remove a slash from the path. This means the 301 from "http://localhost:3838/shiny_example" to "http://localhost:3838/shiny_example/" would get rewritten to to "http://localhost/shiny_exampleshiny_example/”返回时,您会收到 404。
一切都没有问题。基本上,/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 指令为我解决了所有问题。
不确定这是否仍然相关,但我在这里有一个最小的例子: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;
}
}
}
这个问题有 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是[=20=的内容] 完整。
问题是,使用此设置,当我尝试检索 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 Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:3838... connected.
HTTP request sent, awaiting response... 301 Moved Permanently
Location: /shiny_example/ [following]
--2016-06-13 11:05:08-- http://localhost:3838/shiny_example/
Reusing existing connection to localhost:3838.
HTTP request sent, awaiting response... 200 OK
Length: 3136 (3.1K) [text/html]
Saving to: ‘shiny_example.3’
100%[==============================================================================================================================>] 3,136 --.-K/s in 0.04s
2016-06-13 11:05:09 (79.6 KB/s) - ‘shiny_example.3’ saved [3136/3136]
重点是我的。
我认为我的 nginx 配置没有考虑到在请求 Docker 映射端口时存在 301 重定向这一事实。我认为该解决方案涉及 proxy_next_upstream
,但如果您能帮助我尝试在我的上下文中进行设置,我将不胜感激。
我也认为这个问题可以从 Docker 上下文中剔除,但是当从 [=64] 中的闪亮服务器请求资源时,了解如何防止 301 重定向会很好=] 容器,以及这种行为是否是预期的。
如果没有更多输出我无法确定,但怀疑你的错误在你的 proxy_redirect 行:
location /shiny/ {
rewrite ^/shiny/(.*)$ / 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/(.*)$ / 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 header 从“http://localhost:3838" to add the trailing slash, it gets rewritten to "http://localhost/shiny_example" which doesn't exist in your nginx config, plus it may also remove a slash from the path. This means the 301 from "http://localhost:3838/shiny_example" to "http://localhost:3838/shiny_example/" would get rewritten to to "http://localhost/shiny_exampleshiny_example/”返回时,您会收到 404。
一切都没有问题。基本上,/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 指令为我解决了所有问题。
不确定这是否仍然相关,但我在这里有一个最小的例子: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;
}
}
}