Arch Linux安装配置LAMP服务器

安装前

更新下系统

  1. sudo pacman -Syu

Apache

安装配置

1.安装Apache 2.4

  1. sudo pacman -Syu apache

2.编辑/etc/httpd/conf/extra/下的httpd-mpm.conf,调整资源使用配置,下面的配置是针对2GB服务器优化的:
/etc/httpd/conf/extra/httpd-mpm.conf:

  1. <IfModule mpm_prefork_module>
  2.         StartServers            4
  3.         MinSpareServers         20
  4.         MaxSpareServers         40
  5.         MaxRequestWorkers       200
  6.         MaxConnectionsPerChild  4500
  7. </IfModule>

3.编辑httpd-default.conf来关闭KeepAlive
/etc/httpd/conf/extra/httpd-default.conf

  1. KeepAlive Off

4.设置Apache开机启动

  1. sudo systemctl enable httpd.service

配置虚拟主机

虚拟主机可以配置多个域名。这些网站可以使用不同的用户来管理,或者一个用户,看你选择了。有许多种方法来配置虚拟主机,不过推荐使用下面的方法。
1.打开httpd.conf,编辑行DocumentRoot /srv/http来设置根目录:
/etc/httpd/conf/httpd.conf:

  1. DocumentRoot "/srv/http/default"

2.找到在/etc/httpd/conf/httpd.conf文件底部的Include conf/extra/httpd-vhosts.conf,取消其注释:
/etc/httpd/conf/httpd.conf

  1. Include conf/extra/httpd-vhosts.conf

3.打开在extra目录下的httpd-vhosts.conf,编辑虚拟主机示例配置,把example.com域名替换为你的。
/etc/httpd/conf/extra/httpd-vhosts.conf

  1. <VirtualHost *:80>
  2.      ServerAdmin [email protected]
  3.      ServerName example.com
  4.      ServerAlias www.example.com
  5.      DocumentRoot /srv/http/example.com/public_html/
  6.      ErrorLog /srv/http/example.com/logs/error.log
  7.      CustomLog /srv/http/example.com/logs/access.log combined
  8.             <Directory />
  9.                Order deny,allow
  10.                Allow from all
  11.             </Directory>
  12. </VirtualHost>

注意:
ErrorLog和CustomLog建议在每个虚拟主机指定,但不是必须的。如果像以上配置了,日志目录必须在Apache重启前创建好。

4.创建相关的目录

  1. sudo mkdir -p /srv/http/default
  2. sudo mkdir -p /srv/http/example.com/public_html
  3. sudo mkdir -p /srv/http/example.com/logs

5.创建好虚拟主机后,运行以下命令为启动Apache

  1. sudo systemctl start httpd.service

到目前为止,你应该能进入你的网站了。

注意:
当你更改配置时,需要重启Apache
sudo systemctl restart httpd.service

MySQL

安装配置

默认地,Arch Linux使用MariaDB替换了MySQL。MariaDB是用来替代MySQL的开源软件。与mysql相关的系统命令都兼容。
1.安装mariadb, mariadb-clients和libmariadbclient

  1. sudo pacman -Syu mariadb mariadb-clients libmariadbclient

2.安装MariaDB数据目录

  1. sudo mysql_install_db –user=mysql –basedir=/usr –datadir=/var/lib/mysql

3.启动MySQL并设置开机启动

  1. sudo systemctl start mysqld.service
  2. sudo systemctl enable mysqld.service

4.执行mysql_secure_installation,程序会引导你加固MySQL。你可以更改MySQL root密码,删除匿名用户,禁用root远程登录,以及删除test数据库:

  1. mysql_secure_installation

创建MySQL数据库

1.登录MySQL

  1. mysql -u root -p

输入root密码,将进入MySQL命令行终端。
2.创建一个数据库和一个授权此数据库权限的用户。在这个例子中数据库名为webdata,用户为webuser,密码为password:

  1. create database webdata;
  2. grant all on webdata.* to ‘webuser’ identified by ‘password’;

3.退出MySQL

  1. quit

安装好Apache和MySQL后,下面准备安装PHP。

PHP

1.安装PHP,PHP扩展和应用仓库:

  1. sudo pacman -Syu php php-apache

如果需要MySQL支持,需要安装php5-mysql

  1. sudo apt-get install php5-mysql

2.一旦PHP5安装完成,可以编辑/etc/php5/apache2/php.ini来调整配置,如设置错误等级显示,日志,性能等。下面是部分调整:
/etc/php5/apache2/php.ini

  1. error_reporting = E_COMPILE_ERROR|E_RECOVERABLE_ERROR|E_ERROR|E_CORE_ERROR
  2. error_log = /var/log/php/error.log 
  3. max_input_time = 30
  4. extension=mysql.so

注意:
确保以上行已取消注释。注释的行前面带;号。
3.为PHP创建日志目录,并设置所有者为Apache运行用户:

  1. sudo mkdir /var/log/php
  2. sudo chown www-data /var/log/php

4.编辑/etc/httpd/conf/httpd.conf文件来激活PHP模块
/etc/httpd/conf/httpd.conf

  1. # Dynamic Shared Object (DSO) Support
  2. LoadModule php5_module modules/libphp5.so
  3.         
  4. # Supplemental configuration
  5. # PHP 5
  6. Include conf/extra/php5_module.conf
  7.  
  8. # Located in the <IfModule mime_module>
  9. AddType application/x-httpd-php .php
  10. AddType application/x-httpd-php-source .phps

5.同样的文件,注释行LoadModule mpm_event_module modules/mod_mpm_event.so,在前面加#号,并增加行LoadModule mpm_prefork_module modules/mod_mpm_prefork.so:
/etc/httpd/conf/httpd.conf

  1. #LoadModule mpm_event_module modules/mod_mpm_event.so
  2. LoadModule mpm_prefork_module modules/mod_mpm_prefork.so

6.重启Apache:

  1. sudo systemctl restart httpd.service

到目前为止已把LAMP安装完毕。

Debian 8 (Jessie)安装配置LAMP

安装前

更新下系统

  1. sudo apt-get update && sudo apt-get upgrade

Apache

安装配置

1.安装Apache 2.4

  1. sudo apt-get install apache2

2.编辑Apache主配置文件,关闭KeepAlive设置:
/etc/apache2/apache2.conf:

  1. KeepAlive Off

3.打开/etc/apache2/mods-available/mpm_prefork.conf,按你的需求更改,下面的配置是针对2GB服务器优化的:
/etc/apache2/mods-available/mpm_prefork.conf:

  1. # prefork MPM
  2. # StartServers: number of server processes to start
  3. # MinSpareServers: minimum number of server processes which are kept spare
  4. # MaxSpareServers: maximum number of server processes which are kept spare
  5. # MaxRequestWorkers: maximum number of server processes allowed to start
  6. # MaxConnectionsPerChild: maximum number of requests a server process serves
  7.  
  8. <IfModule mpm_prefork_module>
  9.         StartServers              4
  10.         MinSpareServers           20
  11.         MaxSpareServers           40
  12.         MaxRequestWorkers         200
  13.         MaxConnectionsPerChild    4500
  14. </IfModule>
  15.  
  16. # vim: syntax=apache ts=4 sw=4 sts=4 sr noet

4.在Debian 8系统,event模块默认激活。我们禁用此模块并使用prefork模块:

  1. sudo a2dismod mpm_event
  2. sudo a2enmod mpm_prefork

5.重启Apache:

  1. sudo systemctl restart apache2

配置虚拟主机

有许多种方法来配置虚拟主机,不过推荐使用下面的方法。
1.在/etc/apache2/sites-available/目录为你的网站创建一个配置文件,example.com.conf,替换example.com为你自己的域名。
文件:/etc/apache2/sites-available/example.com.conf

  1. <VirtualHost *:80>
  2.      ServerAdmin [email protected]
  3.      ServerName example.com
  4.      ServerAlias www.example.com
  5.      DocumentRoot /var/www/example.com/public_html/
  6.      ErrorLog /var/www/example.com/logs/error.log
  7.      CustomLog /var/www/example.com/logs/access.log combined
  8. </VirtualHost>

注意:
ErrorLog和CustomLog建议在每个虚拟主机指定,但不是必须的。如果像以上配置了,日志目录必须在Apache重启前创建好。

2.创建相关的目录

  1. sudo mkdir -p /var/www/example.com/public_html
  2. sudo mkdir /var/www/example.com/logs

3.激活网站虚拟主机

  1. sudo a2ensite example.com.conf

注意:
如果你需要禁用你的网站,执行:
sudo a2dissite example.com.conf

4.重启Apache

  1. sudo systemctl restart apache2

假如你已经配置了DNS,并指向你的服务器,虚拟主机现在应该可以正常工作了。

MySQL

安装配置

1.安装MySQL

  1. sudo apt-get install mysql-server

当提示输入密码时,输入一个安全的密码
2.执行mysql_secure_installation,程序会引导你加固MySQL。你可以更改MySQL root密码,删除匿名用户,禁用root远程登录,以及删除test数据库:

  1. mysql_secure_installation

创建MySQL数据库

1.登录MySQL

  1. mysql -u root -p

输入root密码,将进入MySQL命令行终端。
2.创建一个数据库和一个授权此数据库权限的用户。在这个例子中数据库名为webdata,用户为webuser,密码为password:

  1. create database webdata;
  2. grant all on webdata.* to ‘webuser’ identified by ‘password’;

3.退出MySQL

  1. quit

安装好Apache和MySQL后,下面准备安装PHP。

PHP

1.安装PHP,PHP扩展和应用仓库:

  1. sudo apt-get install php5 php-pear

如果需要MySQL支持,需要安装php5-mysql

  1. sudo apt-get install php5-mysql

2.一旦PHP5安装完成,可以编辑/etc/php5/apache2/php.ini来调整配置,如设置错误等级显示,日志,性能等。下面是部分调整:
/etc/php5/apache2/php.ini

  1. error_reporting = E_COMPILE_ERROR|E_RECOVERABLE_ERROR|E_ERROR|E_CORE_ERROR
  2. error_log = /var/log/php/error.log 
  3. max_input_time = 30

注意:
确保以上行已取消注释。注释的行前面带;号。
3.为PHP创建日志目录,并设置所有者为Apache运行用户:

  1. sudo mkdir /var/log/php
  2. sudo chown www-data /var/log/php

4.重启Apache:

  1. sudo systemctl restart apache2

到目前为止已把LAMP安装完毕。

CentOS 6安装LAMP

Apache Web服务器

安装和配置

1.安装Apache 2:

  1. sudo yum install httpd

2.编辑/etc/httpd/conf/下的httpd.conf,调整资源占用设置。下面的设置用于内存2GB的服务器:

注意:
在更改任何配置文件之前,建议备份下配置文件:

  1. cp /etc/httpd/conf/httpd.conf ~/httpd.conf.backup

/etc/httpd/conf/httpd.conf部分配置:

  1. KeepAlive Off
  2.  
  3.  
  4. <IfModule prefork.c>
  5.     StartServers        4
  6.     MinSpareServers     20
  7.     MaxSpareServers     40
  8.     MaxClients          200
  9.     MaxRequestsPerChild 4500
  10. </IfModule>

配置Apache虚拟主机

有许多种方法配置虚拟主机,推荐使用下面的方法。Apache默认会监听所有的IP。
1.在/etc/httpd/conf.d目录下创建一个vhost.conf文件,替换example.com为自己的域名:
/etc/httpd/conf.d/vhost.conf部分配置:

  1. NameVirtualHost *:80
  2.  
  3. <VirtualHost *:80>
  4.      ServerAdmin [email protected]
  5.      ServerName example.com
  6.      ServerAlias www.example.com
  7.      DocumentRoot /var/www/example.com/public_html/
  8.      ErrorLog /var/www/example.com/logs/error.log
  9.      CustomLog /var/www/example.com/logs/access.log combined
  10. </VirtualHost>

想添加为其它域名添加虚拟主机时,可以复制VirtualHost代码块,然后更改example.com域名即可。

注意:
ErrorLog和CustomLog建议在每个虚拟主机指定,但不是必须的。如果像以上配置了,日志目录必须在Apache重启前创建好。

2.创建相关的目录

  1. sudo mkdir -p /var/www/example.com/public_html
  2. sudo mkdir /var/www/example.com/logs

3.首次启动Apache,并设置开机启动

  1. sudo service httpd start
  2. sudo /sbin/chkconfig –levels 235 httpd on

在浏览器输入域名之前,正常的话你应该会看到Apache的默认页面。

注意:
一旦你更改了vhost.conf,域名任何其它的配置文件,你都需要重载配置,命令为:
sudo service httpd reload

MySQL

安装和配置

1.安装MySQL

  1. sudo yum install mysql-server

2.启动MySQL,并设置开机启动

  1. sudo service mysqld start
  2. sudo /sbin/chkconfig –levels 235 mysqld on

3.运行mysql_secure_installation加固MySQL,程序会引导你加固MySQL。你可以更改MySQL root密码,删除匿名用户,禁用root远程登录,以及删除test数据库:

  1. mysql_secure_installation

创建MySQL数据库

1.登录MySQL

  1. mysql -u root -p

输入root密码,将进入MySQL命令行终端。
2.创建一个数据库和一个授权此数据库权限的用户。在这个例子中数据库名为webdata,用户为webuser,密码为password:

  1. create database webdata;
  2. grant all on webdata.* to ‘webuser’ identified by ‘password’;

3.退出MySQL

  1. quit

安装好Apache和MySQL后,下面准备安装PHP。

PHP

1.安装PHP,PHP扩展和应用仓库:

  1. sudo yum install php php-pear

如果需要MySQL支持,需要安装php-mysql

  1. sudo yum install php-mysql

2.一旦PHP5安装完成,可以编辑/etc/php.ini来调整配置,如设置错误等级显示,日志,性能等。下面是部分调整:
/etc/php.ini

  1. error_reporting = E_COMPILE_ERROR|E_RECOVERABLE_ERROR|E_ERROR|E_CORE_ERROR
  2. error_log = /var/log/php/error.log
  3. max_input_time = 30

注意:
确保以上行已取消注释。注释的行前面带;号。

3.为PHP创建日志目录,并设置所有者为Apache运行用户:

  1. sudo mkdir /var/log/php
  2. sudo chown apache /var/log/php

4.重启Apache:

  1. sudo service httpd restart

到目前为止已把LAMP安装完毕。

Ubuntu 12.04 (Precise Pangolin)安装LAMP(Apache MySQL PHP)服务器

安装前

更新下系统

  1. sudo apt-get update && sudo apt-get upgrade

Apache

安装配置

1.安装Apache 2

  1. sudo apt-get install apache2

2.编辑Apache主配置文件,调整资源使用设置。下面的设置适用于内存为2GB的机器:
/etc/apache2/apache2.conf部分配置:

  1. KeepAlive Off
  2.  
  3. <IfModule mpm_prefork_module>
  4. StartServers 4
  5. MinSpareServers 20
  6. MaxSpareServers 40
  7. MaxClients 200
  8. MaxRequestsPerChild 4500
  9. </IfModule>

配置虚拟主机

有许多种方法来配置虚拟主机,不过推荐使用下面的方法。
1.在/etc/apache2/sites-available/目录为你的网站创建一个配置文件,example.com.conf,替换example.com为你自己的域名。
文件:/etc/apache2/sites-available/example.com.conf

  1. <VirtualHost *:80>
  2.      ServerAdmin [email protected]
  3.      ServerName example.com
  4.      ServerAlias www.example.com
  5.      DocumentRoot /var/www/example.com/public_html/
  6.      ErrorLog /var/www/example.com/logs/error.log
  7.      CustomLog /var/www/example.com/logs/access.log combined
  8. </VirtualHost>

注意:
ErrorLog和CustomLog建议在每个虚拟主机指定,但不是必须的。如果像以上配置了,日志目录必须在Apache重启前创建好。

2.创建相关的目录

  1. sudo mkdir -p /var/www/example.com/public_html
  2. sudo mkdir /var/www/example.com/logs

3.激活网站虚拟主机

  1. sudo a2ensite example.com.conf

注意:
如果你需要禁用你的网站,执行:
sudo a2dissite example.com.conf

4.重载Apache

  1. sudo service apache2 reload

假如你已经配置了DNS,并指向你的服务器,虚拟主机现在应该可以正常工作了。

MySQL

安装配置

1.安装MySQL

  1. sudo apt-get install mysql-server

当提示输入密码时,输入一个安全的密码
2.执行mysql_secure_installation,程序会引导你加固MySQL。你可以更改MySQL root密码,删除匿名用户,禁用root远程登录,以及删除test数据库:

  1. mysql_secure_installation

创建MySQL数据库

1.登录MySQL

  1. mysql -u root -p

输入root密码,将进入MySQL命令行终端。
2.创建一个数据库和一个授权此数据库权限的用户。在这个例子中数据库名为webdata,用户为webuser,密码为password:

  1. create database webdata;
  2. grant all on webdata.* to ‘webuser’ identified by ‘password’;

3.退出MySQL

  1. quit

安装好Apache和MySQL后,下面准备安装PHP。

PHP

1.安装PHP,PHP扩展和应用仓库:

  1. sudo apt-get install php5 php-pear

如果需要MySQL支持,需要安装php5-mysql

  1. sudo apt-get install php5-mysql

2.一旦PHP5安装完成,可以编辑/etc/php5/apache2/php.ini来调整配置,如设置错误等级显示,日志,性能等。下面是部分调整:
/etc/php5/apache2/php.ini

  1. error_reporting = E_COMPILE_ERROR|E_RECOVERABLE_ERROR|E_ERROR|E_CORE_ERROR
  2. error_log = /var/log/php/error.log 
  3. max_input_time = 30

注意:
确保以上行已取消注释。注释的行前面带;号。
3.为PHP创建日志目录,并设置所有者为Apache运行用户:

  1. sudo mkdir /var/log/php
  2. sudo chown www-data /var/log/php

4.重启Apache:

  1. sudo service apache2 restart

到目前为止已把LAMP安装完毕。

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

为什么Wireshark无法解密HTTPS数据

问题

由于需要定位一个问题,在服务器上tcpdump抓取https数据包,然后下载到本地打开wireshark分析。然后我们下载域名私钥配置到wireshark,发现数据包居然无法解密。是wireshark配置密钥的方法不对?但谷歌了好多文章都是说这样配置的。由于对HTTPS认识不够深,一时不知道如何入手解决。没办法,只能先了解tls这个协议了,于是查看了TLS1.2的RFC文档,终于勉强解答了这个疑惑。

TLS握手整个过程

在解决这个问题之前,先整体了解一下TLS的握手全过程。省略了不常见的过程。如图:
系统管理
下面按顺序介绍各握手步骤。

Client Hello

这是TLS握手的第一步,由客户端发起请求。此协议主要包括了一个客户端生成的随机字符串(用来下面生成session key),还有客户端支持的加密套件列表。如图:
系统管理

Server Hello

服务器收到客户端的Client Hello数据包之后,根据客户端发来的加密套件列表,选择一个加密套件,也生成一个随机字符串返回给客户端。我们看到下图中的加密套件为,密钥交换算法使用ECDHE_RSA,对称加密算法使用AES_256_GCM_SHA384,如图:
系统管理

Server Certificate

接着服务器再返回证书列表,包括证书链及域名证书。返回的证书用来给客户端验证当前连接服务器的身份,防止中间人攻击。

Server Key Exchange

Server Key Exchange协议包,由服务器返回,主要目的是与客户端交换用于数据对称加密的密钥。如图:
系统管理

Server Hello Done

服务器返回此协议数据,告诉客户端已经完成返回所需用于密钥交换的数据。服务器等待客户端响应。

Client Key Exchange

客户端根据服务器返回的DH密钥数据生成DH公共数据也发给服务器,用来生成最终的pre-master-secret。如图:
系统管理

Change Cipher Spec

此协议用于客户端和服务器相互告知也完成密钥交换过程,可以切换到对称加密过程。

到这里大概的TLS握手过程就结束了。为解决本文中的问题,还需要了解密钥交换的算法,RSA和Diffie–Hellman。

密钥交换算法

密钥交换算法目前常用的有RSA和Diffie-Hellman。
对于密钥交换使用RSA算法,pre-master-secret由客户端生成,并使用公钥加密传输给服务器。
对于密钥交换使用Diffie-Hellman算法,pre-master-secret则通过在Key Exchange阶段交换的信息,由各自计算出pre-master-secret。所以pre-master-secret没有存到硬盘,也没有在网络上传输,wireshark就无法获取session key,也就无法解密应用数据。那我们是否可以反向计算出pre-master-secret呢?理论上可以,但是非常困难。
对Diffie-Hellman算法感兴趣的可以参考https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange

解决方法

说了这么多,究竟有什么办法可以让wireshark解密数据?我们可以通过下面几种方法来使wireshark能解密https数据包。

  • 1. 中间人攻击;
  • 2. 设置web服务器使用RSA作为交换密钥算法;
  • 3. 如果是用chrome,firefox,可以设置导出pre-master-secret log,然后wireshark设置pre-master-secret log路径,这样就可以解密了。

Linux FTP命令行被动模式连接PureFtpd超时

问题

最近在服务器安装了pureftpd提供给公司同事上传文件用。在交付前使用FileZilla测试没有问题,但是同事反映在Linux下ftp不能使用。
自己在Linux下测试了一下,一直卡在Entering Passive Mode,最后超时。
FTP

原因

而在windows下使用FileZilla测试并没有问题。仔细看了下FileZilla的日志,发现有一行
FTP
想到pureftpd用的是云主机,而云主机的网卡只绑定有内网IP,外网IP应该是绑定在路由了。猜测ftp客户端使用被动模式连接pureftpd的时候,pureftpd返回了一个内网IP。
通过wireshark抓包发现:
FTP
可以知道服务器的确返回了一个内网地址。

方法

那能不能强制pureftpd返回外网IP呢。通过查看pureftpd文档,发现在pureftpd.conf加入:

ForcePassiveIP 1.1.1.1

1.1.1.1为pureftpd外网ip。重启之后问题解决。

rsync增量传输大文件优化技巧

问题

rsync用来同步数据非常的好用,特别是增量同步。但是有一种情况如果不增加特定的参数就不是很好用了。比如你要同步多个几十个G的文件,然后网络突然断开了一下,这时候你重新启动增量同步。但是发现等了好久都没有进行数据传输,倒是机器的IO一直居高不下。

原因

rsync具体的增量同步算法不太清楚。根据它的表现来看,可能在增量同步已经存在的一个文件时,会校验已传输部分数据是否已源文件一致,校验完成才继续增量同步这个文件剩下的数据。所以如果对一个大文件以这样的算法来增量同步是非常花时间并且占用IO资源的。

方法

半夜花了一个多小时查看了rsync的文档,发现有一个参数能快速恢复大文件的增量同步,–append。设置–append参数会在增量同步时计算文件大小并直接追加新的数据到文件,这样就省了费IO校验的过程。不过这个参数最好只在源文件和目标文件都不会更改的时候使用比较安全,比如备份的文件。

nginx-upload-module模块实现文件上传(multipart/form-data和断点续传)

前言

有时候我们想简单实现文件上传的功能,又不想使用额外的语言(比如PHP、Java),或者想实现文件的断点续传。这个时候Nginx的一个模块nginx-upload-module就能满足我们的需求。

模块安装

下载模块:

  1. cd /tmp
  2. wget https://codeload.github.com/vkholodkov/nginx-upload-module/zip/2.2
  3. unzip 2.2

安装模块:

  1. .configure –add-module=/tmp/nginx-upload-module-2.2/

multipart/form-data表单上传示例

nginx.conf配置:

  1. server {
  2. […]
  3.         location /upload {
  4.                 upload_pass @uploadHandler;
  5.                 upload_store /usr/local/nginx/upload_temp 1;
  6.                 upload_set_form_field $upload_field_name.path "$upload_tmp_path";
  7.         }
  8.  
  9.         location @uploadHandler {
  10.                 proxy_pass http://backend-host;
  11.         }
  12. […]
  13. }

这里在server里定义了upload location,这个location是上传的接口,还有@uploadHandler location,是当文件上传完成后,nginx模块会对这个location发送一些必要的信息,如文件上传的路径。
这里涉及了几个指令:

  • upload_pass @uploadHandler: 上传完成后会发送必要的数据到@uploadHandler;
  • upload_store /usr/local/nginx/upload_temp 1: 文件上传的临时目录;
  • upload_set_form_field $upload_field_name.path “$upload_tmp_path”: 设置文件上传完成后,把文件临时路径发送给upload_pass指定的location。

断点续传示例

nginx.conf配置

  1. server {
  2. […]
  3.         location /resumable_upload {
  4.                 upload_resumable on;
  5.                 upload_state_store /usr/local/nginx/upload_temp ;
  6.                 upload_pass @drivers_upload_handler;
  7.                 upload_store /usr/local/nginx/upload_temp;
  8.                 upload_set_form_field $upload_field_name.path "$upload_tmp_path";
  9.         }
  10.  
  11.         location @resumable_upload_handler {
  12.                 proxy_pass http://localhost:8002;
  13.         }
  14. […]
  15. }

与上一步multipart/form-data表单上传示例配置不同的地方有:

  • upload_resumable on: 开启断点续传功能;
  • upload_state_store /usr/local/nginx/upload_temp: 设置断点续传状态文件存储的目录。

上传文件第一个片段

  1. POST /upload HTTP/1.1
  2. Host: example.com
  3. Content-Length: 51201
  4. Content-Type: application/octet-stream
  5. Content-Disposition: attachment; filename="big.TXT"
  6. X-Content-Range: bytes 0-51200/511920
  7. Session-ID: 1111215056
  8.  
  9. <0-51200的字节文件数据>

上传文件第一个片段服务器响应

  1. HTTP/1.1 201 Created
  2. Date: Thu, 02 Sep 2010 12:54:40 GMT
  3. Content-Length: 14
  4. Connection: close
  5. Range: 0-51200/511920
  6.  
  7. 0-51200/511920

上传文件最后一个片段

  1. POST /upload HTTP/1.1
  2. Host: example.com
  3. Content-Length: 51111
  4. Content-Type: application/octet-stream
  5. Content-Disposition: attachment; filename="big.TXT"
  6. X-Content-Range: bytes 460809-511919/511920
  7. Session-ID: 1111215056
  8.  
  9. <460809-511919字节文件数据>

上传文件最后一个片段服务器响应

  1. HTTP/1.1 200 OK
  2. Date: Thu, 02 Sep 2010 12:54:43 GMT
  3. Content-Type: text/html
  4. Connection: close
  5. Content-Length: 2270
  6.  
  7. <响应的内容>

请求头说明

请求头 说明
Content-Disposition attachment, filename=“上传的文件名”
Content-Type 待上传文件的mime type,如application/octet-stream(注:不能为multipart/form-data)
X-Content-Range 待上传文件字节范围,如第一片段bytes 0-51200/511920,最后一个片段bytes 460809-511919/511920(注:文件第一个字节标号为0,最后一个字节标号为n-1,其中n为文件字节大小)
X-Session-ID 上传文件的标识,由客户端随机指定.因为是断点续传,客户端必须确保同一个文件的所有片段上传标识一致
Content-Length 上传片段的大小

Python上传demo

  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*- 
  3.  
  4.  
  5. import os.path
  6. import requests
  7. import hashlib
  8.  
  9. # 待上传文件路径
  10. FILE_UPLOAD = "/tmp/testfile"
  11. # 上传接口地址
  12. UPLOAD_URL = "http://host/drivers_upload"
  13. # 单个片段上传的字节数
  14. SEGMENT_SIZE = 1048576
  15.  
  16. def upload(fp, file_pos, size, file_size):
  17.     session_id = get_session_id()
  18.     fp.seek(file_pos)
  19.     payload = fp.read(size)
  20.     content_range = "bytes {file_pos}-{pos_end}/{file_size}".format(file_pos=file_pos,
  21.                     pos_end=file_pos+size-1,file_size=file_size)
  22.     headers = {‘Content-Disposition’: ‘attachment; filename="big.TXT"’,’Content-Type’: ‘application/octet-stream’,
  23.                 ‘X-Content-Range’:content_range,’Session-ID’: session_id,’Content-Length’: size}
  24.     res = requests.post(UPLOAD_URL, data=payload, headers=headers)
  25.     print(res.text)
  26.  
  27.  
  28. # 根据文件名hash获得session id
  29. def get_session_id():
  30.     m = hashlib.md5()
  31.     file_name = os.path.basename(FILE_UPLOAD)
  32.     m.update(file_name)
  33.     return m.hexdigest()
  34.  
  35. def main():
  36.     file_pos = 0
  37.     file_size = os.path.getsize(FILE_UPLOAD)
  38.     fp = open(FILE_UPLOAD,"r")
  39.  
  40.     while True:
  41.         if file_pos + SEGMENT_SIZE >= file_size:
  42.             upload(fp, file_pos, file_size – file_pos, file_size)
  43.             fp.close()
  44.             break
  45.         else:
  46.             upload(fp, file_pos, SEGMENT_SIZE, file_size)
  47.             file_pos = file_pos + SEGMENT_SIZE
  48.  
  49. if __name__ == "__main__":
  50.     main()

参考

https://www.nginx.com/resources/wiki/modules/upload/
http://www.grid.net.ru/nginx/upload.en.html

openresty(nginx lua)统计域名状态码、平均响应时间和流量

背景

 

之前我们统计域名状态码、平均响应时间和流量的方法是:在每台机器添加一个定时脚本,来获取每个域名最近一分钟的访问日志到临时文件。然后zabbix再对这个一分钟日志临时文件作相关统计。一直运行良好,最近发现某台服务器突然负载增高。使用iotop查看发现获取最近一分钟日志的脚本占用的IO特别高。停止这个定时任务之后恢复正常。于是就打算使用nginx lua来替换目前的方法。新的方法具有统计时占用资源少,实时的特点。

 

方法介绍

 

使用nginx lua统计网站相关数据的方法为(我们以统计devops.webres.wang 404状态码为例):
记录过程:

  • 1、定义了一个共享词典access,获取当前时间戳,获取当前域名,如devops.webres.wang;
  • 2、我们定义用来存储状态码的词典key为,devops.webres.wang-404-当前时间戳;
  • 3、自增1 key(devops.webres.wang-404-当前时间戳)的值;
  • 4、循环2,3步。

 

查询过程:
提供一个接口,来累加key为devops.webres.wang-404-(前60秒时间戳-当前时间戳)的值,返回结果。

 

方法实现

 

nginx.conf设置

  1. http {
  2. […]
  3. lua_shared_dict access 10m;
  4. log_by_lua_file conf/log_acesss.lua;
  5. server {
  6. […]
  7.    location /domain_status {
  8.         default_type text/plain;
  9.         content_by_lua_file "conf/domain_status.lua";
  10.     }
  11. […]
  12. }
  13. […]
  14. }

 

log_access.lua

  1. local access = ngx.shared.access
  2. local host = ngx.var.host
  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 timestamp = os.date("%s")
  7. local expire_time = 70
  8.  
  9. local status_key = table.concat({host,"-",status,"-",timestamp})
  10. local flow_key = table.concat({host,"-flow-",timestamp})
  11. local req_time_key = table.concat({host,"-reqt-",timestamp})
  12. local total_req_key = table.concat({host,"-total_req-",timestamp})
  13.  
  14. — count total req
  15. local total_req_sum = access:get(total_req_key) or 0
  16. total_req_sum = total_req_sum + 1
  17. access:set(total_req_key, total_req_sum, expire_time)
  18.  
  19. — count status
  20. local status_sum = access:get(status_key) or 0
  21. status_sum = status_sum + 1
  22. access:set(status_key, status_sum, expire_time)
  23.  
  24. — count flow
  25. local flow_sum = access:get(flow_key) or 0
  26. flow_sum = flow_sum + body_bytes_sent
  27. access:set(flow_key, flow_sum, expire_time)
  28.  
  29. — count request time
  30. local req_sum = access:get(req_time_key) or 0
  31. req_sum = req_sum + request_time
  32. access:set(req_time_key, req_sum, expire_time)

domain_status.lua

 

  1. local access = ngx.shared.access
  2. local args = ngx.req.get_uri_args()
  3. local count = args["count"]
  4. local host = args["host"]
  5. local status = args["status"]
  6. local one_minute_ago = tonumber(os.date("%s")) – 60
  7. local now = tonumber(os.date("%s"))
  8.  
  9. local status_total = 0
  10. local flow_total = 0
  11. local reqt_total = 0
  12. local req_total = 0
  13.  
  14. if not host then
  15.         ngx.print("host arg not found.")
  16.         ngx.exit(ngx.HTTP_OK)
  17. end
  18.  
  19. if count == "status" and not status then
  20.         ngx.print("status arg not found.")
  21.         ngx.exit(ngx.HTTP_OK)
  22. end
  23.  
  24. if not (count == "status" or count == "flow" or count == "reqt") then
  25.         ngx.print("count arg invalid.")
  26.         ngx.exit(ngx.HTTP_OK)
  27. end
  28.  
  29. for second_num=one_minute_ago,now do
  30.         local flow_key = table.concat({host,"-flow-",second_num})
  31.         local req_time_key = table.concat({host,"-reqt-",second_num})
  32.         local total_req_key = table.concat({host,"-total_req-",second_num})
  33.  
  34.         if count == "status" then
  35.                 local status_key = table.concat({host,"-",status,"-",second_num})
  36.                 local status_sum = access:get(status_key) or 0
  37.                 status_total = status_total + status_sum
  38.         elseif count == "flow" then
  39.                 local flow_sum = access:get(flow_key) or 0
  40.                 flow_total = flow_total + flow_sum
  41.         elseif count == "reqt" then
  42.                 local req_sum = access:get(total_req_key) or 0
  43.                 local req_time_sum = access:get(req_time_key) or 0
  44.                 reqt_total = reqt_total + req_time_sum
  45.                 req_total = req_total + req_sum
  46.         end
  47. end
  48.  
  49. if count == "status" then
  50.         ngx.print(status_total)
  51. elseif count == "flow" then
  52.         ngx.print(flow_total)
  53. elseif count == "reqt" then
  54.         if req_total == 0 then
  55.                 reqt_avg = 0
  56.         else
  57.                 reqt_avg = reqt_total/req_total
  58.         end
  59.         ngx.print(reqt_avg)
  60. end

使用说明

1、获取域名状态码
如请求devops.webres.wang一分钟内404状态码数量
请求接口http://$host/domain_status?count=status&host=devops.webres.wang&status=404
2、获取域名流量
请求接口http://$host/domain_status?count=flow&host=devops.webres.wang
3、获取域名一分钟内平均响应时间
请求接口http://$host/domain_status?count=reqt&host=devops.webres.wang