nginx 配置文件:服务器,server_name 和上游理解

nginx configuration file : server, server_name and upstream understanding

我有这个 nginx.conf 配置文件继承自 github 项目,我希望有人能向我解释正在做什么:

upstream hello_django {
    server web:8000;
}

server {
    listen 80;
    server_name react-wagtail-api.accordbox.com;
    location / {
        proxy_pass http://hello_django;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        proxy_redirect off;
        client_max_body_size 20M;
    }
    location /static/ {
        alias /app/static/;
    }
    location /media/ {
        alias /app/media/;
    }
}

server {
    listen 80;
    server_name react-wagtail.accordbox.com;
    location / {
      root   /usr/share/nginx/html/build;
      index  index.html index.htm;
      try_files $uri $uri/ /index.html;
    }
}

upstream hello_django {
        server web:8000;
    }

web 服务(在其他地方有一个 docker-compose 容器名称是 web... 它是对那个的引用吗?)? upstream具体定义了什么?

server_name react-wagtail-api.accordbox.com;

如果我没有定义 server_name 会怎样?例如,如果我还没有域? server_name 是浏览器地址栏中输入的域吗?我可以将其定义为本地 ip 并让我的域名提供商进行重定向吗?我可以将其定义为服务器的互联网 IP 并让我的域名提供商进行重定向吗?

由于同一个端口上有两个服务器,我可以定义例如 server_name my_internet_ip/app1server_name my_internet_ip/app2 为端口 80 上的两个服务器提供服务吗?

web 服务在您的情况下解析为某个 IP,与 server_name 相同。您可以阅读有关上游 here, same for server_name 指令的更多信息。

Is web a service (elsewhere there is a docker-compose container name which is web... is it a ref to that?)

一般web这里是上游域名(也可以通过IP地址或UNIX套接字路径指定)。但是,当它在 docker-compose 上下文中执行时,在 nginx 启动时它将使用 docker 内部域名解析系统解析为 web 容器内部 IP。

What does upstream define exactly?

对于这个特定的配置,使用任何一种都没有区别

upstream hello_django {
    server web:8000;
}
server {
    ...
    proxy_pass http://hello_django;

或者直接在proxy_pass指令中指定上游地址:

server {
    ...
    proxy_pass http://web:8000;

真正有用的 upstream 用例包括故障转移(example) or load balancing (example). Read the ngx_http_upstream_module 文档以找出所有可用的功能。

What happens if i don't define a server_name for example in case i don't have yet a domain?

要理解这部分,请务必阅读官方文档中的以下两章:

您完全可以省略服务器块中的 server_name 指令。对于到达 nginx 正在侦听的 TCP 端口的任何 HTTP 请求,定义的 server 块之一(通常是第一个出现在配置中的块,除非使用 default_server 参数明确指定 listen 指令)将作为默认服务器,如果找不到更合适的服务器块。 Host HTTP 请求 header 值用于在此处选择最合适的服务器块,与服务器块指定的 server_name 进行比较,它将与您在浏览器地址栏(假设输入的 IP address/domain 名称实际上指向 nginx 服务器)。这意味着为侦听相同 TCP 端口的不同服务器块指定相同的服务器名称是没有意义的 - 第一个将始终被选择来处理这样的请求,nginx 将抱怨

nginx: [warn] conflicting server name "..." on 0.0.0.0:80, ignored

警告信息。出于开发目的,您可以将所需的域名添加到 hosts 系统文件,将它们指向您的本地计算机,例如

127.0.0.1  react-wagtail.accordbox.com
127.0.0.1  react-wagtail-api.accordbox.com

这样您就可以从本地浏览器使用这些域,生成的 HTTP 请求将包含正确的 Host header 并将使用您的本地 nginx 服务器实例进行处理。

As there are two servers on the same port, can i define for example server_name my_internet_ip/app1 and server_name my_internet_ip/app2 to serve two servers on port 80?

没有。看来您不了解 HTTP 协议的内部结构。在低级别 HTTP 请求将类似于

GET /app1 HTTP/1.1
Host: my_internet_ip
...

如你所见,主机名和请求URL路径是两个完全不同的东西。通常这种任务使用多个位置块来解决:

server {
    server_name example.com;
    location /app1/ {
        ...
    }
    location /app2/ {
        ...
    }
}

但是,它需要底层网络应用程序的支持。两个可用选项是

  • 使用相对 URI 引用资产,例如

    <link rel="stylesheet" type="text/css" href="style.css">
    

    <link rel="stylesheet" type="text/css" href="./style.css">
    

    但不像

    那样使用绝对URI
    <link rel="stylesheet" type="text/css" href="/style.css">
    
  • 使用与 location 指令中指定的相同的 URI 前缀,例如

    <link rel="stylesheet" type="text/css" href="/app1/style.css">
    

原因很明显 - 每个对任何第一个或第二个应用程序资产的请求都应以 /app1//app2/ 前缀开头,以便使用正确的位置块进行处理。

如您所见,我在这里使用 /app1//app2/ 后缀,而不是 /app1/app2。这是很多人常犯的错误,不了解这两者之间的区别。虽然看起来微不足道,但实际上这两者在 nginx 提供的 browsing context which will be / in first case and /app1/ or /app2/ in second case. Unless you are proxying some kind of API endpoint but a whole web application, you probably want the second one. To make it more easy, a location 指令特殊行为方面截然不同:

If a location is defined by a prefix string that ends with the slash character, and requests are processed by one of proxy_pass, fastcgi_pass, uwsgi_pass, scgi_pass, memcached_pass, or grpc_pass, then the special processing is performed. In response to a request with URI equal to this string, but without the trailing slash, a permanent redirect with the code 301 will be returned to the requested URI with the slash appended. If this is not desired, an exact match of the URI and location could be defined like this:

location /user/ {
    proxy_pass http://user.example.com;
}

location = /user {
    proxy_pass http://login.example.com;
}

尽管我们可以使用 <base href="..."> HTML 标记覆盖浏览上下文,但我强烈建议不要使用此解决方法,而是为代理的 Web 应用程序使用正确的 URI 前缀。