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

NGINX on Docker Swarm to serve multiple applicaions on the same port

我知道有人问过类似的问题,但是 none 我找到的主题、文章和博客让我解决了我的问题。让我在这里非常直接和具体:

1.我有什么

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

2。我想要的:

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

3。我没有的:

我没有也不会拥有域名。当我只有一个 public 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 并且所有其他仪表板基本上都是同样的故事

在我的实际堆栈中,我有更多服务,如 hue、kafdrop 等,其中 none 可用。实际上,唯一有用的是 apache、tomcat 和 rstudio。 我很惊讶 rstudio 在身份验证、登录、退出等方面没有问题。这完全没问题。当其他一切都失败时,我实际上不知道它为什么有效。

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

5.问题:

所以我的问题是:

我的最小工作示例如下: 首先初始化 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(/.*)  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(/.*)  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(/.*)  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(/.*)  break;
            proxy_pass $scheme://$upstream_endpoint;
            proxy_redirect $scheme://$upstream_endpoint/ $scheme://$host/spark/;
        }
    }
}

此外,我创建和修改此配置所基于的材料: https://medium.com/@joatmon08/using-containers-to-learn-nginx-reverse-proxy-6be8ac75a757 https://community.rstudio.com/t/running-shinyapps-from-rstudio-server-behind-nginx-proxy/17706/4

我希望有人能帮助我,我无法解决这个问题,所以睡不着;)

对于 Jupyter 和 Spark,我帮不上忙,但希望这个回答对您有所帮助。

如果您打算将某些东西放在反向代理后面,您应该验证它是否可以像您提到的那样在反向代理后面工作。

127.0.0.1/jupyter/tree, it redirects me to 127.0.0.1/tree

因为 Jupyter root 是 /,而不是 /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]+)(.*)$  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-撰写

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可以不一样,需要软要求验证。

你不需要

rewrite ^/rstudio(/.*) break;

因为 nginx 自动遵循正确的规则,你需要重写像 /path 这样的路径规则来切断 path,所以它将是 /(或其他)

resolver 127.0.0.11 valid=5s;

因为你使用本地主机

set $upstream_endpoint jupyter:8888;

因为 proxy_pass.

proxy_redirect $scheme://$upstream_endpoint/ $scheme://$host/jupyter/;

因为 proxy_pass.

其他一切看起来都不错。