Rootop 服务器运维与web架构

2020-04-17
发表者 Venus
nginx $upstream_response_time 里有2个值已关闭评论

nginx $upstream_response_time 里有2个值

在python分析nginx日志时会把upstream_response_time字符串转换为float数据类型,但报错:could not convert string to float: ‘0.000, 0.148’,发现出来2个值。日志中也是2个值。

{略过部分日志字段…,”upstream_response_time”:”0.000, 0.148″}
发现nginx日志中字段 $upstream_response_time 里有2个值,中间由逗号和空格分隔。正常情况下应该只有一个时间。像下面这样:
{略过部分日志字段…,”upstream_response_time”:”0.131″}

查nginx文档:http://nginx.org/en/docs/http/ngx_http_upstream_module.html#var_upstream_response_time

$upstream_response_time
keeps time spent on receiving the response from the upstream server;
the time is kept in seconds with millisecond resolution.
Times of several responses are separated by commas and colons like addresses in the $upstream_addr variable.
最后一句说多个响应时间由逗号和冒号分隔(但实际上是由逗号和空格分隔,可能此处文档有错误)和 $upstream_addr 变量的多个值类似
通过 $upstream_addr 变量查到描述说请求期间如果连接了多个服务器,就会出现2个值。
出现此问题的可能性一般是在upstream中定义了多个server,第一次分配的server无法响应,第二次分配处理成功,则此处就会出现2个值。
可以通过 proxy_next_upstream off; 关闭使用下一个upstream,但是客户端可能会响应为502错误。

2020-04-13
发表者 Venus
nginx屏蔽HEAD OPTIONS请求方法日志已关闭评论

nginx屏蔽HEAD OPTIONS请求方法日志

比如nginx日志里有options方法的请求,对分析日志没实际作用,就需要在记录日志时候屏蔽掉。
{“remote_addr”:”192.168.1.141″,”X-Forwarded-For”:””,”remote_user”:””,”time_local”:”13/Apr/2020:09:58:28 +0800″,”request”:”OPTIONS /login HTTP/1.1″,”method“:”OPTIONS”,”uri”:”/login”,”server_protocol”:”HTTP/1.1″,”request_body”:”userid=11″,”status”:”200″,”body_bytes_sent”:”0″,”http_referer”:””,”user_agent”:”PostmanRuntime/7.24.0″,”upstream_response_time”:”0.006″}

网上有说用if判断请求方法是options,则不记录日志(access_log off),不生效,不是无效,是配置的位置不对。

# 演示下错误的配置方法

server
{
	listen 80;
	server_name admin;

	access_log  /usr/local/nginx/logs/admin_access.log;
	error_log   /usr/local/nginx/logs/admin_error.log;

	if ($request_method = "OPTIONS")
	{
		access_log off;
	}

	location /
	{
		proxy_pass http://192.168.10.60:10102;
	}
}

# 重启nginx

(base) [root@localhost vhost]# /usr/local/nginx/sbin/nginx -t
nginx: [emerg] "access_log" directive is not allowed here in /usr/local/nginx/conf/vhost/admin.conf:11

这个提示就是说access_log配置指令不允许出现在这里。
官方文档表明,access_log 是允许出现在 http, server, location, if in location, limit_except 这几个配置段中。
如果用了if判断,则需要配置在location中。

# 正确方式

server
{
	listen 80;
	server_name admin;

	access_log  /usr/local/nginx/logs/admin_access.log;
	error_log   /usr/local/nginx/logs/admin_error.log;
	
	location /
	{
		proxy_pass http://192.168.10.60:10102;
		
		if ($request_method = "OPTIONS")
		{
			access_log off;
		}
	}
}

如果有多个方法需要屏蔽可以用正则,比如屏蔽options和head方法

if ($request_method ~* OPTIONS|HEAD)
{
    access_log off;
}

这样才可以实现不记录OPTIONS方法日志。

第二种方式:

map $request_method $m {
	OPTIONS 0;
	HEAD 0;
	default 1;
}

server
{
	listen 80;
	server_name admin;

	location /
	{
		proxy_pass http://192.168.10.60:10102;
	}
	
	access_log  /usr/local/nginx/logs/admin_access.log combined if=$m;
	error_log   /usr/local/nginx/logs/admin_error.log;
}

server
{
	listen 80;
	server_name api;

	location /
	{
		proxy_pass http://192.168.10.60:10103;
	}
   access_log  /usr/local/nginx/logs/api_access.log combined if=$m;
   error_log   /usr/local/nginx/logs/api_error.log;

}

通过map指令设置自定义变量(map指令由ngx_http_map_module模块提供,默认安装。)
The ngx_http_map_module module creates variables whose values depend on values of other variables.
注意map指令只能在http{}段中出现。对所有虚拟主机都可以直接用。

官方文档中关于access_log的参数:

access_log path [format [buffer=size] [gzip[=level]] [flush=time] [if=condition]];

The if parameter (1.7.0) enables conditional logging. A request will not be logged if the condition evaluates to “0” or an empty string. In the following example, the requests with response codes 2xx and 3xx will not be logged:

map $status $loggable {
    ~^[23]  0;
    default 1;
}

access_log /path/to/access.log combined if=$loggable;

意思是如果用if参数,后面需要跟着一个变量,这个变量如果是0或者是空字符串,则不会记录日志。
上面的第二种方法就是通过map+access_log中if参数实现的。

2020-04-10
发表者 Venus
goaccess配置采集nginx json格式日志已关闭评论

goaccess配置采集nginx json格式日志

之前为了便于python分析nginx日志,将日志格式改为json格式。但是用goaccess分析json格式还需要再下配置。

nginx中格式配置如下:

log_format api escape=json
	'{'
	'"remote_addr":"$remote_addr",'
	'"X-Forwarded-For":"$http_X_Forwarded_For",'
	'"remote_user":"$remote_user",'
	'"time_local":"$time_local",'
	#'"request":"$request",'
	'"method":"$request_method",'
	'"uri":"$uri",'
	'"server_protocol":"$server_protocol",'
	'"request_body":"$request_body",'
	'"status":"$status",'
	'"body_bytes_sent":"$body_bytes_sent",'
	'"http_referer":"$http_referer",'
	'"user_agent":"$http_user_agent",'
	'"upstream_response_time":"$upstream_response_time"'
	'}';

nginx访问日志如下:

{"remote_addr":"115.220.x.x","X-Forwarded-For":"","remote_user":"","time_local":"09/Apr/2020:13:28:09 +0800","method":"POST","uri":"/xxx/xxx","server_protocol":"HTTP/1.1","request_body":"xxxxxxx","status":"200","body_bytes_sent":"468","http_referer":"","user_agent":"Mozilla/5.0 (Linux; Android 10; LIO-AN00 Build/HUAWEILIO-AN00; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.64 Mobile Safari/537.36","upstream_response_time":"0.003"}

goaccess 官方配置文档:https://goaccess.io/man#custom-log
goaccess配置文件:

time-format %H:%M:%S # 定义时分秒格式 通过%t调用
date-format %d/%b/%Y # 定义日期格式   通过%d调用
log-format %^:"%h",%^:%^,%^:%^,%^:"%d:%t %^",%^:"%m",%^:"%U",%^:"%H",%^:%^,%^:"%s",%^:%^,%^:"%R",%^:"%u",%^:%^

拼出来要匹配的json格式(等于保留冒号和逗号),还是跟以前一样通过%^忽略某个字段,如:
第一个%^即可忽略{“remote_addr”部分,冒号后面双引号中的%h即可匹配到客户端ip地址,以此来依次匹配出需要的字段。

2020-04-10
发表者 Venus
filebeat配置多行日志合并为一条已关闭评论

filebeat配置多行日志合并为一条

java的错误日志通常有多行表示为一次完成的错误日志。收集的时候需要将其合并为一条。否则每行都会做为一条日志发送。
(logstash也是如此)

如:

13:20:05.491 [http-nio-8080-exec-10] ERROR com.xxx.modules.mall.controller.ApiMallGoodsController - 【商品详情】报错:
java.lang.NullPointerException: null
	at com.xxx.modules.mall.goods.service.MallGoodsService.apiMallGoodsInfo(MallGoodsService.java:279)
	...
	略

官方关于多行匹配参数:https://www.elastic.co/guide/en/beats/filebeat/current/multiline-examples.html

# 配置参数

[root@node3 filebeat]# cat filebeat-java.yml 
filebeat.inputs:
- type: log
  paths:
   - /home/dockermount/api/api*/logs/*-error.log
  fields:
   java: true
  fields_under_root: true
  multiline.pattern: '^[0-9]{2}:[0-9]{2}:[0-9]{2}.* \[http-nio'
  multiline.negate: true
  multiline.match: after

output.redis:
  hosts: ["172.19.34.91:50000"]
  password: "******"
  key: "filebeat-java"
  db: 8
  timeout: 10
multiline.pattern 是匹配一条完整日志开头的正则,如 13:20:05.491 [http-nio 开头
multiline.negate 可配置为true或false,如果为true,则代表 不匹配的行
multiline.match  可配置为before或after,对不匹配的行做什么动作。如果为after,则代表不匹配的行附加到之前匹配行的下面

2020-04-09
发表者 Venus
centos7中利用conda实现多python版本已关闭评论

centos7中利用conda实现多python版本

conda可以用来解决系统中多个python版本共存,也支持其他语言。
这里主要用来解决python3.8下写的脚本在centos7中运行。

conda安装需要 Anaconda 或 Miniconda,推荐安装Anaconda。区别去下面官方看。

下载地址:https://www.anaconda.com/distribution/#download-section
安装文档:https://conda.io/projects/conda/en/latest/user-guide/install/linux.html
Anaconda2 用于系统中python版本为2.x
Anaconda3 用于系统中python版本为3.x
我这里为centos7,下载Anaconda2版本的。

[root@localhost ~]# wget -c https://repo.anaconda.com/archive/Anaconda2-2019.10-Linux-x86_64.sh
[root@localhost ~]# bash Anaconda2-2019.10-Linux-x86_64.sh

最后他会提示是否init,输入yes初始化,重新ssh登录服务器。

(base) [root@localhost ~]# 

会看到前面有个 (base) ,这就是进去了conda环境中了。

# 创建一个python3.8的环境

(base) [root@localhost ~]# conda create --name py3 python=3.8

# 查看已有的环境

(base) [root@localhost ~]# conda info --envs
# conda environments:
#
base                  *  /root/anaconda2
py3                      /root/anaconda2/envs/py3

# 切换版本

(base) [root@localhost ~]# source activate py3
(py3) [root@localhost ~]# python -V
Python 3.8.2

# 退出conda环境

(base) [root@localhost ~]# source deactivate base
[root@localhost ~]# 

# 移除环境

[root@localhost ~]# conda remove -n py3 --all

这样就很方便把py3的脚本在centos中运行,而且不用更改系统python版本。

PS:清华镜像及加速配置:https://mirror.tuna.tsinghua.edu.cn/help/anaconda/