通过 $ nginx -V
,你可以看到 nginx 的编译配置信息:
$ nginx -V
nginx version: nginx/1.17.3
built by gcc 8.3.0 (Debian 8.3.0-6)
built with OpenSSL 1.1.1c 28 May 2019
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads ...
其中可以看到 --prefix=/etc/nginx
,nginx 安装时会把相关数据文件写入到该目录,如我们的配置文件 --conf-path
。
每次更改 nginx 的配置文件,你需要执行一下操作:
# 验证配置文件的正确性
$ nginx -T
# 重新加载配置文件
$ nginx -s reload
基础概念
这里推荐 nginx 的官方文档地址:http://nginx.org/en/docs/。
文档中涵盖了各个模块的配置用法,以及默认值,可以填写的上下文位置。
目前 nginx 支持多种服务类型:
http
mail
stream
google perftools
我们 着重介绍 http 服务。其它服务基本知识点都能涵盖到。
安装完 nginx ,我们先来看一看 nginx 的默认配置 /etc/nginx/nginx.conf
,当然可能与你的默认配置不同,不过大同小异:
# worker以什么身份运行
user nginx; // default nobody
# worker进程个数,一般为 CPU 个数,也可选 auto
worker_processes 1; # default 1
# 每个worker可打开的描述符限制
worker_rlimit_nofile 8192;
# 错误日志保存路径和级别
error_log /var/log/nginx/error.log warn;
# 进程pid保存路径
pid /var/run/nginx.pid;
# 指定dns服务器
resolver 10.0.0.1;
events {
# 每个worker最大连接数
worker_connections 1024; # default 1024
}
# http 服务定义
http {
# 加载 mime 类型
include /etc/nginx/mime.types;
# 定义默认数据类型
default_type application/octet-stream;
# 日志格式
log_format main '$remote\_addr - $remote\_user [$time\_local] "$request" '
'$status $body\_bytes\_sent "$http\_referer" '
'"$http\_user\_agent" "$http\_x\_forwarded\_for"';
# 访问日志
access_log /var/log/nginx/access.log main;
# 是否调用sendfile函数(zero copy 方式)来输出文件,如果磁盘IO重负载应用,可设置为off
sendfile on;
# 此选项允许或禁止使用socke的TCP\_CORK的选项,此选项仅在使用sendfile的时候使用
#tcp\_nopush on;
keepalive_timeout 65;
# 代理相关设置
# proxy\_connect\_timeout 90;
# proxy\_read\_timeout 180;
# proxy\_send\_timeout 180;
# proxy\_buffer\_size 256k;
# proxy\_buffers 4 256k;
# proxy\_busy\_buffers\_size 256k;
# proxy\_temp\_file\_write\_size 256k;
# tcp\_nodelay on;
# gzip 压缩
#gzip on;
# 加载其它配置,这样我们在 conf.d 下写的文件才会生效
include /etc/nginx/conf.d/*.conf;
}
加载配置 /etc/nginx/conf.d
,才能让我们的配置生效:
# 加载其它配置
include /etc/nginx/conf.d/*.conf;
一般的,如果是小站点不用去修改默认配置。当流量到达一定程度,需要进行适当优化。
内置变量
内置变量,nginx 各个模块都将请求的一些参数进行变量化,通过 $ + 变量名
即可使用。每个模块或多或少都有自己的变量。着重介绍下核心模块的 内置变量:
# 通过arg\_<name>的方式可取出相关参数,若请求 /foo?name=Tony&age=2,则 arg\_name=tony arg\_age=2
$arg\_name
$args
# 客户端IP地址二进制
$binary\_remote\_addr
# 发送到客户端的字节数,不包括响应头
$body\_bytes\_sent
# 发送给客户端字节数
$bytes\_sent
# 连接序列号
$connection
# 当前已经连接的请求书
$connection\_requests
# Content-Length 请求头
$content\_length
# Content-Type 请求头
$content\_type
# cookie 名称
$cookie\_name
# 当前请求的 root 或 alias 的值
$document\_root
# 与 $uri 相同
$document\_uri
# 优先级:请求行中的 host name,请求头中的 Host,请求匹配的 server name
$host
# host name
$hostname
# 任意请求头字段。变量名的最后一部分是转换为小写的字段名,用下划线替换破折号
$http\_name
# 如果连接在 SSL 模式下运行,则为 on,否则为空字符串
$https
# ? 后如果请求行有参数,或者空字符串
$is\_args
# 设置此变量可以限制响应速度
$limit\_rate
# 当前时间(秒),分辨率为毫秒
$msec
# nginx 版本号
$nginx\_version
# 当前 worker 进程号
$pid
# 如果是 pipelined 则为 p,否则为 .
$pipe
# 代理协议头中的客户端地址,否则为空字符串,代理协议之前必须通过在listen指令中设置 proxy\_protocol 参数来启用
$proxy\_protocol\_addr
# 来自代理协议头的客户端端口,否则为空字符串,代理协议之前必须通过在listen指令中设置 proxy\_protocol 参数来启用
$proxy\_protocol\_port
# 与 $args 相同
$query\_string
# 与当前请求的 root 或 alias 指令值对应的绝对路径名,所有符号链接都解析为实际路径
$realpath\_root
# 客户端地址
$remote\_addr
# 客户端端口
$remote\_port
# 使用 Basic auth 的用户名
$remote\_user
# 完整的请求行
$request
# 请求体,当将请求体读入内存缓冲区时,proxy\_pass、fastcgi\_pass、uwsgi\_pass和scgi\_pass指令处理的位置可以使用变量的值
$request\_body
# 具有请求主体的临时文件的名称
$request\_body\_file
# 如果请求完成则为 OK,否则为空
$request\_completion
# 当前请求的文件路径,基于 root 或 alias 和请求 URI
$request\_filename
# 由16个随机字节生成的惟一请求标识符,以十六进制表示
$request\_id
# 请求长度(包括请求行、头和请求体)
$request\_length
# 请求方法,如 GET 或 POST
$request\_method
# 请求处理时间,从客户端读取第一个字节以来的时间
$request\_time
# 若请求 /foo?a=1&b=2,则 request\_uri=/foo?a=1&b=2
$request\_uri
# 如 http 或 https
$scheme
# 任意响应报头字段,变量名的最后一部分是转换为小写的字段名,用下划线替换破折号
$sent\_http\_name
# 响应结束时发送的任意字段,变量名的最后一部分是转换为小写的字段名,用下划线替换破折号
$sent\_trailer\_name
# 接受请求的服务器的地址
$server\_addr
# 接受请求的 server 名称
$server\_name
# 接受请求的 server 端口
$server\_port
# 请求协议,如 HTTP/1.0 或 HTTP/1.1 或 HTTP/2.0
$server\_protocol
# 响应状态
$status
$tcpinfo\_rtt,$tcpinfo\_rttvar,$tcpinfo\_snd\_cwnd,$tcpinfo\_rcv\_space
# 本地时间ISO 8601标准格式
$time\_iso8601
# 通用日志格式的本地时间
$time\_local
# 若请求 /foo?a=1&b=2,则 uri=/foo
$uri
# 用户代理
$http\_user\_agent
# cookie
$http\_cookie
你还可以通过自定义变量指令 set
进行变量的定义。
server定义
server 即虚拟服务,它用来描述我们站点一些访问规则。需要填写在 http
标签中,可定义多个,如:
http {
server {
...
}
server {
...
}
...
}
一个常见的 server 的定义:
resolver 10.0.0.1;
# 负载均衡
upstream dynamic {
zone upstream_dynamic 64k;
server backend1.example.com weight=5;
server backend2.example.com:8080 fail_timeout=5s slow_start=30s;
server 192.0.2.1 max_fails=3;
server backend3.example.com resolve;
server backend4.example.com service=http resolve;
server backup1.example.com:8080 backup;
server backup2.example.com:8080 backup;
}
# http服务
server {
listen 80;
server_name example.com www.example.com;
location / {
rewrite https://$host; # 重定向到https
}
}
# https 服务
server {
listen 443 ssl; # 监听端口
server_name example.com www.example.com; # 匹配域名
# ssl证书
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers AES128-SHA:AES256-SHA:RC4-SHA:DES-CBC3-SHA:RC4-MD5;
ssl_certificate /usr/local/nginx/conf/cert.pem;
ssl_certificate_key /usr/local/nginx/conf/cert.key;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# 静态服务
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
# 反向代理
location /api {
proxy_pass http://dynamic;
health_check;
}
}
下面就让我们来详细解释下。
http_upstream_module
http_upstream_module,upstream
说白了就是做负载均衡,它可以帮助我们定义一组相同服务的别名,如backend
,当请求到来的时候可以通过相关策略帮我们选一组服务提供响应。
目前只能被 proxy_pass
,fastcgi_pass
,uwsgi_pass
,scgi_pass
,memcached_pass
,grpc_pass
使用。
形式如下:
upstream <name> { # 命名
server <address> [parameters]; # 服务
server <address> [parameters];
...
}
[parameters] 参数可选以下值:
- weight=number,default 1,设置 server 的权重
- max_conns=number,default 0,限制 server 的活跃连接数,0 代表不限制
- max_fails=number,default 1,设置在 fail_timeout 时间内失败的最大次数,可由
proxy_next_upstream
,fastcgi_next_upstream
,uwsgi_next_upstream
,scgi_next_upstream
,memcached_next_upstream
,grpc_next_upstream
指定下组 upstream,0 值代表不启用
- fail_timeout=time,default 10s,设置多长时间判定无连接服务器失败
- backup,标记 server 为备用 server,当 primary server 不可用时启用
- down,标记 server 下线不可用
- resolve,用来监视与服务器域名对应IP地址的更改,它会自动更改上游配置,
upstream
必须驻留在共享内存中,必须写在 http
标签中。
http {
resolver 10.0.0.1;
upstream u {
zone ...;
...
server example.com resolve;
}
}
- route=string,设置 server 路由名称
- server=name,
- slow_start=time,慢启动,server 非正常状态恢复到正常需要的时间
- drain,设置为 drain 模式
其它负载均衡设置:
zone name [size]
,设置共享内存的名称和大小
state file
,
hash key [consistent]
,负载均衡方式,key 可以为文本,变量,或其组合
ip_hash
,负载均衡方式,根据IP地址范围分布 server,用 IPv4 前三个8位或整个IPv6
keepalive connections
,设置到上游 server 保持最大空闲连接
upstream memcached_backend {
server 127.0.0.1:11211;
server 10.0.0.2:11211;
keepalive 32;
}
server {
...
location /memcached/ {
set $memcached\_key $uri;
memcached_pass memcached_backend;
}
}
keepalive_requests number
,设置最大请求连接数
keepalive_timeout timeout
,连接超时时间
ntlm
,允许使用NTLM身份验证代理请求
upstream http_backend {
server 127.0.0.1:8080;
ntlm;
}
server {
...
location /http/ {
proxy_pass http://http\_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
...
}
}
least_conn
,负载均衡方式,将请求传给活跃连接数最少的 server
least_time header | last_byte [inflight]
,负载均衡方式,将请求传给平均响应时间和活跃连接数最少的 server
queue number [timeout=time]
,队列缓存,当选择不到 server 处理请求时放入队列,如果队列满,返回502
random [two [method]]
,负载均衡方式,
sticky
,会话关联,同一客户端请求将会被传给同一 upstream 的同一 server
# cookie
upstream backend {
server backend1.example.com route=a;
server backend2.example.com route=b;
sticky cookie srv_id expires=1h domain=.example.com path=/;
}
# route
map $cookie\_jsessionid $route\_cookie {
~.+\.(?P<route>\w+)$ $route;
}
map $request\_uri $route\_uri {
~jsessionid=.+\.(?P<route>\w+)$ $route;
}
upstream backend {
server backend1.example.com route=a;
server backend2.example.com route=b;
sticky route $route\_cookie $route\_uri;
}
# learn
upstream backend {
server backend1.example.com:8080;
server backend2.example.com:8081;
sticky learn
create=$upstream\_cookie\_examplecookie
lookup=$cookie\_examplecookie
zone=client_sessions:1m;
}
listen
listen 监听设置,来看一看可选参数:
默认 listen *:80 | *:8000;
listen address[:port] [default_server] [ssl] [http2 | spdy] [proxy_protocol] [setfib=number] [fastopen=number] [backlog=number] [rcvbuf=size] [sndbuf=size] [accept_filter=filter] [deferred] [bind] [ipv6only=on|off] [reuseport] [so_keepalive=on|off|[keepidle]:[keepintvl]:[keepcnt]];
listen port [default_server] [ssl] [http2 | spdy] [proxy_protocol] [setfib=number] [fastopen=number] [backlog=number] [rcvbuf=size] [sndbuf=size] [accept_filter=filter] [deferred] [bind] [ipv6only=on|off] [reuseport] [so_keepalive=on|off|[keepidle]:[keepintvl]:[keepcnt]];
listen unix:path [default_server] [ssl] [http2 | spdy] [proxy_protocol] [backlog=number] [rcvbuf=size] [sndbuf=size] [accept_filter=filter] [deferred] [bind] [so_keepalive=on|off|[keepidle]:[keepintvl]:[keepcnt]];
真的多,可平时也没用几个,举例:
listen 127.0.0.1:8000;
listen 127.0.0.1; # 如果只指定地址,默认监听 80
listen 8000;
listen *:8000;
listen localhost:8000;
listen 127.0.0.1 default_server accept_filter=dataready backlog=1024;
# IPv6
listen [::]:8000;
listen [::1];
# unix socket
listen unix:/var/run/nginx.sock;
其它参数说明:
default_server
,如果指定,server 将会成为默认 server
ssl
,开启 ssl 模式,即 https
http2
,正常情况开启 http2 都应该开始 ssl,但 nginx 也支持不开启 ssl 下的 http2 协议
spdy
,和 http2 一样,建议开启 ssl
setfib=number
,监听套接字设置关联的路由表FIB (SO_SETFIB选项)。这目前只适用于FreeBSD
fastopen=number
,为监听套接字启用“TCP Fast Open”(1.5.8),并限制尚未完成三方握手的连接队列的最大长度
backlog=number
rcvbuf=size
,接受 buffer 的大小(SO_CRCVBUF)
sndbuf=size
,发送 buffer 的大小(SO_SNDBUF)
accept_filter=filter
,可选 dataready 和 httpready,在 accept() 前过滤
deferred
,指示在Linux上使用deferred accept() (TCP_DEFER_ACCEPT套接字选项)
bind
,标记指定 address:port 单独的绑定
ipv6only on|off
,只接受 IPv6 连接
reuseport
so_keepaliv on|off|[keepidle]:[keepintv1]:[keepcnt]
,”TCP keepalive” 开关
server_name
server_name,设置虚拟主机的名称。
形式如下:
默认值 server_name "";
server_name name ...;
例1,穷举域名
server {
server\_name example.com www.example.com;
}
例2,通配符写法
server {
server\_name example.com *.example.com www.example.*;
}
例3 这种写法满足例1
server {
server\_name .example.com;
}
例4 正则表达式,以 ~ 开头
server {
server\_name www.example.com ~^www\d+\.example\.com$;
}
例5 正则表达式捕获
server {
server\_name ~^(www\.)?(.+)$;
location / {
root /sites/$2;
}
}
server {
server\_name _;
location / {
root /sites/default;
}
}
例6 正则表达式变量
server {
server\_name ~^(www\.)?(?<domain>.+)$;
location / {
root /sites/$domain;
}
}
server {
server\_name _;
location / {
root /sites/default;
}
}
例7 与空名称使用
server {
server\_name www.example.com "";
}
如果当一个名称匹配多个 server 的是时候,匹配优先级如下:
- 确切的名称
- 以 * 开头的最长的通配符名称
- 以 * 结尾的最长通配符名称
- 第一个匹配的正则表达式
更多匹配规则请查阅:http://nginx.org/en/docs/http/server_names.html
location
location 是用来干嘛的,它是用来根据 URI 进行配置设置的,如:
server {
listen 80;
server_name example.com;
location / { # 普通请求网页
root /usr/share/nginx/html;
index index.html index.htm;
}
location /api { # API请求代理
proxy_pass http://dynamic;
health_check;
}
}
形式如下:
location [ = | ~ | ~* | ^~ ] uri { ... }
- none,如果没有修饰符,则将该位置解释为前缀匹配。这意味着给定的位置将根据请求URI的开头进行匹配,以确定匹配
=
,代表精确匹配,完全相等即匹配
~
,区分大小写的正则表达式匹配
~*
,不区分大小写的正则表达式匹配
^~
,普通字符匹配,如果该选项匹配,只匹配该选项
nginx 的匹配过程如下:
- 精确匹配
=
,如果匹配成功,搜索停止
- 前缀匹配,最长位置匹配,如果该匹配具有
^~
,搜索停止
- 正则匹配,按配置文件中定义的顺序进行匹配。
- 如果第3条规则产生匹配的话,结果被使用。否则,使用第2条规则的结果。
让我们通过一个例子来了解下匹配规则:
location = / {
[ configuration A ]
}
location / {
[ configuration B ]
}
location /documents/ {
[ configuration C ]
}
location ^~ /images/ {
[ configuration D ]
}
location ~* \.(gif|jpg|jpeg)$ {
[ configuration E ]
}
请求 /
将会匹配 A,请求 /index.html
将会匹配 B,请求 /documents/document.html
将会匹配 C,请求 /images/1.gif
将会匹配 D,请求 /documents/1.jpg
将会匹配 E。
ssl mode
ssl 模式可以让我们站点启用 HTTPS,具体详细请参考 http_ssl_module。
想要开启 ssl 模式,需要在 listen
关键字处添加上 ssl
,如:
server {
listen 443 ssl;
server_name example.com;
ssl_certificate example.com.rsa.crt;
ssl_certificate_key example.com.rsa.key;
ssl_certificate example.com.ecdsa.crt;
ssl_certificate_key example.com.ecdsa.key;
...
}
上面的例子是部署双证书,当某一证书因某种原因失效不至于导致站点不能访问。下面来看看参数解释:
ssl_buffer_size size
,default 16k,发送数据的缓冲区的大小
ssl_certificate file
,PEM 格式证书文件
ssl_certificate_key file
,PEM 格式私钥文件
ssl_ciphers ciphers
,default HIGH:!aNULL:!MD5,ssl套件 openssl ciphers
ssl_client_certificate file
,用于验证客户端证书的 CA 文件
ssl_crl file
,用于验证客户端证书的吊销文件
ssl_dhparam file
,为DHE密码指定具有DH参数的文件
ssl_early_data on|off
,default on
ssl_ecdh_curve curve
,default auto,为ECDHE密码指定一条曲线
ssl_password_file file
,私钥密码文件
ssl_prefer_server_ciphers on|off
,是否启用服务器套件偏好
ssl_protocols [SSLv2] [SSLv3] [SSLv3] [TLSv1] [TLSv1.1] [TLSv1.2] [TLSv1.3]
,default TLSv1 TLSv1.1 TLSv1.2,可选的ssl协议
ssl_session_cache off|none|[builtin[:size]] [shared:name:size]
,default none,设置 session cache 的类型和大小
ssl\_session\_cache builtin:1000 shared:SSL:10m;
ssl_session_ticket_key file
,设置一个文件,其中包含用于加密和解密TLS会话票据的密钥
ssl\_session\_ticket\_key current.key;
ssl\_session\_ticket\_key previous.key;
随机一个 AES256(80),AES128(40)
openssl rand 80 > ticket.key
ssl_session_tickets on|off
,default on,是否启用 session ticket
ssl_session_timeout time
,default 5m,超时时间
ssl_stapling on|off
,default off,ocsp 装订 ssl_stapling on; resolver 192.0.2.1;
ssl_stapling_file file
ssl_stapling_responder url
ssl_stapling_verify on|off
,default off
ssl_trusted_certificate file
,指定验证客户端证书的 CA 文件
ssl_verify_client on|off|optional|optional_no_ca
,default off,是否验证客户端证书
ssl_verify_depth number
,default 1,设置客户端证书链的验证深度
相关变量
$ssl\_cipher,已建立连接使用的 ciphers
$ssl\_ciphers,客户端支持的 ciphers
$ssl\_client\_escaped\_cert,urlencoded 客户端证书
$ssl\_client\_fingerprint,SHA1指纹
$ssl\_client\_i\_dn,issuer DN
$ssl\_client\_i\_dn\_legacy,同上,1.11.6之后使用
$ssl\_client\_raw\_cert,PEM格式客户端证书
$ssl\_client\_s\_dn,subject DN
$ssl\_client\_s\_dn\_legacy,同上,1.11.6之后使用
$ssl\_client\_serial,客户端证书序列号
$ssl\_client\_v\_end,客户端证书结束时间
$ssl\_client\_v\_remain,剩余多少天
$ssl\_client\_v\_start,证书开始时间
$ssl\_client\_verify,客户端证书是否验证成功,"SUCCESS" 或 "FAILED:reason" 或 "NONE"
$ssl\_curves,客户端支持的曲线
$ssl\_early\_data
$ssl\_protocol,连接使用的协议
$ssl\_server\_name,从 SNI 获取的 server name
$ssl\_session\_id,连接的 session id
$ssl\_session\_reused,session是否重用,"r" 重用,"." 没有
其它模块
其它模块你需要根据文档及编译信息判断该模块是否默认编译在 nginx 中,并且版本是否匹配:
ngx\_http\_access\_module
ngx_http_addition_module
ngx_http_api_module
ngx_http_auth_basic_module
ngx_http_auth_jwt_module
ngx_http_auth_request_module
ngx_http_autoindex_module
ngx_http_browser_module
ngx_http_charset_module
ngx_http_dav_module
ngx_http_empty_gif_module
ngx_http_f4f_module
ngx_http_fastcgi_module
ngx_http_flv_module
ngx_http_geo_module
ngx_http_geoip_module
ngx_http_grpc_module
ngx_http_gunzip_module
ngx_http_gzip_module
ngx_http_gzip_static_module
ngx_http_headers_module
ngx_http_hls_module
ngx_http_image_filter_module
ngx_http_index_module
ngx_http_js_module
ngx_http_keyval_module
ngx_http_limit_conn_module
ngx_http_limit_req_module
ngx_http_log_module
ngx_http_map_module
ngx_http_memcached_module
ngx_http_mirror_module
ngx_http_mp4_module
ngx_http_perl_module
ngx_http_proxy_module
ngx_http_random_index_module
ngx_http_realip_module
ngx_http_referer_module
ngx_http_rewrite_module
ngx_http_scgi_module
ngx_http_secure_link_module
ngx_http_session_log_module
ngx_http_slice_module
ngx_http_spdy_module
ngx_http_split_clients_module
ngx_http_ssi_module
ngx_http_ssl_module
ngx_http_status_module
ngx_http_stub_status_module
ngx_http_sub_module
ngx_http_upstream_module
ngx_http_upstream_conf_module
ngx_http_upstream_hc_module
ngx_http_userid_module
ngx_http_uwsgi_module
ngx_http_v2_module
ngx_http_xslt_module