【问题标题】:NGINX on Docker Swarm to serve multiple applicaions on the same portDocker Swarm 上的 NGINX 在同一个端口上为多个应用程序提供服务
【发布时间】:2019-08-07 22:35:54
【问题描述】:

我知道有人问过类似的问题,但我发现的所有主题、文章和博客都无法解决我的问题。让我在这里非常直接和具体:

1.我有什么

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

2。我想要什么:

我想将 NGINX 设置为只能向主机公开一个端口 (80 - NGINX) 并通过 NGINX 在同一端口 (80) 但不同的路径上为这 4 个应用程序提供服务。在我的本地开发环境中,我希望 apache 可以在“127.0.0.1/apache”下访问,rstudio 在“127.0.0.1/rstudio”下,spark UI 在“127.0.0.1/spark”下,jupyter 在“127.0.0.1/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 获取它们,并且基本上与所有其他文件相同仪表板

在我的实际堆栈中,我有更多的服务,如 hue、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/;
        }
    }
}

此外,我创建和修改此配置所基于的材料: 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

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

【问题讨论】:

    标签: docker nginx port reverse-proxy docker-swarm


    【解决方案1】:

    对于 Jupyter 和 Spark,我无能为力,但希望这个答案对您有所帮助。

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

    127.0.0.1/jupyter/tree,它将我重定向到 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]+)(.*)$ $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;
    

    只有在soft需要的时候才应该定位,并且这些名字,比如X-Real-IP可以不同,你需要用soft requirements来验证它。

    你不需要

    重写 ^/rstudio(/.*) $1 break;

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

    解析器 127.0.0.11 有效=5s;

    因为你使用本地主机

    设置 $upstream_endpoint jupyter:8888;

    因为 proxy_pass。

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

    因为 proxy_pass。

    其他一切看起来都不错。

    【讨论】:

    • 谢谢,这很有帮助。如果我理解正确,只需 3 个问题: 1. 你说我不需要我的一些配置行,它可以简化,但是你提到了 localhost。如果我将它部署在远程服务器上,它们仍然可以简化吗? 2.所以本质上是:如果一个应用程序支持可配置的根目录,那么它将在反向代理上工作,否则没有办法让它工作? 3. 可能很愚蠢,但是有没有办法(例如标题)告诉应用程序“嘿,127.0.0.1 不再是您的根主机,127.0.0.1/jupyter/ 是您的根主机并使用它”而不需要每个应用程序变化?
    • 1) 是的,您需要删除 default_server 并添加 server_name。 2)不,想象一下,你有严格的root,/,并且所有链接都指向这个root,所以如果用户尝试输入www.example.com/app/my_ip,这个应用程序将指向www.example.com/my_ip,但是nginx对my_ip一无所知,所以你需要将应用程序的所有路径添加到nginx,这可能是一场噩梦。 3)正如我提到的变量,是的,创建像 X-Forwarded-For 这样的变量是为了告知应用程序真正的价值,但应用程序应该支持它。想象一下,你收集传入的 ip,如果应用程序在 nginx 后面运行,它总是会收到 nginx 本地 ip,所以 X-Real-IP 会有所帮助
    • 我想知道是否可以请您尝试坚持普通的技术写作,Dmitrii。 “you”这个词并不难打,“u”在这里不合适。留给社交媒体吧! :-).
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-12-19
    • 2021-10-17
    • 2014-11-06
    • 2019-10-29
    • 2018-06-16
    • 2019-01-13
    • 1970-01-01
    相关资源
    最近更新 更多