Nginx日志管理
Nginx的日志分为访问日志和错误日志两种。Nginx的访问日志,记录了用户的来源IP、浏览器信息、响应状态等,使用者也可通过Nginx的日志格式指令添加更多有用的信息并输出到访问日志中。通过对访问日志的分析,可以让网站管理者清晰地了解网站的安全性、性能、可用性及网站运行的PV、UV等数据。错误日志会记录Nginx加载配置时的配置指令检查出的异常、Nginx运行时请求处理的异常及服务器调试信息,通过错误日志可以为排查问题或优化Nginx配置参数、提升高并发处理能力提供帮助。
1、Nginx日志配置
Nginx的日志输出位置及内容格式是通过access_log及error_log指令配置实现的。Nginx日志默认是文本格式,通过Nginx提供的log_format可以输出为Json格式,并支持自定义日志输出的内容。
1.1、访问日志
Nginx的访问日志主要记录用户客户端的请求信息(见表)。用户的每次请求都会记录在访问日志中,access_log指令可以设置日志的输出方式及引用的日志格式。
-
访问日志指令


-
在同一级别的指令域中,也可指定多个日志。
-
指令值中的第一个参数用于设置输出日志的方式,默认是输出到本地的文件中。该指令也支持输出到syslog或内存缓冲区中。
-
该指令在stream指令域中时,默认值为off。
bashaccess_log off; -
参数path,设置日志输出的文件路径或syslog服务器地址。
bashaccess_log logs/access.log combined; -
参数format,设置关联log_format指令定义的日志格式名。
-
参数buffer,设置日志文件缓冲区大小。当缓冲区日志数据超出该值时,缓冲区日志数据会被写到磁盘文件。默认缓冲区大小为64KB。
-
参数flush,设置日志缓冲区刷新的时间间隔,缓冲区日志的保护时间超过这个设定值时,缓冲区日志数据会被写到磁盘文件。
-
参数gzip,设置缓冲区数据的压缩级别,缓冲区数据会被压缩后再写出到磁盘文件。压缩级别范围1~9,级别越高压缩比越高,系统资源消耗也最大,默认级别为1。
bashaccess_log logs/log.gz combined gzip flush=5m; -
参数if,设置是否记录日志,当参数值的条件成立,即不为0或空时,才记录日志。
bashmap $status $loggable { ~^[23] 0; default 1; } access_log logs/access.log combined if=$loggable;
-
-
日志格式指令


- 指令值参数name用于设置日志格式名。该名称全局唯一,可以被access_log引用。
- 指令值参数escape用于设置日志输出字符串编码格式,json支持中文字符内容输出。
- 指令值参数string用于设置日志输出格式字符串。该字符串由Nginx公共变量和仅在日志写入时存在的变量组成。HTTP常用变量如表所示。
-
HTTP日志变量

配置样例如下:bash# 普通格式日志 log_format main '$remote_addr - $connection - $remote_user [$time_local] "$request" - $upstream_addr' '$status - $body_bytes_sent - $request_time - "$http_referer" ' '"$http_user_agent" - "$http_x_forwarded_for" - '; # JSON格式日志 log_format json '{"@timestamp": "$time_iso8601", ' '"connection": "$connection", ' '"remote_addr": "$remote_addr", ' '"remote_user": "$remote_user", ' '"request_method": "$request_method", ' '"request_uri": "$request_uri", ' '"server_protocol": "$server_protocol", ' '"status": "$status", ' '"body_bytes_sent": "$body_bytes_sent", ' '"http_referer": "$http_referer", ' '"http_user_agent": "$http_user_agent", ' '"http_x_forwarded_for": "$http_x_forwarded_for", ' '"request_time": "$request_time"}';Nginx TCP/UDP的访问日志的变量与HTTP的访问日志的变量是不同的,TCP/UDP常见日志变量如表所示。
-
TCP/UDP日志输出变量

Nginx的TCP/UDP的日志处理是在连接处理阶段结束时才发生,所以TCP/UDP代理的访问日志只在连接关闭时才被记录。访问日志格式配置样例如下:bash# 普通格式日志 log_format tcp '$remote_addr - $connection - [$time_local] $server_addr: $server_port ' '- $status - $upstream_addr - $bytes_received - $bytes_sent - $session_time ' '- $proxy_protocol_addr:$proxy_protocol_port '; # JSON格式日志 log_format json '{"@timestamp": "$time_iso8601", ' '"connection": "$connection", ' '"remote_addr": "$remote_addr", ' '"server_addr": "$server_addr:$server_port" ' '"status": "$status" ' '"upstream_addr": "$upstream_addr" ' '"bytes_received": "$bytes_received" ' '"bytes_sent": "$bytes_sent" ' '"session_time": "$session_time" ' '"proxy_protocol_addr": "$proxy_protocol_addr:$proxy_protocol_port" '}'打开日志缓存指令见表。
-
打开日志缓存指令

-
默认配置下,Nginx每次将缓冲区日志数据保存到磁盘中,都需要先打开文件并获得文件描述符,然后向该文件描述符的文件中写入日志数据,最后关闭该文件描述符的文件。该指令把打开文件的文件描述符(文件句柄)存储在缓存中,进而提升写入日志的效率。
-
指令值max用于设置缓存中存储的文件描述符的最大数量,超过该值时,将按照LRU算法对缓存中文件描述符进行关闭。
-
指令值参数inactive用于设置缓存中每个文件描述符存活的时间,默认为10s。
-
指令值参数min_uses用于设置可被缓存文件描述符的最小使用次数,默认为1次。
-
指令值参数valid用于设置缓存检查频率,默认为60s。
-
指令值off用于关闭打开日志缓存的功能。
bashopen_log_file_cache max=1000 inactive=20s valid=1m min_uses=2; logs/access.log combined;
-
1.2、错误日志
Nginx的错误日志可以帮助用户及时判断Nginx配置及运行时出错的原因,错误日志也可以通过Nginx内置指令进行配置,但不支持格式定义。配置指令如表所示。
- 错误日志指令
-
在同一级别的指令域中,也可指定多个日志。
-
指令值中的第一个参数是输出日志的方式,默认是输出到本地的文件中。该指令也支持输出到syslog或内存缓冲区中。
basherror_log syslog:server=192.168.2.109 error; error_log memory:32m debug; error_log /dev/null; # 访问文件不存在时,记入错误日志 log_not_found on; -
指令值中第二个参数是输出日志的级别,指定的级别将包含自身及级别值比其小的所有级别日志,日志内容会保存到第一个参数设定的输出位置。
-
- 错误日志级别

1.3、日志归档Logrotate
Nginx日志存储为文件时,同一access_log指令设置的日志文件是以单文件形式存储的,在日常使用中为方便维护,通常需要将日志文件按日期进行归档。虽然Nginx本身并没有这一功能,但实现日志归档的方法仍有很多,此处推荐使用Logrotate实现日志归档管理。Logrotate是CentOS操作系统内置日志管理工具,该工具可对系统中生成的大量日志文件进行归档管理,其允许对日志文件实行压缩、删除或邮寄等操作。Logrotate可以按照每天、周、月或达到某一大小的日志文件进行归档操作,Logrotate基于anacrontab实现计划任务,只需在/etc/logrotate.d目录下编写相关日志管理配置文件,就可以无须人工干预使用自动化方式完成日志归档操作。
-
(1)Logrotate安装
bashyum -y install logrotate -
(2)Logrotate文件目录
bash/etc/logrotate.conf # logrotate主配置文件 /usr/sbin/logrotate # logrotate二进制文件 /etc/logrotate.d/ # 自定义logrotate配置文件 /var/lib/logrotate/logrotate.status # logrotate管理日志执行记录的状态文件 -
(3)Logrotate命令参数
bash-d, --debug # 测试归档配置文件 -f, --force # 立即执行归档操作 -m, --mail=command # 指定发送邮件的命令(默认为'/bin/mail') -s, --state=statefile # 设置logrotate.status文件路径,可用于区分在同 # 一系统下以不同用户身份运行的logrotate任务 -v, --verbose # 显示配置详细信息 -l, --log=STRING # 将Logrotate执行的详情输出到指定的文件 logrotate -v /etc/logrotate.conf # 显示配置文件详细信息 logrotate -d /etc/logrotate.d/syslog -l /var/log/logrotate.log # 配置文件,执行测试 logrotate -f /etc/logrotate.d/syslog # 立即执行当前配置文件 -
(4)Logrotate配置指令
Logrotate配置指令如表所示。



- copy与create是两种互斥的归档执行方式。
- copy方式是将日志文件复制一份后清空原日志文件的内容,并对复制的文件进行归档操作,应用程序继续向原日志文件输出日志。因日志文件复制与清空操作存在时间间隔,所以切割操作会因日志量的大小及实时产生的频率存在丢失的情况。
- create方式是将日志文件重命名,因日志文件的inode编号不变,应用程序会向新命名的文件输出日志。Logrotate新创建原日志文件名的文件后执行重启或以信号机制通知应用程序重新向新日志文件输出日志内容,完成切割操作。
- 当与同一自定义配置匹配的日志文件为多个时,会并发执行归档操作。
-
(5)Logrotate管理Nginx日志
根据Logrotate的功能特点,建议选择create方式进行日志归档管理,配置样例如下:bashvi /etc/logrotate.d/nginx /usr/local/nginx/logs/*.log { daily # 日志归档周期为1天 size 1 # 日志文件最小为1字节时才执行归档 minsize 1 # 日志文件最小为1字节时才执行归档 notifempty # 日志文件不为空时才执行归档 dateext # 归档文件名添加时间字符串 dateformat -%Y%m%d%H # 归档文件名时间字符串格式为-%Y%m%d%H dateyesterday # 归档文件名时间字符串以归档操作的前一天为时间戳 extension .log # 归档文件名中保留日志的扩展名 compress # 归档文件执行压缩 delaycompress # 在归档执行的下个周期再进行压缩 create # 以创建新文件方式实现日志归档 olddir /data/backup/nginx_logs # 归档文件存储目录 createolddir # 归档文件存储目录不存在时自动创建 postrotate # 归档执行后执行脚本 /usr/local/nginx/sbin/nginx -s reopen -g "pid /run/nginx.pid;" # 通知Nginx重新打开日志文件 endscript sharedscripts # 启用脚本共享模式 maxage 7 # 归档文件最多保留7天 rotate 7 # 归档文件最多保留7份 }
1.4、日志输出到syslog
Nginx的访问日志和错误日志都支持将日志直接输出到syslog服务端,对于syslog输出配置指令参数如表所示。

配置样例如下:
bash
error_log syslog:server=192.168.1.1 debug;
access_log syslog:server=unix:/var/log/nginx.sock,nohostname;
access_log syslog:server=[2001:db8::1]:12345,facility=local7,tag=nginx,severity=info combined;
2、Nginx日志收集分析
Nginx的访问日志中可以记录用户的IP、访问方法、访问URI、响应状态及响应数据大小等HTTP请求处理中会涉及的各种信息,通过这些信息可以实现访问用户来源分布、用户请求URI排行、响应数据大小及并发连接的分析和统计。
2.1、ELK简介
ELK(Elasticsearch、Logstash、Kibana)是开源的实时日志收集分析解决方案。ELK访问逻辑如图9-1所示,是由Elasticsearch、Logstash、Kibana这三款软件和数据采集客户端(如Filebeat)等实现日志采集、储存、搜索分析等操作。

- (1)Elasticsearch是一款用Java语言开发的,基于Lucene的开源搜索引擎,它提供了分布式多用户的全文搜索、分析、存储能力。Elasticsearch的常见关键词如表所示。


- (2)Logstash是基于C/S架构,对日志进行收集、过滤、转发的日志收集引擎,它可以同时从多个源获取数据,动态地将客户端采集的数据进行分拣、过滤,并转发到不同存储服务器。Logstash是以pipeline方式处理每条日志信息的,在每个pipeline中都有输入(INPUTS)、过滤(FILTERS)、输出(OUTPUTS)3个处理动作。每个处理动作可由一个或多个插件实现复杂的功能。输入处理是获取日志数据;过滤处理可以对日志进行分拣、修改;输出处理则是将日志数据发送给目标存储服务器。Logstash工作原理如图所示。

- (3)Kibana是Elasticsearch的Web管理工具,它提供了友好的界面化操作方式和统计分析的Dashboard工具,让使用者只需简单点击就可完成基本的数据搜索、分析等工作。
- (4)Filebeat隶属于Beats工具包,是负责文件数据采集的客户端工具。Filebeat由pro-spector和harvester两个主要组件组成。prospector目前只支持log文件和stdin两个输入类型,是harvester的管理进程,负责按照配置参数paths的内容查找日志文件,并为每个日志文件分配一个harvester。harvester负责实时读取单个日志文件,harvester将日志内容发送给底层的libbeat, libbeat将日志数据发送给配置文件中指定的输出目标。Filebeat工作原理如图所示。

2.2、ELK安装
ELK支持多种安装方式,鉴于Docker化部署的便捷性,本小节以基于docker-compose脚本的Docker化来部署ELK环境,部署示意如图所示。

-
(1)初始化系统环境
首先要初始化系统环境并安装Docker应用。bash# 安装yum工具 yum install -y yum-utils # 安装Docker官方yum源 yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo # 安装docker及docker-compose应用 yum install -y docker-ce docker-compose # 设置docker服务开机自启动 systemctl enable docker # 启动docker服务 systemctl start docker # 优化内核参数,设置一个进程拥有VMA(虚拟内存区域)的最大数量为262144 sysctl -w vm.max_map_count=262144 -
(2)编写docker-compose文件
使用docker-compose工具进行ELK容器运行编排。docker-compose文件如下:bashcat elk.yaml version: '2' services: elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:7.0.1 container_name: elasticsearch701 environment: - discovery.type=single-node - bootstrap.memory_lock=true - "ES_JAVA_OPTS=-Xms512m -Xmx512m" ulimits: memlock: soft: -1 hard: -1 hostname: elasticsearch restart: always ports: - "9200:9200" - "9300:9300" kibana: image: docker.elastic.co/kibana/kibana:7.0.1 container_name: kibana701 hostname: kibana depends_on: - elasticsearch restart: always ports: - "5601:5601" logstash: image: docker.elastic.co/logstash/logstash:7.0.1 container_name: logstash701 hostname: logstash restart: always depends_on: - elasticsearch ports: - "5044:5044" # 运行ELK容器 docker-compose -felk.yaml up -ddocker-compose是功能非常强的容器运行编排工具,内部含有很多配置指令可以完成容器的资源配置、运行、服务依赖、网络配置等运行时的编排配置,具体指令说明可参照docker-compose的官方文档。
-
(3)数据持久化
Docker的镜像(Image)文件存放在一个只读层,而容器(Container)的文件则是存放在可写层,当容器删除或重建时,该容器运行时变更的文件将会丢失,所以需要通过外挂卷的方式将变更的配置和文件保存到主机系统中。ELK容器有Elasticsearch、Logstash和Kibana 3个容器,这3个容器都需要实现数据持久化。bashcd /opt/data/apps # 创建容器外挂卷目录及数据存储目录 mkdir -p {elasticsearch/data,elasticsearch/config,elasticsearch/modules,elastic-search/plugins,kibana/config,logstash/pipeline,logstash/config} # 复制容器数据到数据存储目录 docker cp elasticsearch701:/usr/share/elasticsearch/data elasticsearch docker cp elasticsearch701:/usr/share/elasticsearch/config elasticsearch docker cp elasticsearch701:/usr/share/elasticsearch/modules elasticsearch docker cp elasticsearch701:/usr/share/elasticsearch/plugins elasticsearch docker cp logstash701:/usr/share/logstash/config logstash docker cp logstash701:/usr/share/logstash/pipeline logstash docker cp kibana701:/usr/share/kibana/config kibana # Logstash配置 cat>logstash/pipeline/logstash.conf<<EOF input { beats { port => 5044 codec =>"json" } } output { elasticsearch { hosts => ["http://10.10.4.37:9200"] index => "logstash-nginx-%{[@metadata][version]}-%{+YYYY.MM.dd}" } } EOF # 配置目录权限 chown -R 1000:1000 elasticsearch/* chown -R 1000:1000 logstash/* # 配置docker-compose脚本,挂载数据存储目录 cat elk.yaml version: '2' services: elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:7.0.1 container_name: elasticsearch701 environment: - discovery.type=single-node - bootstrap.memory_lock=true - "ES_JAVA_OPTS=-Xms512m -Xmx512m" ulimits: memlock: soft: -1 hard: -1 volumes: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro - /opt/data/apps/elasticsearch/modules:/usr/share/elasticsearch/modules - /opt/data/apps/elasticsearch/plugins:/usr/share/elasticsearch/plugins - /opt/data/apps/elasticsearch/data:/usr/share/elasticsearch/data - /opt/data/apps/elasticsearch/config:/usr/share/elasticsearch/config hostname: elasticsearch restart: always ports: - "9200:9200" - "9300:9300" kibana: image: docker.elastic.co/kibana/kibana:7.0.1 container_name: kibana701 hostname: kibana volumes: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro - /opt/data/apps/kibana/config:/usr/share/kibana/config depends_on: - elasticsearch restart: always ports: - "5601:5601" logstash: image: docker.elastic.co/logstash/logstash:7.0.1 container_name: logstash701 hostname: logstash volumes: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro - /opt/data/apps/logstash/pipeline:/usr/share/logstash/pipeline - /opt/data/apps/logstash/config:/usr/share/logstash/config restart: always depends_on: - elasticsearch ports: - "5044:5044" # 运行ELK容器 docker-compose -f elk.yaml up -d -
(4)Nginx配置
在运行Nginx的主机上把Nginx日志定义为json格式,编辑nginx.conf文件并在http指令域添加如下指令:bashlog_format json '{"@timestamp": "$time_iso8601", ' '"connection": "$connection", ' '"remote_addr": "$remote_addr", ' '"remote_user": "$remote_user", ' '"request_method": "$request_method", ' '"request_uri": "$request_uri", ' '"server_protocol": "$server_protocol", ' '"status": "$status", ' '"body_bytes_sent": "$body_bytes_sent", ' '"http_referer": "$http_referer", ' '"http_user_agent": "$http_user_agent", ' '"http_x_forwarded_for": "$http_x_forwarded_for", ' '"request_time": "$request_time"}'; -
(5)Filebeat安装
在Nginx服务器安装Filebeat进行Nginx日志采集。bash# 安装Filebeat rpm -ivh https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-7.0.1 -x86_64.rpm # 设置输出数据到Logstash及Logstash地址 sed -i "s/#output.logstash:/output.logstash:/g" /etc/filebeat/filebeat.yml sed -i "s/#hosts: \[\"localhost:5044\"\]/ hosts: \[\"10\.10\.4\.37:5044\"\]/g" /etc/filebeat/filebeat.yml # 关闭直接输出数据到Elasticsearch sed -i "s/output.elasticsearch/#output.elasticsearch/g" /etc/filebeat/filebeat.yml sed -i "s/hosts: \[\"localhost:9200\"\]/#hosts: \[\"localhost:9200\"\]/g" /etc/filebeat/filebeat.yml # 安装Filebeat Nginx模块 filebeat modules enable nginx # 配置Filebeat Nginx模块 cat >/etc/filebeat/modules.d/nginx.yml<<EOF - module: nginx access: enabled: true var.paths: ["/usr/local/nginx/logs/*access.log"] error: enabled: true var.paths: ["/usr/local/nginx/logs/*error.log"] EOF # 检查配置 filebeat test config filebeat test output # 启动Filebeat systemctl restart filebeat # 设置为自启动 systemctl enable filebeat -
(6)Kibana展示
在浏览器中打开http://10.10.4.37:5601,在右侧菜单栏中选择management→index_patterns→Create indexpattern,然后输入logstash-nginx-*,接着点击NextStep添加Nginx日志索引。在左侧菜单栏中点击Discover选择logstash-nginx-就可以实时查看Nginx输出的访问或错误日志了。
2.3、Nginx日志分析
Nginx通常被置于服务器访问的入口,其访问日志可以全局记录用户访问的来源、响应时间,以及用户行为热点等数据,通过对访问日志的分析,可以清晰地了解用户来源、用户行为习惯及自身服务器性能等情况。借助ELK的高性能处理能力,可以实时地将数据分析结果展现给服务器的维护人员及应用的开发人员,进而不断提高业务的可用性及产品的用户体验。Nginx的日志分析可以分为安全分析、性能分析、可用性分析及访问统计分析4个方面。
-
(1)安全分析
通常黑客对互联网应用的入侵都是先从Web服务器漏洞扫描开始的,最常用的扫描方式就是在URL中加入特定的脚本、命令或字符串不断尝试访问,并根据返回结果判断被扫描网站是否存在漏洞或后门。如SQL注入攻击会在访问的URL中带有select、and、or、order by等常见的SQL语句,XSS攻击会在访问的URL中带有javascript、vbscript、onmouseover、eval等Javascript或VBscript脚本命令。对管理后台入口的扫描也是常用的手段之一,多数情况下管理后台的安全加固是最容易被忽视的,往往认为不提供访问链接就高枕无忧了,而admin、manage等关键词通常会轻而易举地被穷举出来。
这些不安全的访问痕迹都会被Nginx服务器记录到访问日志中,并通过ELK对Nginx访问日志中的request_uri字段进行关键字过滤和展示,以求在第一时间了解这些不安全事件并提前做好防范工作。
-
(2)性能分析
一个网站性能的最直接体现就是请求的响应时间。通常用户的请求响应时间都是以毫秒为单位计算的,若用户的请求响应时间以秒为单位时,将极大地加大用户的等待时间,进而影响用户体验。为方便对请求响应的分析,可以将下表所示的Nginx服务器提供的变量添加到访问日志中,以记录请求链中消耗的时间。

对请求响应时间的分析,可以通过ELK对访问日志$request_time字段的时间做排名,对时间值比较大的URL从SQL、代码、架构等多方面分析原因。
-
(3)可用性分析
HTTP请求的每条访问都会有相应的访问状态码,访问状态码标识了请求成功或失败的状态。常见状态码标识参见3.3.2节。通过ELK对访问日志按照状态码维度统计总访问量,可以很直观地展示当前网站的可用性比率。
-
(4)访问统计分析
访问统计分析,可以让网站管理者最直观地了解网站被访问及用户的访问情况,常见的是PV及UV统计。PV(PageView)即页面浏览量或点击量,可以让网站管理者清晰地了解当前网站的访问量;UV(Unique Visitor)即独立访客量,以每个同一IP(remote_addr)、同一客户端类型(http_user_agent)可被识别为独立访客作为统计单位。PV体现了用户的访问量,UV体现了访问当前网站的人数。URL的访问数量统计,可以清晰地展示网站的哪些功能被大量使用,可以让网站管理者知道用户对网站功能的喜好,以便进行相关的产品优化。