提供:ZStack云计算
本教程为如何在Ubuntu 14.04上实现Nginx与LEMP系列四篇中的第四篇。
Nginx是一套高性能Web服务器,负责处理互联网上各大型站点的日常负载。其特别擅长处理高并发连接与大量静态内容。
尽管很多用户都对Nginx的功能有所了解,但也有不少朋友还不熟悉Nginx配置文件的使用方法。在本教程中,我们将探讨Nginx配置文件的基本结构,并提供相关指导帮助大家设计出自己的文件。
本教程将涵盖主Nginx配置文件内的各基本结构。此文件的具体位置取决于该软件的安装位置。对于大多数发行版,该文件位于/etc/nginx/nginx.conf。如果在这里找不到,其也可能存在于/usr/local/nginx/conf/nginx.conf或者/usr/local/etc/nginx/nginx.conf处。
首先需要注意的是,主配置文件的组织方式采用树状结构,由大括号进行分组定义。在Nginx当中,这些大括号限定的区域被称为“上下文(conexts)”,因为其中容纳着相关配置细节。基本上,这些区域负责提供组织化结构与状态化,用于决定是否应用其中包含的配置。
由于各上下文可分层包含,因此Nginx提供一种继承层级。作为一般性规则,如果某条指令在多个嵌套范围内有效,则广泛上下文中的声明将以默认值的形式被传递至任意子上下文。各子上下文可随意覆盖这些值。需要强调的是,任何指向数组类指令的覆盖都会替换原有值,而非作为附加值存在。
指令只能作用于其设计所面向的对应上下文区域内。Nginx会将超出指定上下文的指令视为错误。Nginx说明文档 就提到了各指令适用的上下文,因此推荐大家认真阅读。
下面,我们将探讨在使用Nginx时最为常用的各上下文。
首先要说的是core上下文,Nginx利用其创建继承树并拆分各离散配置块。这类上下文构成了Nginx配置的主体结构。
最为常见的则是main或者global上下文。这是惟一一种不会被包含在常见上下文块内的上下文,具体如下:
# main上下文不可被任何其它上下文所包含
. . .
context {
. . .
}
任何存在于这些块之外的指令都被称为main上下文。请注意,如果大家的Nginx配置采用现代设计风格,那么部分文件将包含存在于大括号上下文区域外的指令,但在进行配置整合时其仍会被作为区域内指令使用。
Main上下文代表Nginx配置的宏观环境。其作用在于配置各项在基础层面对应用整体发生影响的细节。其中部分指令会影响到层级更低的上下文,但也有相当一部分不会得到继承——因为其无法在低层级中进行覆盖。
另外,我们还需要在main上下文中配置工作进程运行所立足的用户及组、工作进程数量以及保存主进程PID的文件。大家甚至可以定义更为细化的项目,例如工作程序的CPU优先级与工作进程“亲和力”应用整体的默认错误文件可在此层级进行设置(可被更多特定上下文所覆盖)。
Events上下文被包含在main上下文当中。其用于设定全局选项,从而影响Nginx在一般层面处理连接的具体方式。每套Nginx配置中只能包含一条events上下文。
以下为配置中的events上下文部分,其不归属于任何大括号限定的上下文:
# main context
events {
# events context
. . .
}
Nginx使用基于事件的连接处理模式,因此此上下文中定义的指令会检测工作进程应当如何处理连接。一般来讲,这里的指令要么负责选定需要使用的连接处理技术,要么修改这些方法的具体实现方式。
通常情况下,连接处理方法会根据当前平台上的可用项目进行自动选定。在Linux系统中,epoll方法通常是最佳选择。
其它可以配置的项目还包括每个工作程序所能处理的连接数量、某一工作程序同时只能处理一条连接抑或得到通知后处理全部未完成连接,以及工作程序是否轮流对事件做出响应等等。
在将Nginx配置为Web服务器或者反向代理时,http上下文将负责完成配置主体。此上下文将容纳定义程序如何处理HTTP或者HTTPS连接所必需的全部指令及其它上下文。
Http上下文可以说是events上下文的表亲,因此二者可以并排列出而非相互嵌套。二者皆属于main上下文的子上下文:
# main context
events {
# events context
. . .
}
http {
# main context
. . .
}
虽然低级上下文负责更为具体地指定请求处理方式,但这一级别上的指令却可以控制每套虚拟服务器的内部默认定义。有相当一部分指令被配置于此上下文及更低层级,具体取决于其功能的继承定位。
部分指令可用于控制默认的访问与错误日志位置(access_log与error_log)、为文件操作配置同步I/O(aio, sendfile与directio)、配置服务器发生错误时的状态(error_page)等。其它指令则可配置压缩(gzip与gzip_disable)、调整TCP保持运行设置(keepalive_disable,keepalive_requests与eepalive_timeout)并指定Nginx需要遵循的数据包与系统调用优化方式(sendfile, tcp_nodelay与tcp_nopush)。也有部分指令配置应用级文件root与目录文件(root与index),以及设置多种用于存储不同数据类型的hash表(*_hash_bucket_size与*_hash_max_size负责指定服务器名称、类型与变量)。
Server上下文在http上下文内进行声明。这是我们介绍的第一种被限定在大括号之内的嵌套型上下文。其同时也是第一种允许进行多项声明的上下文。
Server上下文的格式一般如下所示,注意其包含在http上下文当中:
# main context
http: {
# http context
server {
# first server context
}
server {
# second server context
}
}
之所以允许server上下文进行多项声明,是因为每个实例都会定义一套特定虚拟服务器以处理客户端请求。大家可以根据需要设置多个server块,其各自处理不同的连接子集。
为了实现多server块,server上下文类型也是Nginx必须采用的选定算法。每条客户端请求都会根据单一server上下文中的定义配置进行处理,这样Nginx才能决定使用哪种server上下文更为合适。负责决定具体使用哪套server块的指令用于应答以下请求:
这些指令可覆盖多数于http上下文中定义的指令,包括logging、document root以及compression等等。另外,这些指令提取自http上下文,因此我们也可以配置文件以响应各项请求(try_files)、提交重新定向与重新写入(return与rewrite)以及设定任意变量(set)。
下面要说的是location上下文。Location上下文与server上下文拥有许多共性。例如,我们同样可以定义多条location上下文,且每条location用于处理特定类型的客户端请求,并通过选择算法将location指定至特定客户端请求。
尽管负责选定server块的指令由server上下文进行定义,但location中的组件则能够处理被指定在其定义位置的请求。
其常规语法如下:
location match_modifierlocation_match {
. . .
}
Location块位于server上下文之内,但与server块不同,其可彼此进行嵌套。我们可以借此创建更为通用的location上下文,从而捕捉特定流量子集,而后根据更为具体的其它上下文定义加以处理:
# 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
}
}
}
尽管server上下文根据IP地址/端口请求与“Host”标题头下的主机名称进行选定,但location块会进一步查看请求URI,从而在server块内进行深层处理。请求URI属于该请求的一部分,位于域名或者IP地址/端口部分之后。
因此如果某客户端在端口80上请求http://www.example.com/blog,那么http、www.example.com以及端口80都将被用于进行server块选定。在选定服务器后,/blog部分(即请求URI)将用于同定义位置进行评估,旨在使用哪种上下文作为请求的下一步响应依据。
Location上下文中的其它很多指令也都可用于该父层级。此层级中的新指令允许大家前往document root之外(即别名)、将该位置标记为内部访问(internal)以及代理至其它服务器或者位置)使用http、fastcgi、scgi以及uwsgi代理)。
尽管以上示例已经说明了Nginx中的基本场景,但仍存在着其它一些上下文供我们使用。以下上下文之所以被独立拿出来讨论,是因为它们更多属于可选模块,且只用于特定情况或者大多数用户很少接触。
在这里,我们不会探讨全部可用上下文。以下上下文也仅仅是稍加提及,不会深入说明:
-split_clients: 用于将服务器接收到的客户端进行分类,具体为利用变量按照百分比进行客户端标记。其随后可用于为不同主机提供差别内容,从而实现A/B测试。
-perl / perl_set: 用于为其出现的位置配置Perl处理程序。只限于Perl处理情况。
-map: 用于根据其它变量的值为当前某变量赋值。其为单一变量值提供映射,从而检测下一变量应如何设置。
-geo: 用于指定映射。不过,此映射专门用于分类客户端IP地址。其根据连接IP地址为变量赋值。
-types: 同样用于映射。此上下文用于将MIME类型映射至与之关联的文件扩展。如此一来,Nginx就能够通过该文件溯源至主nginx.conf配置文件。
-charset_map:另一映射类上下文,用于将转换表从某一字符集映射至另一字符集。在该上下文报头中,这两个字符集皆被列出以实现映射转移。
这些上下文的使用频率都比较低,因此这里只是稍加提及。
Upstream上下文用于定义及配置上游服务器。基本上,该上下文定义一套经过命名的服务器池,Nginx随后可将请求代理至其中。此上下文可能会在配置多种类型的代理时被用到。
Upstream上下文应被放置在http上下文内,且存在于任意特定server上下文之外。其一般格式如下:
# main context
http {
# http context
upstream upstream_name {
# upstream context
server proxy_server1;
server proxy_server2;
. . .
}
server {
# server context
}
}
Upstream上下文随后可在server或者location块中按名称引用,从而向服务器池传递特定类型的请求。另外,它还使用一套算法(默认为round-robin)检测哪套特定服务器处理该请求。此上下文能够帮助Nginx在进行请求代理时实现负载均衡。
尽管Nginx的主要作用是充当Web或者反向代理服务器,但其也可以作为高性能邮件代理服务器。此上下文用于mail类型的各项指令,并在main或者global上下文中进行定义(但在http上下文之外)。
Mail上下文的主要作用是在服务器上提供邮件代理解决方案的配置区。Nginx能够将验证请求重新定向至某外部验证服务器。其随后访问POP3及IMAP邮件服务器,从而提供实际邮件数据。Mail上下文还可配置为接入SMTP Relayhost。
Mail上下文的一般形式如下:
# main context
events {
# events context
}
mail {
# mail context
}
If上下文可用于提供条件性指令处理机制。与编程中的if语句一样,Nginx中的if指令会在特定返回值为“true”的情况下执行对应指令。
Nginx中的If上下文由重写模块提供,且主要用于本上下文。由于Nginx会利用多种其它预设指令测试某请求的条件,因此在大多数情况下,我们并不需要主动使用if。这一点非常重要,Nginx社区还专门在if is evil页面中对此进行了强调。
问题在于,if块的添加有可能影响Nginx的处理顺序,进而造成意想不到的结果。在该上下文中惟一比较安全的就是return与rewrite指令。另外需要注意的是,if上下文在使用时会渲染一条在当前上下文中无用的try_files指令。
大多数情况下,if会被用于检测是否需要rewirte或者return。其往往存在于location块当中,因此if上下文的常见格式如下:
# main context
http {
# http context
server {
# server context
location location_match {
# location context
if (test_condition) {
# if context
}
}
}
}
Limit_except上下文用于限制特定HTTP方法在某location上下文中的适用范围。例如,如果只允许特定客户端访问POST内容,但每个人都应有能力阅读该内容,则可使用limit_except块来定义此要求。
以上示例中的limit_except如下:
. . .
# 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方法放行。这样一来,任意客户端都能够使用GET与HEAD变量,但只有来自192.168.1.1/24子网的客户端获准使用其它方法。
现在大家已经对各类常见上下文有了一定了解,下面我们将探讨处理Nginx上下文时的最佳实践。
很多指令能够在多种上下文内生效。例如,相当一部分指令可存在于http、server或者location上下文中。这就让我们能够灵活地进行指令设置。
不过总体来讲,我们最好是在最高上下文内声明指令,包括其适用性,并在必要时覆盖至低级上下文。具体理由如下。
首先,进行高层级声明允许我们避免同类上下文间的重复。例如,在以下示例中,各location声明的是同一document root:
http {
server {
location / {
root /var/www/html;
. . .
}
location /another {
root /var/www/html;
. . .
}
}
}
大家完全可以将该root移出server块,甚至移出http块:
http {
root /var/www/html;
server {
location / {
. . .
}
location /another {
. . .
}
}
}
多数情况下,server层级是最理想的选择,但以更高层级进行声明也有其它优势。我们不仅能够减少指令的设置次数,同时也可以直接在更多子元素内使用默认值,避免因遗漏而造成运行错误。
如果大家希望根据客户端请求中的具体信息选择处理方法,那么用户通常会使用if上下文来进行条件性处理。但这种作法存在以下几种弊端。
首先,if指令通常会返回管理员预料之外的结果。尽管同样的输入结果总会返回同样的输出结果,但Nginx采用的环境解释方法可能会与预期稍有不同。
第二,已经存在经过优化的预设指令来完成这类用途。Nginx鼓励大家使用选定server块及location块的方式选择算法。因此如果可以,大家最好还是将不同配置移动至其对应块中,从而由对应算法处理选择逻辑。
例如,相较于使用rewrite来获取必要格式的用户请求,大家应当尝试为该请求设置两组块,其一代表需要的方法,其二则捕捉大量请求并将其重新定向(可能属于rewrite)至正确块中。
这样一来,我们能够更轻松地读取结果,同时提升性能表现。正确的请求往往无需经过另外处理,而不正确的请求则进行重新定向而非重写,这将大大降低系统的运行负担。
到这里,大家应该已经掌握了Nginx中各常见上下文以及用于创建块以进行上下文定义的各项指令。
另外请参阅Nginx说明文档以获取更多详细信息。请注意,创建我们自己的优化配置不仅能够提升可维护性,往往也能同时实现性能改善。
本文来源自DigitalOcean Community。英文原文:Understanding the Nginx Configuration File Structure and Configuration Contexts By Justin Ellingwood
翻译:diradw