安装

自动安装

# CentOS
sudo yum install nginx

# Ubuntu
sudo apt-get install nginx

安装后nginx路径为/usr/sbin/nginx,配置文件为/etc/nginx/nginx.conf

手动安装

  1. 下载&解压
wget http://nginx.org/download/nginx-1.17.10.tar.gz
tar zxvf nginx-1.17.10.tar.gz
cd nginx-1.17.10
  1. 调整配置
./configure --without-http_rewrite_module
./configure --prefix=/usr/local/nginx --with-http_ssl_module # 安装SSL模块

如果有的依赖库没有,需要先安装

# centos
yum -y install pcre* #安装使nginx支持rewrite
yum -y install gcc-c++
yum -y install zlib*
yum -y install openssl openssl-devel

# ubuntu
apt-get install openssl
apt-get install libssl-dev

# 安装完后再次检查配置
./configure

查看默认安装的模块

cat nginx-1.17.2/auto/options | grep YES
  1. 编译&安装
make
sudo make install
  1. 修改配置
cd /usr/local/nginx/
vi conf/nginx.conf

# 参看当前 Nginx 最终的配置
sbin/nginx -T
# 检查配置是否有问题
nginx -t -c <配置路径>
  1. 启动Nginx
cd /usr/local/nginx

# 启动
sbin/nginx

# 停止/退出/重启/重载配置
sbin/nginx -s stop/quit/reopen/reload
  1. 配置 nginx 开机自启

参考:https://www.nginx.com/resources/wiki/start/topics/examples/systemd/

将以下内容写入文件/lib/systemd/system/nginx.service

[Unit]
Description=The NGINX HTTP and reverse proxy server
After=syslog.target network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target

[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/local/nginx/sbin/nginx -t
ExecStart=/usr/local/nginx/sbin/nginx
ExecReload=/usr/local/nginx/sbin/nginx -s reload
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target

ubuntu版本

[Unit]
Description=A high performance web server and a reverse proxy server
Documentation=man:nginx(8)
After=network.target

[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/local/nginx/sbin/nginx -t -q -g 'daemon on; master_process on;'
ExecStart=/usr/local/nginx/sbin/nginx -g 'daemon on; master_process on;'
ExecReload=/usr/local/nginx/sbin/nginx -g 'daemon on; master_process on;' -s reload
ExecStop=-/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx.pid
TimeoutStopSec=5
KillMode=mixed

[Install]
WantedBy=multi-user.target

启动

systemctl enable nginx   # 设置开机启动 Nginx
systemctl disable nginx  # 关闭开机启动 Nginx

配置详解

基本结构

main        # 全局配置,对全局生效
├── events  # 配置影响 nginx 服务器或与用户的网络连接
├── http    # 配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置
   ├── upstream # 配置后端服务器具体地址,负载均衡配置不可或缺的部分
   ├── server   # 配置虚拟主机的相关参数,一个 http 块中可以有多个 server 块
   ├── server
   │   ├── location  # server 块可以包含多个 location 块,location 指令用于匹配 uri
   │   ├── location
   │   └── ...
   └── ...
└── ...

配置文件的语法规则

  • 配置文件由指令与指令块构成
  • 每条指令以 “;” 分号结尾,指令与参数间以空格符号分隔
  • 指令块以 {} 大括号将多条指令组织在一起
  • include 语句允许组合多个配置文件以提升可维护性
  • 通过 # 符号添加注释,提高可读性
  • 通过 $ 符号使用变量
  • 部分指令的参数支持正则表达式,例如常用的 location 指令

内置变量

变量 含义
$host 请求的host
$request_method 请求类型:GET,POST等
$remote_addr 客户端IP地址
$remote_port 客户端端口
$args 请求中的参数
$content_length Header中的Content-length
$http_user_agent 客户端agent
$http_cookie 客户端cookie
$server_protocol 请求的协议,如HTTP/1.1
$server_name 服务器名称
$server_addr 服务器地址
$server_port 服务器端口

设置变量

设置变量$temp,返回字符串 “hello world”

server {
    listen       80;
    server_name  test.com;

    location / {
        set $temp hello;
        return "$temp world";
    }
}

日志配置

# 错误日志放在 main 全局区块中对全局生效
#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

http {
    # 设置日志格式
    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  logs/access.log  main;
    # 关闭日志
    access_log off;
}

Nginx常用功能

HTTP服务器

这样如果访问 http://ip 就会默认访问到 /usr/local/app 目录下面的 index.html,如果一个网站只是静态页面的话,那么就可以通过这种方式来实现部署,比如一个静态官网。

server {
    listen       80;
    server_name  localhost;

    location / {
        root   /usr/local/app;
        index  index.html;
    }
}

动静分离

就是把动态和静态的请求分开。方式主要有两种:

  • 一种是纯粹把静态文件独立成单独的域名,放在独立的服务器上,也是目前主流推崇的方案
  • 一种方法就是动态跟静态文件混合在一起发布, 通过 nginx 配置来分开
# 所有静态请求都由nginx处理,存放目录为 html
location ~ \.(gif|jpg|jpeg|png|bmp|swf|css|js)$ {
    root    /usr/local/resource;
    expires     10h; # 设置过期时间为10小时
}

# 所有动态请求都转发给 tomcat 处理
location ~ \.(jsp|do)$ {
    proxy_pass  127.0.0.1:8888;
}

反向代理

server {
    listen    8080;
    server_name  localhost;
    
    # 用户访问 ip:8080/test 下的所有路径代理到 github
    location /test {
        proxy_pass   https://github.com;
    }

    # 所有 /api 下的接口访问都代理到本地的 8888 端口
    location /api {
        proxy_pass   http://127.0.0.1:8888;
    }
}

正向代理

正向代理的对象是客户端,服务器端看不到真正的客户端。

resolver 8.8.8.8 # 谷歌的域名解析地址
server {
    resolver_timeout 5s; // 设超时时间
    location / {
        # 当客户端请求我的时候,我会把请求转发给它
        # $host 要访问的主机名 $request_uri 请求路径
        proxy_pass http://$host$request_uri;
    }
}

访问控制

server {
    location ~ ^/index.html {
        # index.html 页面访问控制
        # 禁止 192.168.1.1 和 192.168.1.2 两个 ip 访问,其它全部允许
        # 从上到下的顺序,匹配到了便跳出
        deny 192.168.1.1;
        deny 192.168.1.2;
        allow all;
    }
}

负载均衡

轮询策略(默认)

每个请求按时间顺序逐一分配到不同的后端服务器,如果有后端服务器挂掉,能自动剔除。但是如果其中某一台服务器压力太大,出现延迟,会影响所有分配在这台服务器下的用户。

http {
    upstream test.com {
        server 192.168.1.12:8887;
        server 192.168.1.13:8888;
    }
    server {
        location /api {
            proxy_pass  http://test.com;
        }
    }
}

根据服务器权重

http {
    upstream test.com {
        server 192.168.1.12:8887 weight=9;
        server 192.168.1.13:8888 weight=1;
    }
    server {
        location /api {
            proxy_pass  http://test.com;
        }
    }
}

客户端 ip 绑定(ip_hash)

来自同一个 ip 的请求永远只分配一台服务器,有效解决了动态网页存在的 session 共享问题。

http {
    upstream test.com {
        ip_hash;
        server 192.168.1.12:8887;
        server 192.168.1.13:8888;
    }
    server {
        location /api {
            proxy_pass  http://test.com;
        }
    }
}

最小连接数策略

将请求优先分配给压力较小的服务器,它可以平衡每个队列的长度,并避免向压力大的服务器添加更多的请求。

http {
    upstream test.com {
     least_conn;
        server 192.168.1.12:8887;
        server 192.168.1.13:8888;
    }
    server {
        location /api {
            proxy_pass  http://test.com;
        }
    }
}

最快响应时间策略(依赖于第三方 NGINX Plus)

http {
    upstream test.com {
     fair;
        server 192.168.1.12:8887;
        server 192.168.1.13:8888;
    }
    server {
        location /api {
            proxy_pass  http://test.com;
        }
    }
}

按访问 url 的 hash 结果(第三方)

按访问 url 的 hash 结果来分配请求,使每个 url 定向到同一个后端服务器,后端服务器为缓存时比较有效。

http {
    upstream test.com {
        hash $request_uri;
        hash_method crc32;
        server 192.168.1.12:8887;
        server 192.168.1.13:8888;
    }
    server {
        location /api {
            proxy_pass  http://test.com;
        }
    }
}

gzip 压缩

开启 gzip 压缩可以大幅减少 http 传输过程中文件的大小,可以极大的提高网站的访问速度,基本是必不可少的优化操作

# 开启gzip 压缩
gzip  on;
# gzip_types
# gzip_static on;
# gzip_proxied expired no-cache no-store private auth;
# gzip_buffers 16 8k;
gzip_min_length 1k;
gzip_comp_level 4;
gzip_http_version 1.0;
gzip_vary off;
gzip_disable "MSIE [1-6]\.";

请求限制

通过 limit_req_zone 限制请求数

http{
    # 设置共享内存空间
    # 如果共享内存空间被耗尽,服务器将会对后续所有的请求返回 503 (Service Temporarily Unavailable) 错误。
    limit_conn_zone $binary_remote_addrzone=limit:10m; 
    server{
     location /{
             # 同一用户地址同一时间只允许有5个连接
             limit_conn addr 5; 
        }
    }
}

通过 limit_conn_zone 限制并发连接数

# 限制平均每秒不超过一个请求,同时允许超过频率限制的请求数不多于5个。
limit_req_zone $binary_remote_addr zone=creq:10 mrate=10r/s;
server{
    location /{
        limit_req zone=creq burst=5;
    }
}

请求过滤

根据请求类型过滤

# 非指定请求全返回 403
if ( $request_method !~ ^(GET|POST|HEAD)$ ) {
    return 403;
}

根据状态码过滤

error_page 502 503 /50x.html;
location = /50x.html {
    root /usr/share/nginx/html;
}

根据 URL 名称过滤

if ($host = zy.com' ) {
     #其中 $1是取自regex部分()里的内容,匹配成功后跳转到的URL。
     rewrite ^/(.*)$  http://www.zy.com/$1  permanent;
}

location /test {
    // /test 全部重定向到首页
    rewrite  ^(.*)$ /index.html  redirect;
}

禁止指定 user_agent

# http_user_agent 为浏览器标识
# 禁止 user_agent 为baidu、360和sohu,~*表示不区分大小写匹配
if ($http_user_agent ~* 'baidu|360|sohu') {
    return 404;
}

# 禁止 Scrapy 等工具的抓取
if ($http_user_agent ~* (Scrapy|Curl|HttpClient)) {
    return 403;

代理缓存

nginx 可以对访问过的内容在 nginx 服务器本地建立副本,这样在一段时间内再次访问该数据,就不需要通过 nginx 服务器再次向后端服务器发出请求,减小数据传输延迟,提高访问速度:

proxy_cache_path usr/local/cache levels=1:2 keys_zone=my_cache:10m;

server {
    listen       80;
    server_name  test.com;

    location / {
        proxy_cache my_cache;
        proxy_pass http://127.0.0.1:8888;
        proxy_set_header Host $host;
    }
}

上面的配置表示:nginx 提供一块 10 M 的内存用于缓存,名字为 my_cache, levels 等级为 1:2,缓存存放的路径为 usr/local/cache

路径分离与路径转发

  1. test1.doc.test.club 自动指向 /usr/local/html/doc/test1 服务器地址;
  2. test2.doc.test.club 自动指向 /usr/local/html/doc/test2 服务器地址;
server {
    listen       80;
    server_name  ~^([\w-]+)\.doc\.test\.club$;
    root /usr/local/html/doc/$1;
}
  1. test1.serv.test.club/api?name=a 自动转发到 127.0.0.1:8080/test1/api?name=a
  2. test2.serv.test.club/api?name=a 自动转发到 127.0.0.1:8080/test2/api?name=a
server {
    listen       80;
    server_name ~^([\w-]+)\.serv\.test\.club$;

    location / {
        proxy_set_header        X-Real-IP $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header        Host $http_host;
        proxy_set_header        X-NginX-Proxy true;
        proxy_pass              http://127.0.0.1:8080/$1$request_uri;
    }
}

单页面项目 history 路由配置

server {
    listen       80;
    server_name  fe.sherlocked93.club;

    location / {
        root       /usr/local/app/dist;  # vue 打包后的文件夹
        index      index.html index.htm;
        try_files  $uri $uri/ /index.html @rewrites; # 默认目录下的 index.html,如果都不存在则重定向

        expires -1;                          # 首页一般没有强制缓存
        add_header Cache-Control no-cache;
    }

    location @rewrites { // 重定向设置
        rewrite ^(.+)$ /index.html break;
    }
}

配置 HTTPS

证书申请

# 安装 certbot
wget https://dl.eff.org/certbot-auto
chmod a+x certbot-auto

# 申请证书(注意:需要把要申请证书的域名先解析到这台服务器上,才能申请):
sudo ./certbot-auto certonly --standalone --email admin@abc.com -d test.com -d www.test.com

执行上面指令,按提示操作。

Certbot 会启动一个临时服务器来完成验证(会占用 80 端口或 443 端口,因此需要暂时关闭 Web 服务器),然后 Certbot 会把证书以文件的形式保存,包括完整的证书链文件和私钥文件。

文件保存在 /etc/letsencrypt/live/ 下面的域名目录下。

配置HTTPS

server{
    listen 443 ssl http2; // 这里还启用了 http/2.0

    ssl_certificate /etc/letsencrypt/live/test.com/fullchain.pem; # 证书文件地址
    ssl_certificate_key /etc/letsencrypt/live/test.com/privkey.pem; # 私钥文件地址

    server_name test.com www.test.com; # 证书绑定的域名
}

server {
    listen      80;
    server_name test.com www.test.com;

    # 单域名重定向
    if ($host = 'www.sherlocked93.club'){
        return 301 https://www.sherlocked93.club$request_uri;
    }

    # 全局非 https 协议时重定向
    if ($scheme != 'https') {
        return 301 https://$server_name$request_uri;
    }

    # 或者全部重定向
    return 301 https://$server_name$request_uri;
}

Nginx模块

模块分类

  • 核心模块:nginx 最基本最核心的服务,如进程管理、权限控制、日志记录;
  • 标准 HTTP 模块:nginx 服务器的标准 HTTP 功能;
  • 可选 HTTP 模块:处理特殊的 HTTP 请求
  • 邮件服务模块:邮件服务
  • 第三方模块:作为扩展,完成特殊功能

核心模块

  • ngx_core
  • ngx_errlog
  • ngx_conf
  • ngx_events
  • ngx_event_core
  • ngx_epll
  • ngx_regex

标准HTTP模块

  • ngx_http
  • ngx_http_core #配置端口,URI 分析,服务器相应错误处理,别名控制 (alias) 等
  • ngx_http_log #自定义 access 日志
  • ngx_http_upstream #定义一组服务器,可以接受来自 proxy, Fastcgi,Memcache 的重定向;主要用作负载均衡
  • ngx_http_static
  • ngx_http_autoindex #自动生成目录列表
  • ngx_http_index #处理以/结尾的请求,如果没有找到 index 页,则看是否开启了 random_index;如开启,则用之,否则用 autoindex
  • ngx_http_auth_basic #基于 http 的身份认证 (auth_basic)
  • ngx_http_access #基于 IP 地址的访问控制 (deny,allow)
  • ngx_http_limit_conn #限制来自客户端的连接的响应和处理速率
  • ngx_http_limit_req #限制来自客户端的请求的响应和处理速率
  • ngx_http_geo
  • ngx_http_map #创建任意的键值对变量
  • ngx_http_split_clients
  • ngx_http_referer #过滤 HTTP 头中 Referer 为空的对象
  • ngx_http_rewrite #通过正则表达式重定向请求
  • ngx_http_proxy
  • ngx_http_fastcgi #支持 fastcgi
  • ngx_http_uwsgi
  • ngx_http_scgi
  • ngx_http_memcached
  • ngx_http_empty_gif #从内存创建一个 1×1 的透明 gif 图片,可以快速调用
  • ngx_http_browser #解析 http 请求头部的 User-Agent 值
  • ngx_http_charset #指定网页编码
  • ngx_http_upstream_ip_hash
  • ngx_http_upstream_least_conn
  • ngx_http_upstream_keepalive
  • ngx_http_write_filter
  • ngx_http_header_filter
  • ngx_http_chunked_filter
  • ngx_http_range_header
  • ngx_http_gzip_filter
  • ngx_http_postpone_filter
  • ngx_http_ssi_filter
  • ngx_http_charset_filter
  • ngx_http_userid_filter
  • ngx_http_headers_filter #设置 http 响应头
  • ngx_http_copy_filter
  • ngx_http_range_body_filter
  • ngx_http_not_modified_filter

可选HTTP模块

  • ngx_http_addition:在响应请求的页面开始或者结尾添加文本信息
  • ngx_http_degradation:在低内存的情况下允许服务器返回 444 或者 204 错误
  • ngx_http_perl
  • ngx_http_flv:支持将 Flash 多媒体信息按照流文件传输,可以根据客户端指定的开始位置返回 Flash
  • ngx_http_geoip:支持解析基于 GeoIP 数据库的客户端请求
  • ngx_google_perftools
  • ngx_http_gzip:gzip 压缩请求的响应
  • ngx_http_gzip_static:搜索并使用预压缩的以.gz 为后缀的文件代替一般文件响应客户端请求
  • ngx_http_image_filter:支持改变 png,jpeg,gif 图片的尺寸和旋转方向
  • ngx_http_mp4:支持.mp4,.m4v,.m4a 等多媒体信息按照流文件传输,常与 ngx_http_flv 一起使用
  • ngx_http_random_index:当收到 / 结尾的请求时,在指定目录下随机选择一个文件作为 index
  • ngx_http_secure_link:支持对请求链接的有效性检查
  • ngx_http_ssl:支持 https
  • ngx_http_stub_status
  • ngx_http_sub_module:使用指定的字符串替换响应中的信息
  • ngx_http_dav:支持 HTTP 和 WebDAV 协议中的 PUT/DELETE/MKCOL/COPY/MOVE 方法
  • ngx_http_xslt:将 XML 响应信息使用 XSLT 进行转换

邮件服务模块

  • ngx_mail_core
  • ngx_mail_pop3
  • ngx_mail_imap
  • ngx_mail_smtp
  • ngx_mail_auth_http
  • ngx_mail_proxy
  • ngx_mail_ssl

第三方模块

  • echo-nginx-module:支持在 nginx 配置文件中使用 echo/sleep/time/exec 等类 Shell 命令
  • memc-nginx-module
  • rds-json-nginx-module:使 nginx 支持 json 数据的处理
  • lua-nginx-module

直播模块

HTTPS server示例

server {
    listen       443 ssl;
    server_name  devcloud;

    ssl_certificate      /usr/local/ssl/mycert.pem;
    ssl_certificate_key  /usr/local/ssl/mykey.key;

    ssl_session_cache    shared:SSL:1m;
    ssl_session_timeout  5m;

    ssl_ciphers  HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers  on;

    location / {
        root   html;
        index  index.html index.htm;
    }
  
    location /jupyter {
        proxy_pass http://127.0.0.1:8888;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_redirect off;
    }

    location /prometheus/ {
      	proxy_pass http://127.0.0.1:9090/;
    }

    location /grafana/ {
      	proxy_pass http://127.0.0.1:3000/;
    }

    location /kibana/ {
      	proxy_pass http://127.0.0.1:5601/kibana/;
    }
}

References