ubuntu 16.04升级nginx版本

因为刚刚在 V2EX 看到的 nginx 爆出了中度危险漏洞,于是决定将正在用的nginx服务都升个级喵~

默认 Ubuntu 自带的 nginx 都比较 out, 正确的姿势是从官方源安装

  • 在 /etc/apt/sources.list.d/ 下添加一个 nginx.list 文件,内容如下:
deb http://nginx.org/packages/mainline/ubuntu/ xenial nginx  
deb-src http://nginx.org/packages/mainline/ubuntu/ xenial nginx  
  • 添加 nginx 的 key,并更新 apt
curl http://nginx.org/keys/nginx_signing.key | sudo apt-key add  
sudo apt update  
需要注意的是,Ubuntu 自带的 nginx 系列模组会干扰nginx本体安装,所以先备份配置文件,删除ubuntu的默认模组,再重装nginx
sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak  
sudo apt remove nginx nginx-common nginx-full nginx-core  
sudo apt install nginx  
sudo rm /etc/nginx/nginx.conf  
sudo cp /etc/nginx/nginx.conf.bak /etc/nginx/nginx.conf  

另外一点是此时 nginx 被 mask 了……解除并重启它:

sudo systemctl unmask nginx  
sudo systemctl start nginx  

测试无误后,加上重启自启动

sudo systemctl enable nginx  

NGINX编译安装动态模块(不需重新编译nginx)

虽然我一贯会自行编译Nginx,而且我多会选用阿里的Tengine或者YiChun Zhang的OpenResty,可难免会遇到一些特殊情况。

例如:我最近接手的一个小项目。其官方运维偏偏选择的是RPM包。如果是Nginx 1.9.11版本之前,我只能选择跟他们的运维商量:“hi,哥们!我需要用到的一些第三方模块必须重新编译安装Nginx呢!” 所幸,他们用的Centos 7.0,而官方的RPM仓库自带的版本已经支持到Nginx 1.10.2啦。我深呼吸一下,考虑到他们的使用习惯,我作出了第二个选择:Nginx动态模块。

可是。。。我真的是第一次操作动态模块的编译和加载。所以,我才会写下这则手记。

我主要参考的三篇文章如下:

  • https://www.nginx.com/blog/compiling-dynamic-modules-nginx-plus/
  • https://www.nginx.com/resources/wiki/extending/new_config/
  • https://www.nginx.com/resources/wiki/extending/converting/

我这里记录一下ngx_cache_purge的动态编译过程。

cd /usr/local/src #养成源码统一放置的位置,方便你我他。这家在职运维是到处乱丢的,吐槽一下!
nginx -v #返回的是nginx version: nginx/1.10.2, 确保下一步源码版本一致哟
wget -c http://nginx.org/download/nginx-1.10.2.tar.gz
wget -c https://github.com/FRiCKLE/ngx_cache_purge/archive/2.3.tar.gz -O ngx_cache_purge_2.3.tar.gz
tar zxf nginx-1.10.2.tar.gz
tar zxf ngx_cache_purge_2.3.tar.gz

转化动态模块的config文件

因为这个模块比较老,作者并没有对它做config配置的更新。换言之,源码下载后并不能支持编译为动态模块,我们参考官方文档(见上列表),试试看。

cd ./ngx_cache_purge-2.3/
cp config config.bk
vim config
###
if [ "$HTTP_PROXY" = "YES" ]; then
    have=NGX_HTTP_PROXY . auto/have
fi

if [ "$HTTP_FASTCGI" = "YES" ]; then
    have=NGX_HTTP_FASTCGI . auto/have
fi

if [ "$HTTP_SCGI" = "YES" 2]; then
    have=NGX_HTTP_SCGI . auto/have
fi

if [ "$HTTP_UWSGI" = "YES" ]; then
    have=NGX_HTTP_UWSGI . auto/have
fi

ngx_addon_name=ngx_http_cache_purge_module
CACHE_PURGE_SRCS="$ngx_addon_dir/ngx_cache_purge_module.c"

if [ -n "$ngx_module_link" ]; then
    ngx_module_type=HTTP
    ngx_module_name="$ngx_addon_name"
  /  ngx_module_srcs="$CACHE_PURGE_SRCS"

    . auto/module
else
    HTTP_MODULES="$HTTP_MODULES $ngx_addon_name"
    NGX_ADDON_SRCS="$NGX_ADDON_SRCS $CACHE_PURGE_SRCS"
fi

have=NGX_CACHE_PURGE_MODULE . auto/have
###

现在,我们可以进行动态模块的编译啦。

cd ../nginx-1.10.2
nginx -V
./configure --add-dynamic-module=../ngx_cache_purge-2.3/ ##这一步要注意:必须将之前的配置指令都拷贝过来,否则会有binary不兼容错误。
make modules
ls objs/

一切正常的话,输出会提示将so文件输出到了objs目录下。 他们这台服务器也没有SUDO相关配置,所以我以上都是以root身份直接运行的。您可能需要su指令。

最后,我们加载动态模块测试。

cp objs/ngx_http_cache_purge_module.so /usr/lib64/nginx/modules/
vim /usr/share/nginx/modules/mod-http-cache-purge.conf #该服务器是CENTOS 7.0,我看到nginx.conf中已经存在载入动态模块的include语句,所以就按照约定执行
###
load_module "/usr/lib64/nginx/modules/ngx_http_cache_purge_module.so";
###
systemctl restart nginx

Nginx平滑升级到最新版本

(一)简述:

早上收到nginx最新漏洞的通知,Nginx官方发布最新的安全公告,在Nginx范围过滤器中发现了一个安全问题(CVE-2017-7529),通过精心构造的恶意请求可能会导致整数溢出并且不正确处理范围,从而导致敏感信息泄漏。
当使用Nginx标准模块时,如果文件头从缓存返回响应,允许攻击者获取缓存文件头。在某些配置中,缓存文件头可能包含后端服务器IP地址或其他敏感信息。此外,如果使用第三方模块有潜在的可能导致拒绝服务。

影响版本
Nginx 0.5.6-1.13.2
漏洞等级
中危
Nginx 在官方公告中称发现了一个范围过滤器中的安全问题。通过精心构造的恶意请
求能造成整数溢出,对范围的不当处理会导致敏感信息泄漏。
No. 漏洞名称 漏洞危害
CVE-2017-7529 Nginx range 过滤器整形溢出漏洞 高危

针对 CVE–2017–7529 修复建议
针对 Nginx range 过滤器整形溢出漏洞的修复建议

  1. 下面的配置可以作为暂时的解决办法:
    max_ranges 1;
  2. 建议受影响用户尽快升级至 1.13.3, 1.12.1
  3. 及时安装官方补丁。

虽然临时可以解决,不过还是建议升级到最新的版本,官方建议升级到1.12.1。

(二)具体的升级步骤:

(1)升级和安装nginx第三方模块一样,需要查看原来安装nginx的版本以及编译的参数:

[root@ittestserver1 opt]# /usr/local/nginx2/sbin/nginx -V
nginx version: nginx/1.10.3
built by gcc 4.4.7 20120313 (Red Hat 4.4.7-16) (GCC) 
built with OpenSSL 1.1.0e  16 Feb 2017
TLS SNI support enabled
configure arguments: --prefix=/usr/local/nginx2 --with-http_stub_status_module --with-http_ssl_module --with-http_realip_module --with-http_gzip_static_module --with-http_stub_status_module --with-http_stub_status_module --with-http_v2_module --with-openssl=/tmp/install/openssl-1.1.0e --with-http_v2_module

(2)下载要升级的nginx版本

[root@ittestserver1 soft]# wget http://nginx.org/download/nginx-1.12.1.tar.gz
--2017-07-17 15:41:24--  http://nginx.org/download/nginx-1.12.1.tar.gz
正在解析主机 nginx.org... 206.251.255.63, 95.211.80.227, 2001:1af8:4060:a004:21::e3, ...
正在连接 nginx.org|206.251.255.63|:80... 已连接。
已发出 HTTP 请求,正在等待回应... 200 OK
长度:981093 (958K) [application/octet-stream]
正在保存至: “nginx-1.12.1.tar.gz”
90% [=================================================>     ] 892,302      265K/s eta(英国中部时100%[======================================================>] 981,093      291K/s   in 3.3s    
2017-07-17 15:41:28 (291 KB/s) - 已保存 “nginx-1.12.1.tar.gz” [981093/981093])

(3)解压ningx下载的压缩包编译make,切记不要make install。

[root@ittestserver1 soft]# tar xf nginx-1.12.1.tar.gz 
[root@ittestserver1 soft]# cd nginx-1.12.1
[root@ittestserver1 nginx-1.12.1]# ls
auto  CHANGES  CHANGES.ru  conf  configure  contrib  html  LICENSE  man  README  src
[root@ittestserver1 nginx-1.12.1]# ./configure  --prefix=/usr/local/nginx2 
--with-http_stub_status_module 
--with-http_ssl_module 
--with-http_realip_module 
--with-http_gzip_static_module 
--with-http_stub_status_module 
--with-http_stub_status_module 
--with-http_v2_module 
--with-openssl=/tmp/install/openssl-1.1.0e 
--with-http_v2_module
checking for OS
 + Linux 2.6.32-358.el6.x86_64 x86_64
checking for C compiler ... found
 + using GNU C compiler
 + gcc version: 4.4.7 20120313 (Red Hat 4.4.7-17) (GCC) 
checking for gcc -pipe switch ... found
checking for -Wl,-E switch ... found
checking for gcc builtin atomic operations ... found
checking for C99 variadic macros ... found
checking for gcc variadic macros ... found
checking for gcc builtin 64 bit byteswap ... found
checking for unistd.h ... found
checking for inttypes.h ... found
checking for limits.h ... found
checking for sys/filio.h ... not found
checking for sys/param.h ... found
checking for openat(), fstatat() ... found
checking for getaddrinfo() ... found
checking for PCRE library ... found
checking for PCRE JIT support ... found
checking for zlib library ... found
creating objs/Makefile
Configuration summary
  + using system PCRE library
  + using OpenSSL library: /tmp/install/openssl-1.1.0e
  + using system zlib library
  nginx path prefix: "/usr/local/nginx2"
  nginx binary file: "/usr/local/nginx2/sbin/nginx"
  nginx modules path: "/usr/local/nginx2/modules"
  nginx configuration prefix: "/usr/local/nginx2/conf"
  nginx configuration file: "/usr/local/nginx2/conf/nginx.conf"
  nginx pid file: "/usr/local/nginx2/logs/nginx.pid"
  nginx error log file: "/usr/local/nginx2/logs/error.log"
  nginx http access log file: "/usr/local/nginx2/logs/access.log"
  nginx http client request body temporary files: "client_body_temp"
  nginx http proxy temporary files: "proxy_temp"
  nginx http fastcgi temporary files: "fastcgi_temp"
  nginx http uwsgi temporary files: "uwsgi_temp"
  nginx http scgi temporary files: "scgi_temp"
[root@ittestserver1 nginx-1.12.1]# make

由于make的时间比较长,需要稍等下。

(4)make编译完后会在安装目录下生成一个objs目录且在该目录下有一个nginx执行文件。

[root@ittestserver1 nginx-1.12.1]# ls
auto     CHANGES.ru  configure  html     Makefile  objs    src
CHANGES  conf        contrib    LICENSE  man       README
[root@ittestserver1 nginx-1.12.1]# ll objs/
总用量 7124
-rw-r--r-- 1 root root   17459 7月  17 15:48 autoconf.err
-rw-r--r-- 1 root root   43530 7月  17 15:48 Makefile
-rwxr-xr-x 1 root root 7152312 7月  17 15:51 nginx
-rw-r--r-- 1 root root    5345 7月  17 15:51 nginx.8
-rw-r--r-- 1 root root    7066 7月  17 15:48 ngx_auto_config.h
-rw-r--r-- 1 root root     657 7月  17 15:48 ngx_auto_headers.h
-rw-r--r-- 1 root root    6242 7月  17 15:48 ngx_modules.c
-rw-r--r-- 1 root root   38232 7月  17 15:51 ngx_modules.o
drwxr-xr-x 9 root root    4096 7月  17 15:48 src

(5)备份原来老的nginx文件

[root@ittestserver1 nginx-1.12.1]# mv /usr/local/nginx2/sbin/nginx /usr/local/nginx2/sbin/nginx.bak
[root@ittestserver1 nginx-1.12.1]# cp objs/nginx
nginx    nginx.8  
[root@ittestserver1 nginx-1.12.1]# cp objs/nginx  /usr/local/nginx2/sbin/

[root@ittestserver1 nginx-1.12.1]# /usr/local/nginx2/sbin/nginx -t
nginx: the configuration file /usr/local/nginx2/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx2/conf/nginx.conf test is successful

(6)使用make upgrade替换老的nginx进程

[root@ittestserver1 nginx-1.12.1]# make upgrade
/usr/local/nginx2/sbin/nginx -t
nginx: the configuration file /usr/local/nginx2/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx2/conf/nginx.conf test is successful
kill -USR2 `cat /usr/local/nginx2/logs/nginx.pid`
sleep 1
test -f /usr/local/nginx2/logs/nginx.pid.oldbin
kill -QUIT `cat /usr/local/nginx2/logs/nginx.pid.oldbin`

(7)执行/usr/local/nginx2/sbin/nginx -V查看nginx最新的版本及编译的参数

[root@ittestserver1 nginx-1.12.1]# /usr/local/nginx2/sbin/nginx -V
nginx version: nginx/1.12.1
built by gcc 4.4.7 20120313 (Red Hat 4.4.7-17) (GCC) 
built with OpenSSL 1.1.0e  16 Feb 2017
TLS SNI support enabled
configure arguments: --prefix=/usr/local/nginx2 --with-http_stub_status_module --with-http_ssl_module --with-http_realip_module --with-http_gzip_static_module --with-http_stub_status_module --with-http_stub_status_module --with-http_v2_module --with-openssl=/tmp/install/openssl-1.1.0e --with-http_v2_module

至此升级完成。

Nginx负载均衡NFS配置

Nginx配置

首先在两台服务器上部署同一个项目,例如下:
测试网站节点1: http://192.168.168.61/nfstest/
测试网站节点2: http://192.168.64.145/nfstest/

在主站进行nginx配置

upstream nfstest {
    server 192.168.64.145:9575 weight=5;
    server 192.168.168.61:80 weight=5; 
    fair;                           
}

现在负载均衡初步完成了。upstream按照轮询(默认)方式进行负载,每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。虽然这种方式简便、成本低廉。但缺点是:可靠性低和负载分配不均衡。适用于图片服务器集群和纯静态页面服务器集群。

upstream还有其它的分配策略,分别如下:

weight(权重)

指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。如下所示,10.0.0.88的访问比率要比10.0.0.77的访问比率高一倍。

upstream linuxidc{ 
      server 10.0.0.77 weight=5; 
      server 10.0.0.88 weight=10; 
}

ip_hash(访问ip)

每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。

upstream favresin{ 
      ip_hash; 
      server 10.0.0.10:8080; 
      server 10.0.0.11:8080; 
}

fair(第三方)

按后端服务器的响应时间来分配请求,响应时间短的优先分配。与weight分配策略类似。

 upstream favresin{      
      server 10.0.0.10:8080; 
      server 10.0.0.11:8080; 
      fair; 
}

url_hash(第三方)

按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。
注意:在upstream中加入hash语句,server语句中不能写入weight等其他的参数,hash_method是使用的hash算法。

 upstream resinserver{ 
      server 10.0.0.10:7777; 
      server 10.0.0.11:8888; 
      hash $request_uri; 
      hash_method crc32; 
}

upstream还可以为每个设备设置状态值,这些状态值的含义分别如下:

  • down 表示单前的server暂时不参与负载.
  • weight 默认为1.weight越大,负载的权重就越大。
  • max_fails :允许请求失败的次数默认为1.当超过最大次数时,返回proxy_next_upstream 模块定义的错误.
  • fail_timeout : max_fails次失败后,暂停的时间。
  • backup: 其它所有的非backup机器down或者忙的时候,请求backup机器。所以这台机器压力会最轻。
upstream bakend{ #定义负载均衡设备的Ip及设备状态 
      ip_hash; 
      server 10.0.0.11:9090 down; 
      server 10.0.0.11:8080 weight=2; 
      server 10.0.0.11:6060; 
      server 10.0.0.11:7070 backup; 
}

NFS配置

环境:
两台服务器之间能正常通信
192.168.64.145 A服务器(文件实际保存)
192.168.168.61 B服务器

一、A服务器配置

1、安装NFS

使用rpm -qa | grep nfs 与 rpm -qa | grep rpcbind 可以查看是否有安装。
在CentOS内可以使用『yum install nfs-utils 』来安装。

2、设置服务机上的共享目录

  [root@www ~]# vi /etc/exports 
  /chroot/www/nfstest/WebContent/source 192.168.168.61(rw)
  #为192.168.168.61读写操作source目录权限
  /chroot/www/nfstest/WebContent/source *(rw,no_root_squash) 
  #任何人都可以应用source目录

3、启动NFS

设定文档后,开始启动, NFS启动之前需要先启动rpcbind才行。

  #如果rpcbind本来就已经在执行了,那就不需要启动啊!
  [root@www ~]# /etc/init.d/rpcbind start 
  #启动nfs
  [root@www ~]# /etc/init.d/nfs start 
  #启动nfslock
  [root@www ~]# /etc/init.d/nfslock start 
  [root@www ~]# chkconfig rpcbind on 
  [root@www ~]# chkconfig nfs on 
  [root@www ~]# chkconfig nfslock on

4、NFS 的连线观察

[root@www ~]# showmount [-ae] [hostname|IP]
选项与参数:

-a :显示目前主机与用户端的NFS 连线分享的状态;
-e :显示某部主机的/etc/exports 所分享的目录资料。

显示出刚刚所设定好的相关exports分享目录资讯

  [root@iZuf6ixy03u72vzno4jsiuZ ~]# showmount -e localhost
  Export list for localhost:
  /chroot/www/nfstest/source (everyone)
  /tmp                       (everyone)

二、B服务器配置

1.启动必备的服务
若没有启动才启动,有启动则保持原样不动

[root@clientlinux ~]# /etc/init.d/rpcbind start 
[root@clientlinux ~]# /etc/init.d/nfslock start 

2.查询A服务器提供哪些资源供使用

[root@jstu565zbb65jg ~]# showmount -e 192.168.64.145
Export list for 192.168.64.145:
/chroot/www/nfstest/source (everyone)<==这是等一下要挂载的目录
/tmp
                       (everyone)

3.建立挂载点

[root@clientlinux ~]# mkdir -p /chroot/www/nfstest/source 
[root@clientlinux ~]# mount -t nfs 192.168.64.145:/chroot/www/nfstest/source  /chroot/www/nfstest/source

4.卸载挂载点

[root@clientlinux ~]# umount /chroot/www/nfstest/source

推荐文档:
http://linux.vbird.org/linux_server/0330nfs.php#nfsserver_need

Centos7安装配置mariaDB + nginx + php-fpm环境

Centos7 搭建mariaDB + nginx + php环境

系统更新

  • 1.安装开启安装EPEL YUM源
[root@localhost ~]# yum -y install epel-release
[root@localhost ~]# yum makecache
  • 2.系统更新
[root@localhost ~]# yum update
  • 3.解决The remote SSH server rejected X11 forwarding request提示
[root@localhost ~]# yum install xorg-x11-xauth
# 编辑/etc/ssh/sshd_config文件中的X11Forwarding参数为yes
[root@localhost ~]# vim /etc/ssh/sshd_config
  • 4.安装编译组件和依赖
[root@localhost ~]# yum -y install gcc gcc-c++ make unixODBC wxBase wxGTK wxGTK-gl ncurses-devel zlib zlib-devel openssl openssl-devel kernel-devel m4 xmlto net-tools lksctp-tools socat cmake perl perl-JSON libtool pcre pcre-devel yasm yasm-devel libmcrypt libmcrypt-devel libvpx libvpx-devel tiff tiff-devel libpng libpng-devel freetype freetype-devel jpeg jpeg-devel libgd libgd-devel t1lib t1lib-devel gd gd-devel

修改主机名

  • 1.将/etc/hostname中的值修改为localhost或者其他主机名
[root@localhost ~]# vim /etc/hostname
  • 2.在/etc/sysconfig/network 中添加或者修改为HOSTNAME=localhost或者其他主机名
[root@localhost ~]# vim /etc/sysconfig/network
  • 3.将/etc/hosts 中各项值修改为localhost或者其他主机名
[root@localhost ~]# vim /etc/hosts
  • 4.临时修改主机名
[root@localhost ~]# hostname localhost

添加用户

添加新的用户账号使用useradd命令,其语法如下

useradd 选项 用户名

其中各选项含义如下:
代码:

  • -c comment 指定一段注释性描述。
  • -d 目录 指定用户主目录,如果此目录不存在,则同时使用-m选项,可以创建主目录。
  • -g 用户组 指定用户所属的用户组。
  • -G 用户组,用户组 指定用户所属的附加组。
  • -s Shell文件 指定用户的登录Shell。
  • -u 用户号 指定用户的用户号,如果同时有-o选项,则可以重复使用其他用户的标识号。
  • 用户名 指定新账号的登录名

  • 1.添加nginx用户

[root@localhost ~]# groupadd nginx
[root@localhost ~]# useradd -c nginx -g nginx nginx -s /sbin/nologin
  • 2.添加网站专用wwwroot用户
[root@localhost ~]# groupadd www
[root@localhost ~]# useradd -c www -g www www -s /sbin/nologin

安装MARIADB

  • 1.使用yum命令安装mariaDB
[root@localhost]# yum -y install mariadb mariadb-server
  • 2.设置开机启动
[root@localhost ~]# systemctl enable mariadb
  • 3.启动MySQL服务
[root@localhost ~]# systemctl start mariadb
  • 4.MariaDB的相关简单配置
[root@localhost ~]# mysql_secure_installation
# 首先是设置密码,会提示先输入密码,初次运行直接回车
Enter current password for root (enter for none):
# 设置密码,是否设置root用户密码,输入y并回车或直接回车
Set root password? [Y/n]
# 设置root用户的密码
New password:
# 再输入一次你设置的密码
Re-enter new password:
# 其他配置
# 是否删除匿名用户,回车
Remove anonymous users? [Y/n]
# 是否禁止root远程登录,回车
Disallow root login remotely? [Y/n]
# 是否删除test数据库,回车
Remove test database and access to it? [Y/n]
# 是否重新加载权限表,回车
Reload privilege tables now? [Y/n]
# 初始化MariaDB完成,接下来测试登录
# mysql -uroot -proot密码
  • 5.授权远程用户登录
# 允许的IP地址,%为任意地址
mysql> GRANT ALL PRIVILEGES ON *.* TO 'root'@'IP地址' IDENTIFIED BY 'youpassword' WITH GRANT OPTION;
mysql> FLUSH PRIVILEGES;

安装PHP

  • 1.安装php
[root@localhost ~]# yum -y install php php-devel
  • 2.安装php扩展
[root@localhost ~]# yum -y install php-mysql php-gd php-imap php-ldap php-odbc php-pear php-xml php-xmlrpc
  • 3.安装php-fpm
# 安装 php-fpm
[root@localhost ~]# yum -y install php-fpm
# 设置fpm开机自启动
[root@localhost ~]# systemctl enable php-fpm
# 启动fpm
[root@localhost ~]# systemctl start php-fpm
# php-fpm配置文件路径/etc/php-fpm.conf

安装NGINX

  • 1.下载nginx
# 切换到src目录
[root@localhost ~]# cd /usr/local/src
# 下载nginx源码包
[root@localhost src]# wget http://nginx.org/download/nginx-1.12.0.tar.gz
# 解压并切换到nginx目录
[root@localhost src]# tar zxvf nginx-1.12.0.tar.gz
[root@localhost src]# cd nginx-1.12.0
# 配置编译选项
[root@localhost nginx-1.12.0]# ./configure --with-http_stub_status_module --with-http_ssl_module --with-http_gzip_static_module --with-http_realip_module --with-pcre
# 安装
[root@localhost nginx-1.12.0]# make & make install
  • 2.启动关闭nginx
# 常规启动、关闭和重启,不会改变启动时指定的配置文件
[root@localhost ~]# /usr/local/nginx/sbin/nginx
[root@localhost ~]# /usr/local/nginx/sbin/nginx -s stop
[root@localhost ~]# /usr/local/nginx/sbin/nginx -s reload
# 设置开机自启动
[root@localhost ~]# vim /lib/systemd/system/nginx.service
# 写入以下内容
# --------------------------------------------------
[Unit]
Description=nginx
After=network.target

[Service]
Type=forking
ExecStart=/usr/local/nginx/sbin/nginx
ExecReload=/usr/local/nginx/sbin/nginx -s reload
ExecStop=/usr/local/nginx/sbin/nginx -s stop
PrivateTmp=true

[Install]
WantedBy=multi-user.target
# --------------------------------------------------
[root@localhost ~]# systemctl enable nginx
  • 3.添加php支持
# 修改nginx.conf,去掉以下代码的注释
        location ~ .php$ {
            root           html;
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
            include        fastcgi_params;
        }
# 重启nginx
[root@localhost]# /usr/local/nginx/sbin/nginx -s reload
  • 4.解决访问php提示File not found.
# 修改nginx配置文件
# 源文件
fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
# 修改后的文件,将 /scripts 修改为$document_root
fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;

CentOS7 nginx keepalived主备实例配置

nginx1 ip:192.168.12.4 #MASTER
nginx2 ip:192.168.12.10 #BACKUP
nginx_vip :192.168.12.100

原理可参考:
http://www.keepalived.org/documentation.html
系统为CentOS7

1、配置一下yum源

curl -L http://mirrors.aliyun.com/repo/Centos-7.repo > /etc/yum.repos.d/CentOS-Base.repo
curl -L http://mirrors.aliyun.com/repo/epel-7.repo > /etc/yum.repos.d/epel.repo

yum -y install keepalived nginx

2、设置服务器基本环境

关闭防火墙:iptables -F;service iptables save
关闭seLinux:setenforce 0;sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/sysconfig/selinux

3、配置一下nginx,好区分测试环境

192.168.12.4:
echo 192.168.12.4 > /usr/share/nginx/html/index.html

192.168.12.10:
echo 192.168.12.10 > /usr/share/nginx/html/index.html

4、配置keepalived.conf

[[email protected]]# cat keepalived.conf 
! Configuration File for keepalived
global_defs {
notification_email {
[email protected]
}
notification_email_from [email protected]
smtp_server 127.0.0.1
smtp_connect_timeout 30
router_id LVS_DEVEL
}

vrrp_script check_nginx {
script "sh /etc/keepalived/check_nginx.sh" 
interval 2 
}

vrrp_instance VI_1 {
state MASTER
interface eth0
mcast_src_ip 192.168.12.4
virtual_router_id 51
priority 100
advert_int 2
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.12.100
}
track_script {
check_nginx
}
}

[[email protected]]# cat keepalived.conf 
! Configuration File for keepalived
global_defs {
notification_email {
[email protected]
}
notification_email_from [email protected]
smtp_server 127.0.0.1
smtp_connect_timeout 30
router_id LVS_DEVEL
}

vrrp_script check_nginx {
script "sh /etc/keepalived/check_nginx.sh" 
interval 2 
}

vrrp_instance VI_1 {
state BACKUP
interface eth0
mcast_src_ip 192.168.12.10
virtual_router_id 51
priority 99
advert_int 2
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.12.100
}
track_script {
check_nginx
}
}

脚本check_nginx.sh内容如下

#!/bin/bash

A=`pgrep nginx|wc -l` 
if [ $A -eq 0 ];then 
/bin/systemctl start nginx.service
if [ `pgrep nginx|wc -l` -eq 0 ];then
/bin/systemctl stop keepalived.service
fi
fi

5、启动

在两个服务器执行启动命令

service nginx start
service keepalived start

6、检查

[[email protected] keepalived]# ip addr
1: lo:  mtu 65536 qdisc noqueue state UNKNOWN qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host 
valid_lft forever preferred_lft forever
2: eth0:  mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:0c:29:85:83:e8 brd ff:ff:ff:ff:ff:ff
inet 192.168.12.4/24 brd 192.168.12.255 scope global eth0
valid_lft forever preferred_lft forever
inet 192.168.12.100/32 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::20c:29ff:fe85:83e8/64 scope link 
valid_lft forever preferred_lft forever
[[email protected] keepalived]# ip addr
1: lo:  mtu 65536 qdisc noqueue state UNKNOWN qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host 
valid_lft forever preferred_lft forever
2: eth0:  mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:0c:29:62:6a:50 brd ff:ff:ff:ff:ff:ff
inet 192.168.12.10/24 brd 192.168.12.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::20c:29ff:fe62:6a50/64 scope link 
valid_lft forever preferred_lft forever

仔细观察可以发现192.168.12.4的服务器多了一个IP,就是192.168.12.100,这个就是VIP
再看日志发现主服务器日志有这下面一条:

Jul 12 10:30:57 localhost Keepalived_vrrp[2510]: VRRP_Instance(VI_1) Entering MASTER STATE

备服务器日志

Jul 11 00:01:00 localhost Keepalived_vrrp[111937]: VRRP_Instance(VI_1) Entering BACKUP STATE

综上主备已搭建成功

7、模拟主nginx宕机

使用shell awk获取Nginx Apache一分钟内的网站访问日志

之前我们有写过如何获取网站一分钟内的日志,不过日志格式是要求以tab分隔,当遇到日志以空格分隔,前面的脚本就无效了,这里我们提供以空格分隔的日志格式获取一分钟内日志的shell脚本。

# 日志目录
LOG_DIR="/etc/apache2/logs/domlogs/"
# 临时目录
TEMP_DIR="/tmp/log/"
mkdir -p $TEMP_DIR
cd $LOG_DIR

log_names=`find ./ -maxdepth 1 -type f | grep -v -E "bytes_log|offsetftpbytes"`

for log_name in $log_names;
do

#设置路径
split_log="$TEMP_DIR/$log_name"
access_log="${LOG_DIR}/$log_name"

#取出最近一分钟日志
tac $access_log  | awk '
BEGIN{
cmd="date -d "1 minute ago" +%s"
cmd|getline oneMinuteAgo
}
{
day = substr($4,2,2)
month = substr($4,5,3)
year = substr($4,9,4)
time = substr($4,14,8)
time_str = day" "month" "year" "time
cmd="date -d ""time_str"" +%s"
cmd|getline log_date
if (log_date>=oneMinuteAgo){
print
} else {
exit;
}
}' > $split_log

done

# 删除空文件
find ./ -size 0 -exec rm {} ;

使用docker运行nginx

方法一、通过 Dockerfile构建

创建Dockerfile
首先,创建目录nginx,用于存放后面的相关东西。

runoob@runoob:~$ mkdir -p ~/nginx/www ~/nginx/logs ~/nginx/conf

www目录将映射为nginx容器配置的虚拟目录
logs目录将映射为nginx容器的日志目录
conf目录里的配置文件将映射为nginx容器的配置文件
进入创建的nginx目录,创建Dockerfile

FROM debian:jessie

MAINTAINER NGINX Docker Maintainers "[email protected]"

ENV NGINX_VERSION 1.10.1-1~jessie

RUN apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys 573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62 
        && echo "deb http://nginx.org/packages/debian/ jessie nginx" >> /etc/apt/sources.list 
        && apt-get update 
        && apt-get install --no-install-recommends --no-install-suggests -y 
                                                ca-certificates 
                                                nginx=${NGINX_VERSION} 
                                                nginx-module-xslt 
                                                nginx-module-geoip 
                                                nginx-module-image-filter 
                                                nginx-module-perl 
                                                nginx-module-njs 
                                                gettext-base 
        && rm -rf /var/lib/apt/lists/*

# forward request and error logs to docker log collector
RUN ln -sf /dev/stdout /var/log/nginx/access.log 
        && ln -sf /dev/stderr /var/log/nginx/error.log

EXPOSE 80 443

CMD ["nginx", "-g", "daemon off;"]

通过Dockerfile创建一个镜像,替换成你自己的名字

docker build -t nginx .

创建完成后,我们可以在本地的镜像列表里查找到刚刚创建的镜像

runoob@runoob:~/nginx$ docker images nginx
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nginx               latest              555bbd91e13c        3 days ago          182.8 MB

方法二、docker pull nginx

查找Docker Hub上的nginx镜像

runoob@runoob:~/nginx$ docker search nginx
NAME                      DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
nginx                     Official build of Nginx.                        3260      [OK]       
jwilder/nginx-proxy       Automated Nginx reverse proxy for docker c...   674                  [OK]
richarvey/nginx-php-fpm   Container running Nginx + PHP-FPM capable ...   207                  [OK]
million12/nginx-php       Nginx + PHP-FPM 5.5, 5.6, 7.0 (NG), CentOS...   67                   [OK]
maxexcloo/nginx-php       Docker framework container with Nginx and ...   57                   [OK]
webdevops/php-nginx       Nginx with PHP-FPM                              39                   [OK]
h3nrik/nginx-ldap         NGINX web server with LDAP/AD, SSL and pro...   27                   [OK]
bitnami/nginx             Bitnami nginx Docker Image                      19                   [OK]
maxexcloo/nginx           Docker framework container with Nginx inst...   7                    [OK]
...

这里我们拉取官方的镜像

runoob@runoob:~/nginx$ docker pull nginx

等待下载完成后,我们就可以在本地镜像列表里查到REPOSITORY为nginx的镜像。
使用nginx镜像
运行容器

runoob@runoob:~/nginx$ docker run -p 80:80 --name mynginx -v $PWD/www:/www -v $PWD/conf/nginx.conf:/etc/nginx/nginx.conf -v $PWD/logs:/wwwlogs  -d nginx  
45c89fab0bf9ad643bc7ab571f3ccd65379b844498f54a7c8a4e7ca1dc3a2c1e
runoob@runoob:~/nginx$

命令说明:
-p 80:80:将容器的80端口映射到主机的80端口
–name mynginx:将容器命名为mynginx
-v $PWD/www:/www:将主机中当前目录下的www挂载到容器的/www
-v $PWD/conf/nginx.conf:/etc/nginx/nginx.conf:将主机中当前目录下的nginx.conf挂载到容器的/etc/nginx/nginx.conf
-v $PWD/logs:/wwwlogs:将主机中当前目录下的logs挂载到容器的/wwwlogs
查看容器启动情况

runoob@runoob:~/nginx$ docker ps
CONTAINER ID        IMAGE        COMMAND                      PORTS                         NAMES
45c89fab0bf9        nginx        "nginx -g 'daemon off"  ...  0.0.0.0:80->80/tcp, 443/tcp   mynginx
f2fa96138d71        tomcat       "catalina.sh run"       ...  0.0.0.0:81->8080/tcp          tomcat

通过浏览器访问
虚拟化技术

Zabbix3监控Nginx性能状态

本文主要介绍使用zabbix来监控nginx的性能状态,包括当前活动的连接数,已经收到的请求数,已经处理的请求数等。

编辑nginx配置文件

vim /etc/nginx/nginx.conf
server {
        listen  8082;
        location /nginx_status {
                stub_status on;
                access_log   off;
        }
}

重启nginx

/etc/init.d/nginx restart

创建监控脚本

mkdir /etc/zabbix/scripts/
chown zabbix:zabbix -R /etc/zabbix/scripts/
chmod 750 /etc/zabbix/scripts/
vim /etc/zabbix/scripts/nginx-stats.sh
#!/bin/bash
##### OPTIONS VERIFICATION #####
if [[ -z "$1" || -z "$2" || -z "$3" ]]; then
  exit 1
fi
##### PARAMETERS #####
RESERVED="$1"
METRIC="$2"
STATSURL="$3"
CURL="/usr/bin/curl"
CACHE_TTL="55"
CACHE_FILE="/tmp/zabbix.nginx.`echo $STATSURL | md5sum | cut -d" " -f1`.cache"
EXEC_TIMEOUT="1"
NOW_TIME=`date '+%s'`
##### RUN #####
if [ -s "${CACHE_FILE}" ]; then
  CACHE_TIME=`stat -c"%Y" "${CACHE_FILE}"`
else
  CACHE_TIME=0
fi
DELTA_TIME=$((${NOW_TIME} - ${CACHE_TIME}))
#
if [ ${DELTA_TIME} -lt ${EXEC_TIMEOUT} ]; then
  sleep $((${EXEC_TIMEOUT} - ${DELTA_TIME}))
elif [ ${DELTA_TIME} -gt ${CACHE_TTL} ]; then
  echo "" >> "${CACHE_FILE}" # !!!
  DATACACHE=`${CURL} --insecure -s "${STATSURL}" 2>&1`
  echo "${DATACACHE}" > "${CACHE_FILE}" # !!!
  chmod 640 "${CACHE_FILE}"
fi
#
if [ "${METRIC}" = "active" ]; then
  cat "${CACHE_FILE}" | grep "Active connections" | cut -d':' -f2
fi
if [ "${METRIC}" = "accepts" ]; then
  cat "${CACHE_FILE}" | sed -n '3p' | cut -d" " -f2
fi
if [ "${METRIC}" = "handled" ]; then
  cat "${CACHE_FILE}" | sed -n '3p' | cut -d" " -f3
fi
if [ "${METRIC}" = "requests" ]; then
  cat "${CACHE_FILE}" | sed -n '3p' | cut -d" " -f4
fi
if [ "${METRIC}" = "reading" ]; then
  cat "${CACHE_FILE}" | grep "Reading" | cut -d':' -f2 | cut -d' ' -f2
fi
if [ "${METRIC}" = "writing" ]; then
  cat "${CACHE_FILE}" | grep "Writing" | cut -d':' -f3 | cut -d' ' -f2
fi
if [ "${METRIC}" = "waiting" ]; then
  cat "${CACHE_FILE}" | grep "Waiting" | cut -d':' -f4 | cut -d' ' -f2
fi
#
exit 0

增加执行权限

chown zabbix:zabbix /etc/zabbix/scripts/nginx-stats.sh
chmod 540 /etc/zabbix/scripts/nginx-stats.sh

测试脚本

sudo -u zabbix /etc/zabbix/scripts/nginx-stats.sh none active http://192.168.42.70:8082/nginx_status

到zabbix agent添加nginx监控

vim /etc/zabbix/zabbix_agentd.conf
UserParameter=nginx[*],/etc/zabbix/scripts/nginx-stats.sh "none" "$1" "$2"

重启zabbix agent

/etc/init.d/zabbix-agentd restart

测试监控

zabbix_get -s HOST -k "nginx[active,http://192.168.42.70:8082/nginx_status]"

添加监控项

在zabbix web中依次添加监控项,如添加active

nginx[active,http://192.168.42.70:8082/nginx_status]

完成后依次添加accepts,handled,requests,reading,writing,waiting的监控

OpenResty(Nginx Lua)统计网站访问信息

背景

之前的一篇文章openresty(nginx lua)统计域名状态码、平均响应时间和流量实现了对域名状态码,平均响应时间和流量的统计。但之前的统计方法没有实现当某一域名404或500等状态码超过一定数量后发送具体的url来快速定位位置。这个功能我们其实是通过统计网站日志来实现了。为了摆脱对网站日志的依赖以及提高统计性能,我们尝试把此功能也用nginx lua来实现。具体的使用方法与之前的文章一样,这里只是更新了两个lua脚本。

使用方法

1、获取域名devops.webres.wang 404状态码数量

  1. curl -s "localhost/domain_status?count=status&host=devops.webres.wang&status=404"

输出:
10 688
第一列为状态码数量,第二列为域名请求总数
2、获取当域名devops.webres.wang 404状态码超过50个时,输出前10个url

  1. curl -s "localhost/domain_status?count=statusUrl&host=devops.webres.wang&status=404&exceed=50&output=10"

输出:
/hello-world 90
/centos 10

第一列为url,第二列为url请求次数。
3、获取域名devops.webres.wang upstream一分钟内平均耗时

  1. curl -s "localhost/domain_status?count=upT&host=devops.webres.wang"

输出:
0.02 452
第一列为upstream平均耗时,第二列为域名总请求次数。
4、获取当域名devops.webres.wang upstream平均耗时超过0.5秒时,输出其url

  1. curl -s "localhost/domain_status?count=upTUrl&host=devops.webres.wang&exceed=0.5"

输出:
/hello.php 0.82 52
第一列为url,第二列为此url平均耗时,第三列为此url请求次数。监控此接口数据可以快速定位出具体哪些url慢了。
5、获取域名devops.webres.wang request time平均耗时

  1. curl -s "localhost/domain_status?count=reqT&host=devops.webres.wang"

输出:
1.82 52
第一列为平均耗时,第二列为域名请求数。request time是指完成整个请求所需要的时间(包括把数据传输到用户浏览器的时间)。对于php请求,upstream time指的是nginx把php请求传给fastcgi到完成数据接收所需时间。所以request time永远大于upstream time。
6、获取域名devops.webres.wang占用的带宽(单位:字节/秒)

  1. curl -s "localhost/domain_status?count=flow&host=devops.webres.wang"

输出:
1024 52
第一列为此域名一分钟内平均传输速率,单位为字节/秒,第二列为域名请求总数。

相关脚本

log_acesss.lua

  1. local access = ngx.shared.access
  2. local host = ngx.var.host or "unknow"
  3. local status = ngx.var.status
  4. local body_bytes_sent = ngx.var.body_bytes_sent
  5. local request_time = ngx.var.request_time
  6. local upstream_response_time = ngx.var.upstream_response_time or 0
  7. local request_uri = ngx.var.request_uri or "/unknow"
  8. local timestamp = ngx.time()
  9. local expire_time = 70
  10.  
  11. local status_key = table.concat({host,"-",status,"-",timestamp})
  12. local flow_key = table.concat({host,"-flow-",timestamp})
  13. local req_time_key = table.concat({host,"-reqt-",timestamp})
  14. local up_time_key = table.concat({host,"-upt-",timestamp})
  15. local total_key = table.concat({host,"-total-",timestamp})
  16.  
  17. — 域名总请求数
  18. local n,e = access:incr(total_key,1)
  19. if not n then
  20. access:set(total_key, 1, expire_time)
  21. end
  22.  
  23. — 域名状态码请求数
  24. local n,e = access:incr(status_key,1)
  25. if not n then
  26. access:set(status_key, 1, expire_time)
  27. end
  28.  
  29. — 域名流量
  30. local n,e = access:incr(flow_key,body_bytes_sent)
  31. if not n then
  32. access:set(flow_key, body_bytes_sent, expire_time)
  33. end
  34.  
  35. — 域名请求耗时
  36. local n,e = access:incr(req_time_key,request_time)
  37. if not n then
  38. access:set(req_time_key, request_time, expire_time)
  39. end
  40.  
  41. — 域名upstream耗时
  42. local n,e = access:incr(up_time_key,upstream_response_time)
  43. if not n then
  44. access:set(up_time_key, upstream_response_time, expire_time)
  45. end
  46.  
  47. — 获取不带参数的uri
  48. local m, err = ngx.re.match(request_uri, "(.*?)\?")
  49. local request_without_args = m and m[1] or request_uri
  50.  
  51. — 存储状态码大于400的url
  52. if tonumber(status) >= 400 then
  53. — 拼接url,状态码,字节数等字段
  54. local request_log_t = {}
  55. table.insert(request_log_t,host)
  56. table.insert(request_log_t,request_without_args)
  57. table.insert(request_log_t,status)
  58. local request_log = table.concat(request_log_t," ")
  59.  
  60. — 把拼接的字段储存在字典中
  61. local log_key = table.concat({"status-",timestamp})
  62. local request_log_dict = access:get(log_key) or ""
  63. if request_log_dict == "" then
  64. request_log_dict = request_log
  65. else
  66. request_log_dict = table.concat({request_log_dict,"n",request_log})
  67. end
  68. access:set(log_key, request_log_dict, expire_time)
  69. end
  70.  
  71. — 存储upstream time大于0.5的url
  72. if tonumber(upstream_response_time) > 0.5 then
  73. — 拼接url,状态码,字节数等字段
  74. local request_log_t = {}
  75. table.insert(request_log_t,host)
  76. table.insert(request_log_t,request_without_args)
  77. table.insert(request_log_t,upstream_response_time)
  78. local request_log = table.concat(request_log_t," ")
  79.  
  80. — 把拼接的字段储存在字典中
  81. local log_key = table.concat({"upt-",timestamp})
  82. local request_log_dict = access:get(log_key) or ""
  83. if request_log_dict == "" then
  84. request_log_dict = request_log
  85. else
  86. request_log_dict = table.concat({request_log_dict,"n",request_log})
  87. end
  88. access:set(log_key, request_log_dict, expire_time)
  89. end

domain_status.lua

  1. — 各参数用法:
  2. — count=status,host=xxx.com,status=404 统计xxx.com域名一分钟内404状态码个数.
  3. — count=statusUrl,host=xxx.com,status=404,exceed=50,output=30 当xxx.com域名404状态码一分钟内超过50个时,输出前30个url,否则返回空.
  4.  
  5. — count=upT,host=xxx.com 统计xxx.com域名一分钟内平均upsteam耗时
  6. — count=upTUrl,host=xxx.com,exceed=0.5 输出upstreamTime超过0.5秒的url,没有就返回空
  7.  
  8. — count=reqT,host=xxx.com 统计xxx.com域名一分钟内平均请求耗时
  9. — count=flow,host=xxx.com 统计xxx.com域名一分钟内流量(单位字节/秒)
  10.  
  11. — 函数: 获取迭代器值
  12. local get_field = function(iterator)
  13.     local m,err = iterator
  14.     if err then
  15.         ngx.log(ngx.ERR, "get_field iterator error: ", err)
  16.         ngx.exit(ngx.HTTP_OK)
  17.     end
  18.     return m[0]
  19. end
  20.  
  21. — 函数: 按值排序table
  22. local getKeysSortedByValue = function (tbl, sortFunction)
  23.   local keys = {}
  24.   for key in pairs(tbl) do
  25.     table.insert(keys, key)
  26.   end
  27.  
  28.   table.sort(keys, function(a, b)
  29.     return sortFunction(tbl[a], tbl[b])
  30.   end)
  31.  
  32.   return keys
  33. end
  34.  
  35. — 函数: 判断table是否存在某元素
  36. local tbl_contain = function(table,element)
  37.     for k in pairs(table) do
  38.         if k == element then
  39.             return true
  40.         end
  41.     end
  42.     return false
  43. end
  44.  
  45. local access = ngx.shared.access
  46. local now = ngx.time()
  47. local one_minute_ago = now – 60
  48.  
  49. — 获取参数
  50. local args = ngx.req.get_uri_args()
  51. local count_arg = args["count"]
  52. local host_arg = args["host"]
  53. local status_arg = args["status"]
  54. local exceed_arg = args["exceed"]
  55. local output_arg = args["output"]
  56. local count_t = {["status"]=0,["statusUrl"]=0,["upT"]=0,["upTUrl"]=0,["reqT"]=0,["flow"]=0}
  57.  
  58. — 检查参数是否满足
  59. if not tbl_contain(count_t,count_arg) then
  60.     ngx.print("count arg invalid.")
  61.     ngx.exit(ngx.HTTP_OK)
  62. end
  63.  
  64. if not host_arg then
  65.     ngx.print("host arg not found.")
  66.     ngx.exit(ngx.HTTP_OK)
  67. end
  68.  
  69. if count_arg == "status" and not status_arg then
  70.     ngx.print("status arg not found.")
  71.     ngx.exit(ngx.HTTP_OK)
  72. end
  73.  
  74. if count_arg == "statusUrl" and not (status_arg and exceed_arg and output_arg)  then
  75.     ngx.print("status or exceed or output arg not found.")
  76.     ngx.exit(ngx.HTTP_OK)
  77. end
  78.  
  79. if count_arg == "upTUrl" and not exceed_arg then
  80.     ngx.print("exceed arg not found.")
  81.     ngx.exit(ngx.HTTP_OK)
  82. end
  83.  
  84. — 检查参数是否合法
  85. if status_arg and ngx.re.find(status_arg, "^[0-9]{3}$") == nil then
  86.     ngx.print("status arg must be a valid httpd code.")
  87.     ngx.exit(ngx.HTTP_OK)
  88. end
  89.  
  90. if exceed_arg and ngx.re.find(exceed_arg, "^[0-9.]+$") == nil then
  91.     ngx.print("exceed arg must be a number.")
  92.     ngx.exit(ngx.HTTP_OK)
  93. end
  94.  
  95. if output_arg and ngx.re.find(output_arg, "^[0-9]+$") == nil then
  96.     ngx.print("output arg must be a number.")
  97.     ngx.exit(ngx.HTTP_OK)
  98. end
  99.  
  100. — 开始统计
  101. local url
  102. local status_code
  103. local upstream_time
  104. local status_total = 0
  105. local host
  106. local req_total = 0
  107. local flow_total = 0
  108. local reqtime_total = 0
  109. local upstream_total = 0
  110. local status_url_t = {}
  111. local upstream_url_t = {}
  112. local upstream_url_count_t = {}
  113.  
  114. local status_log
  115. local upt_log
  116.  
  117. for second_num=one_minute_ago,now do
  118.     local flow_key = table.concat({host_arg,"-flow-",second_num})
  119.     local req_time_key = table.concat({host_arg,"-reqt-",second_num})
  120.     local up_time_key = table.concat({host_arg,"-upt-",second_num})
  121.     local total_req_key = table.concat({host_arg,"-total-",second_num})
  122.     local log_key
  123.     local log_line
  124.  
  125.     — 合并状态码大于等于400的请求日志到变量status_log
  126.     log_key = table.concat({"status-",second_num})
  127.     log_line = access:get(log_key) or ""
  128.     if not (log_line == "") then
  129.         status_log = table.concat({log_line,"n",status_log})
  130.     end
  131.  
  132.     — 合并upstream time大于0.5秒的请求日志到变量upt_log
  133.     log_key = table.concat({"upt-",second_num})
  134.     log_line = access:get(log_key) or ""
  135.     if not (log_line == "") then
  136.         upt_log = table.concat({log_line,"n",upt_log})
  137.     end
  138.  
  139.     — 域名总请求数
  140.     local req_sum = access:get(total_req_key) or 0
  141.     req_total = req_total + req_sum
  142.  
  143.     if count_arg == "status" or count_arg == "statusUrl" then
  144.         local status_key = table.concat({host_arg,"-",status_arg,"-",second_num})
  145.         local status_sum = access:get(status_key) or 0
  146.         status_total = status_total + status_sum
  147.     end
  148.  
  149.     if count_arg == "flow" then
  150.         local flow_sum = access:get(flow_key) or 0
  151.         flow_total = flow_total + flow_sum
  152.     end
  153.  
  154.     if count_arg == "reqT" then
  155.         local req_time_sum = access:get(req_time_key) or 0
  156.         reqtime_total = reqtime_total + req_time_sum
  157.     end
  158.  
  159.     if count_arg == "upT" then
  160.         local up_time_sum = access:get(up_time_key) or 0
  161.         upstream_total = upstream_total + up_time_sum
  162.     end
  163. end
  164.  
  165. — 统计状态码url
  166. if count_arg == "statusUrl" and status_log and not (status_log == "") then
  167.     local iterator, err = ngx.re.gmatch(status_log,".+n")
  168.     if not iterator then
  169.         ngx.log(ngx.ERR, "status_log iterator error: ", err)
  170.         return
  171.     end
  172.     for line in iterator do
  173.         if not line[0] then
  174.             ngx.log(ngx.ERR, "line[0] is nil")
  175.             return
  176.         end
  177.         local iterator, err = ngx.re.gmatch(line[0],"[^ n]+")
  178.         if not iterator then
  179.             ngx.log(ngx.ERR, "line[0] iterator error: ", err)
  180.             return
  181.         end
  182.  
  183.         host = get_field(iterator())
  184.         url = get_field(iterator())
  185.         status_code = get_field(iterator())
  186.  
  187.         if status_code == status_arg then
  188.             if status_url_t[url] then
  189.                 status_url_t[url] = status_url_t[url] + 1
  190.             else
  191.                 status_url_t[url] = 1
  192.             end
  193.         end
  194.  
  195.     end   
  196. end
  197.  
  198. — 统计upstream time大于0.5秒url
  199. if count_arg == "upTUrl" and upt_log and not (upt_log == "") then
  200.     local iterator, err = ngx.re.gmatch(upt_log,".+n")
  201.     if not iterator then
  202.         ngx.log(ngx.ERR, "upt_log iterator error: ", err)
  203.         return
  204.     end
  205.     for line in iterator do
  206.         if not line[0] then
  207.             ngx.log(ngx.ERR, "line[0] is nil")
  208.             return
  209.         end
  210.         local iterator, err = ngx.re.gmatch(line[0],"[^ n]+")
  211.         if not iterator then
  212.             ngx.log(ngx.ERR, "line[0] iterator error: ", err)
  213.             return
  214.         end
  215.  
  216.         host = get_field(iterator())
  217.         url = get_field(iterator())
  218.         upstream_time = get_field(iterator())
  219.         upstream_time = tonumber(upstream_time) or 0
  220.  
  221.         — 统计各url upstream平均耗时
  222.         if host == host_arg then
  223.             if upstream_url_t[url] then
  224.                 upstream_url_t[url] = upstream_url_t[url] + upstream_time
  225.             else
  226.                 upstream_url_t[url] = upstream_time
  227.             end
  228.  
  229.             if upstream_url_count_t[url] then
  230.                 upstream_url_count_t[url] = upstream_url_count_t[url] + 1
  231.             else
  232.                 upstream_url_count_t[url] = 1
  233.             end
  234.         end   
  235.     end   
  236. end
  237.  
  238. — 输出结果
  239. if count_arg == "status" then
  240.     ngx.print(status_total," ",req_total)
  241.  
  242. elseif count_arg == "flow" then
  243.     ngx.print(flow_total," ",req_total)
  244.  
  245. elseif count_arg == "reqT" then
  246.     local reqt_avg = 0
  247.     if req_total == 0 then
  248.         reqt_avg = 0
  249.     else
  250.         reqt_avg = reqtime_total/req_total
  251.     end
  252.     ngx.print(reqt_avg," ",req_total)
  253.  
  254. elseif count_arg == "upT" then
  255.     local upt_avg = 0
  256.     if req_total == 0 then
  257.             upt_avg = 0
  258.     else
  259.             upt_avg = upstream_total/req_total
  260.     end
  261.     ngx.print(upt_avg," ",req_total)
  262.  
  263. elseif count_arg == "statusUrl" then
  264.     if status_total > tonumber(exceed_arg) then
  265.         — 排序table
  266.         status_url_t_key = getKeysSortedByValue(status_url_t, function(a, b) return a > b end)
  267.         local output_body = ""
  268.         for i, uri in ipairs(status_url_t_key) do
  269.             if output_body == "" then
  270.                 output_body = table.concat({uri," ",status_url_t[uri]})
  271.             else   
  272.                 output_body = table.concat({output_body,"n",uri," ",status_url_t[uri]})
  273.             end
  274.             if i >= tonumber(output_arg) then
  275.                 ngx.print(output_body)
  276.                 ngx.exit(ngx.HTTP_OK)
  277.             end               
  278.         end
  279.  
  280.         ngx.print(output_body)
  281.         ngx.exit(ngx.HTTP_OK)
  282.     end
  283.  
  284. elseif count_arg == "upTUrl" then
  285.     local max_output = 30
  286.     local total_time = 0
  287.     local total_count = 0
  288.     local output_body = ""
  289.     local i = 0
  290.     for url in pairs(upstream_url_t) do
  291.         i = i + 1
  292.         total_time = upstream_url_t[url]
  293.         total_count = upstream_url_count_t[url]
  294.         avg_time = upstream_url_t[url] / upstream_url_count_t[url]
  295.         if avg_time > tonumber(exceed_arg) then
  296.             output_body = table.concat({url," ",avg_time," ",total_count,"n",output_body})
  297.         end
  298.  
  299.         if i >= max_output then
  300.             ngx.print(output_body)
  301.             ngx.exit(ngx.HTTP_OK)
  302.         end           
  303.     end
  304.     ngx.print(output_body)
  305.     ngx.exit(ngx.HTTP_OK)
  306.  
  307. end