首页 > 解决方案 > 使用 Dockerized NGINX 反向代理到 Dockerized REST API

问题描述

请看底部的后续

我是 NGINX 的新手,正在尝试为多个 REST API 和 SPA 应用程序入口点设置一个简单的内部开发 Ubuntu 服务器,因此我可以学习一些 NGINX 基础知识。

我想要服务的所有 API 和 SPA 都是 dockerized,并且每个都在 localhost(Docker 的主机)端口上公开其服务(用于 API)或页面(用于 SPA)。

例如,我有一个 APIlocalhost:60380和一个 Angular SPA 应用程序localhost:4200,每个都在自己的 Docker 容器中运行。

我可以确认这些工作正常,因为我可以通过它们基于 localhost 的 URL 访问它们。每个 API 还在其 URL 上提供了一个Swagger入口点,例如localhost:60380/swagger(或者,更详细地说,localhost:60380/swagger/index.html)。

我现在想让 NGINX 监听localhost:80,并根据请求的 URL 对每个相应的服务进行反向代理请求。为了保持干净,NGINX 也是 dockerized,即从使用 NGINX 开源版本的容器运行。

为了对 NGINX 进行 dockerize,我按照https://docs.nginx.com/nginx/admin-guide/installing-nginx/installing-nginx-docker/的说明进行操作,即我从nginx映像运行容器,使用卷指向主机的NGINX 配置和静态内容的文件夹。/我刚刚更改了 Docker 命令,因为我在使用文档中建议的基于挂载的语法时遇到了问题(即使我指定了选项,这似乎不是一个允许的字符bind;请注意以下命令是从 执行的/var):

docker run --name mynginx -v $(pwd)/www:/usr/share/nginx/html:ro -v $(pwd)/nginx/conf:/etc/nginx/conf:ro -p 80:80 -d nginx

IE:

作为测试,我在映射为卷源的主机文件夹中创建了几个静态网站,即:

这两个文件夹都只有一个静态网页 ( index.html)。

我在主机的/var/nginx/conf文件夹中放置了一个nginx.conf文件来为这两个静态网站提供服务。这是我想出的配置:

error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
  worker_connections 1024;
}

http {
  # include imports configuration from a separate file.
  # In this case it imports a types block, mapping each MIME type
  # to a file extension, e.g.:
  # types {
  #   text/html              html htm shtml;
  #   text/css               css;
  #   application/javascript js;
  # ... etc
  # }
  include /etc/nginx/mime.types;

  # the default type used if no mapping is found in types:
  # here the browser will just download the file.
  default_type application/octet-stream;

  # log's format: the 1st parameter is the format's name (main);
  # the second is a series of variables with different values
  # for every request.
  log_format main
    '$remote_addr - $remote_user [$time_local] "$request" '
    '$status $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for"';

  # path to the log file and log format's name (main, defined above).
  access_log /var/log/nginx/access.log main;

  # set to on: do not block on disk I/O.
  sendfile on;

  # keep connection alive timeout. As a page usually has a lot of assets,
  # this keeps the connection alive the time required to send them;
  # otherwise, a new connection would be created for each asset.
  keepalive_timeout 65;

  # enable output compression. Recommendation is on.
  gzip on;

  # include all the .conf files under this folder:
  include /etc/nginx/conf.d/*.conf;
}

server {
  listen 80;
  server_name localhost;
  location /site1 {
    root /usr/share/nginx/html/site1;
    index index.html index.htm;
  }
  location /site2 {
    root /usr/share/nginx/html/site2;
    index index.html index.htm;
  }
  # redirect server error pages to the static page /50x.html
  error_page 500 502 503 504 /50x.html;
  location = /50x.html {
    root /usr/share/nginx/html;
  }
}

这很好用,我可以从localhost/site1和浏览这两个站点localhost/site2

然后我开始了我的一个 dockerized API 暴露在localhost:60380. 我在 NGINX 配置中,在同一个服务器块中添加了以下内容,以便在(及其在 处大摇大摆)location到达它:localhost/sample/apilocalhost/sample/api/swagger

  location /sample/api {
    proxy_pass_header Server;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection keep-alive;
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Scheme $scheme;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_pass http://localhost:60380;
  }

由于这是一个 ASP.NET Core Web API,我使用https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view建议的配置作为起点=aspnetcore-3.1。除了一些标头传递方向之外,这与在如何使用 nginx 在 Docker 容器上为 Web 应用程序提供服务中找到的并没有本质上的不同。

然后我将 NGINX 配置保存在主机文件夹中,并通知 NGINX 用docker kill -s HUP <mycontainername>.

无论如何,虽然我仍然能够访问 API localhost:60380,并且两个静态 Web 仍然可以工作,但在访问localhost/sample/api或时我得到了 404 localhost/sample/api/swagger

我尝试按照此处proxy_redirect http://localhost:60380/ /sample/api/;的建议添加,但没有任何变化。

你能建议我做错了什么吗?

更新 1

我尝试将尾随添加/到 URI,但我仍然得到 404。如果这适用于 Kaustubh(请参阅下面的答案),这让我感到困惑,因为我仍在 404;或者我们做了一些不同的事情。让我也回顾一下,以帮助像我这样的其他没有经验的读者:

  1. 准备主机:
cd /var

mkdir nginx
cd nginx
mkdir conf
cd ..

mkdir www
cd www
mkdir site1
cd ..
mkdir site2
cd ..

然后index.html在每个文件夹中添加一个页面/var/www/site1,然后在/var/www/site2下面添加一个页面:nginx.confvar/nginx/conf

error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
  worker_connections 1024;
}

http {
  # include imports configuration from a separate file.
  # In this case it imports a types block, mapping each MIME type
  # to a file extension, e.g.:
  # types {
  #   text/html              html htm shtml;
  #   text/css               css;
  #   application/javascript js;
  # ... etc
  # }
  include /etc/nginx/mime.types;

  # the default type used if no mapping is found in types:
  # here the browser will just download the file.
  default_type application/octet-stream;

  # log's format: the 1st parameter is the format's name (main);
  # the second is a series of variables with different values
  # for every request.
  log_format main
    '$remote_addr - $remote_user [$time_local] "$request" '
    '$status $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for"';

  # path to the log file and log format's name (main, defined above).
  access_log /var/log/nginx/access.log main;

  # set to on: do not block on disk I/O.
  sendfile on;

  # keep connection alive timeout. As a page usually has a lot of assets,
  # this keeps the connection alive the time required to send them;
  # otherwise, a new connection would be created for each asset.
  keepalive_timeout 65;

  # enable output compression. Recommendation is on.
  gzip on;

  # include all the .conf files under this folder:
  include /etc/nginx/conf.d/*.conf;
}

server {
  listen 80;
  server_name localhost;

  location /site1 {
    root /usr/share/nginx/html/site1;
    index index.html index.htm;
  }
  location /site2 {
    root /usr/share/nginx/html/site2;
    index index.html index.htm;
  }
  # https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-3.1
  # https://stackoverflow.com/questions/57965728/how-to-use-nginx-to-serve-a-web-app-on-a-docker-container
  # https://serverfault.com/questions/801725/nginx-config-for-restful-api-behind-proxy
  location /sample/api {
    # proxy_redirect http://localhost:60380/ /sample/api/;
    proxy_pass_header Server;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection keep-alive;
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Scheme $scheme;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_pass http://localhost:60380/;
  }
  # redirect server error pages to the static page /50x.html
  error_page 500 502 503 504 /50x.html;
  location = /50x.html {
    root /usr/share/nginx/html;
  }
}
  1. docker run --name mynginx -v $(pwd)/www:/usr/share/nginx/html:ro -v $(pwd)/nginx/conf:/etc/nginx/conf:ro -p 80:80 -d --net=host nginx(注意添加--net=host

  2. 导航到localhost/site1and localhost/site2:这行得通。

  3. 启动您的 API localhost:60380(这是我的示例中的 API 端口)。我可以看到它在工作localhost:60380,它的招摇页面在localhost:60380/swagger.

  4. 导航到localhost/sample/api:404。对于localhost/sample/api/swagger/index.html具有此前缀的任何其他 URI 或其他 URI 相同。

标签: dockernginx-reverse-proxy

解决方案


我试图在我的最后尽可能地复制这一点。--net=host只有在我在docker runnginx 的命令中使用后,我才能让它工作。下面是我使用的命令。我不得不使用这个选项,因为 nginx docker 容器无法连接到我的 api docker 容器

$ docker run --name nginx -v $(pwd):/usr/share/nginx/html:ro -v $(pwd)/default.conf:/etc/nginx/conf.d/default.conf:ro -p 80:80 --net=host -id nginx

/etc/nginx/conf.d/default.conf是 nginx 中的默认虚拟主机配置,显示Welcome to nginx page.

我将其更改为以下配置:

server {
    listen       80;
    server_name  localhost;

    # For static files
    location / {
        root /usr/share/nginx/html;
        index index.html index.htm;
    }

    # For reverse_proxy
    location /sample/api {
        proxy_pass http://localhost:8080/;
    }
}

根据这个答案,端口号后面的斜杠应该解决这个问题。

我已经测试了相同的结果并且它有效。


推荐阅读