Docker Swarm上的NGINX可在同一端口上为多个应用程序提供服务

Docker Swarm上的NGINX可在同一端口上为多个应用程序提供服务,docker,nginx,port,reverse-proxy,docker-swarm,Docker,Nginx,Port,Reverse Proxy,Docker Swarm,我知道也有人问过类似的问题,但我发现的所有主题、文章和博客都无法解决我的问题。让我在这里直截了当地说: 1。我所拥有的: Docker Swarm集群(1个本地节点),NGINX作为反向代理,为了这个示例:apache、spark、rstudio和jupyter笔记本容器 2。我想要的: 我想将NGINX设置为只向主机公开一个端口(80-NGINX),并通过同一端口(80)但不同的路径通过NGINX为这4个应用程序提供服务。在我的本地开发环境中,我希望apache可以在“127.0.0.1/ap

我知道也有人问过类似的问题,但我发现的所有主题、文章和博客都无法解决我的问题。让我在这里直截了当地说:

1。我所拥有的

Docker Swarm集群(1个本地节点),NGINX作为反向代理,为了这个示例:apache、spark、rstudio和jupyter笔记本容器

2。我想要的:

我想将NGINX设置为只向主机公开一个端口(80-NGINX),并通过同一端口(80)但不同的路径通过NGINX为这4个应用程序提供服务。在我的本地开发环境中,我希望apache可以在“127.0.0.1/apache”下访问,在“127.0.0.1/rstudio”下访问rstudio,在“127.0.0.1/spark”下访问spark UI,在“127.0.0.1/jupyter”下访问jupyter。所有这些应用程序在内部使用不同的端口,这不是问题(apache-80、spark-8080、rstudio-8787、jupyter-8888)。我希望它们在主机上的外部使用相同的端口

3。我没有的:

我没有也不会有域名。当我所拥有的只是服务器或我拥有的多个服务器的公共IP时,我的堆栈应该能够工作。没有域名。我看到了很多关于如何使用主机名做我想做的事情的例子,我不想这样。我只想通过IP和路径访问堆栈,例如123.123.123.123/jupyter

4。我想到了什么:

现在来谈谈我的实际问题——我有一个部分有效的解决方案。 具体来说,apache和rstudio工作正常,jupyter和spark则不然。我不是说jupyter重定向会引起问题。当我转到127.0.0.1/jupyter时,我被重定向到登录页面,但它没有重定向到127.0.0.1/jupyter/tree,而是将我重定向到127.0.0.1/tree,当然它并不存在。Spark UI无法正确渲染,因为所有css和js文件都在127.0.0.1/Spark/some.css下,但Spark UI试图从127.0.0.1/some.css中获取它们,而其他所有仪表盘基本上也是如此

在我的实际堆栈中,我有更多的服务,如色调、kafdrop等,但它们都不起作用。实际上,唯一有效的工具是apache、tomcat和rstudio。 令我惊讶的是,rstudio在身份验证、登录、注销等方面都没有问题。它完全可以。我真的不知道为什么它会起作用,当其他一切都失败了

我也试着用Traefik做同样的事情——同样的结果。使用traefik,我甚至无法设置rstudio,所有仪表板都遇到了相同的问题-无法正确加载静态内容,或者仪表板带有登录页面-错误的重定向

5。问题:

因此,我的问题是:

  • 我想完成的事情可能吗
  • 如果不是,为什么可以使用不同的主机名,但同一主机上的不同路径不起作用
  • 如果可能的话,我应该如何设置NGINX才能正常工作
下面是我最简单的工作示例: 首先初始化swarm并创建网络:

docker swarm init


docker network create -d overlay --attachable bigdata-net
docker-compose.yml

version: '3'

services:
    nginx:
        image: nginx:alpine
        volumes:
            - ./nginx.conf:/etc/nginx/nginx.conf:ro
        ports:
            - 80:80
            - 443:443
        networks:
            - bigdata-net
        deploy:
            mode: replicated
            replicas: 1
            restart_policy:
                condition: any

    apache:
        image: httpd:alpine
        networks:
            - bigdata-net
        deploy:
            mode: replicated
            replicas: 1
            restart_policy:
                condition: any

    rstudio:
        image: rocker/rstudio:3.5.2
        networks:
            - bigdata-net
        environment:
            - PASSWORD=admin
        deploy:
            mode: replicated
            replicas: 1
            restart_policy:
                condition: any

    jupyter:
        image: jupyter/all-spark-notebook:latest
        networks:
            - bigdata-net
        deploy:
            mode: replicated
            replicas: 1
            restart_policy:
                condition: any

    spark:
        image: bde2020/spark-master:2.2.1-hadoop2.7
        networks:
            - bigdata-net
        deploy:
            mode: replicated
            replicas: 1
            restart_policy:
                condition: on-failure
nginx.conf

worker_processes auto;

events {
    worker_connections 1024; 
}

http {

    log_format compression '$remote_addr - $remote_user [$time_local] '
        '"$request" $status $upstream_addr '
        '"$http_referer" "$http_user_agent" "$gzip_ratio"';

    server {
        listen 80;
        listen [::]:80;
        access_log /var/log/nginx/access.log compression;

        ######### APACHE
        location = /apache { # without this only URL with tailing slash would work
            return 301 /apache/;
        }

        location /apache/ {
            set $upstream_endpoint apache:80;
            resolver 127.0.0.11 valid=5s;
            rewrite ^/apache(/.*) $1 break;
            proxy_pass $scheme://$upstream_endpoint;
            proxy_redirect $scheme://$upstream_endpoint/ $scheme://$host/apache/;
        }

        ######### RSTUDIO
        location = /rstudio { # without this only URL with tailing slash would work
            return 301 /rstudio/;
        }

        location /rstudio/ {
            set $upstream_endpoint rstudio:8787;
            resolver 127.0.0.11 valid=5s;
            rewrite ^/rstudio(/.*) $1 break;
            proxy_pass $scheme://$upstream_endpoint;
            proxy_redirect $scheme://$upstream_endpoint/ $scheme://$host/rstudio/;
        }

        ######### JUPYTER
        location = /jupyter { # without this only URL with tailing slash would work
            return 301 /jupyter/;
        }

        location /jupyter/ {
            set $upstream_endpoint jupyter:8888;
            resolver 127.0.0.11 valid=5s;
            rewrite ^/jupyter(/.*) $1 break;
            proxy_pass $scheme://$upstream_endpoint;
            proxy_redirect $scheme://$upstream_endpoint/ $scheme://$host/jupyter/;
        }

        ######### SPARK
        location = /spark { # without this only URL with tailing slash would work
            return 301 /spark/;
        }

        location /spark/ {
            set $upstream_endpoint spark:8080;
            resolver 127.0.0.11 valid=5s;
            rewrite ^/spark(/.*) $1 break;
            proxy_pass $scheme://$upstream_endpoint;
            proxy_redirect $scheme://$upstream_endpoint/ $scheme://$host/spark/;
        }
    }
}
此外,我创建和修改此配置所依据的材料:


我希望有人能帮助我,因为我无法解决这个问题,所以我一直在睡觉;)

我无法帮助你学习Jupyter和Spark,但希望这个答案能帮助你

如果您计划在反向代理后放置某些内容,那么应该验证它是否可以在反向代理后工作,如您所述

127.0.0.1/jupyter/tree,它将我重定向到127.0.0.1/tree

因为Jupyter的根是
/
,而不是
/Jupyter
,所以您需要在配置中找到如何更改它,例如Grafana

# The full public facing url you use in browser, used for redirects and emails
# If you use reverse proxy and sub path specify full url (with sub path)
root_url = https://example.com/grafana
NGINX配置可以简化,看看这个例子:

nginx配置

# /etc/nginx/conf.d/default.conf

server {
    listen 8080 default_server;

    location / {
        proxy_pass     http://echo:8080/;

        proxy_set_header X-Real-IP           $remote_addr;
        proxy_set_header X-Forwarded-Host    $host;
        proxy_set_header X-Forwarded-Port    $server_port;
        proxy_set_header X-Forwarded-Proto   $scheme;
        proxy_set_header X-Forwarded-Request $request;
        proxy_set_header X-Forwarded-Agent   $http_user_agent;
    }

    location ~ /echo([0-9]+)/ {
        rewrite ^/echo([0-9]+)(.*)$ $2 break;
        proxy_pass     http://echo:8080;

        proxy_set_header X-Real-IP           $remote_addr;
        proxy_set_header X-Forwarded-Host    $host;
        proxy_set_header X-Forwarded-Port    $server_port;
        proxy_set_header X-Forwarded-Proto   $scheme;
        proxy_set_header X-Forwarded-Request $request;
        proxy_set_header X-Forwarded-Agent   $http_user_agent;
    }
}
docker compose

version: "3.2"

services:
    nginx:
        image: nginx:alpine
        ports:
            - '8080:8080'
        volumes:
            - ./default.conf:/etc/nginx/conf.d/default.conf

    echo:
        image: caa06d9c/echo
测试

$ curl -L localhost:8080/echo1/

{
    "method": "GET",
    "path": "/",
    "ip": "172.31.0.1",
    "headers": {
        "X-Forwarded-Host": "localhost",
        "X-Forwarded-Port": "8080",
        "X-Forwarded-Proto": "http",
        "X-Forwarded-Agent": "curl/7.54.0",
        "X-Forwarded-Request": "GET /echo1/ HTTP/1.1"
    }
}
备注

变数

proxy_set_header  Host              $http_host;
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;
只有在软件需要时才应将其放置到位,并且这些名称(如
X-Real-IP
)可能不同,您需要使用软件需求验证它

你不需要

重写^/rstudio(/.*)$1中断

因为nginx会自动遵循正确的规则,所以您需要为路径(如
/path
)重写规则,以切断
路径,因此它将是
/
(或其他)

分解器127.0.0.11有效=5s

因为您使用localhost

设置$upstream_端点jupyter:8888

因为你的通行证

代理重定向$scheme://$upstream_endpoint/$scheme://$host/jupyter/

因为你的通行证


其他一切看起来都不错。

谢谢,这很有帮助。只要3个问题,如果我理解正确:1。你说我不需要我的配置的一些行,它可以被简化,但是你提到了localhost。如果我把它部署在远程服务器上,它们还能简化吗?2.所以本质上是这样的:如果一个应用程序支持可配置的根目录,那么它将在反向代理上工作,否则就没有办法让它工作了?3.可能很愚蠢,但是有没有一种方法(例如标题)可以告诉应用程序“嘿,127.0.0.1不再是你的根主机,127.0.0.1/jupyter/是你的根主机,而不是你的根主机,并且使用它”而不改变每个应用程序?1)是的,你需要删除默认的服务器并添加服务器名称。2) 不,想象一下,你有一个严格的根,/,所有链接都指向这个根,所以如果用户试图输入www.example.com/app/my_ip,这个应用程序将指向www.example.com/my_ip,但是nginx对my_ip一无所知,所以你需要向nginx添加app的所有路径,这可能是一场噩梦。3) 正如我提到的变量,是的,像X-Forwarded-For这样的变量是用来告知应用程序真正的价值的,但应用程序应该支持它。想象一下,你收集传入的ip,如果应用程序在nginx后面工作,它将始终接收nginx本地ip,因此X-Real-ip将有帮助我想知道我是否可以请你尝试坚持普通的技术写作,Dmitri。Th