Fork me on GitHub

Nginx从入门到实践

结合实践、收集各种场景、常见问题,讲解Nginx中最实用的Webserver场景,提供一套整体的搭建配置方式

Nginx中间件,不局限于业务逻辑,有效独立于后台开发框架(不论后端是Java开发、PHP开发、或者其他语言框架)都能做到平台通用

不仅重实践、也会结合原理(如:Http协议、操作系统),让你理解背后的原理更有利于你解决实际问题(如:bug解决、二次开发等)

起始

环境调试确认

1
2
yum -y install gcc gcc-c++ autoconf pcre pcre-devel make automake
yum -y install wget httpd-tools vim
1
cd /opt;mkdir app download logs work backup

关闭iptables

1
2
3
4
5
6
7
8
9
10
11
# 查看
iptables -L

# 关闭
iptables -F

# 查看
iptables -t nat -L

# 关闭
iptables -t nat -F

中间件架构

Nginx优势

  • 多路IO复用
  • 使用Epoll模型
  • 轻量级
  • CPU亲和
  • sendfile

安装

http://nginx.org/en/linux_packages.html#stable

1
vim /etc/yum.repos.d/nginx.repo

内容如下S

1
2
3
4
5
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/7/$basearch/
gpgcheck=0
enabled=1

1
yum install nginx
1
2
3
4
5
# 查看版本
nginx -v

# 查看安装编译参数
nginx -V

基本参数使用

安装目录

rpm -ql nginx

路径类型作用
/etc/logrotate.d/nginx配置文件nginx日志轮转,用于logrotate服务的日志切割
/etc/nginx
/etc/nginx/nginx.confd
/etc/nginx/conf.d
/etc/nginx/conf.d/default.conf
目录,配置文件nginx主配置文件
/etc/nginx/scgi_params
/etc/nginx/uwsgi_params
/etc/nginx/fastcgi_params
配置文件cgi配置相关,fastcgi配置
/etc/nginx/koi-utf
/etc/nginx/koi-win
/etc/nginx/win-utf
配置文件编码转换映射转化文件
/etc/nginx/mime.types配置文件设置http协议的Content-Type与扩展名对应关系
/usr/lib/systemd/system/nginx-debug.service
/usr/lib/systemd/system/nginx.service
/etc/sysconfig/nginx
/etc/sysconfig/nginx-debug
配置文件用于配置出系统守护进程管理器管理方式
/usr/lib64/nginx/modules
/etc/nginx/modules
目录nginx模块目录
/usr/sbin/nginx
/usr/sbin/nginx-debug
命令nginx服务的启动管理的终端命令
/usr/share/doc/nginx-1.14.2
/usr/share/doc/nginx-1.14.2/COPYRIGHT
/usr/share/man/man8/nginx.8.gz
文件, 目录nginx的手册和帮助文件
/var/cache/nginx目录nginx的缓存目录
/var/log/nginx目录nginx的日志目录

安装编译参数

nginx -V

编译选项作用
–prefix=/etc/nginx
–sbin-path=/usr/sbin/nginx
–modules-path=/usr/lib64/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
执行对应模块时,nginx所保留的临时性文件
–user=nginx
–group=nginx
设定nginx进程启动的用户和组用户
–with-cc-opt=parameters设置额外的参数将被添加到CFLAGS变量
–with-ld-opt=parameters设置附加的参数,链接系统库

默认配置语法

---
user设置nginx服务的系统使用用户
worker_processes工作进程数
error_lognginx的错误日志
pidnginx服务启动时候pid
eventsworker_connections
use
每个进程允许最大连接数
工作进程数

log

http://nginx.org/en/docs/http/ngx_http_log_module.html
错误日志,格式如下

1
2018/12/11 15:51:32 [error] 5809#5809: *37 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 120.194.101.83, server: _, request: "GET /favicon.ico HTTP/1.1", host: "120.79.65.88", referrer: "http://120.79.65.88/"

error_log /var/log/nginx/error.log warn;
日志存放位置/var/log/nginx/error.log
级别warn

access_log 记录了哪些用户,哪些页面以及用户浏览器、ip和其他的访问信息,形式如下

1
222.88.236.165 - - [11/Dec/2018:16:38:42 +0800] "GET /static/js/login.js HTTP/1.1" 200 14459 "http://mxonline.iceflower.xyz:8081/login/?next=/course/info/6/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36" "-"

格式设置

1
2
3
4
5
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;

具体的设置可以在官网中找到,可进行自定制

模块

http://nginx.org/ru/docs/http/ngx_http_stub_status_module.html

sub_status

--with-http_stub_status_module nginx的客户端状态

配置nginx文件,写在server中

1
2
3
location = /mystatus {
stub_status;
}

重启nginx,浏览器访问IP+/mystatus,格式如下

1
2
3
4
Active connections: 1 
server accepts handled requests
43 43 79
Reading: 0 Writing: 1 Waiting: 0

Active connections
当前活动客户端连接数,包括Waiting连接数.
accepts
已接受的客户端连接总数.
handled
处理的连接总数.通常,参数值与accepts 除非已达到某些资源限制(例如, worker_connections限制)相同.
requests
客户端请求的总数.
Reading
nginx正在读取请求标头的当前连接数.
Writing
nginx将响应写回客户端的当前连接数.
Waiting
当前等待请求的空闲客户端连接数.

random_index

http://nginx.org/en/docs/http/ngx_http_random_index_module.html
--with-http_random_index_module 目录中选择一个随机主页

1
2
3
4
location / {
root /opt/app/code/html;
random_index on;
}

root 后面是主页面存放位置
不会选择隐藏文件作为主页面

sub_module

http://nginx.org/en/docs/http/ngx_http_sub_module.html
--with-http_sub_module HTTP内容替换

1
2
3
4
5
location / {
sub_filter '<a href="http://127.0.0.1:8080/' '<a href="https://$host/';
sub_filter '<img src="http://127.0.0.1:8080/' '<img src="https://$host/';
sub_filter_once on;
}

敏感词,流媒体替换

请求限制

limit_conn_module

limit_conn_module 连接频率限制
http://nginx.org/en/docs/http/ngx_http_limit_conn_module.html

连接限制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
http {
limit_conn_zone $binary_remote_addr zone=addr:10m;

...

server {

...

location /api/ {
limit_conn addr 10;
limit_conn_status 503;
}
}
}

示例中定义key(zone=addr)为addr,分配内存大小为10m(zone=addr:10m)(如果限制域的存储空间耗尽了,对于后续所有请求,服务器都会返回503),同一个ip($binary_remote_addr)和服务器连接超过10个(limit_conn addr 10)将会被拦截并返回503(limit_conn_status 503)错误码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
limit_conn_zone
语法: limit_conn_zone key zone=name:size;(设置限制规则、区域名称及分配的内存大小)
可配置区域: http
key: 必选项;设置限制规则;取值可以是text文本、nginx变量或两者的组合;实例中使用的nginx变量$binary_remote_addr表示根据每个ip限制并发
name: 必选项; 自定义一个区域名称; 任意字符串
size: 分配内存的大小


limit_conn
语法: limit_conn zone number; (使用由limit_conn_zone定义的拦截规则, 并设置具体的限制连接数量)
可配置区域: http, server, location
zone: 必选项; 由limit_conn_zone(zone=name)定义的名称; 表示使用定义的哪个限制规则
number: 必选项; 正整数; 表示具体的限制连接数量


limit_conn_status
语法: limit_conn_status code;
默认值: 503
可配置区域: http, server, location
表示超出limit_req配置的请求数量后返回给客户端的错误码使用该指令
  

limit_conn_log_level
语法: limit_conn_log_level info | notice | warn | error;
默认值: limit_conn_log_level error;
可配置区域: http, server, location
当服务器拒绝处理由于速率超过或延迟请求处理而拒绝处理请求时,设置所需的日志记录级别.

limit_req_module

limit_req_module 请求频率限制
http://nginx.org/en/docs/http/ngx_http_limit_req_module.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
http {
limit_req_zone $binary_remote_addr zone=req_perip:50m rate=10r/s;

...

server {

...

location /api/ {
limit_req zone=req_perip burst=50 nodelay;
limit_req_status 503;
}
}
}

示例中定义的区域名称为req_perip(zone=req_perip),分配内存大小为50m(如果限制域的存储空间耗尽了,对于后续所有请求,服务器都会返回503),同一个ip($binary_remote_addr)平均处理的请求频率不能超过每秒10次(rate=10r/s); 如果超过每秒10次但超过的请求数量小于等于50(burst=50)时,会延迟请求.如果超过每秒的请求数超过50,则立即返回503(limit_req_status 503)给客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
limit_req_zone
语法: limit_req_zone key zone=name:size rate=rate;(可理解为该指令用来定义限制请求频率)
可配置区域: http
key: 必选项;取值范围: 1,text(文本); 2,nginx变量;3,text和nginx变量的组合; name: 必选项;自定义字符串
size: 必选项;分配内存大小,用来保存键值的状态参数
rate: 必选项;每秒可请求的频率(r/s), 或每分钟可请求的频率(r/m)
  

limit_req
语法: limit_req zone=name [burst=number] [nodelay]; (可理解为使用定义的限制请求频率,一定是先定义后使用!也就是一定要有limit_req_zone指令的配置后才能使用该配置)
可配置区域: http, server, location
name: 必选项;自定义字符串, 名字必须与limit_req_zone中zone=name这个名字一致
number: 必选项;正整数数字, 平均每秒允许不超过limit_req_zone指令中rate规定的请求数,并且不会超过该值所指定数量的请求,可延迟请求的数量
nodelay: 可选配置,表示请求频率超过rate规定值后又超过burst规定值后立即返回客户端503(可设置返回code)
  

limit_req_status
语法: limit_req_status code;
默认值: 503
可配置区域: http, server, location
表示超出limit_req配置的请求数量后返回给客户端的错误码使用该指令
  

limit_req_log_level
语法: limit_req_log_level info | notice | warn | error;
默认值: limit_req_log_level error;
可配置区域: http, server, location
当服务器拒绝处理由于速率超过或延迟请求处理而拒绝处理请求时,设置所需的日志记录级别.

$binary_remote_addr是$remote_addr(客户端IP)的二进制格式,固定占用4个字节.而$remote_addr按照字符串存储,占用7-15个字节.用$binary_remote_addr可以节省空间.

访问控制

http_access_module

http_access_module 基于IP的访问控制
http://nginx.org/en/docs/http/ngx_http_access_module.html

1
2
3
4
5
6
7
location / {
deny 192.168.1.1; # 拒绝访问
allow 192.168.1.0/24; # 允许访问
allow 10.1.1.0/16;
allow 2001:0db8::/32; # IPV6
deny all;
}

局限性

IP1 客户端
IP2 代理服务器
IP3 服务器
基于客户端的IP,但是对于Nginx来说,它不会管你哪个是真正的客户端,如果我们的访问不是客户端与服务端直接连接,而是通过了一层代理,比如它的代理可以负载均衡、CDN的这种代理实现,也就是我们的访问不是客户端直接访问的服务端,而是通过其他的中间件访问服务端,这时候会出现一个问题,因为Nginx的access_module它是基于remote_addr这个变量来识别客户端的IP的,那么如果一个ip通过中间件访问服务端,那么Nginx认为访问的ip就是中间件的IP,那么我们在基于IP做限制的时候,那么其实是没有作用的.所以这样的话,准确性是不高的,所以就是利用nginx的access_module有局限性.

解决办法

  • 使用http_x_forwarded_for来解决这个问题

    但是http_x_forwarded_for进行访问控制会存在问题,因为是一个协议要求的,并不是所有的cdn和代理厂商它会按照要求来做,甚至x_forwarded_for存在被修改的可能,因为只是一个头信息,所以最终还是不真实.

    http_x_forwardded_for也是Nginx的http头变量的一个常用的变量,它和remote_addr是有区别的.不同的是,x_forwarded_for是http协议中规定头中要携带的,所以在客户端访问中间件,再访问服务端的时候,那么服务端通过Nginx会记录真实IP和中间件的IP.
    格式:http_x_forwarded_for = 客户端ip,第一台代理ip,第二台代理ip,第N台代理ip….,所以http_x_forwarded_for是由一连串以逗号分隔的ip组成的.

  • 结合geo模块
    http://nginx.org/ru/docs/http/ngx_http_geo_module.html

  • 通过HTTP自定义变量传递

http_auth_basic_module

http_auth_basic_module 基于用户的信任登录
http://nginx.org/en/docs/http/ngx_http_auth_basic_module.html


1
2
3
4
location / {
auth_basic "输入密码";
auth_basic_user_file user_passwd;
}

htpasswd安装
yum install httpd-tools -y

首次创建并加入
htpasswd -c ./user_passwd admin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@GYJ nginx]# cd /etc/nginx/
[root@GYJ nginx]# ls
conf.d fastcgi_params koi-win modules nginx.conf.rpmnew uwsgi_params
default.d koi-utf mime.types nginx.conf scgi_params win-utf
[root@GYJ nginx]# htpasswd -c ./user_passwd admin
New password:
Re-type new password:
Adding password for user admin
[root@GYJ nginx]# ls
conf.d fastcgi_params koi-win modules nginx.conf.rpmnew user_passwd win-utf
default.d koi-utf mime.types nginx.conf scgi_params uwsgi_params
[root@GYJ nginx]# vim user_passwd
[root@GYJ nginx]# cat user_passwd
admin:$apr1$I04BhX5R$9JexKOl2aYCe.Tww67XMu0
[root@GYJ nginx]#

http_auth_basic_module局限性
用户信息依赖文件方式,管理操作机械,效率不高

解决方法:
Nginx结合LUA实现高效验证
Nginx结合LDAP,利用nginx_auth_ldap模块结合LDAP,利用nginx_auth_ldap模块

第二章

静态资源web服务

静态资源类型

CDN

CDN的基本原理是广泛采用各种缓存服务器,将这些缓存服务器分布到用户访问相对集中的地区或网络中,在用户访问网站时,利用全局负载技术将用户的访问指向距离最近的工作正常的缓存服务器上,由缓存服务器直接响应用户请求.

配置语法




tcp_nopush 告诉nginx在一个数据包里发送所有头文件,而不一个接一个的发送.
tcp_nodelay 告诉nginx不要缓存数据,而是一段一段的发送–当需要及时发送数据时,就应该给应用设置这个属性,这样发送一小块数据信息时就不能立即得到返回值.



开启nginx gzip压缩后,网页、css、js等静态资源的大小会大大的减少,从而可以节约大量的带宽,提高传输效率,给用户快的体验,但会消耗cpu资源.


http_gzip_static_module 预读gzip功能

nginx配置,在相应的目录下添加相应的文件,取消注释进行检测查看

浏览器缓存

HTTP协议定义的缓存机制(如: Expires; Cache-control等)

无缓存请求

有缓存请求

校验机制

校验顺序

Expires

跨站访问


防盗链

http://nginx.org/en/docs/http/ngx_http_referer_module.html


1
2
3
4
valid_referers 代表可信referer,此处可信referer的设置将会使变量$invalid_referer的值为「0」,反之$invalid_referer的值为「1」,则执行IF条件名并返回相关限制结果.
none是指当referer为空的时候,比如在链接器中直接打开一个图片链接,若要禁止用户直接访问,则必须省掉none.
blocked 是指经过某些代理或firewall过滤后的referer,比如省略了url前缀等,同样地,若要禁止用户直接访问,则必须省掉blocked.
server_names 是nginx配置文件中的server name项,代指网站域名.

代理服务

http://nginx.org/en/docs/http/ngx_http_proxy_module.html


正向代理和反向代理的区别在于对象的不同
正向代理的对象是客户端,反向代理的对象是服务器
正向代理: A向C借钱,由于一些情况不能直接向C借钱,于是A想了一个办法,他让B去向C借钱,这样B就代替A向C借钱,A就得到了C的钱,C并不知道A的存在,B就充当了A的代理人的角色.
反向代理: A向B借钱,B没有拿自己的钱,而是悄悄地向C借钱,拿到钱之后再交给A,A以为是B的钱,他并不知道C的存在. (个人搭建的豆瓣api)


豆瓣api代理

1
2
3
4
5
6
7
8
9
10
11
12
location  /v2 {
proxy_store off;
proxy_redirect off;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Referer 'no-referrer-when-downgrade';
proxy_set_header User-Agent 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36';
proxy_connect_timeout 600;
proxy_read_timeout 600;
proxy_send_timeout 600;
proxy_pass https://api.douban.com/v2/;
}

负载均衡服务

负载均衡 建立在现有网络结构之上,它提供了一种廉价有效透明的方法扩展网络设备和服务器的带宽、增加吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性.
负载均衡,英文名称为Load Balance,其意思就是分摊到多个操作单元上进行执行,例如Web服务器、FTP服务器、企业关键应用服务器和其它关键任务服务器等,从而共同完成工作任务.

四层负载均衡: 即在OSI第4层(TCP层)工作.此种Load Balance不理解应用协议(如HTTP/FTP/MySQL等等).例子:LVS,F5.
七层负载均衡: OSI的最高层,应用层.此时,该Load Balancer能理解应用协议.例子: haproxy,MySQL Proxy.

http://nginx.org/en/docs/http/ngx_http_upstream_module.html

Syntax: upstream name { … }
Default: —
Context: http

1
2
3
4
5
6
7
8
9
10
11
12
13
14
upstream backend {
server backend1.example.com weight=5;
server backend2.example.com:8080;
server unix:/tmp/backend3;

server backup1.example.com:8080 backup;
server backup2.example.com:8080 backup;
}

server {
location / {
proxy_pass http://backend;
}
}

调度算法

缓存服务

客户端缓存, nginx缓存, 服务器缓存

http://nginx.org/en/docs/http/ngx_http_proxy_module.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Syntax: proxy_cache_path path [levels=levels] [use_temp_path=on|off] keys_zone=name:size [inactive=time] [max_size=size] [manager_files=number] [manager_sleep=time] [manager_threshold=time] [loader_files=number] [loader_sleep=time] [loader_threshold=time] [purger=on|off] [purger_files=number] [purger_sleep=time] [purger_threshold=time];
Default: —
Context: http



Syntax: proxy_cache zone | off;
Default:
proxy_cache off;
Context: http, server, location


# 设置不同响应代码的缓存时间
Syntax: proxy_cache_valid [code ...] time;
Default: —
Context: http, server, location


# 缓存维度(键)
Syntax: proxy_cache_key string;
Default: proxy_cache_key $scheme$proxy_host$request_uri;
Context: http, server, location

如何清理指定缓存

  • rm -rf 缓存目录
  • 第三方扩展模块ngx_cache_purge

如何让部分页面不缓存

Syntax: proxy_no_cache string …;
Default: —
Context: http, server, location

分片请求


优势:每个子请求收到的数据都会形成独立文件,一个请求中断了,其他请求不受影响,原本情况请求中断,再次请求文件将从头开始,而开启分片请求,就接下去获取未请求的小文件

劣势:当文件很大或者slice很小时,可能会导致文件描述符耗尽等情况

1
2
3
4
5
6
Syntax: slice size;
Default:
slice 0;
Context: http, server, location

erver, location

第三章

动静分离

动静分离是将网站静态资源(JavaScript,CSS,img等文件)与后台应用分开部署,提高用户访问静态代码的速度,降低对后台应用访问.

动静分离的一种做法是将静态资源部署在nginx上,后台项目部署到应用服务器上,根据一定规则静态资源的请求全部请求nginx服务器,达到动静分离的目标.

rewrite规则

Rewrite规则常见正则表达式

Rewrite 主要的功能就是实现URL的重写,Nginx的rewrite功能是使用nginx提供的全局变量或自己设置的变量,结合正则表达式和标志位实现url重写以及重定向.

Syntax: rewrite regex replacement [flag];
Default: —
Context: server, location, if
flag:

  • last:停止处理当前的 ngx_http_rewrite_module 指令集合,开始搜索能够匹配修改过的 URI 的新的 location
  • break:跟 break 指令一样,停止处理当前的 ngx_http_rewrite_module 指令集合
  • redirect:返回使用 302 代码的临时重定向,在替换字符串不以“http//”,“https//”或“$scheme”开头时使用
  • permanent:返回使用 301 代码的永久重定向


浏览器访问/break返回的时404, 访问/last/test都是success
访问/break时会从 root中查找/test/文件,访问/last时会新建一个/test的请求

高级模块

secure_link模块

http://nginx.org/en/docs/http/ngx_http_secure_link_module.html
ngx_http_secure_link_module模块能够检查请求链接的权限以及是否过期,多用于下载服务器防盗链

原理
1)加密串与过期时间作为url中的参数
2)nginx服务器接收到了过期时间,也使用过期时间、配置里密钥、文件uri生成加密串
3)计算的加密串与url传递的加密串进行对比

通过将请求中传递的校验和值与为请求计算的值进行比较来验证所请求链接的真实性.如果链接的生命周期有限且时间已过,则该链接将被视为过时.这些检查的状态在$secure_link变量中可用.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
location  / {
secure_link $arg_md5,$arg_expires; #设置两个变量
secure_link_md5 "xxxxxx$remote_addr$arg_expires"; #设置md5,当作口令

if ($secure_link = "") {
# 资源不存在或哈希比对失败
return 403;
}

if ($secure_link = "0") {
# 时间戳过期
return 410;
}
}


$secret = 'xxxxxx'; # 密钥
$path = '/web/nginx-1.4.2.tar.gz'; # 下载文件
# 下载到期时间,time是当前时间,300表示300秒,也就是说从现在到300秒之内文件不过期
$expire = time()+300;
# 用文件路径、密钥、过期时间生成加密串
$md5 = base64_encode(md5($secret . $path . $expire, true));
$md5 = strtr($md5, '+/', '-_');
$md5 = str_replace('=', '', $md5);
# 加密后的下载地址
echo '<a href=http://s1.down.ttlsa.com/web/nginx-1.4.2.tar.gz?md5='.$md5.'&expires='.$expire.'>nginx-1.4.2</a>';
echo '<br>http://s1.down.ttlsa.com/web/nginx-1.4.2.tar.gz?md5='.$md5.'&expires='.$expire;

打开下载地址,等到5分钟后在下载一次,你会发现点击下载会跳转到410页面

Geoip读取地域信息

http://nginx.org/en/docs/http/ngx_http_geoip_module.html

安装

1
yum install nginx-module-geoip

安装的文件在/etc/nginx/modules文件夹下

1
2
3
4
5
6
7
8
9
10
[root@GYJ modules]# pwd
/etc/nginx/modules
[root@GYJ modules]# ls
ngx_http_geoip_module-debug.so ngx_http_perl_module.so
ngx_http_geoip_module.so ngx_http_xslt_filter_module-debug.so
ngx_http_image_filter_module-debug.so ngx_http_xslt_filter_module.so
ngx_http_image_filter_module.so ngx_stream_geoip_module-debug.so
ngx_http_js_module-debug.so ngx_stream_geoip_module.so
ngx_http_js_module.so ngx_stream_js_module-debug.so
ngx_http_perl_module-debug.so ngx_stream_js_module.so

创建目录geoip

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mkdir -p /etc/nginx/geoip/
cd /etc/nginx/geoip/

# 下载IP库信息文件
wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz
wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz

# 解压
gunzip GeoIP.dat.gz
gunzip GeoLiteCity.dat.gz


[root@GYJ geoip]# ls
GeoIP.dat GeoLiteCity.dat

在nginx全局配置中加载模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@GYJ nginx]# cat nginx.conf
# For more information on configuration, see:
# * Official English Documentation: http://nginx.org/en/docs/
# * Official Russian Documentation: http://nginx.org/ru/docs/

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

# Load dynamic modules. See /usr/share/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;
load_module "modules/ngx_http_geoip_module.so";
load_module "modules/ngx_stream_geoip_module.so";

events {
worker_connections 1024;
}

配置访问接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@GYJ conf.d]# cat HtmlTest.conf 
geoip_country /etc/nginx/geoip/GeoIP.dat;
geoip_city /etc/nginx/geoip/GeoLiteCity.dat;

server {
listen 80;
server_name ip; # substitute your machine's IP address or FQDN

sendfile on;

location /ip {
if ($geoip_country_code != CN) {
return 403;
}
}

location /myip {
default_type text/plain;
return 200 "$remote_addr $geoip_country_name $geoip_country_code $geoip_city";
}

}

关闭代理,访问/myip即可看到自己的IP信息;使用国外代理,访问/ip会显示403

HTTPS服务

https://www.wosign.com/news/httpsjiami_20180817.htm

超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息,HTTP协议以明文方式发送内容,不提供任何方式的数据加密,如果攻击者截取了Web浏览器和网站服务器之间的传输报文,就可以直接读懂其中的信息,因此,HTTP协议不适合传输一些敏感信息,比如:信用卡号、密码等支付信息.
为了解决HTTP协议的这一缺陷,需要使用另一种协议:安全套接字层超文本传输协议HTTPS,为了数据传输的安全,HTTPS在HTTP的基础上加入了SSL协议,SSL依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密.

HTTPS就是使用SSL/TLS协议进行加密传输,让客户端拿到服务器的公钥,然后客户端随机生成一个对称加密的秘钥,使用公钥加密,传输给服务端,后续的所有信息都通过该对称秘钥进行加密解密,完成整个HTTPS的流程.

证书签名生成

http://nginx.org/en/docs/http/ngx_http_ssl_module.html

检测是否安装openssl

1
2
[root@GYJ conf.d]# openssl version
OpenSSL 1.0.2k-fips 26 Jan 2017

  1. 生成key密钥
  2. 生成证书签名请求文件(csr文件)
  3. 生成证书签名文件(CA文件)
    https://www.cnblogs.com/jingxiaoniu/p/6745254.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
cd /etc/nginx
mkdir ssl_key
cd ssl_key

# 创建服务器证书密钥文件 server.key:
openssl genrsa -des3 -out server.key 1024
输入密码,确认密码,自己随便定义,但是要记住,后面会用到.

# 创建服务器证书的申请文件 server.csr
openssl req -new -key server.key -out server.csr

# 输出内容为:
Enter pass phrase for root.key: ← 输入前面创建的密码
Country Name (2 letter code) [AU]:CN ← 国家代号,中国输入CN
State or Province Name (full name) [Some-State]:BeiJing ← 省的全名,拼音
Locality Name (eg, city) []:BeiJing ← 市的全名,拼音
Organization Name (eg, company) [Internet Widgits Pty Ltd]:MyCompany Corp. ← 公司英文名
Organizational Unit Name (eg, section) []: ← 可以不输入
Common Name (eg, YOUR name) []: ← 此时不输入
Email Address []:admin@mycompany.com ← 电子邮箱,可随意填
Please enter the following ‘extra’ attributes
to be sent with your certificate request
A challenge password []: ← 可以不输入
An optional company name []: ← 可以不输入

# 备份一份服务器密钥文件
cp server.key server.key.org

# 生成证书文件server.crt
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt

配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
server
{
listen 443;
server_name ip;
ssl on;
ssl_certificate /etc/nginx/ssl_key/server.crt;
ssl_certificate_key /etc/nginx/ssl_key/server.key;

index index.html index.htm;
location / {
root /opt/app/code;
}
}

重启nginx,浏览器访问,如果返回的是503,查看一下nginx的错误日志

1
2
3
4
5
6
7
8
tail -f /var/log/nginx/error.log

2018/12/14 21:03:55 [emerg] 30086#30086: SSL_CTX_use_PrivateKey_file("/etc/nginx/ssl_key/server.key") failed (SSL: error:0906406D:PEM routines:PEM_def_callback:problems getting password error:0906A068:PEM routines:PEM_do_header:bad password read error:140B0009:SSL routines:SSL_CTX_use_PrivateKey_file:PEM lib)


cd /etc/nginx/ssl_key
# 去除文件口令(重启nginx无需输入密码)
openssl rsa -in server.key.org -out server.key

重新启动即可,这个证书是自己颁发的,浏览器会提示不安全

苹果要求的openssl后台

先检查一下openssl版本

1
2
[root@GYJ ssl_key]# openssl version
OpenSSL 1.0.2k-fips 26 Jan 2017

如果版本不满足,可以升级一下版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/sh
cd /opt/download
wget https://www.openssl.org/source/openssl-1.0.2k.tar.gz
tar -zxvf openssl-1.0.2k.tar.gz
cd openssl-1.0.2k
./config --prefix=/usr/local/openssl
make && make install
mv /usr/bin/openssl /usr/bin/openssl.OFF
mv /usr/include/openssl /usr/include/openssl.OFF
ln -s /usr/local/openssl/bin/openssl /usr/bin/openssl
ln -s /usr/local/openssl/include/openssl /usr/include/openssl
echo "/usr/local/openssl/lib" >>/etc/ld.so.conf
ldconfig -v
openssl version -a

查看签名文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@GYJ ssl_key]# openssl x509 -noout -text -in server.crt 
Certificate:
Data:
Version: 1 (0x0)
Serial Number:
f2:da:9a:30:90:5f:4a:1b
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=CN, ST=HeNan, L=NanYang, O=Ice Corp
Validity
Not Before: Dec 14 12:59:08 2018 GMT
Not After : Dec 14 12:59:08 2019 GMT
Subject: C=CN, ST=HeNan, L=NanYang, O=Ice Corp
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (1024 bit)
Modulus:
...
Exponent: 65537 (0x10001)
Signature Algorithm: sha256WithRSAEncryption
...

可以看到,算法是sha256,公钥是1024 bit

生成满足苹果要求的crt文件

1
2
3
cd /etc/nginx/ssl_key

openssl req -x509 -days 365 -sha256 -nodes -newkey rsa:2048 -keyout server.key -out server_apple.crt

重新输入一些公司信息

修改一下配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server
{
listen 443;
server_name ip;
ssl on;
# ssl_certificate /etc/nginx/ssl_key/server.crt;
ssl_certificate /etc/nginx/ssl_key/server_apple.crt;
ssl_certificate_key /etc/nginx/ssl_key/server.key;

index index.html index.htm;
location / {
root /usr/share/nginx/html;
}
}

重新启动服务器即可

HTTPS服务优化

  1. 激活长连接keepalive
  2. 设置ssl session 缓存

Nginx与Lua的开发

实现灰度发布

LuaJIT

wget http://luajit.org/download/LuaJIT-2.0.2.tar.gz

tar -zxvf LuaJIT-2.0.2.tar.gz

cd LuaJIT-2.0.2

make install PREFIX=/usr/local/LuaJIT

/etc/profile 文件中加入环境变量
注意路径
export LUAJIT_LIB=/usr/local/LuaJIT/lib
export LUAJIT_INC=/usr/local/LuaJIT/include/luajit-2.0

ngx_devel_kit和lua-nginx-module

cd /root/download

wget https://github.com/simpl/ngx_devel_kit/archive/v0.3.0.tar.gz

wget https://github.com/openresty/lua-nginx-module/archive/v0.10.9rc7.tar.gz

分别解压

重新编译Nginx

cd /root/download

wget http://nginx.org/download/nginx-1.15.5.tar.gz
tar -zxvf nginx-1.15.5.tar.gz
cd nginx-1.15.5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
[root@GYJ nginx-1.15.5]# ./configure --prefix=/etc/nginx  \
--sbin-path=/usr/sbin/nginx \
--modules-path=/usr/lib64/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 \
--with-http_addition_module \
--with-http_auth_request_module \
--with-http_dav_module \
--with-http_flv_module \
--with-http_gunzip_module \
--with-http_gzip_static_module \
--with-http_mp4_module \
--with-http_random_index_module \
--with-http_realip_module \
--with-http_secure_link_module \
--with-http_slice_module \
--with-http_ssl_module \
--with-http_stub_status_module \
--with-http_sub_module \
--with-http_v2_module \
--with-mail \
--with-mail_ssl_module \
--with-stream \
--with-stream_realip_module \
--with-stream_ssl_module \
--with-stream_ssl_preread_module \
--with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong \
--param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC' \
--with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie' \
--add-module=/root/download/ngx_devel_kit-0.3.0 \
--add-module=/root/download/lua-nginx-module-0.10.9rc7

最后加入lua的模块,注意路径别错了

1
2
3
# 如若出现:
# error: the HTTP gzip module requires the zlib library.
# yum install zlib zlib-devel 一下即可

make -j 4 && make install

加载lua库,加入到ld.so.conf文件

1
2
3
4
echo "/usr/local/LuaJIT/lib" >> /etc/ld.so.conf

# 然后执行如下命令:
ldconfig

查看一下是否安装成功

简单配置

1
2
3
wget https://github.com/agentzh/lua-resty-memcached/archive/v0.11.tar.gz
tar -zxvf v0.11.tar.gz
cp -r lua-resty-memcached-0.11/lib/resty /usr/local/share/lua/5.1/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
server {
listen 80;
server_name localhost;

#charset koi8-r;
access_log /var/log/nginx/log/host.access.log main;

location /hello {
default_type 'text/plain';
content_by_lua 'ngx.say("hello, lua")';
}

location /myip {
default_type 'text/plain';
content_by_lua '
clientIP = ngx.req.get_headers()["x_forwarded_for"]
ngx.say("IP:",clientIP)
';
}

location / {
default_type "text/html";
content_by_lua_file /opt/app/lua/dep.lua;
#add_after_body "$http_x_forwarded_for";
}

location @server{
proxy_pass http://127.0.0.1:9090;
}

location @server_test{
proxy_pass http://127.0.0.1:8080;
}

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

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
clientIP = ngx.req.get_headers()["X-Real-IP"]
if clientIP == nil then
clientIP = ngx.req.get_headers()["x_forwarded_for"]
end
if clientIP == nil then
clientIP = ngx.var.remote_addr
end
local memcached = require "resty.memcached"
local memc, err = memcached:new()
if not memc then
ngx.say("failed to instantiate memc: ", err)
return
end
local ok, err = memc:connect("127.0.0.1", 11211)
if not ok then
ngx.say("failed to connect: ", err)
return
end
local res, flags, err = memc:get(clientIP)
ngx.say("value key: ",res,clientIP)
if err then
ngx.say("failed to get clientIP ", err)
return
end
if res == "1" then
ngx.exec("@server_test")
return
end
ngx.exec("@server")

第四章

Nginx常见问题和排错经验,实践应用场景中的方法处理Nginx安全,常见的应用层安全隐患,复杂访问控制,Nignx的sql防注入安全策略,Nginx的整体配置,搭建合理Nginx中间件架构配置步骤、策略Nginx性能优化:架构优化,操作系统优化、Nginx优化等…

常见问题

多个server_name中虚拟主机读取的优先级

当多个配置文件中server_name相同,端口也相同时,Nginx读取配置文件的时候是按照文件名顺序进行读取的,优先读取第一个文件名下的配置.
server1.conf,server2.conf,那优先加载的配置是server1.conf下面的配置.
启动时会抛出警告

多个location匹配的优先级

=:进行普通字符精确匹配,也就是完全匹配(高优先级)
^~:表示普通字符匹配,使用前缀匹配(高优先级)
~ ~*:表示执行一个正则匹配()

https://segmentfault.com/a/1190000002797606

try_files使用

按顺序检查文件是否存在

1
2
3
location / {
try_files $uri $uri/ /index.html
}

$uri 这个是nginx的一个变量,存放着用户访问的地址,
比如:http://www.xxx.com/index.html, 那么$uri就是/index.html

$uri/ 代表访问的是一个目录,比如:http://www.xxx.com/hello/test/,那么$uri/就是 /hello/test/

完整的解释就是:try_files去尝试到网站目录读取用户访问的文件,如果第一个变量存在,就直接返回;
不存在继续读取第二个变量,如果存在,直接返回;不存在直接跳转到第三个参数上.

比如用户访问这个网地址:http://www.xxx.com/test.html
try_files首先会判断他是文件,还是一个目录,结果发现他是文件,与第一个参数 $uri变量匹配.
然后去到网站目录下去查找test.html文件是否存在,如果存在直接读取返回.如果不存在直接跳转到第三个参数,而第三个参数是一个location

alias和root的使用区别

1
2
3
location /request_path/image/ {
root /loca_path/image/;
}

一个请求http://XXX.XXX.XXX.XXX/request_path/iamge/cat.png,则实际的资源路径为/local_path/image/request_path/image/cat.png,实际是root+请求路径.

1
2
3
location /request_path/image/ {
alias /loca_path/image/;
}

一个请求http://XXX.XXX.XXX.XXX/request_path/iamge/cat.png,则实际的资源路径为/loca_path/image/cat.png,没有请求的那个路径.

如何获取用户真实的ip信息

使用remote_addr
使用X-Forwarded-For
使用X-Real-IP(自定义)

常见错误码

413 Request Entity Too Large
修改用户上传文件限制 client_max_body_size

502 bad gateway
后端服务器无响应

504 Gateway Time-out
后端服务执行超时

403 访问被拒绝
404 资源未找到
400 请求参数错误

性能优化

ab压测工具

http://httpd.apache.org/docs/current/programs/ab.html

系统与Nginx性能优化

  • 网络
  • 系统
  • 服务
  • 程序
  • 数据库,底层服务

文件句柄设置

  • 文件句柄, Linux一切皆文件,文件句柄可以理解为就是一个索引
    • 文件句柄会随着我们进程的调用频繁增加
    • 系统默认对文件句柄有限制,不能让一个进程无限的调用
    • 需要限制每个进程和每个服务使用多大的文件句柄
    • 文件句柄是必须要调整的优化参数
  • 设置方式
    • 系统全局性修改
    • 用户局部性修改(vim /etc/security/limits.conf)
    • 进程局部性修改
      (vim /etc/nginx/nginx.conf)加入如下内容:
      worker_rlimit_nofile 35535;

CPU亲和配置

CPU亲和, 减少进程之间不断频繁迁移, 减少性能损耗

将Nginx worker进程绑到不同的核心上

1
2
3
4
5
6
7
8
9
10
11
//启动多少worker进程, 官方建议和cpu核心一致, 第一种绑定组合方式
#worker_processes 24;
#worker_cpu_affinity 000000000001 000000000010 000000000100 000000001000 000000010000 000000100000 000001000000 000010000000 000100000000 001000000000 010000000000 10000000000;

//第二种方式
#worker_processes 2;
#worker_cpu_affinity 101010101010 010101010101;

//最佳方式绑定方式
worker_processes auto;
worker_cpu_affinity auto;

查看nginx worker进程绑定至对应cpu

1
ps -eo pid,args,psr|grep [n]ginx

Nginx通用配置优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
user  nginx;
worker_processes auto; #使用的工作线程数
worker_put_affinity auto; # CPU亲和配置
# worker_cpu_affinity 01 10;

error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

# 调整至1w以上,负荷较高建议2-3w以上
worker_rlimit_nofile 35535; #文件句柄设置

events {
use epoll; # 使用epoll模型,提高并发性能
worker_connections 10240; # 每一个worker可以处理的连接数
}

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
#Charset
charset uft-8; #字符集配置

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 on;
#tcp_nopush on; # 静态服务器推荐打开
#tcp_nodelay on;
keepalive_timeout 65;

gzip on;
gzip_disable "MSIE [1-6]\."; # IE6以前不支持Gzip的压缩,关闭
gzip_http_version 1.1;

include /etc/nginx/conf.d/*.conf;
}

Nginx安全

常见的恶意行为

爬虫行为和恶意抓取,资源盗用

预防方法:
基础防盗链功能
secure_link_module-数据加密
access_module-对后台提供IP防护

攻击手段之暴力破解

密码撞库

预防方法:
提高密码复杂度
access_module-对后台提供IP防护
预警机制(结合lua)

文件上传漏洞

利用可以上传文件的接口将恶意代码植入到服务器中,再通过url去访问以执行代码

SQL注入

Nginx+LUA防火墙功能

https://github.com/loveshell/ngx_lua_waf

Nginx版本更新和本身漏洞

静态资源服务的功能设计

Nginx作为代理服务的需求

需求设计评估

-------------本文结束感谢您的阅读-------------
坚持原创技术分享,您的支持将鼓励我继续创作!
0%