Nginx 入门

2016-04-02 linux web software

Nginx (发音 “Engine X”) 是一款轻量级且高性能的 Web 服务器、反向代理服务器,同时也是一个 IMAP POP3 SMTP 服务器,完全通过 C 语言编写,支持多平台,并且在 BSD-like 协议下发行。

是由俄罗斯的 Igor Sysoev 开发,其特点是占有内存少、并发能力强、丰富的功能集和示例配置文件。在此仅简单介绍其安装使用方法。

nginx logo

简介

很多操作可以直接参考官方网站的文档 www.nginx.org ,在此仅列举其中比较常用的一些操作,使用的操作系统是 CentOS 。

----- 在CentOS中直接通过yum安装
# yum install nginx --enablerepo=epel

----- 启动,然后可以通过127.1直接访问,也可以通过enable设置开机启动
# systemctl start nginx.service

仅介绍一下安装 Nginx 之后的默认配置选项:默认的服务器根目录是 /usr/share/nginx/html,配置文件在 /etc/nginx 目录下,默认是 /etc/nginx/nginx.conf,日志默认在 /var/log/nginx 目录下。

常用操作

如下是经常会使用的命令。

----- 查看相关进程的资源
# ps axw -o pid,ppid,user,%cpu,vsz,wchan,command | egrep 'nginx:'

----- 其它的一些常见的命令
# kill -QUIT `cat /run/nginx.pid`        ← 从容停止
# kill -TERM|INT `cat /run/nginx.pid`    ← 快速停止
# kill -USR1 `cat /run/nginx.pid`        ← 重新打开一个日志文件
# kill -USR2 `cat /run/nginx.pid`        ← 升级可执行文件
# pkill -9 nginx                         ← 强制停止

# nginx -t -c /etc/nginx/nginx.conf      ← 检查配置文件的语法
# kill -HUP `cat /run/nginx.pid`         ← 重新加载配置文件
# nginx -s reload                        ← 同上

另外,为了防止由于安全配置导致报错,可通过 setenforce 0 关闭 SeLinux,之前被坑过 ^_^

现在可以直接通过浏览器打开 http://localhost/ 即可。当然,如果发现不符合期望的,可以直接查看默认的错误日志 /var/log/nginx/error.log

源码安装

如下是,如何通过源码安装、配置 nginx 。

1. 安装头文件

安装时依赖 pcre、openssl、gzip 三个库,在 CentOS 中,可以通过如下命令查看以及安装。

# rpm -qa | grep -E '(zlib|pcre|openssl)'
# yum install zlib zlib-devel pcre pcre-devel openssl openssl-devel
# yum install libxslt libxslt-devel
# yum install perl-ExtUtils-Embed

其中各个库的作用如下:

  • pcre: 用来作地址重写的功能;
  • zlib:用于传输数据打包,省流量,但消耗资源;
  • openssl:提供ssl加密协议;
  • xslt:用于过滤转换XML请求;
  • gd: 用于JPEG/GIF/PNG图片的一个过滤器

2. 添加用户

一般启动时会使用 nginx 用户,所以通过如下方式添加相应的用户、用户组。

# groupadd nginx
# useradd -r -g nginx -s /sbin/nologin -M nginx

其中 -r 指定系统用户;-g 指定用户组;-s 指定 shell;-M 不创建 home 。

3. 下载编译

直接从官网 www.nginx.org 下载源码,然后通过如下方式编译、安装。

$ ./configure      \
    --prefix=/usr/share/nginx \                    安装目录,存放服务器文件
    --sbin-path=/usr/sbin/nginx \                  可执行文件路径
    --conf-path=/etc/nginx/nginx.conf \            默认配置文件
    --error-log-path=/var/log/nginx/error.log \    报错文件,通过error_log修改
    --http-log-path=/var/log/nginx/access.log \    日志文件,通过access_log修改
    --pid-path=/run/nginx.pid \                    存储主进程号,通过pid修改
    --user=nginx \                                 进程工作用户,通过user指定
    --group=nginx \                                用户组,通过user指定

    --lock-path=/run/lock/subsys/nginx  \
    --http-client-body-temp-path=/var/lib/nginx/tmp/client_body \
    --http-proxy-temp-path=/var/lib/nginx/tmp/proxy \
    --http-fastcgi-temp-path=/var/lib/nginx/tmp/fastcgi \
    --http-uwsgi-temp-path=/var/lib/nginx/tmp/uwsgi \
    --http-scgi-temp-path=/var/lib/nginx/tmp/scgi  \

    --with-file-aio  \                             依赖libaio库
    --with-ipv6  \                                 支持IPV6
    --with-http_ssl_module  \                      使用https协议模块
    --with-http_spdy_module  \                     Google的SPDY协议支持

    --with-pcre \                                  支持地址重写
    --with-pcre-jit \
    --with-google_perftools_module \               依赖Google Perf工具,没有研究过
    --with-debug \                                 添加调试日志

    --add-module=/path/to/echo-nginx-module        编译添加三方模块

    --with-ld-opt='-Wl,-E'

nginx 没有通过 autotools 进行编译,而是通过自己的 shell 脚本实现,其中相应的脚本都在 auto 目录下,其中编译生成的文件都保存在 objs 目录下。

编译完成之后,可以通过如下命令安装即可。

# make install

4. 启动测试

源码编译完成之后,可以直接通过如下方式启动。

# /usr/sbin/nginx

5. systemd 支持

对于新一代的 init 支持,需要添加如下文件。

$ cat /usr/lib/systemd/system/nginx.service
[Unit]
Description=The nginx HTTP and reverse proxy server
After=network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
PIDFile=/run/nginx.pid
# Nginx will fail to start if /run/nginx.pid already exists but has the wrong
# SELinux context. This might happen when running `nginx -t` from the cmdline.
# https://bugzilla.redhat.com/show_bug.cgi?id=1268621
ExecStartPre=/usr/bin/rm -f /run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/bin/kill -s HUP $MAINPID
KillSignal=SIGQUIT
TimeoutStopSec=5
KillMode=process
PrivateTmp=true

[Install]
WantedBy=multi-user.target

然后就可以通过如下方式进行控制了。

$ systemctl [start|reload|restart|stop] nginx.service
$ journalctl -f -u nginx.service                             查看日志

环境

配置完 Nginx 服务之后,实际上会在设置的 root 目录下添加符号 接,从而可以针对不同的运行环境,如 Bootstrap、echarts、php、DVWA,可以直接通过如下方式设置:

basic:     只包括了基本的测试。
    ln -s basic html

bootstrap: 一个不错的前端框架。
    ln -s ../bootstrap/bootstrap-3.3.5-dist html

echarts:
    cd echarts-roots
    ln -s ../../echarts/echarts.min.js echarts.js
    cd ..
    ln -s echarts-root html

php-root:  PHP的安装环境
    ln -s php-root html

DVWA:      一个安全测试环境
    ln -s /home/andy/Workspace/security/dvwa/DVWA-1.9 html

变量

Nginx 配置文件的使用就像是一门微型的编程语言,既然如此,肯定不会少 “变量”,其支持内建变量,可以直接查看源码 http/ngx_http_variables.cngx_http_core_variables[] 的实现。

如下,使用了 ngx_rewrite 标准模块的 set 配置指令对变量 $foobar 进行了赋值操作,也即把字符串 hello world 赋给了它;如下需要依赖 echo 模块,详细安装方法参考下面的内容。

location /hello {
    set $foobar "hello world";
    echo "foobar: $foobar";
}

然后使用类似如下的 curl 命令测试即可。

$ curl 'http://localhost:80/hello'

内置变量

与 Apache 类似,在 core module 中支持一些内置的变量,常见的如下:

  • $arg_PARAMETER
    GET 请求的 PARAMETER 值。
  • $args
    请求行中的参数。
  • $query_string
    与 $args 相同。
  • $binary_remote_addr
    二进制码形式的客户端地址。
  • $body_bytes_sent
    发送页面的字节数。
  • $content_length
    请求头中的 Content-length 字段。
  • $content_type
    请求头中的 Content-Type 字段。
  • $cookie_COOKIE
    COOKIE 的值。
  • $document_root
    当前请求在 root 指令中指定的值。
  • $document_uri
    与 $uri 相同。
  • $uri
    请求中的当前URI,不含请求参数,参数位于$args,如 “/foo/bar.html” 。
  • $request_uri
    包含客户端请求参数的原始 URI,无法修改,可以查看 $uri 改写。
  • $is_args
    如果 $args 设置,值为 “?",否则为 “"。
  • $limit_rate
    可以限制连接速率。
  • $nginx_version
    当前运行的 nginx 版本号。
  • $remote_addr
    客户端的 IP 地址。
  • $remote_port
    客户端的端口。
  • $remote_user
    已经经过 Auth Basic Module 验证的用户名。
  • $request_filename
    当前连接请求的文件路径,由 root 或 alias 指令与 URI 请求生成。
  • $request_body
    包含请求的主要信息,在 proxy_pass、fastcgi_pass 中比较有意义。
  • $request_method
    客户端请求的动作,通常为 GET 或 POST。
  • $server_addr
    服务器地址。
  • $server_name
    服务器名称。
  • $server_port
    请求到达服务器的端口号。
  • $server_protocol
    请求使用的协议,通常是 HTTP/1.0 或 HTTP/1.1。

配置文件

server_name

根据 HTTP 请求的 header Host 选择 nginx 配置文件里符合条件的 server_name 的 server 配置,也就是说一个配置文件里可以配置多个不同域名的服务。

其匹配的顺序如下: 1) 完全匹配 (www.example.com);2) 后缀匹配 (*.example.com);3) 前缀匹配 (www.example.*);4) 正则匹配。

若前面四项都没匹配上,则根据以下顺序:1) listen 指令里配置了 default 或 default_server 的 server;2) 第一个匹配上 listen 的 server。

server {
    listen 8000;
    server_name www;
    location / {
        echo "first";
    }
}

server {
    listen  8000;
    server_name www.example.com;
    location / {
        echo "second";
    }
}

server {
    listen 8000;
    server_name www.example.*;
    location / {
        echo "third";
    }
}

server {
    listen 8000;
    server_name ~\w+.com;
    location / {
        echo "forth";
    }
}

server {
    listen 8000;
    server_name ~.*example.com;
    location / {
        echo "fifth";
    }
}

对于如上的示例,可以通过如下命令进行测试,当然为了测试需要在 /etc/hosts 配置文件中添加如下内容。

127.1 www www.example.com www.example.org www.foobar.com example.com example.org

注意 fifth 实际与 forth 冲突,不会出现 fifth 的。

$ curl http://www:8000/                 ← 全匹配
first
$ curl http://www.example.com:8000/     ← 全匹配
second
$ curl http://www.example.org:8000/     ← 前缀匹配
third
$ curl http://example.com:8000/         ← 第一个正则匹配
forth
$ curl http://www.foobar.com:8000/      ← 同上
forth
$ curl http://example.org/              ← 无匹配,返回第一个符合的listen的
first

如果在配置文件里再加入一个配置。

server {
    listen 8000 default;
    server_name _;

    location / {
        echo "sixth";
    }
}

则访问 http://example.org/ 返回 sixth 。

accept_mutex

简单来说,这个参数主要用来处理惊群问题,可以通过如下方式配置。

events {
	accept_mutex off;
}

当新连接到达时,如果激活了 accept_mutex,那么多个 worker 将以串行方式来处理,只有一个会被唤醒,其它的 worker 继续保持休眠状态;如果没有激活 accept_mutex,那么所有的 worker 都被唤醒,不过只有一个 worker 能获取新连接,其它的 worker 会重新进入休眠状态。

不过不像 Apache 会启动成百上千的进程,如果发生惊群问题的话,影响相对较大;但是对 Nginx 而言,一般来说,worker_processes 会设置成 CPU 个数,所以最多也就几十个,即便发生惊群问题的话,影响相对也较小。

另外,高版本的 Linux 中,accept 不存在惊群问题,但是 epoll_wait 等操作还有。

如果关闭了它,可能会引起一定程度的惊群问题,表现为上下文切换增多 (sar -w) 或者负载上升,但是如果网站访问量比较大,为了系统的吞吐量,建议关闭。

关闭之后一般各个工作进程的负载会更均衡,可以通过 github ngx-req-distr 测试。

文件服务器

可以通过 Nginx 搭建静态服务器,可以支持断点续传、多线程下载。

增加配置

/etc/nginx/conf.d 目录下包含了各种配置文件,如果要增加新配置,可以直接在该目录下新增配置文件 fileserver.conf

server {
	listen 80;                      # 监听端口
	server_name hostname;           # 如果没有DNS解析,可以设置IP地址
	client_max_body_size 4G;        # 设置最大文件大小
	charset utf-8;                  # 防止出现中文乱码
	root /files;                    # 指定相对路径的根目录,如下的location会相对该路径
	location /packages {             # 实际存放文件的目录为/files/packages/
		auth_basic "Restricted"; # 输入密码时的提示语
		auth_basic_user_file /etc/nginx/pass_file; # 认证时用户密码文件存放路径
		autoindex on;            # 自动生成文件索引
		autoindex_exact_size on; # 显示文件大小
		autoindex_localtime on;  # 显示本地文件时间
	}
}

然后通过 nginx -t 检查语法是否正确,通过 nginx -s reload 重新加载配置。

可以通过如下方式添加用户。

htpasswd -c -d /etc/nginx/pass_file foobar

断点续传

一般第一次请求时会得到 200 状态码,而 206 则表示客户端通过发送范围请求头 Range 抓取到了资源的部分数据,通常用于断点续传、并发下载。

在使用前,需要知道文件大小以及远程服务器是否支持 206 请求,可以使用 curl -I URL 查看头部信息。

在相应的头部信息中,包含了两个关键的请求头:

  • Accept-Ranges: bytes 表明服务器支持Range请求,而且服务器所支持的单位是字节,支持断点续传、同时下载文件的多个部分;如果是none表示不支持。
  • Content-Length: 36907 表明了响应实体的大小。

通过如上的方式,可以判断是否支持断点续传,在发送请求的时候,只需要添加类似 Range: bytes=0-1024 的头部即可。

常用模块

安装完 Nginx 之后,可以通过 -V 参数,查看编译时使用的参数;很多三方的模块可以参考 NGINX 3rd Party Modules ,保存了一个比较详细的列表。

对于三方模块,可以通过如下方法添加到二进制文件中。

$ ./configure \
   --add-module=/path/to/echo-nginx-module

echo

用来显示一些常见的变量,不过这个模块不包含在 Nginx 源码中,可以通过如下方法进行安装;首先从 github openresty/echo-nginx-module

可以下载之后通过如下方式编译。

$ ./configure --add-module=/path/to/echo-nginx-module

简单的可以通过如下方式使用,也可以从官网查看详细的使用方法。

location /hello {
  echo "hello, world!";
}

然后执行如下命令测试即可。

$ curl 'http://localhost:80/hello'

ngx_http_map_module

该模块可以用来创建变量,将这些变量的值与其它变量相关联,允许分类或者同时映射多个值到多个不同值并储存到一个变量中,而且仅在变量被接受的时候执行视图映射操作,对于处理没有引用变量的请求时,这个模块并没有性能上的缺失。

详细可以参考 Nginx 官方文档 Module ngx_http_map_module

auth_basic_module

关于该模块的内容详细可以查看 Nginx 基本认证模块 ,使用简单介绍如下。

需要保证已经安装了 ngx_http_auth_basic_module 模块,同样可以通过 nginx -V 查看,如果不需要可以使用 --without-http_auth_basic_module

location / {
    auth_basic           "closed site";
    auth_basic_user_file conf/htpasswd;
}

其中 auth_basic 的字符串内容可以任意,例如 RestrictedNginxStatus 等;另外,需要注意的是 auth_basic_user_file 的路径,否则会出现 403 错误。

其中密码文件的格式内容如下,其中密码通过 crypt() 函数加密,以及 Apache 基于 MD5 的变种 Hash 算法 (apr1) 生成,可以通过 Apache 中的 httpd-tools 中的 htpasswd 加密,或者使用 openssl passwd 获取。

# comment
name1:password1
name2:password2:comment
name3:password3

可以通过如下命令生成密码。

$ sh -c "openssl passwd -apr1 >> /etc/nginx/.htpasswd"
$ printf "ttlsa:$(openssl passwd -crypt 123456)\n" >>conf/htpasswd
$ htpasswd -c conf/htpasswd username

其它

记录一些常见的问题,以及解决方法。

管理脚本

一个 Bash 管理脚本。

#!/bin/bash
# chkconfig: - 30 21
# description: http service.
# Source Function Library
. /etc/init.d/functions
# Nginx Settings
#the shell is used as a tool that start, stop, restart the servie nginx

NGINX_SBIN="/usr/local/nginx/sbin/nginx"
NGINX_CONF="/usr/local/nginx/conf/nginx.conf"
NGINX_PID="/usr/local/nginx/logs/nginx.pid"
RETVAL=0
prog="Nginx"

start() {
        echo -n $"Starting $prog: "
        mkdir -p /dev/shm/nginx_temp
        daemon $NGINX_SBIN -c $NGINX_CONF
        RETVAL=$?
        echo
        return $RETVAL
}

stop() {
        echo -n $"Stopping $prog: "
        killproc -p $NGINX_PID $NGINX_SBIN -TERM
        rm -rf /dev/shm/nginx_temp
        RETVAL=$?
        echo
        return $RETVAL
}

reload(){
        echo -n $"Reloading $prog: "
        killproc -p $NGINX_PID $NGINX_SBIN -HUP
        RETVAL=$?
        echo
        return $RETVAL
}

restart(){
        stop
        start
}

configtest(){
    $NGINX_SBIN -c $NGINX_CONF -t
    return 0
}

case "$1" in
  start)
        start
        ;;
  stop)
        stop
        ;;
  reload)
        reload
        ;;
  restart)
        restart
        ;;
  configtest)
        configtest
        ;;
  *)
        echo $"Usage: $0 {start|stop|reload|restart|configtest}"
        RETVAL=1
esac

exit $RETVAL

监控 nginx 的工作状态

监控需要在编译时添加 --with-http_stub_status_module 选项,默认不会包含在内;然后在配置文件中添加如下内容。

location /status {
    stub_status on;
    access_log off;
    allow 127.0.0.1;
    deny all;
}

修改完之后重新加载,然后可以通过 http://localhost/status 查看,此时会显示如下信息。

Active connections: 557             # 对后端发起的活动连接数
server accepts handled requests     # 总共处理的链接数;成功握手次数;总共处理的请求
35 35 65
# 读取到客户端Header信息数;返回给客户端的Header信息数;等待下次请求的驻留连接
Reading: 3 Writing: 16 Waiting: 8

注意,对于最后一行的 Waiting,当开启 keep-alive 的情况下,这个值等于 active - (reading + writing),意思就是说已经处理完正在等候下一次请求指令的驻留连接。

启动报错

记录下遇到的一些常见问题。

Can’t load XXX

例如报错为 Can't load '/usr/local/lib64/perl5/auto/nginx/nginx.so',该报错是在使用 perl 模块时可能引入的报错,也就是编译时使用 --with-http_perl_module 参数。

# /usr/sbin/nginx
Can't load '/usr/local/lib64/perl5/auto/nginx/nginx.so' for module nginx:
    /usr/local/lib64/perl5/auto/nginx/nginx.so: undefined symbol:
    ngx_http_perl_handle_request at /usr/share/perl5/XSLoader.pm line 68.
at /usr/local/lib64/perl5/nginx.pm line 56.
Compilation failed in require.
BEGIN failedcompilation aborted.
nginx: [alert] perl_parse() failed: 255

此时,需要在编译的时候添加一个连接选项。

$ ./configure --with-http_perl_module --with-ld-opt="-Wl,-E"

然后,重新安装即可。

403 Forbidden

可以通过查看 error 日志,通常是由于权限不足导致,最简单的方式可以尝试在配置文件中设置 user root;,也就是 nginx 的子进程采用 root 用户。

当然,通常也可以使用 chown nginx:nginx -R /var/www 解决。

参考

可以参考官方文档 nginx.org docs