了解Nginx配置文件结构和配置上下文

引言

Nginx是一款高性能的网页服务器,负责处理互联网上最大的一些网站的负载。它尤其擅长处理多个并发连接,并且在转发或提供静态内容方面表现出色。在本指南中,我们将着重讨论Nginx配置文件的结构以及如何设计您的文件的一些指导原则。

理解Nginx配置上下文

本指南将涵盖主要的Nginx配置文件结构。该文件的位置取决于Nginx的安装方式。在许多Linux发行版中,该文件位于/etc/nginx/nginx.conf。如果该位置不存在,也可能位于/usr/local/nginx/conf/nginx.conf或/usr/local/etc/nginx/nginx.conf。

当您查看主配置文件时,首先要注意的一点是它以类似于树状结构的方式组织,用一对括号({ 和 })标记。在Nginx文档中,这些括号定义的区域被称为“上下文”,因为它们包含根据关注领域划分的配置细节。这种划分提供了组织结构,并带有一些条件逻辑,用于决定是否应用其中的配置。

由于上下文的层次可以嵌套在一起,Nginx允许继承配置。一般规则是,如果一个指令在多个嵌套范围内都是有效的,则在更广泛的上下文中声明的值将作为默认值传递给任何子上下文。子上下文可以覆盖这些值。需要注意的是,对于任何数组类型的指令的覆盖将替换先前的值,而不是添加到它。

指令只能在它们被设计用于的上下文中使用。当Nginx读取包含在错误上下文中声明的指令的配置文件时,会抛出错误。Nginx文档包含了每个指令可用于的上下文信息,这使其成为一个有用的参考。

在下面,我们将讨论在使用Nginx时最常见的情境。

核心背景

我们将讨论的第一组上下文是Nginx用于创建层次树并分离不同配置块的核心上下文。这些上下文组成了Nginx配置的主要结构。

主要背景

最常见的情境是“主要”或“全球”情境。这是唯一一个不包含在典型情境块内的情境。

以下是/etc/nginx/nginx.conf的解释
# The main context is here, outside any other contexts

. . .

context {

    . . .

}

只要完全位于这些块之外的任何指令都属于“主”上下文。请记住,如果您的Nginx配置以模块化方式设置,即在多个文件中具有配置选项,那么一些文件包含的指令看起来可能存在于括号上下文之外,但在配置加载时将包含在上下文中。

主上下文代表了Nginx配置的最广泛环境。它用于配置影响整个应用程序的细节。尽管本节中的指令会影响到较低的上下文,但其中许多指令无法在较低层级中被覆盖。

在主上下文中配置的一些常见细节包括运行工作进程的系统用户和组、工作进程的数量以及保存主Nginx进程ID的文件。可以在此级别设置整个应用程序的默认错误文件(可以在更具体的上下文中覆盖此设置)。

事件的背景

“事件”上下文被包含在“主”上下文中。它用于设置全局选项,影响Nginx在一般水平上处理连接的方式。在Nginx配置中只能定义一个事件上下文。

在配置文件中,此上下文将以以下方式显示,不属于任何其他括号上下文之内。

/etc/nginx/nginx.conf 可以被简单地翻译为“/etc/nginx/nginx配置文件”。
# main context

events {

    # events context
    . . .

}

Nginx采用基于事件的连接处理模型,所以在此上下文中定义的指令决定了工作进程应如何处理连接。主要的指令可以用来选择连接处理技术或修改这些方法的实现方式。

通常情况下,平台会自动选择最高效的连接处理方法。对于Linux系统而言,epoll 方法通常是最佳选择。

其他可配置的项目包括每个工作人员能处理的连接数量、一个工作人员是否一次只能处理一个连接或在接到待处理的连接通知后处理所有待处理的连接,以及工作人员是否轮流响应事件。

HTTP 上下文

定义HTTP上下文可能是Nginx最常见的用途。在配置Nginx作为Web服务器或反向代理时,“http”上下文将包含大部分配置。该上下文将包含所有指令和其他上下文,用于定义程序如何处理HTTP或HTTPS连接。

Http上下文是事件上下文的兄弟节点,因此它们应该并列列出,而不是嵌套在一起。它们都是主要上下文的子节点。

/etc/nginx/nginx.conf can be paraphrased in Chinese as “/etc/nginx/nginx.conf 配置文件”.
# main context

events {
    # events context

    . . .

}

http {
    # http context

    . . .

}

在较低的上下文中,更具体地处理请求的方式,而在这一层级上的指令控制了每个虚拟服务器的默认设置。大量的指令可以在这个上下文及以下级别进行配置,这取决于您希望继承的方式。

你可能会遇到的一些指令控制着访问日志和错误日志的默认位置(access_log和error_log),配置文件操作的异步I/O(aio,sendfile和directio),以及错误发生时服务器的状态配置(error_page)。其他指令配置压缩(gzip和gzip_disable),微调TCP保持活动设置(keepalive_disable,keepalive_requests和keepalive_timeout),以及Nginx遵循的尝试优化数据包和系统调用的规则(sendfile,tcp_nodelay和tcp_nopush)。额外的指令配置应用程序级别的文档根目录和索引文件(root和index),并设置用于存储不同类型数据的各种哈希表(* _hash_bucket_size和* _hash_max_size用于server_names,types和variables)。更多信息请参考Nginx文档。

服务器上下文

“server”上下文是在“http”上下文中声明的。这是我们第一个嵌套的、有括号的上下文示例。这也是第一个允许多个声明的上下文。

服务器上下文的一般格式可能如下所示。请记住,这些位于http上下文中。

/etc/nginx/nginx.conf 可以被翻译为: /etc/nginx/nginx.conf
# main context

http {

    # http context

    server {

        # first server context

    }

    server {

        # second server context

    }

}

你可以声明多个服务器上下文,因为每个实例定义了一个处理客户端请求的特定虚拟服务器。你可以拥有任意数量的服务器模块,每个模块可以处理一组特定的连接子集。

这种上下文类型也是Nginx必须使用来选择算法的第一种类型。每个客户端请求将根据单个服务器上下文中定义的配置进行处理,因此Nginx必须根据请求的细节来决定哪个服务器上下文最合适。有两种常见的选项,它们在使用域名上有所区别。

  • listen: The IP address / port combination that this server block is designed to respond to. If a request is made by a client that matches these values, this block will potentially be selected to handle the connection.
  • server_name: This directive is the other component used to select a server block for processing. If there are multiple server blocks with listen directives of the same specificity that can handle the request, Nginx will parse the “Host” header of the request and match it against this directive.

在这个环境中,指令可以覆盖许多可能在HTTP环境中定义的指令,包括日志记录、文档根目录、压缩等。除了从HTTP上下文中获取的指令外,我们还可以配置文件来尝试响应请求(通过try_files指令),发出重定向和重写(return和rewrite指令),以及设置任意变量(set指令)。

地理背景

你将经常面对的下一个上下文是位置上下文。位置上下文与服务器上下文有许多关系性特征相似。例如,可以定义多个位置上下文,每个位置用于处理特定类型的客户端请求,通过选择算法,通过将位置定义与客户端请求进行匹配来选择每个位置。

虽然决定是否选择服务器块的指令是在服务器上下文中定义的,但决定某个位置是否能处理请求的组件位于位置定义中(即开启位置块的那一行)。

一般的语法样式如下:

/etc/nginx/nginx.conf 可以被改写为:nginx配置文件路径为 /etc/nginx/nginx.conf。
location match_modifier location_match {

    . . .

}

以下是原文的中文翻译:

“位置块(location blocks)存在于服务器上下文中,它与服务器块不同的是,可以互相嵌套。这对于创建一个更通用的位置上下文以捕捉特定的流量子集,然后根据更具体的标准在内部的其他上下文中进一步处理它非常有用。”

/etc/nginx/nginx.conf 可以翻译为「Nginx配置文件」。
# main context

server {
    
    # server context

    location /match/criteria {

        # first location context

    }

    location /other/criteria {

        # second location context

        location nested_match {

            # first nested location

        }

        location other_nested {

            # second nested location

        }

    }

}

服务器上下文根据请求的IP地址/端口组合和“Host”头中的主机名进行选择,而定位块则通过查看请求URI在服务器块内进一步细分请求处理。请求URI是在域名或IP地址/端口组合之后的请求部分。

例如,假设一个客户端请求使用80端口的http://www.example.com/blog。分别使用http、www.example.com和端口80来确定选择哪个服务器块。选择服务器后,会使用定义的位置来评估请求URI中的/blog部分,以确定响应请求所需使用的进一步上下文。

在位置上下文中,您可能会看到许多指示符,这些指示符也可以在父级别上使用。此级别上的新指示符允许您访问文档根目录之外的位置(别名),将位置标记为仅内部可访问(内部),以及代理到其他服务器或位置(使用HTTP、FastCGI、SCGI和uWSGI代理)。

其他情境

尽管上述例子展示了您在使用Nginx时会遇到的基本情境,但还存在其他一些情境。以下情境仅在特定情况下使用,或者用于大多数人不会使用的功能:

  • split_clients: This context is configured to split the clients that the server receives into categories by labeling them with variables based on a percentage. These can then be used to do A/B testing by providing different content to different hosts.
  • perl / perl_set: These contexts configure Perl handlers for the location they appear in. This will only be used for processing with Perl.
  • map: This context is used to set the value of a variable depending on the value of another variable. It provides a mapping of one variable’s values to determine what the second variable should be set to.
  • geo: Like the above context, this context is used to specify a mapping. However, this mapping is specifically used to categorize client IP addresses. It sets the value of a variable depending on the connecting IP address.
  • types: This context is again used for mapping. This context is used to map MIME types to the file extensions that should be associated with them. This is usually provided with Nginx through a file that is sourced into the main nginx.conf config file.
  • charset_map: This is another example of a mapping context. This context is used to map a conversion table from one character set to another. In the context header, both sets are listed and in the body, the mapping takes place.

上游背景

上游上下文用于定义和配置“上游”服务器。该上下文定义了一个被命名的服务器池,Nginx可以将请求代理到这个池中的服务器上。当您配置不同类型的代理时,很可能会使用这个上下文。

上游上下文应该放置在http上下文中,而不是任何特定的服务器上下文之外。形式如下:

/etc/nginx/nginx.conf 的中文本地化解释
# main context

http {

    # http context

    upstream upstream_name {

        # upstream context

        server proxy_server1;
        server proxy_server2;

        . . .

    }

    server {

        # server context

    }

}

然后可以通过名称在服务器或位置块内引用上游上下文,将特定类型的请求传递给已定义的服务器池。然后,上游将使用算法(默认为轮询)来确定要将请求交给哪个特定的服务器。此上下文使我们的Nginx在代理请求时能够进行一些负载平衡。

如果场景

“if”语境可以建立以提供指令的条件处理。就像传统编程中的if语句一样,Nginx中的if指令将在给定的测试返回“true”时执行包含的指令。

在Nginx中,if上下文是由rewrite模块提供的,这也是此上下文的主要预期用途。由于Nginx将使用许多其他专用指令来测试请求的条件,因此if 不应用于大多数形式的条件执行。这是如此重要的一点,以至于Nginx社区创建了一个名为if is evil的页面。

问题在于Nginx的处理顺序经常会导致意料之外的结果。在这些情况下,只有return和rewrite指令被认为是可靠且安全的。另外,当使用if上下文时要注意,它会使同一上下文中的try_files指令无效。

大多数情况下,会使用一个if语句来确定是否需要重新编写或返回。这些if语句通常存在于位置块中,所以常见的形式会类似于这样:

/nginx的配置文件在/etc/nginx/nginx.conf中。
# main context

http {

    # http context

    server {

        # server context

        location location_match {

            # location context

            if (test_condition) {

                # if context

            }

        }

    }

}

limit_except指令所指的上下文

limit_except上下文用于限制位置上下文中某些HTTP方法的使用。例如,如果只有特定的客户端应该有权限访问POST内容,而每个人都应该有读取内容的能力,可以使用limit_except块来定义此要求。

以上例子将会看起来像这样:

/nginx目录下的nginx.conf文件
. . .

# server or location context

location /restricted-write {

    # location context

    limit_except GET HEAD {

        # limit_except context

        allow 192.168.1.1/24;
        deny all;
    }
}

当遇到除了上下文标题列出的HTTP方法以外的任何HTTP方法时,此指令将适用于上下文中的指令(用于限制访问)。上述示例的结果是,任何客户端都可以使用GET和HEAD动词,但只有来自192.168.1.1/24子网的客户端可以使用其他方法。

关于语境,要遵守的一般规则。

现在您已经了解了在探索Nginx配置时可能遇到的常见环境,我们可以讨论在处理Nginx环境时使用的一些最佳实践。

在最高上下文中应用指令。

许多指令适用于多个上下文环境。例如,有很多指令可以放置在http、server或location的上下文中。这使得我们在设置这些指令时具有灵活性。

一般而言,最好将指令声明在其适用的最高上下文中,并根据需要在较低上下文中进行覆盖。这是因为Nginx实施了继承模型。使用这种策略有许多原因。

首先,高层次的声明使得你可以在兄弟上下文之间避免不必要的重复。例如,在下面的例子中,每个位置声明了相同的文档根部。

/nginx/nginx.conf文件
http {
    server {
        location / {
            root /var/www/html;

            . . .

        }

        location /another {
            root /var/www/html;

            . . .

        }

    }
}

你可以将根路径移到服务器块,甚至移到http块中,像这样:

http {
    root /var/www/html;
    server {
        location / {

            . . .

        }

        location /another {

            . . .

        }
    }
}

大多数情况下,服务器级别是最合适的,但在更高级别声明具有其优点。这不仅使您可以在更少的地方设置指令,而且还可以将默认值级联到所有的子元素,以防止在较低级别遗漏指令而导致错误的情况。这对于长配置来说可能是一个重大问题。在更高级别声明可以为您提供一个良好的默认设置。

使用多个兄弟上下文而不是使用if逻辑进行处理。

当您想根据客户端请求中的某些信息来区别处理请求时,通常用户会尝试使用 “if” 的上下文来条件化处理。我们之前简要提到了这个问题的几个方面。

首先是“if”指令经常产生与管理员预期不符的结果。尽管在相同的输入下处理总是会得到相同的结果,但是Nginx解释环境的方式可能与未经过大量测试的假设不同。

原因之二是因为已经有了为许多这些目的而优化、专门制定的指令。Nginx已经使用了一种经过充分记录的选择算法,用于选择服务器块和位置块等任务。如果可能的话,最好将不同的配置移动到它们自己的块中,以便该算法可以处理选择过程的逻辑。

举个例子,不要依赖于重新编写来将用户提供的请求转换成你想要处理的格式,你应该尝试设置两个块来处理请求,其中一个代表所需的方法,另一个用于捕获混乱的请求并将它们重定向(可能还要重新编写)到正确的块中。

结果通常更易读,并且具有更高的性能优势。正确的请求不需要进行任何额外的处理,而且在许多情况下,不正确的请求可以通过重定向而不是重写来处理,这样可以降低开销。

结论 (jié

到这个阶段,你应该已经对Nginx的最常见上下文和定义它们的指令有很好的掌握。

始终查阅Nginx的文档以获取指令可放置的上下文信息,并评估最有效的位置。在创建配置时要小心,这将提高可维护性,并且通常会提高性能。

接下来,您可以学习如何在Nginx中配置密码身份验证。

发表回复 0

Your email address will not be published. Required fields are marked *


广告
将在 10 秒后关闭
bannerAds