nginx配置文档学习笔记

Last Updated:

2022-08-12

nginx是如何处理一次请求的(How nginx process a request)

先根据nginx的配置文件的规则,这个请求打到相应的端口了,这才叫做“收到请求”

收到请求后,nginx首先决定哪一个server来处理这个请求

这一步,是通过请求的Host字段,来配合server_name命令来决定的

如果没有匹配的server_name配置,那么会使用default server来处理。

这个default server是配置的第一条规则(listen该端口的第一条规则)

当然,可以通过显式地使用listendefault_server参数,来指定该端口的default_server规则。 例如: listen 80 default_server;

常用命令(directives)

server_name 命令(directive)

server_name文档

规则命中的优先级

  1. 精准匹配名字
  2. 通配符打头,优先级随长度正向变换(长度越长,优先级越高)(*.testdomain.com*.com更先命中)
  3. 通配符结尾,优先级随长度正向变换(长度越长,优先级越高)(testdomain.test.*test.*更先命中)
  4. 正则规则(在配置文件中出现地越早,更先命中)

性能优化

上述nginx配置文档学习笔记,针对1-3项,nginx分别存储了3张缓存表(表a、表b、表c)。 表a,缓存了所有精准匹配名字的规则; 表b,缓存了所有通配符打头的规则; 表c,缓存了所有通配符结尾的规则。 当一次收到请求后,nginx先搜一遍表a、再搜一遍表b、最后搜一遍表c;都没有命中后,就会根据正则,以此检验。 所以从性能最优角度,采取如下几个性能优化小技巧。

  1. 把大部分经常访问的server_name显示写出来,再使用通配符规则进行兜底
  2. 统配规则,能尽量使用通配符打头,而不是通配符结尾

Punycode

有一些IDNs,在nginx配置的时候,server_name要使用punycode。或者说的更直白一点,中文域名,要使用punycode。 例如,我以前使用过域名连培培.中国,如果为了给这条规则命名一个server,我得使用这个域名对应的punycode,也即 xn--2jsa9304b.xn--fiqs8s punycode转换器

使用nginx进行负载均衡(load balance)

文档地址

负载均衡算法(nginx默认自带)

轮询调度(round-robin)算法(nginx默认的负载均衡算法)

被连接最少的服务器(least-connected)优先算法

ip-hash算法

快速配置(默认是轮询算法)

http {
    upstream myapp1 {
        server srv1.example.com;
        server srv2.example.com;
        server srv3.example.com;
    }

    server {
        listen 80;

        location / {
            proxy_pass http://myapp1;
        }
    }
}

改成least-connected优先算法

将上面配置里头的myapp1,加一行,改成

upstream myapp1 {
        least_conn;
        server srv1.example.com;
        server srv2.example.com;
        server srv3.example.com;
    }

改成ip-hash算法(唯一能保证session一致,原理是,同一个client ip的所有请求,最后都会打到最开始连接过去的服务器上,即同一台服务器)

将上面配置里头的myapp1,加一行,改成

upstream myapp1 {
        ip_hash;;
        server srv1.example.com;
        server srv2.example.com;
        server srv3.example.com;
    }

其他高级算法,需要加钱、加module

上述的round-robin、least-connected、ip-hash算法,在配置之时,都可以加权(weighted)

upstream myapp1 {
        ip_hash;;
        server srv1.example.com weight=4;
        server srv2.example.com;
        server srv3.example.com;
    }

当nginx做反向代理服务器的时候,有一个“健康检查”(health check)

用来检查,上面myapp1指定的服务器组中的服务器,是不是"死了"。

如何判断服务器“死了”

fail_timeout时间内,连续max_fails次连接失败,那么就判定服务器“死了”

判断服务器“死了”之后,多久再重新进行重新判断,服务器是否“活了”

自最近判断服务器“死了”的时间点,过去fail_timeout时间后,nginx会再一次对服务器进行probe,如果能连上了,那么就判定服务器“活了”。

配置https服务器

典型的一个基础配置

server {
    listen              443 **ssl**;
    server_name         www.example.com;
    ssl_certificate     **www.example.com.crt**;
    ssl_certificate_key **www.example.com.key**;
    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers         HIGH:!aNULL:!MD5;
    ...
}

其中,ssl_certificate_key(私钥),需要在服务器上,限制文件的读权限(access right),毕竟是敏感的信息。 不过这个私钥文件,一定需要能被nginx的主进程(master process)可读取。

https服务器配置的性能优化

开多个worker_process

SSL相关操作会消耗cpu资源。所以在多核系统中,nginx需要跑多个worker_process(数量不少于n核系统的n)

开启ssl_session_cache

在ssl对平行或者子链接,进行handshake时,可以将sessions存在这个缓存,重复使用,从而能减少SSL的handshake操作(复用)

reuse SSL session parameters to avoid SSL handshakes for parallel and subsequent connections. The sessions are stored in an SSL session cache shared between workers and configured by the ssl_session_cache directive.

开启keepalive链接(ssl_session_timeout)

链接keepalive,从而通过该链接能一次性多发送几个request

by enabling keepalive connections to send several requests via one connection

One megabyte of the cache contains about 4000 sessions. The default cache timeout is 5 minutes.

样例配置

**worker_processes auto**;

http {
    **ssl_session_cache   shared:SSL:10m**;
    **ssl_session_timeout 10m**;

    server {
        listen              443 ssl;
        server_name         www.example.com;
        **keepalive_timeout   70**;

        ssl_certificate     www.example.com.crt;
        ssl_certificate_key www.example.com.key;
        ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers         HIGH:!aNULL:!MD5;
        ...

ssl证书链(SSL certificate chains)

一些浏览器会对你配置在nginx服务器上的证书,报一些warning,认为你的证书不安全;而同时,在另一些浏览器上,不会有任何的warning.

这是由于给你发证书的机构,不是最大的全局的那种机构。给你发证书的机构,使用了一个中介证书,然后给你发的证书。 所以,你需要将给你发证书的机构,提供给你的“证书包”,和发给你的证书进行一下“粘贴”,从而生成你可使用的证书。

cat www.example.com.crt bundle.crt > www.example.com.chained.crt

你nginx配置https的时候,使用这个“粘贴”而成的证书www.example.com.chained.crt

证书合成过程中,“粘贴”顺序出错,nginx会报如下错误

SSL_CTX_use_PrivateKey_file(" ... /www.example.com.key") failed (SSL: error:0B080074:x509 certificate routines: X509_check_private_key:key values mismatch)

验证查看服务器是否返回“证书链”的方法

openssl s_client -connect www.godaddy.com:443

如果没有返回“证书链”(a certificate bundle has not beed added),那么返回的信息,只有#0

CONNECTED(00000005)

depth=2 C = US, O = Internet Security Research Group, CN = ISRG Root X1

verify error:num=20:unable to get local issuer certificate

verify return:0

---

Certificate chain

 0 s:/CN=www.petersonlian.com

 i:/C=US/O=Let's Encrypt/CN=R3

 1 s:/C=US/O=Let's Encrypt/CN=R3

 i:/C=US/O=Internet Security Research Group/CN=ISRG Root X1

 2 s:/C=US/O=Internet Security Research Group/CN=ISRG Root X1

 i:/O=Digital Signature Trust Co./CN=DST Root CA X3

---

Server certificate

-----BEGIN CERTIFICATE-----

MIIFQDCCBCigAwIBAgISA5E6hUdu19R/K6zxcE/BPvLlMA0GCSqGSIb3DQEBCwUA

MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD

EwJSMzAeFw0yMTExMzAxNTE2NDVaFw0yMjAyMjgxNTE2NDRaMB8xHTAbBgNVBAMT

FHd3dy5wZXRlcnNvbmxpYW4uY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
...

只配置一个server,来处理http和https请求

server {
    listen              80;
    listen              443 ssl;
    server_name         www.example.com;
    ssl_certificate     www.example.com.crt;
    ssl_certificate_key www.example.com.key;
    ...
}

(https)无法一个ip,多个server name

这是因为SSL链接比http链接建立地更早,所以,nginx建立SSL链接的时候,是不知道http的请求的server name。所以,nginx只会返回默认的证书(可能是www.petersonlian.com的证书,而不是www.peterson.org的证书)

解决方案1,从证书本身着手

证书的SubjectAltName里头,多填写几个server name(把www.peteronlian.com, www.peterson.org 等,放进去)

但是,毕竟这个SubjectAltName字段长度有限

server name的填写,尽量使用一些统配符,例如: *.peteronlian.com

解决方案2, SNI( Server Name Indication)

A more generic solution for running several HTTPS servers on a single IP address is TLS Server Name Indication extension (SNI, RFC 6066), which allows a browser to pass a requested server name during the SSL handshake and, therefore, the server will know which certificate it should use for the connection. SNI is currently supported by most modern browsers, though may not be used by some old or special clients.

nginx是如何处理TCP/UDP会话(Session)的

文档post

一个会话,是被一连串的“阶段”(phase)来处理的

  1. Post-accept
  2. Pre-access
  3. Access
  4. SSL
  5. Preread
  6. Content (此阶段决定并操作: 是proxied还是upstream,还是直接return)
  7. Log

nginx的njs(跳过未读)