Nginx详解

札记

Posted by 0xl00se on October 8, 2019

一、Nginx 官方介绍

nginx has one master process and several worker processes. The main purpose of the master process is to read and evaluate configuration, and maintain worker processes. Worker processes do actual processing of requests. nginx employs event-based model and OS-dependent mechanisms to efficiently distribute requests among worker processes. The number of worker processes is defined in the configuration file and may be fixed for a given configuration or automatically adjusted to the number of available CPU cores (see worker_processes).

The way nginx and its modules work is determined in the configuration file. By default, the configuration file is named nginx.conf and placed in the directory /usr/local/nginx/conf, /etc/nginx, or /usr/local/etc/nginx.

二、互联网的一篇介绍

  • 传统上基于进程或线程模型架构的 Web 服务通过每进程或每线程处理并发连接请求,这势必会在网络和 I/O 操作时产生阻塞,其另一个必然结果则是对内存或 CPU 的利用率低下。生成一个新的进程/线程需要事先备好其运行时环境,这包括为其分配堆内存和栈内存,以及为其创建新的执行上下文等。这些操作都需要占用 CPU,而且过多的进程/线程还会带来线程抖动或频繁的上下文切换,系统性能也会由此进一步下降。
  • 在设计的最初阶段,Nginx 的主要着眼点就是其高性能以及对物理计算资源的高密度利用,因此其采用了不同的架构模型。受启发于多种操作系统设计中基于”事件”的高级处理机制,nginx采用了模块化、事件驱动、异步、单线程及非阻塞的架构,并大量采用了多路复用及事件通知机制。在 Nginx 中,连接请求由为数不多的几个仅包含一个线程的进程 Worker 以高效的回环(run-loop)机制进行处理,而每个 Worker 可以并行处理数千个的并发连接及请求。
  • 如果负载以 CPU 密集型应用为主,如 SSL 或压缩应用,则 Worker 数应与 CPU 数相同;如果负载以 IO 密集型为主,如响应大量内容给客户端,则 Worker 数应该为 CPU 个数的 1.5 或 2 倍。
  • Nginx会按需同时运行多个进程:一个主进程(Master)和几个工作进程(Worker),配置了缓存时还会有缓存加载器进程(Cache Loader)和缓存管理器进程(Cache Manager)等。所有进程均是仅含有一个线程,并主要通过”共享内存”的机制实现进程间通信。主进程以root用户身份运行,而 Worker、Cache Loader 和 Cache manager 均应以非特权用户身份运行。
  • 主进程主要完成如下工作:
    • 1.读取并验正配置信息;
    • 2.创建、绑定及关闭套接字;
    • 3.启动、终止及维护worker进程的个数;
    • 4.无须中止服务而重新配置工作特性;
    • 5.控制非中断式程序升级,启用新的二进制程序并在需要时回滚至老版本;
    • 6.重新打开日志文件,实现日志滚动;
    • 7.编译嵌入式perl脚本;
  • Worker 进程主要完成的任务包括:
    • 1.接收、传入并处理来自客户端的连接;
    • 2.提供反向代理及过滤功能;
    • 3.nginx任何能完成的其它任务;
  • Cache Loader 进程主要完成的任务包括:
    • 1.检查缓存存储中的缓存对象;
    • 2.使用缓存元数据建立内存数据库;
  • Cache Manager 进程的主要任务:
    • 1.缓存的失效及过期检验;

三、Nginx资料说明

四、Nginx 用包管理器安装

1.RHEL/CentOS

安装依赖

sudo yum install yum-utils

新建yum源并打开编辑其内容 vim /etc/yum.repos.d/nginx.repo

[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

默认,包管理器会使用nginx的稳定版本,如果想要使用mainline版本,执行如下命令:

sudo yum-config-manager --enable nginx-mainline

执行如下命令,即可开始安装。

sudo yum install nginx

2.Debian

安装依赖

sudo apt install curl gnupg2 ca-certificates lsb-release

设置apt源为nginx的稳定版本,执行如下命令:

echo "deb http://nginx.org/packages/debian `lsb_release -cs` nginx" \
    | sudo tee /etc/apt/sources.list.d/nginx.list

如果想要使用mainline版本,执行如下命令:

echo "deb http://nginx.org/packages/mainline/debian `lsb_release -cs` nginx" \
    | sudo tee /etc/apt/sources.list.d/nginx.list

导入一个官方nginx的签名key,使得apt可以校验nginx包的完整性与安全性

curl -fsSL https://nginx.org/keys/nginx_signing.key | sudo apt-key add -

校验拥有的合适key

sudo apt-key fingerprint ABF5BD827BD9BF62

其输出应当包含573B FD6B 3D8F BC64 1079 A6AB ABF5 BD82 7BD9 BF62此指纹

pub   rsa2048 2011-08-19 [SC] [expires: 2024-06-14]
      573B FD6B 3D8F BC64 1079  A6AB ABF5 BD82 7BD9 BF62
uid   [ unknown] nginx signing key <signing-key@nginx.com>

执行如下命令开始安装

sudo apt update
sudo apt install nginx

3.Ubuntu

安装依赖

sudo apt install curl gnupg2 ca-certificates lsb-release

设置apt源为nginx的稳定版本,执行如下命令:

echo "deb http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" \
    | sudo tee /etc/apt/sources.list.d/nginx.list

如果想要使用mainline版本,执行如下命令:

echo "deb http://nginx.org/packages/mainline/ubuntu `lsb_release -cs` nginx" \
    | sudo tee /etc/apt/sources.list.d/nginx.list

导入一个官方nginx的签名key,使得apt可以校验nginx包的完整性与安全性

curl -fsSL https://nginx.org/keys/nginx_signing.key | sudo apt-key add -

校验拥有的合适key

sudo apt-key fingerprint ABF5BD827BD9BF62

其输出应当包含573B FD6B 3D8F BC64 1079 A6AB ABF5 BD82 7BD9 BF62此指纹

pub   rsa2048 2011-08-19 [SC] [expires: 2024-06-14]
      573B FD6B 3D8F BC64 1079  A6AB ABF5 BD82 7BD9 BF62
uid   [ unknown] nginx signing key <signing-key@nginx.com>

执行如下命令开始安装

sudo apt update
sudo apt install nginx

4.SLES

安装依赖

sudo zypper install curl ca-certificates gpg2

设置zypper源为nginx的稳定版,执行如下命令

sudo zypper addrepo --gpgcheck --type yum --refresh --check \
    'http://nginx.org/packages/sles/$releasever' nginx-stable

如果想要使用mainline版本,执行如下命令:

sudo zypper addrepo --gpgcheck --type yum --refresh --check \
    'http://nginx.org/packages/mainline/sles/$releasever' nginx-mainline

导入一个官方nginx的签名key,使得zypper/rpm可以校验nginx包的完整性与安全性

curl -o /tmp/nginx_signing.key https://nginx.org/keys/nginx_signing.key

校验下载的文件包含合适的key

gpg --with-fingerprint /tmp/nginx_signing.key

其输出应当包含573B FD6B 3D8F BC64 1079 A6AB ABF5 BD82 7BD9 BF62此指纹

pub  2048R/7BD9BF62 2011-08-19 [expires: 2024-06-14]
      Key fingerprint = 573B FD6B 3D8F BC64 1079  A6AB ABF5 BD82 7BD9 BF62
uid nginx signing key <signing-key@nginx.com>

导入此key到rpm数据库里

sudo rpmkeys --import /tmp/nginx_signing.key

执行如下命令开始安装

sudo zypper install nginx

5.Alpine

安装依赖

sudo apk add openssl curl ca-certificates

设置apk源为nginx的稳定版,执行如下命令

printf "%s%s%s\n" \
    "http://nginx.org/packages/alpine/v" \
    `egrep -o '^[0-9]+\.[0-9]+' /etc/alpine-release` \
    "/main" \
    | sudo tee -a /etc/apk/repositories

如果想要使用mainline版本,执行如下命令:

printf "%s%s%s\n" \
    "http://nginx.org/packages/mainline/alpine/v" \
    `egrep -o '^[0-9]+\.[0-9]+' /etc/alpine-release` \
    "/main" \
    | sudo tee -a /etc/apk/repositories

导入一个官方nginx的签名key,使得apk可以校验nginx包的完整性与安全性

curl -o /tmp/nginx_signing.rsa.pub https://nginx.org/keys/nginx_signing.rsa.pub

校验下载的文件包含合适的key

openssl rsa -pubin -in /tmp/nginx_signing.rsa.pub -text -noout

其输出应当包含如下内容

Public-Key: (2048 bit)
Modulus:
    00:fe:14:f6:0a:1a:b8:86:19:fe:cd:ab:02:9f:58:
    2f:37:70:15:74:d6:06:9b:81:55:90:99:96:cc:70:
    5c:de:5b:e8:4c:b2:0c:47:5b:a8:a2:98:3d:11:b1:
    f6:7d:a0:46:df:24:23:c6:d0:24:52:67:ba:69:ab:
    9a:4a:6a:66:2c:db:e1:09:f1:0d:b2:b0:e1:47:1f:
    0a:46:ac:0d:82:f3:3c:8d:02:ce:08:43:19:d9:64:
    86:c4:4e:07:12:c0:5b:43:ba:7d:17:8a:a3:f0:3d:
    98:32:b9:75:66:f4:f0:1b:2d:94:5b:7c:1c:e6:f3:
    04:7f:dd:25:b2:82:a6:41:04:b7:50:93:94:c4:7c:
    34:7e:12:7c:bf:33:54:55:47:8c:42:94:40:8e:34:
    5f:54:04:1d:9e:8c:57:48:d4:b0:f8:e4:03:db:3f:
    68:6c:37:fa:62:14:1c:94:d6:de:f2:2b:68:29:17:
    24:6d:f7:b5:b3:18:79:fd:31:5e:7f:4c:be:c0:99:
    13:cc:e2:97:2b:dc:96:9c:9a:d0:a7:c5:77:82:67:
    c9:cb:a9:e7:68:4a:e1:c5:ba:1c:32:0e:79:40:6e:
    ef:08:d7:a3:b9:5d:1a:df:ce:1a:c7:44:91:4c:d4:
    99:c8:88:69:b3:66:2e:b3:06:f1:f4:22:d7:f2:5f:
    ab:6d
Exponent: 65537 (0x10001)

移动此key到可信的apk存储位置

sudo mv /tmp/nginx_signing.rsa.pub /etc/apk/keys/

执行如下命令开始安装

sudo apk add nginx

五、Nginx 源码编译安装

  • 官网下载传统稳定版本 1.8.1,大小:814K
  • 官网安装说明:https://www.nginx.com/resources/wiki/start/topics/tutorials/install/
  • 源码编译配置参数说明:
  • 开始安装:
    • 安装依赖包:yum install -y gcc gcc-c++ pcre pcre-devel zlib zlib-devel openssl openssl-devel
    • 预设几个文件夹,方便等下安装的时候有些文件可以进行存放:
      • mkdir -p /usr/local/nginx /var/log/nginx /var/temp/nginx /var/lock/nginx
    • 下载源码包:wget http://nginx.org/download/nginx-1.8.1.tar.gz
    • 解压:tar zxvf nginx-1.8.1.tar.gz
    • 进入解压后目录:cd nginx-1.8.1/
    • 编译配置:
      ./configure \
      --prefix=/usr/local/nginx \
      --pid-path=/var/local/nginx/nginx.pid \
      --lock-path=/var/lock/nginx/nginx.lock \
      --error-log-path=/var/log/nginx/error.log \
      --http-log-path=/var/log/nginx/access.log \
      --with-http_gzip_static_module \
      --http-client-body-temp-path=/var/temp/nginx/client \
      --http-proxy-temp-path=/var/temp/nginx/proxy \
      --http-fastcgi-temp-path=/var/temp/nginx/fastcgi \
      --http-uwsgi-temp-path=/var/temp/nginx/uwsgi \
      --with-http_ssl_module \
      --http-scgi-temp-path=/var/temp/nginx/scgi 
    
    • 编译:make
    • 安装:make install
  • 启动 Nginx
    • 先检查是否在 /usr/local 目录下生成了 Nginx 等相关文件:cd /usr/local/nginx;ll,正常的效果应该是显示这样的:
      drwxr-xr-x. 2 root root 4096 3月  22 16:21 conf
      drwxr-xr-x. 2 root root 4096 3月  22 16:21 html
      drwxr-xr-x. 2 root root 4096 3月  22 16:21 sbin
    
    • 停止防火墙:service iptables stop
      • 或是把 80 端口加入到的排除列表:
      • sudo iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
      • sudo service iptables save
      • sudo service iptables restart
    • 启动:/usr/local/nginx/sbin/nginx,启动完成 shell 是不会有输出的
    • 检查 时候有 Nginx 进程:ps aux | grep nginx,正常是显示 3 个结果出来
    • 检查 Nginx 是否启动并监听了 80 端口:netstat -ntulp | grep 80
    • 访问:192.168.10.128,如果能看到:Welcome to nginx!,即可表示安装成功
    • 检查 Nginx 启用的配置文件是哪个:/usr/local/nginx/sbin/nginx -t
    • 刷新 Nginx 配置后重启:/usr/local/nginx/sbin/nginx -s reload
    • 停止 Nginx:/usr/local/nginx/sbin/nginx -s stop
    • 如果访问不了,或是出现其他信息看下错误立即:vim /var/log/nginx/error.log

六、把 Nginx 添加到系统服务中

  • 新建文件:vim /etc/init.d/nginx
  • 添加如下内容:
#!/bin/bash


#nginx执行程序路径需要修改

nginxd=/usr/local/nginx/sbin/nginx

# nginx配置文件路径需要修改

nginx_config=/usr/local/nginx/conf/nginx.conf

# pid 地址需要修改

nginx_pid=/var/local/nginx/nginx.pid


RETVAL=0
prog="nginx"

# Source function library.

. /etc/rc.d/init.d/functions
# Source networking configuration.

. /etc/sysconfig/network
# Check that networking is up.

[ ${NETWORKING} = "no" ] && exit 0
[ -x $nginxd ] || exit 0

# Start nginx daemons functions.

start() {
if [ -e $nginx_pid ];then
   echo "nginx already running...."
   exit 1
fi

echo -n $"Starting $prog: "
daemon $nginxd -c ${nginx_config}
RETVAL=$?
echo
[ $RETVAL = 0 ] && touch /var/lock/subsys/nginx
return $RETVAL
}

# Stop nginx daemons functions.

# pid 地址需要修改

stop() {
	echo -n $"Stopping $prog: "
	killproc $nginxd
	RETVAL=$?
	echo
	[ $RETVAL = 0 ] && rm -f /var/lock/subsys/nginx /var/local/nginx/nginx.pid
}

# reload nginx service functions.

reload() {
	echo -n $"Reloading $prog: "
	#kill -HUP `cat ${nginx_pid}`
	
	killproc $nginxd -HUP
	RETVAL=$?
	echo
}

# See how we were called.

case "$1" in
	start)
		start
		;;
	stop)
		stop
		;;
	reload)
		reload
		;;
	restart)
		stop
		start
		;;
	status)
		status $prog
		RETVAL=$?
		;;
	*)

	echo $"Usage: $prog {start|stop|restart|reload|status|help}"
	exit 1

esac
exit $RETVAL
  • 修改权限:chmod 755 /etc/init.d/nginx
  • 启动服务:service nginx start
  • 停止服务:service nginx stop
  • 重启服务:service nginx restart

七、Nginx 全局变量介绍

  • $arg_PARAMETER #这个变量包含GET请求中,如果有变量PARAMETER时的值。
  • $args #这个变量等于请求行中(GET请求)的参数,例如foo=123&bar=blahblah;
  • $binary_remote_addr #二进制的客户地址。
  • $body_bytes_sent #响应时送出的body字节数数量。即使连接中断,这个数据也是精确的。
  • $content_length #请求头中的Content-length字段。
  • $content_type #请求头中的Content-Type字段。
  • $cookie_COOKIE #cookie COOKIE变量的值
  • $document_root #当前请求在root指令中指定的值。
  • $document_uri #与$uri相同。
  • $host #请求主机头字段,否则为服务器名称。
  • $hostname #Set to the machine’s hostname as returned by gethostname
  • $http_HEADER
  • $is_args #如果有$args参数,这个变量等于”?”,否则等于”“,空值。
  • $http_user_agent #客户端agent信息
  • $http_cookie #客户端cookie信息
  • $limit_rate #这个变量可以限制连接速率。
  • $query_string #与$args相同。
  • $request_body_file #客户端请求主体信息的临时文件名。
  • $request_method #客户端请求的动作,通常为GET或POST。
  • $remote_addr #客户端的IP地址。
  • $remote_port #客户端的端口。
  • $remote_user #已经经过Auth Basic Module验证的用户名。
  • $request_completion #如果请求结束,设置为OK. 当请求未结束或如果该请求不是请求链串的最后一个时,为空(Empty)。
  • $request_method #GET或POST
  • $request_filename #当前请求的文件路径,由root或alias指令与URI请求生成。
  • $request_uri #包含请求参数的原始URI,不包含主机名,如:”/foo/bar.php?arg=baz”。不能修改。
  • $scheme #HTTP方法(如http,https)。
  • $server_protocol #请求使用的协议,通常是HTTP/1.0或HTTP/1.1。
  • $server_addr #服务器地址,在完成一次系统调用后可以确定这个值。
  • $server_name #服务器名称。
  • $server_port #请求到达服务器的端口号。
  • $uri #不带请求参数的当前URI,$uri不包含主机名,如”/foo/bar.html”。该值有可能和$request_uri 不一致。
  • $request_uri是浏览器发过来的值。该值是rewrite后的值。例如做了internal redirects后。

八、Nginx 配置

  • Nginx 默认配置文件:vim /usr/local/nginx/conf/nginx.conf

1.Nginx 在 1.8.1 版本下的默认配置(去掉注释)

user root;#我这里习惯使用 root,所以这里需要这样设置。如果你有为你的 nginx 专门配置一个用户,这里需要改为你的用户

worker_processes  1;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout  60;

    server {
        listen       80;
        server_name  localhost;

        location / {
            root   html;
            index  index.html index.htm;
        }

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

2.HTTP 服务,虚拟主机

  • 停止防火墙:service iptables stop,防止出现特别干扰
  • 编辑默认的配置文件:vim /usr/local/nginx/conf/nginx.conf
  • 设置两个虚拟主机(通过端口来区分开)
user root;#我这里习惯使用 root,所以这里需要这样设置。如果你有为你的 nginx 专门配置一个用户,这里需要改为你的用户

worker_processes  1;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout  60;

    # 一个 server 代表一个虚拟主机
    
    server {
        listen       80;
        server_name  localhost;

        location / {
            # 虚拟机根目录是 /usr/local/nginx/html 目录
            
            root   html;
            # 虚拟机首页是 /usr/local/nginx/html 目录下这两个文件
            
            index  index.html index.htm;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
    
    server {
        # 第二个虚拟机的端口是 81,服务地址还是本地
        
        listen       81;
        server_name  localhost;

        location / {
            root   /mnt/html;
            index  index.html index.htm;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}
  • 设置两个虚拟主机(通过域名来区分开)
user root;#我这里习惯使用 root,所以这里需要这样设置。如果你有为你的 nginx 专门配置一个用户,这里需要改为你的用户

worker_processes  1;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout  60;

    # 一个 server 代表一个虚拟主机
    
    server {
        listen       80;
        # 两个虚拟主机都使用 80 端口,设置不同域名
        
        server_name  0xl00se.github.io;

        location / {
            # 虚拟机根目录是 /usr/local/nginx/html 目录
            
            root   html;
            # 虚拟机首页是 /usr/local/nginx/html 目录下这两个文件
            
            index  index.html index.htm;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
    
    server {
        listen       80;
        # 两个虚拟主机都使用 80 端口,设置不同域名
        
        server_name  0xl00se.wordpress.com;

        location / {
            root   /mnt/html;
            index  index.html index.htm;
        }

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

3.反向代理和负载均衡

  • 最精简的环境:一台虚拟机
    • 1 个 JDK
    • 1 个 Nginx
    • 2 个 Tomcat
  • Nginx 配置:
user root;#我这里习惯使用 root,所以这里需要这样设置。如果你有为你的 nginx 专门配置一个用户,这里需要改为你的用户

worker_processes  1;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout  60;

    # 自己定义的两个 tomcat 请求地址和端口
    
    # 也就是当浏览器请求:0xl00se.github.io 的时候从下面这两个 tomcat 中去找一个进行转发
    
    upstream tomcatCluster {
        server 192.168.10.128:8080;
        server 192.168.10.129:8081;
        
        # 添加 weight 字段可以表示权重,值越高权重越大,默认值是 1,最大值官网没说,一般如果设置也就设置 3,5,7 这样的数
        
        # 官网:https://www.nginx.com/resources/admin-guide/load-balancer/#weight
        
        # server 192.168.10.128:8080 weight=2;
        
        # server 192.168.10.129:8081 weight=1;
        
    }

    server {
        listen       80;
        server_name  0xl00se.github.io;

        location / {
            proxy_pass   http://tomcatCluster;
            index  index.html index.htm;
        }
    }
}

4.负载策略

nginx可以根据客户端IP进行负载均衡,在upstream里设置ip_hash,就可以针对同一个C类地址段中的客户端选择同一个后端服务器,除非那个后端服务器宕了才会换一个。

nginx的upstream目前支持的5种方式的分配:

1、轮询(默认)
每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。
upstream backserver {
    server 192.168.10.128;
    server 192.168.10.129;
}

2、指定权重
指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。
upstream backserver {
    server 192.168.10.128 weight=10;
    server 192.168.10.129 weight=10;
}

3、IP绑定 ip_hash
每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。
upstream backserver {
    ip_hash;
    server 192.168.10.128:80;
    server 192.168.10.129:80;
}

4、fair(第三方)
按后端服务器的响应时间来分配请求,响应时间短的优先分配。
upstream backserver {
    server server1;
    server server2;
    fair;
}

5、url_hash(第三方)
按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。
upstream backserver {
    server squid1:3128;
    server squid2:3128;
    hash $request_uri;
    hash_method crc32;
}

在需要使用负载均衡的server中增加 

proxy_pass http://backserver/; 
upstream backserver { 
    ip_hash; 
    server 127.0.0.1:9090 down; (down 表示单前的server暂时不参与负载) 
    server 127.0.0.1:8080 weight=2; (weight 默认为1.weight越大,负载的权重就越大) 
    server 127.0.0.1:6060; 
    server 127.0.0.1:7070 backup; (其它所有的非backup机器down或者忙的时候,请求backup机器) 
} 

max_fails :允许请求失败的次数默认为1.当超过最大次数时,返回proxy_next_upstream 模块定义的错误 
fail_timeout:max_fails次失败后,暂停的时间

5.配置 HTTPS 服务(SSL 证书配置)

  • 免费申请 SSL 证书渠道
  • 一般你会下载下面两个文件:certificate.crtprivate.key
  • 如果你需要把 crt 和 key 的证书转换成 keystore(如果你有这个需求的话)
  • 从 key 和 crt 生成 pkcs12 格式的 keystore,生成过程会让人你输入密码,这个密码下面会用到,我这里假设输入 123456
    • openssl pkcs12 -export -in certificate.crt -inkey private.key -out youmeek.p12 -name youmeek -CAfile certificate.crt -caname -chain
    • keytool -importkeystore -v -srckeystore youmeek.p12 -srcstoretype pkcs12 -srcstorepass 123456 -destkeystore youmeek.keystore -deststoretype jks -deststorepass 123456
  • 修改 nginx 配置文件,增加对 HTTPS 支持(下面的配置是基于默认安装 nginx 后的配置)
  • vim /usr/local/nginx/conf/nginx.conf
worker_processes  1;
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  60;
    
    # 如果访问 http 也直接跳转到 https
    
    server {
        listen       80;
        server_name 0xl00se.github.io;
        return 301 https://$server_name$request_uri;
    }
    
    # crt 和 key 文件的存放位置根据你自己存放位置进行修改
    
    server {
        listen       443;
        server_name  0xl00se.github.io;
        ssl  on;
        ssl_certificate     /opt/ssl/certificate.crt;
        ssl_certificate_key /opt/ssl/private.key;
        location / {
            root   html;
            index  index.html index.htm;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

九、Nginx 配置文件常用配置积累

1.location 配置

= 开头表示精确匹配
^~ 开头表示uri以某个常规字符串开头,不是正则匹配
~ 开头表示区分大小写的正则匹配;
~* 开头表示不区分大小写的正则匹配
/ 通用匹配, 如果没有其它匹配,任何请求都会匹配到

location / {

}

location /user {

}

location = /user {

}

location /user/ {

}

location ^~ /user/ {

}

location /user/0xl00se {

}

location ~ /user/0xl00se {

}

location ~ ^(/cas/|/casclient1/|/casclient2/|/casclient3/) {

}

location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|ico|woff|woff2|ttf|eot|txt)$ {

}

location ~ .*$ {

}

十、安装第三方模块

nginx是不支持动态安装加载模块的,因此需要重新编译源码安装。为了避免原来的配置被覆盖, 需要按照如下操作。(如果是第一次安装或者不用考虑第一次安装被覆盖的情况 则略过以下操作 正常make && make install安装即可)

  • 重新安装时,首次安装时做的配置(比如编译时的 –prefix 参数等)必须都进行配置,可以通过nginx -V 可以查看当时安装时的配置。比如上次安装事 指定了安装目录 这次也必须指定。 ./configure –prefix=/安装目录 –add-module=/第三方模块的目录 …其他配置项

  • 重新配置完后,执行make && make install

十一、启用 Gzip 压缩

nginx实现资源压缩的原理是通过ngx_http_gzip_module模块拦截请求,并对需要做gzip的类型做gzip,而ngx_http_gzip_module是Nginx默认集成的,不需要重新编译,直接开启即可。

配置如下:

    # $gzip_ratio计算请求的压缩率,$body_bytes_sent请求体大小
    
    log_format  main  '$remote_addr - $remote_user [$time_local] "$host" - "$request"' '$gzip_ratio - $body_bytes_sent - $request_time';

    access_log  logs/access.log  main;

    # 开启gzip
    
    gzip on;

    # 启用gzip压缩的最小文件,小于设置值的文件将不会压缩
    
    gzip_min_length 1k;

    # gzip 压缩级别,1-9,数字越大压缩的越好,也越占用CPU时间,后面会有详细说明
    
    gzip_comp_level 5;

    # 进行压缩的文件类型。javascript有多种形式。其中的值可以在 mime.types 文件中找到。
    
    gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png application/vnd.ms-fontobject font/ttf font/opentype font/x-woff image/svg+xml;

    # 是否在http header中添加Vary: Accept-Encoding,建议开启
    
    gzip_vary on;

    # 禁用IE 6 gzip
    
    gzip_disable "MSIE [1-6]\.";

    # 设置压缩所需要的缓冲区大小
    
    gzip_buffers 32 4k;

    # 设置gzip压缩针对的HTTP协议版本
    
    gzip_http_version 1.0 | 1.1;

十二、Nginx 过滤特定用户代理(User Agents)访问

你可以很容易地阻止User-Agents,如扫描器,机器人以及滥用你服务器的垃圾邮件发送者。

## Block download agents

if ($http_user_agent ~* LWP::Simple|BBBike|wget) {
    return 403;
}

阻止Soso和有道的机器人如下所示,

## Block some robots

if ($http_user_agent ~* Sosospider|YodaoBot) {
    return 403;
}

十三、防止图片盗链

图片或HTML盗链的意思是有人直接用你网站的图片地址来显示在他的网站上。最终的结果,你需要支付额外的宽带费用。这通常是在论坛和博客。我强烈建议您封锁,并阻止盗链行为。

# Stop deep linking or hot linking

location /images/ {
    valid_referers none blocked github.io 0xl00se.github.io;
    if ($invalid_referer) {
        return   403;
    }
}

可以重定向并显示指定图片。

location /images/ {
    valid_referers blocked github.io 0xl00se.github.io;
    if ($invalid_referer) {
        rewrite ^/images/uploads.*.(gif|jpg|jpeg|png)$ http://0xl00se.github.io/banned.jpg last 
    }
}

十四、目录限制

你可以对指定的目录设置访问权限。所有的网站目录应该一一配置,只允许必须的目录访问权限。 通过IP地址限制访问 你可以通过IP地址来限制访问目录/admin/,如下所示。

location /docs/ {
    ## block one workstation
 
    deny    192.168.1.1;
    
    ## allow anyone in 192.168.1.0/24
    
    allow   192.168.1.0/24;
    
    ## drop rest of the world
    
    deny    all;
}

通过密码保护目录

依次执行如下命令:

创建密码文件并增加"user"用户。
mkdir /usr/local/nginx/conf/.htpasswd/
htpasswd -c /usr/local/nginx/conf/.htpasswd/passwd user

编辑nginx.conf,加入需要保护的目录。

# Password Protect /personal-images/ and /delta/ directories
    
location ~ /(personal-images/.|delta/.) {
    auth_basic  "Restricted";
    auth_basic_user_file   /usr/local/nginx/conf/.htpasswd/passwd;
}

一旦密码文件已经生成,你也可以用以下的命令来增加允许访问的用户。

htpasswd -s /usr/local/nginx/conf/.htpasswd/passwd userName

十五、Nginx 缓存

开启简单的缓存配置,只需要两个指令:proxy_cache_path和proxy_cache。 proxy_cache_path是配置缓存的存放地址和其他的一些常用配置,proxy_cache指令是为了启动缓存。

proxy_cache_path /path/to/cache levels=1:2 keys_zone=mycache:10m max_size=10g inactive=60m use_temp_path=off;
server {
  location / {
    proxy_cache mycache;
    proxy_pass http://my_upstream;
  }
}

相关配置说明如下:

  • /path/to/cache 本地路径,用来设置Nginx缓存资源的存放地址
  • levels 默认所有缓存文件都放在同一个/path/to/cache下,但是会影响缓存的性能, 因此通常会在/path/to/cache下面建立子目录用来分别存放不同的文件。 假设levels=1:2,Nginx为将要缓存的资源生成的key为acee871902034c97aa74ab456706fff2,那么key的最后一位0, 以及倒数第2-3位6d作为两级的子目录,也就是该资源最终会被缓存到/path/to/cache/2/ff目录中
  • key_zone 在共享内存中设置一块存储区域来存放缓存的key和metadata(类似使用次数), 这样nginx可以快速判断一个request是否命中或者未命中缓存,1m可以存储8000个key,10m可以存储80000个key
  • max_size 最大cache空间,如果不指定,会使用掉所有disk space,当达到配额后,会删除最少使用的cache文件
  • inactive 未被访问文件在缓存中保留时间,本配置中如果60分钟未被访问则不论状态是否为expired,缓存控制程序会删掉文件。inactive默认是10分钟。 需要注意的是,inactive和expired配置项的含义是不同的,expired只是缓存过期,但不会被删除,inactive是删除指定时间内未被访问的缓存文件
  • use_temp_path 如果为off,则nginx会将缓存文件直接写入指定的cache文件中,而不是使用temp_path存储,official建议为off,避免文件在不同文件系统中不必要的拷贝
  • proxy_cache 启用proxy cache,并指定key_zone。另外,如果proxy_cache off表示关闭掉缓存。

proxy_cache其他相关指令集

proxy_no_cache

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

该指令用于定义满足条件的响应不会被保存到缓存中。在条件字符串中至少有一个条件不为空或者0,符合这样条件的响应才不会被缓存。举例如下:

proxy_no_cache $cookie_nocache $arg_nocache$arg_comment;
proxy_no_cache $http_pragma    $http_authorization;

其中,cookie_nocache、arg_nocache...皆为变量,可以根据你访问的匹配策略来设置,其值只有2类,0和非0;
访问匹配策略例如:

if ($request_uri ~ ^/(login|register|password\/reset)/) { 
    set $cookie_nocache 1; 
}

如果在此链式配置中,只要有一个值不为0,则不会cache;例如:

proxy_no_cache $cookie_nocache(0) $arg_nocache(1) $arg_comment(0)

则不会被cache。 注:一般会配合proxy_cache_bypass共同使用;

proxy_cache_bypass

Syntax: proxy_cache_bypass string ...;
Default: —
Context: http , server , location

该指令用于定义哪些情况不从cache读取,直接从backend获取资源;配置方式同proxy_no_cache。

proxy_cache_key

Syntax: proxy_cache_key string;
Default: proxy_cache_key $scheme$proxy_host$request_uri;
Context: http, server, location

给缓存数据定义一个键,例如

proxy_cache_key "$host$request_uri $cookie_user";

默认情况下,该指令的值的字符串

proxy_cache_key $scheme$proxy_host$uri$is_args$args;

proxy_cache_methods

Syntax: proxy_cache_methods GET | HEAD | POST ...;
Default: proxy_cache_methods GET HEAD;
Context: http, server, location

该指令用于设置缓存哪些HTTP方法,默认缓存HTTP GET/HEAD方法,不缓存HTTP POST 方法。

proxy_cache_valid

Syntax: proxy_cache_valid [code ...] time;
Default:  —
Context:  http, server, location

设置不同响应码的缓存时间,当不指定响应码的时候,例如

proxy_cache_valid 5m;

只对响应码为200,301,302的访问请求资源设置缓存时间,此外可以个性化定制,例如:

proxy_cache_valid 200 302 10m; 
proxy_cache_valid 301 1h; 
proxy_cache_valid 404 1m; 
proxy_cache_valid any 1m;

此外,还可以在相应header里设置优先级更高的缓存有效时间:

"X-Accel-Expires",设置响应的缓存过期时间,以秒为单位;0为不缓存;
如果没有设置"X-Accel-Expires" header,则关于缓存的配置策略可能会在"Expires"或者"Cache-Control" header中;
如果header含有"Set-Cookie",则响应不会被缓存,类似的配置可以在"proxy_ignore_header"中可见;
header包含"Vary"并且设置为"*",则请求不会被缓存,如果"Vary"有具体的值,则对应的请求会被缓存;

proxy_ignore_headers

Syntax:  proxy_ignore_headers field ...;
Default:  —
Context:  http, server, location

不缓存包含在field的响应header,可以设置的值有:"X-Accel-Redirect", "X-Accel-Expires", "X-Accel-Limit-Rate","X-Accel-Buffering", "X-Accel-Charset", "Expires", "Cache-Control", "Set-Cookie" (0.8.44), and "Vary"。
如果上述的header field没有设置为忽略,则header filed中有"X-Accel-Expires", "Expires", "Cache-Control", "Set-Cookie", and "Vary"的话,响应会被缓存。

proxy_cache_min_uses指令

Syntax:  proxy_cache_min_uses number;
Default: proxy_cache_min_uses 1;
Context: http, server, location

该指令用于设置缓存的最小使用次数,默认值为1

nginx缓存扩展

proxy_cache_use_stale增强站点容错能力 

源站有问题时,nginx可以通过proxy_cache_use_stale指令开启容错能力,即使用缓存内容来响应客户端的请求。举例如下:

location / {  
    proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;  
}  

如上配置表示,当作为cache的NGINX收到源站返回error、timeout或者其他指定的5XX错误,并且在其缓存中有请求文件的陈旧版本,则会将这些陈旧版本的文件而不是错误信息发送给客户端。

多磁盘分割缓存

使用NGINX,不需要建立一个RAID(磁盘阵列)。如果有多个硬盘,NGINX可以用来在多个硬盘之间分割缓存。举例如下:

我们假设每块硬盘挂载在相应的目录中:/mnt/disk1、/mnt/disk2、/mnt/disk3

proxy_cache_path /mnt/disk1 levels=1:2 keys_zone=cache_1:256m max_size=1024G use_temp_path=off;
proxy_cache_path /mnt/disk2 levels=1:2 keys_zone=cache_2:256m max_size=1024G use_temp_path=off;
proxy_cache_path /mnt/disk3 levels=1:2 keys_zone=cache_3:256m max_size=1024G use_temp_path=off;

split_clients $request_uri $disk {
    33.3%     1;
    33.3%     2;
    *         3;
}

location / {
    proxy_pass http://backend;
    proxy_cache_key $request_uri;
    proxy_cache cache_$disk;
}

在这份配置中,使用了3个独立的缓存,每个缓存专用一块硬盘,另外,3个独立的线程池也各自专用一块硬盘。
缓存之间(其结果就是磁盘之间)的负载均衡使用split_clients模块,split_clients非常适用于这个任务。
在 proxy_cache_path指令中设置use_temp_path=off,表示NGINX会将临时文件保存在缓存数据的同一目录中。这是为了避免在更新缓存时,磁盘之间互相复制响应数据。

十六、Nginx 处理跨域请求

server {
    listen 8080;
    server_name localhost;

    location /api {
        add_header 'Access-Control-Allow-Origin' $http_origin;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' '*';

        if ($request_method = "OPTIONS") {
            add_header 'Access-Control-Allow-Origin' $http_origin;
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
            add_header 'Access-Control-Allow-Headers' '*';
            add_header 'Content-Length' 0;
            add_header 'Content-Type' 'text/plain, charset=utf-8';
            return 204;
        }

        proxy_pass http://192.168.10.128:8888;
    }
}

十七、使用 logrotate 做 nginx 日志轮询分割

  • 前提:
    • 我 nginx 的成功日志路径:/var/log/nginx/access.log
    • 我 nginx 的错误日志路径:/var/log/nginx/error.log
    • pid 路径:/var/local/nginx/nginx.pid
  • 一般情况 CentOS 是装有:logrotate,你可以检查下:rpm -ql logrotate,如果有相应结果,则表示你也装了。
  • logrotate 配置文件一般在:
    • 全局配置:/etc/logrotate.conf 通用配置文件,可以定义全局默认使用的选项。
    • 自定义配置,放在这个目录下的都算是:/etc/logrotate.d/
  • 针对 nginx 创建自定义的配置文件:vim /etc/logrotate.d/nginx
  • 文件内容如下:
/var/log/nginx/access.log /var/log/nginx/error.log {
	create 644 root root
	notifempty
	daily
	rotate 15
	missingok
	dateext
	sharedscripts
	postrotate
	    if [ -f /var/local/nginx/nginx.pid ]; then
	    
	        kill -USR1 `cat /var/local/nginx/nginx.pid`
	    fi
	endscript
}

  • /var/log/nginx/access.log /var/log/nginx/error.log:多个文件用空格隔开,也可以用匹配符:/var/log/nginx/*.log
  • notifempty:如果是空文件的话,不转储
  • create 644 root root:create mode owner group 转储文件,使用指定的文件模式创建新的日志文件
  • 调用频率,有:daily,weekly,monthly可选
  • rotate 15:一次将存储15个归档日志。对于第16个归档,时间最久的归档将被删除。
  • sharedscripts:所有的日志文件都轮转完毕后统一执行一次脚本
  • missingok:如果日志文件丢失,不报错继续执行下一个
  • dateext:文件后缀是日期格式,也就是切割后文件是:xxx.log-20131216.gz 这样,如果注释掉,切割出来是按数字递增,即前面说的 xxx.log-1 这种格式
  • postrotate:执行命令的开始标志
  • endscripthttp:执行命令的结束标志
  • if 判断的意思不是中止Nginx的进程,而是传递给它信号重新生成日志,如果nginx没启动不做操作
  • 更多参数可以看:http://www.cnblogs.com/zengkefu/p/5498324.html

手动执行测试:/usr/sbin/logrotate -vf /etc/logrotate.d/nginx

  • 参数:-f选项来强制logrotate轮循日志文件,-v参数提供了详细的输出。
  • 验证是否手动执行成功,查看 cron 的日志即可:grep logrotate /var/log/cron

设置 crontab 定时任务:vim /etc/crontab,添加下面内容:

//每天01点30分执行一次
30 01 * * *  /usr/sbin/logrotate -f /etc/logrotate.d/nginx

十八、安全预防

# 在配置文件中设置自定义缓存以限制缓冲区溢出攻击的可能性

client_body_buffer_size 1K;
client_header_buffer_size 1k;
client_max_body_size 1k;
large_client_header_buffers 2 1k;


# client_body_buffer_size 1k; (默认8k或16k)这个指令可以指定连接请求实体的缓冲区大小。如果连接请求超过缓存区指定的值,那么这些请求实体的整体或部分将尝试写入一个临时文件。

# client_header_buffer_size 1k; 指定客户端请求头部的缓冲区大小。绝大多数情况下一个请求头不会大于1k,不过如果有来自于wap客户端的较大的cookie它可能会大于 1k,Nginx将分配给它一个更大的缓冲区,这个值可以在large_client_header_buffers里面设置。

# client_max_body_size 1k; 指令指定允许客户端连接的最大请求实体大小,它出现在请求头部的Content-Length字段。如果请求大于指定的值,客户端将收到一个”Request Entity Too Large” (413)错误。记住,浏览器并不知道怎样显示这个错误。

# large_client_header_buffers 2 1k; 指定客户端一些比较大的请求头使用的缓冲区数量和大小。请求字段不能大于一个缓冲区大小,如果客户端发送一个比较大的头,nginx将返回”Request URI too large” (414)。同样,请求的头部最长字段不能大于一个缓冲区,否则服务器将返回”Bad request” (400)。缓冲区只在需求时分开。默认一个缓冲区大小为操作系统中分页文件大小,通常是4k或8k,如果一个连接请求最终将状态转换为keep-alive,它所占用的缓冲区将被释放。



# 将timeout设低来防止DOS攻击 所有这些声明都可以放到主配置文件中

client_body_timeout 10; 
client_header_timeout 10; 
keepalive_timeout 5 5;
send_timeout 10;


# client_body_timeout 10; 指令指定读取请求实体的超时时间。这里的超时是指一个请求实体没有进入读取步骤,如果连接超过这个时间而客户端没有任何响应,Nginx将返回一个"Request time out" (408)错误。

# client_header_timeout 10; 指令指定读取客户端请求头标题的超时时间。这里的超时是指一个请求头没有进入读取步骤,如果连接超过这个时间而客户端没有任何响应,Nginx将返回一个"Request time out" (408)错误。

# keepalive_timeout 5 5;  参数的第一个值指定了客户端与服务器长连接的超时时间,超过这个时间,服务器将关闭连接。参数的第二个值(可选)指定了应答头中Keep-Alive: timeout=time的time值,这个值可以使一些浏览器知道什么时候关闭连接,以便服务器不用重复关闭,如果不指定这个参数,nginx不会在应 答头中发送Keep-Alive信息。(但这并不是指怎样将一个连接"Keep-Alive")参数的这两个值可以不相同。

# send_timeout 10; 指定了发送给客户端应答后的超时时间,Timeout是指没有进入完整established状态,只完成了两次握手,如果超过这个时间客户端没有任何响应,nginx将关闭连接。


# 使用NginxHttpLimitZone模块来限制指定的会话或者一个IP地址的特殊情况下的并发连接。

limit_zone slimits $binary_remote_addr 5m;
limit_conn slimits 5;