Linux 运维手册之 NGINX 编译及优化

分类:Linux 评论: 0

NGINX 的性能优化是网站体验中重要的一环,在机器性能一定的情况下,参数优化能明显提高网站加载速度。

性能优化

系统级优化

文件句柄数限制了系统打开文件的最大限制,在访问用户多的情况下就会导致可用资源枯竭,因此需要优化。

# vi /etc/security/limits.conf

添加以下两行(添加至文件末尾)

* soft nofile 51200
* hard nofile 51200

配置级优化

进程优化策略

NGINX 启动时会启动一个主进程(master)和若干个工作进程(worker),在高并发的情形下,工作进程数量不足会导致吞吐量不足,以便保证响应大量用户的请求。

修改 worker_processes 参数。

小贴士:一般情况下此数值等于系统的处理器核心数。

Linux 查看 CPU 个数及总核数

# grep 'processor' /proc/cpuinfo | wc -l

此参数也可以改为 auto,自动检测系统核心数量。

worker_processes auto;

处理器绑定,可将某个进程绑定至某个核心,可以更高效得完成任务,提高硬件资源的利用率。

worker_cpu_affinity auto;

例子:

双核 CPU 推荐配置

worker_processes    2;
worker_cpu_affinity 0101 1010;

四核 CPU 推荐配置

worker_processes    4;
worker_cpu_affinity 0001 0010 0100 1000;

调整单个工人进程的允许连接数

events {
    worker_connections  1024;
}

小贴士:理论总并发 = 工作进程数 * 单进程允许连接数 (此数据受机器的硬件限制,仅供参考)

资源优化参数

语法:sendfile
选项:on / off
默认:关闭
语段:http, server, location, if in location

实例配置

location /video/ {
    sendfile       on;
    tcp_nopush     on;
    aio            on;
}

此选项可优化磁盘 I/O,不阻塞磁盘读写,会发送一个消息提示所需资源不在内存中,然后读取文件的前 128K 字节 (取决于 read_ahead 参数)来启动异步数据加载。

语法:tcp_nopush
选项:on / off
默认:关闭
语段:http, server, location

实例配置

location /video/ {
    sendfile       on;
    tcp_nopush     on;
    aio            on;
}

此选项启用或禁用在 UNIX 上使用 TCP_NOPUSH 套接字选项或在 Linux 上使用 TCP_CORK 套接字选项。仅在使用 sendfile 时才启用这些选项。

启用该选项允许在 UNIX/Linux 中发送响应头和文件的开头,然后以完整数据包发送文件。

语法:aio
选项:on | off | threads[=pool]
默认:关闭
语段:http, server, location

实例配置

location /video/ {
    aio            on;
    directio       512;
    output_buffers 1 128k;
}

此选项启用或禁用在FreeBSD和Linux上使用异步文件 I/O(AIO)

资源传输参数

使用 Gzip 压缩参数可以压缩资源,节约传输流量及带宽。

语法:gzip
选项:on | off
默认:关闭
语段:http, server, location, if in location

是否启用数据压缩,默认情况下不使用。

语法:gzip_comp_level level
选项:level
默认:gzip_comp_level 1
语段:http, server, location

数据压缩比率(等级),等级越高,压缩率越高,相应地消耗处理器资源也越高。

语法:gzip_http_version
选项:1.0 | 1.1
默认:1.1
语段:http, server, location

使用压缩的协议,默认是 1.1。

实例配置

    location ~ .*\.(jpg|gif|png)$ {
        gzip on;
        gzip_http_version 1.1;
        gzip_comp_level 2;
        gzip_types text/plain application/json application/x-javascript application/css application/xml application/xml+rss text/javascript application/x-httpd-php image/jpeg image/gif image/png;
        root /static/img;

小贴士:不推荐在压缩比低的资源中使用压缩功能,节约的带宽有限,但是会明显提高系统资源的占用,得不偿失。

连接方式优化

NGINX 与后端程序如 PHP 可以使用 TCP/IP 方式连接或 Unix Domain Socket 方式连接。

连接方式 网络通信 缺点
TCP/IP 因走网络栈,会占用网卡,且性能较差
Unix Domain Socket 高并发情况下不稳定

因 Unix Domain Socket 实质上为文件,因此推荐将其生成在 /dev/shm 目录中(此目录为内存映射目录,文件实质会存在高速内存中),修改 PHP-FPM 的配置文件即可。

在高并发情况下,Socket 方式下会频繁报错。

connect() to unix:/dev/shm/php-fcgi.sock failed (11: Resource temporarily unavailable) while connecting to upstream

解决方式有两种:

方式1在NGINX配置中添加以下配置即可。

default backlog=1024

编译优化

在系统及系统配置中进行优化,受限于很多条件,然而可以在编译期间进行优化,优化更加底层,效果更加明显(若无特殊需求无需编译)。

默认参数

CentOS 官方编译版本默认配置

[root@domain ~]# nginx -V
nginx version: nginx/1.14.2
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-28) (GCC) 
built with OpenSSL 1.0.2k-fips  26 Jan 2017
TLS SNI support enabled
configure arguments: --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'

Debian 官方编译版本默认配置

root@domain:~# nginx -V
nginx version: nginx/1.14.2
built by gcc 6.3.0 20170516 (Debian 6.3.0-18+deb9u1) 
built with OpenSSL 1.1.0f  25 May 2017 (running with OpenSSL 1.1.1b  26 Feb 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 --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='-g -O2 -fdebug-prefix-map=/data/builder/debuild/nginx-1.14.2/debian/debuild-base/nginx-1.14.2=. -specs=/usr/share/dpkg/no-pie-compile.specs -fstack-protector-strong -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fPIC' --with-ld-opt='-specs=/usr/share/dpkg/no-pie-link.specs -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie'

小贴士:其他发行版推荐先使用官方源安装一遍,然后复制其官方编译参数后在其基础上进行修改。

额外组件

LuaJIT

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

小贴士:推荐使用 LuaJIT-2.1.0-beta3 版进行编译(更新于2019年3月)

NDK(ngx_devel_kit)

$ wget https://github.com/simplresty/ngx_devel_kit/archive/v0.3.1rc1.tar.gz

小贴士:更新版本至 0.3.1(更新于2019年3月)

Lua

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

小贴士:更新版本至 0.10.14(更新于2019年3月)

NGINX

# wget https://nginx.org/download/nginx-1.14.2.tar.gz

小贴士:更新版本至 1.14.2(更新于2019年3月)

Brotli

# git clone https://github.com/google/ngx_brotli.git
# cd ngx_brotli && git submodule update --init

小贴士:新版本已经无需手动编译 libbrotli 库文件(更新于2019年3月)

OpenSSL

# wget -c  https://github.com/openssl/openssl/archive/OpenSSL_1_1_1.tar.gz
# tar xzf OpenSSL_1_1_1.tar.gz
# mv openssl-OpenSSL_1_1_1 openssl

小贴士:为了支持 TLS 1.3 需要使用 OpenSSL v1.1.1(更新于2018年11月)

TCMalloc

TCMalloc 的全称为 Thread-Caching Malloc,是 Alphabet (Google) 开发的开源工具 Google PerfTools 中的一个成员。与标准的 glibc 库的 Malloc 相比,TCM 库在内存分配效率和速度上要高很多,这在很大程度上提高了服务器在高并发情况下的性能,从而降低了系统的负载。

# wget https://github.com/gperftools/gperftools/releases/download/gperftools-2.7/gperftools-2.7.tar.gz

小贴士:更新版本至 2.7(更新于2018年4月)

PageSpeed

PageSpeed 是一个常用的扩展模块,主要的功能是针对前端页面进行的服务器优化,对于前端来说可以省略优化 CSS JS 以及图片的过程。源码下载

构建环境

编译 LuaJIT

$ tar xf LuaJIT-2.0.5.tar.gz
$ cd LuaJIT-2.0.5
$ make && sudo make install

编译 Libunwind

$ wget http://download.savannah.nongnu.org/releases/libunwind/libunwind-1.3.1.tar.gz
$ tar zxf libunwind-1.3.1.tar.gz
$ cd libunwind-1.3.1
$ CFLAGS=-fPIC ./configure
$ make CFLAGS=-fPIC
$ sudo make CFLAGS=-fPIC install

编译 TCMalloc

$ tar xf gperftools-2.7.tar.gz
$ cd gperftools-2.7
$ ./configure
$ make -j2 && sudo make install
$ sudo echo '/usr/local/lib' > /etc/ld.so.conf.d/local.conf
$ sudo ldconfig 

小贴士:若已经编译安装 libunwind ,则无需安装依赖,否则请安装 libunwind-devel(CentOS/RadHat)或 libunwind-dev(Debian/Ubuntu)。

编译 PageSpeed

NPS_VERSION=1.13.35.2-stable
cd
wget https://github.com/apache/incubator-pagespeed-ngx/archive/v${NPS_VERSION}.zip
unzip v${NPS_VERSION}.zip
nps_dir=$(find . -name "*pagespeed-ngx-${NPS_VERSION}" -type d)
cd "$nps_dir"
NPS_RELEASE_NUMBER=${NPS_VERSION/beta/}
NPS_RELEASE_NUMBER=${NPS_VERSION/stable/}
psol_url=https://dl.google.com/dl/page-speed/psol/${NPS_RELEASE_NUMBER}.tar.gz
[ -e scripts/format_binary_url.sh ] && psol_url=$(scripts/format_binary_url.sh PSOL_BINARY_URL)
wget ${psol_url}
tar -xzvf $(basename ${psol_url})  # extracts to psol/

完整编译参数

# ./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-openssl=../openssl \
--with-openssl-opt='enable-tls1_3 enable-weak-ssl-ciphers' \
--with-http_ssl_module \
--with-http_stub_status_module \
--with-http_sub_module \
--with-http_v2_module \
--with-mail --with-mail_ssl_module \
--with-http_xslt_module=dynamic \
--with-http_image_filter_module=dynamic \
--with-http_geoip_module=dynamic \
--with-http_perl_module=dynamic \
--with-google_perftools_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-dynamic-module=../ngx_devel_kit/ \
--add-dynamic-module=../ngx_lua_module/ \
--add-module=/usr/local/src/ngx_brotli \
--add-module=/usr/local/src/ngx_pagespeed

注意:enable-tls1_3 是让 OpenSSL 支持 TLS 1.3 的关键选项;而 enable-weak-ssl-ciphers 的作用是继续支持 3DES 等不安全的加密套件,如果准备支持 IE8,才需要加上这个选项。

配置变更

ssl_protocols              TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers                TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-128-CCM-8-SHA256:TLS13-AES-128-CCM-SHA256:EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+ECDSA+AES128:EECDH+aRSA+AES128:RSA+AES128:EECDH+ECDSA+AES256:EECDH+aRSA+AES256:RSA+AES256:EECDH+ECDSA+3DES:EECDH+aRSA+3DES:RSA+3DES:!MD5;

小贴士:如果你打算继续支持 IE8,可以去掉包含 3DES 的项目。

pid        /run/nginx.pid;
google_perftools_profiles /tmp/tcmalloc;

load_module  modules/ndk_http_module.so;
load_module  modules/ngx_http_lua_module.so;

小贴士:使用 TCMalloc 并加载两个额外模块,使用 lsof -n | grep tcmalloc 即可验证。

更好的压缩方式 Brotli

Brotli 是基于LZ77算法的一个现代变体、霍夫曼编码和二阶上下文建模。Google软件工程师在2015年9月发布了包含通用无损数据压缩的 Brotli 增强版本,特别侧重于HTTP压缩。其中的编码器被部分改写以提高压缩比,编码器和解码器都提高了速度,流式API已被改进,增加更多压缩质量级别。

与常见的通用压缩算法不同,Brotli使用一个预定义的120千字节字典。该字典包含超过13000个常用单词、短语和其他子字符串,这些来自一个文本和HTML文档的大型语料库。预定义的算法可以提升较小文件的压缩密度。

使用 Brotli 替换 Deflate 来对文本文件压缩通常可以增加20%的压缩密度,而压缩与解压缩速度则大致不变。

Brotli is a generic-purpose lossless compression algorithm that compresses data using a combination of a modern variant of the LZ77 algorithm, Huffman coding and 2nd order context modeling, with a compression ratio comparable to the best currently available general-purpose compression methods. It is similar in speed with deflate but offers more dense compression.

配置参数

    brotli on;
    brotli_comp_level 6;
    brotli_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript image/svg+xml;

添加位置和参数功能与 Gzip 一致

检查是否生效

打开你的网页,用浏览器开发者工具调试,在 Network 选项中,发现有 content-encoding:br

编译报错

若出现以下报错

./configure: error: the GeoIP module requires the GeoIP library.
You can either do not enable the module or install the library.

解决

# yum install geoip-devel   # CentOS/RadHat
# apt install libgeoip-dev   # Debian/Ubuntu

若出现以下报错

./configure: error: perl module ExtUtils::Embed is required

解决

# yum install perl-ExtUtils-Embed  # CentOS/RadHat

若出现以下报错

/usr/bin/ld: cannot find -lperl
collect2: error: ld returned 1 exit status
objs/Makefile:2080: recipe for target 'objs/ngx_http_perl_module.so' failed
make[1]: *** [objs/ngx_http_perl_module.so] Error 1
make[1]: Leaving directory '/usr/local/src/nginx-1.15.9'
Makefile:8: recipe for target 'build' failed
make: *** [build] Error 2

解决

# apt install libperl-dev          # Debian/Ubuntu

若出现以下报错

./configure: error: the HTTP image filter module requires the GD library.
You can either do not enable the module or install the libraries.

解决

# yum install gd gd-devel   # CentOS/RadHat
# apt install libgd-dev     # Debian/Ubuntu

若出现以下报错

./configure: error: the HTTP XSLT module requires the libxml2/libxslt
libraries. You can either do not enable the module or install the libraries.

解决

# yum install libxslt-devel  # CentOS/RadHat
# apt install libxslt-dev  # Debian/Ubuntu

若出现以下报错

./configure: error: SSL modules require the OpenSSL library.
You can either do not enable the modules, or install the OpenSSL library
into the system, or build the OpenSSL library statically from the source
with nginx by using --with-openssl=<path> option.

解决

# yum install openssl-devel       # CentOS/RadHat
# apt install openssl libssl-dev   # Debian/Ubuntu

若出现以下报错

./configure: error: the HTTP rewrite module requires the PCRE library.
You can either disable the module by using --without-http_rewrite_module
option, or install the PCRE library into the system, or build the PCRE library
statically from the source with nginx by using --with-pcre=<path> option.

解决

# yum install pcre-devel  # CentOS/RadHat
# apt install libpcre3 libpcre3-dev # Debian/Ubuntu

若出现以下报错

./configure: error: You need a C++ compiler for C++ support.

解决

# yum groups install "Development Tools"  # CentOS/RadHat
# apt install build-essential # Debian/Ubuntu

若出现以下报错

./configure: error: the HTTP gzip module requires the zlib library.
You can either disable the module by using --without-http_gzip_module
option, or install the zlib library into the system, or build the zlib library
statically from the source with nginx by using --with-zlib=<path> option.

解决办法

# yum install zlib zlib-devel  # CentOS/RadHat
# apt install zlib1g-dev       # Debian/Ubuntu

若出现以下报错

./configure: error: module ngx_pagespeed requires the pagespeed optimization library.
Look in /usr/local/src/nginx-1.15.12/objs/autoconf.err for more details.

解决办法

# apt-get install build-essential zlib1g-dev libpcre3 libpcre3-dev unzip uuid-dev  # Debian/Ubuntu

配置报错

若出现以下报错

# nginx -t
nginx: [emerg] dlopen() "/etc/nginx/modules/ngx_http_lua_module.so" failed (libluajit-5.1.so.2: cannot open shared object file: No such file or directory) in /etc/nginx/nginx.conf:10
nginx: configuration file /etc/nginx/nginx.conf test failed

解决

# ln -s /usr/local/lib/libluajit-5.1.so.2 /lib64/libluajit-5.1.so.2

若出现以下报错

# nginx -t
nginx: error while loading shared libraries: libbrotlienc.so.1: cannot open shared object file: No such file or directory

解决

# 64 bit
$ ln -s /usr/local/lib/libbrotlienc.so.1 /lib64/
# 32 bit
$ ln -s /usr/local/lib/libbrotlienc.so.1 /lib/

其他优化

禁止使用 IP 访问

背景:

Q:为什么要禁止 IP 访问页面呢?
A:这样做是为了避免他人把未备案的域名解析到自身服务器 IP ,而导致服务器被断网,防止此类事情的发生。

方案:

1)在 server 段里插入正则,例如:

listen       80;
server_name  www.domain.com;
if ($host != 'www.domain.com') {
    return 403;
}

2)添加一个 server 主机,例如:

server {
    listen 80 default;
    server_name _;
    return 403;
}

在一般情况下使用方案二比较简单,若域名申请了证书并配置了 HTTPS ,请使用 方案一。且在 443 主机内也需要配置。

除了配置文件外,架构上的优化也很明显,特别对于高负载的场景下。

架构性优化

此部分在此省略,在相关链接中有详细介绍。

安全性优化

隐藏版本信息

一般来讲漏洞只针对特定版本或者某些版本,因此需要隐藏服务器版本号。

修改配置文件的 http 段内加入

server_tokens  off;

降权启动

使用 NGINX 时禁止使用 root 用户启动,请使用低权限用户进行登录。特别是编译安装模式下。


相关链接


参考链接

回复