使用ELK分析Nginx日志实践

安装好ELK后,可以分析Nginx的日志查看访问量,甚至可以根据ip地址解析出GPS信息,最后生成用户访问热力图,本文是基于ELK 5.6.3版本。

1.配置Nginx日志的Logstash文件

在logstash的config目录下

vim logstash.conf

添加:

input {
        file {
                path => ["/data/log/nginx/www_access.log"]
        }
}
filter {
        grok {
                match => { "message" => "%{IPORHOST:clientip} - - \[%{HTTPDATE:time}\] \"%{WORD:verb} %{URIPATHPARAM:request} HTTP/%{NUMBER:httpversion}\" %{NUMBER:http_status_code} %{NUMBER:bytes} \"%{GREEDYDATA:referrer}\" \"%{GREEDYDATA:agent}\" \"%{GREEDYDATA:xforwardedfor}\""}
        }
        geoip {
                source => "clientip"
                        target => "geoip"
                        # fields => ["city_name", "continent_code", "country_code2", "country_code3", "country_name", "ip", "latitude", "longitude", "postal_code", "region_name", "timezone"]
                        database => "/data/src/logstash/GeoLite2-City_20171107/GeoLite2-City.mmdb"
                        add_field => [ "[geoip][coordinates]", "%{[geoip][longitude]}" ]
                        add_field => [ "[geoip][coordinates]", "%{[geoip][latitude]}"  ]
        }
        mutate {
                convert => [ "[geoip][coordinates]", "float"]
        }
}
output {
        stdout { codec => rubydebug }
        elasticsearch {
                hosts  => "127.0.0.1:9200"
                        index  => "logstash-nginx-%{+YYYY.MM.dd}"
        }
}

重点说明:

以上的配置中input下的file是Nginx的access文件。

下面filter的grok的match里的规则对应的是Nginx默认配置的日志格式,一般来说Nginx的配置文件里access日志后面加上main,下面的geoip是用来将IP地址解析成GPS信息,database是在 https://dev.maxmind.com/geoip/geoip2/geolite2/ 下载的,选择的是GeoLite2 City MaxMind DB binary gzipped,解压后将数据库mmdb格式的地址写入。

最后的output里将elasticsearch的地址写入,值得注意的是index一定要logstash打头,否则后面使用Kibana做用户访问热力图的时候会报错“No Compatible Fields: The “[nginx-access-]YYYY-MM-dd” index pattern does not contain any of the following field types: geo_point”,原因是索引格式为[nginx-access-]YYYY-MM-dd的日志文件由logstash输出到Elasticsearch;在Elasticsearch中,所有的数据都有一个类型,什么样的类型,就可以在其上做一些对应类型的特殊操作。geo信息中的location字段是经纬度,我们需要使用经纬度来定位地理位置;在Elasticsearch中,对于经纬度来说,要想使用Elasticsearch提供的地理位置查询相关的功能,就需要构造一个结构,并且将其类型属性设置为geo_point,此错误明显是由于我们的geo的location字段类型不是geo_point。

2.Kibana配置Index Patterns

到Kibana选择Management->选择Index Patterns->点击Create Index Pattern按钮->在Index pattern的输入框输入logstash-nginx-*

3.Kibana配置地图

Visualize->New->选择Maps下的Coordinate Map->选择刚刚配置的logstash-nginx-*->点击Geo Coordinates->Field选择geoip.location->点击“播放键”apply changes

最后结果:

ELK安装和实践

今年博主转战创业公司,随着系统的稳定和访问量的提升,急需一个在线的日志分析系统提供帮助,业界最知名的当属ELK了。架设这套体系的目的有三个:

1. 收集多台服务器上的日志信息,不需要上不同的服务器查看日志,方便分析处理问题。

2. 可视化界面方便搜索关键词和日志类型,处理潜在的bug。

3. 分析Nginx的访问量,是数据说话,让整个系统的运行情况了然于胸。
不多说了,直接切入正题

1. 安装Elasticsearch

下载最新的Elasticsearch tar包

wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.6.3.tar.gz

解压

tar zxvf elasticsearch-5.6.3.tar.gz

进入解压后目录

cd elasticsearch-5.6.3

如果想要设置其他服务器访问,需要修改配置,将network.host修改为本机的外网或内网ip地址,如果只要用127.0.0.1就略过

vim config/elasticsearch.yml

启动Elasticsearch

bin/elasticsearch -d

2. 安装Logstash

下载最新的logstash tar包

wget https://artifacts.elastic.co/downloads/logstash/logstash-5.6.3.tar.gz

解压

tar zxvf logstash-5.6.3.tar.gz

进入解压后目录

cd logstash-5.6.3

增加配置

vim config/logstash.conf

添加:

input {
        tcp {
                mode => "server"
                host => "localhost"
                port => 4567 
                codec => multiline {
                        pattern => "^\t"                                
                        what => "previous"
                }       
        }
}
filter {
        grok {
                match => { "message" => "%{TIMESTAMP_ISO8601:time} \[%{GREEDYDATA:thread}\] \[%{GREEDYDATA:id}\] \[%{GREEDYDATA:id2}\] %{LOGLEVEL:loglevel} %{JAVACLASS:class}(\s-\s%{GREEDYDATA:info})?"}
        }
}
output {
        stdout { codec => rubydebug }
        elasticsearch {
                hosts  => "127.0.0.1:9200"
                index => "logstash-platform-%{+YYYY.MM.dd}"
        }
}

说明:以上的配置中input下的tcp是根据log4j里配置写的,在我们的JAVA工程里log4j的配置文件部分内容如下:

<Appenders>
    <!-- 日志输出到logstash -->
    <Socket name="LogStash" host="localhost" port="4567">
        <PatternLayout pattern="%d [%t] [%X{X-UUID}] [X-UID-%X{X-UID}] %-5level %logger - %msg%n" />
    </Socket>
</Appenders>
<Loggers>
    <Root level="DEBUG">
        <AppenderRef ref="Console"/>
        <AppenderRef ref="LogStash"/>
    </Root>
</Loggers>

以上XML中的host和port就对应了logstash配置中的host和port。

codec的multiline的作用是为了将exception报错输出的内容合并到一行去,即如果是以制表符\t开头的一行输入内容都连接到上一行(previous)。

filter中的grok用与匹配输入的message并做文本的截取,这里面是正则表达式,大家可以到 http://grokdebug.herokuapp.com/ 调试自己的match规则。

output里面hosts就是elasticsearch的http地址,index是logstash在elasticsearch里面创建的索引的名字,它的默认值是”logstash-%{+YYYY.MM.dd}”,我把它配置成了”logstash-platform-%{+YYYY.MM.dd}”目的是跟nginx拉过来的日志做个区分。
如果有什么问题建议大家查阅logstash官方文档,很容易理解。

最后启动logstash

nohup bin/logstash -f config/logstash.conf & > /dev/null

3. 安装Kibana

下载最新的Kibana tar包

wget https://artifacts.elastic.co/downloads/kibana/kibana-5.6.3-linux-x86_64.tar.gz

解压

tar zxvf kibana-5.6.3-linux-x86_64.tar.gz

进入解压后目录

cd kibana-5.6.3-linux-x86_64

修改配置,将elasticsearch.url改为上面安装的elasticsearch的地址

vim config/kibana.yml

启动kibana

nohup bin/kibana & > /dev/null

4. 给kibana增加登录认证

centos需要运行htpassword,先安装httpd-tools

sudo yum install -y httpd-tools

给admin这个用户设置密码

sudo htpasswd -c /data/nginx/.htpasswd admin

最后配置nginx反向代理,这里就不说了。
随着JAVA的工程启动,会忘logstash写入日志信息,这时候可以通过

curl -XGET 127.0.0.1:9200/_cat/indices

查看是否存在logstash-platform开头的索引生成。如果不正常,可以去logstash目录下的logs去查看什么原因。正常就打开Kibana,配置index pattern为logstash-platform*。

搞定后大家就可以自己去把玩Kibana发掘下它的牛逼之处了!

Nginx报错open failed 13: Permission denied while reading upstream的解决方法

今天我们的系统出现了一个很诡异的情况,一个页面里有几个js文件和图片无法读到,Chrome浏览器里的status是:net::ERR_CONTENT_LENGTH_MISMATCH。而同一个页面其他的js、css和图片都没有问题,status是200。

遇到这个问题我第一反应是这个系统的tomcat我们也没有人动过,这几个资源文件在服务上是用的,那就是看看Nginx有没有问题,刚好今天有人动过Nginx,那就就去看看Nginx的access.log和error.log的日志。发现error.log里面有一个报错:

2016/06/05 17:58:06 [crit] 1017#0: *182 open() “/usr/local/nginx/proxy_temp/4/02/0000000024” failed (13: Permission denied) while reading upstream, client: ***.***.***.***, server: *****, request: “GET /idp/resources/images/mitreid-connect.ico HTTP/1.1”, upstream: “https://*****:8444/idp/resources/images/mitreid-connect.ico”, host: “*****”, referrer: “******”

proxy_temp目录是干什么的?我们的Nginx有个配置proxy_temp_file_write_size,当文件超过该参数设置的大小时,Nginx会先将文件写入临时目录,默认为Nginx安装目下/proxy_temp目录。

默认情况下,nginx是以www启动的,用ll命令查看proxy_temp目录,我们发现用户和用户组是nobody,难怪Permission denied!

我们用

chown -R www:www proxy_temp

把proxy_temp目录改成nobody的用户组权限,重启Nginx后问题就解决了!

Nginx报504 gateway timeout错误的解决方法

最近在工作中,需要做Excel导入的功能,由于Excel的数据比较多,而且我们的服务端程序需要对数据的内容做校验,会调用很多的外部服务接口,所以毫无悬念的导入Excel接口调用超过了一分钟,并且报错:504 gateway timeout。以下是两种解决思路:
1. 优化业务代码
一个接口调用超过一分钟,一定有可以优化的地方,看看数据库或者接口的调用是否合理,是否可以合并请求。
2. 修改Nginx的服务器配置
如果实在是优化不了了,可以把Nginx的超时时间上调。
看看时间是否符合要求,在nginx.config里面的三个参数:
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;

以上的单位是秒。

如果使用了Nginx的代理,可以在块里加上:
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
变成:
location /foo {
     proxy_pass http://xxx.xxx.xxx.xxx:8080/foo;
     proxy_set_header Host $host;
     proxy_set_header X-Real-IP $remote_addr;
     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
     proxy_connect_timeout 300s;
     proxy_send_timeout 300s;
     proxy_read_timeout 300s;
     access_log /var/log/nginx/access.foo.log main;
     error_log /var/log/nginx/error.foo.log;
}

Zend Framework 2在Nginx服务器下无法路径重写的解决办法

今天我在我的pcDuino上添加Zend Framework 2应用程序,在Nginx中添加了Server的配置之后打开网站,发现首页没有任何问题,但是其他的Module都无法进入,我想想感觉问题应该出在路径重写的问题上。我想要不我把.htaccess的规则改写成Nginx的rewrite规则吧!然后我打开.htaccess,内容如下:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteCond %{REQUEST_URI}::$1 ^(/.+)(.+)::\2$
RewriteRule ^(.*) - [E=BASE:%1]
RewriteRule ^(.*)$ %{ENV:BASE}index.php [NC,L]

有种虽不明但觉利的感觉,算了,我放弃我刚刚的想法,怎么办怎么办?算了,我还是去咨询一下把,于是跑到Stackoverflow去问了下这个问题,有个老外回答了下:

You really do not need any rewrite rules to make Nginx and ZF2 to play nice together. Here is a very simple Nginx configuration which I use:

server {
    listen *:80;
    server_name DOMAIN;

    # Character Set
    charset utf-8;

    # Logs
    access_log /vhosts/DOMAIN/logs/access_log main;
    error_log /vhosts/DOMAIN/logs/error_log;

    # Directory Indexes
    index index.php;

    # Document Root
    root /vhosts/DOMAIN/public;

    # Location
    location / {
        try_files $uri $uri/ /index.php;
    }

    # Error Pages
    error_page 404 /404.html;
    error_page 500 502 503 504 /50x.html;

    # PHP-FPM Support
    location ~ \.php$ {
        fastcgi_pass unix:/usr/local/etc/php-fpm/DOMAIN.sock;
        include fastcgi.conf;
    }

    # Block access to .htaccess
    location ~ \.htaccess {
        deny all;
    }
}

Of course change the paths to your current setup. Since you did not mention what type of PHP installation you are using I can’t help you there because I am currently using PHP-FPM.

Using this very simple setup all my modules are working as expected. For example I could visithttp://example.com/some/url OR http://example.com/index.php/some/url

Nginx also has a simple configuration for ZF http://wiki.nginx.org/Zend_Framework#Time_for_nginx

我试了下,果然成功了!

最重要的是配置Location的:

location / {
    try_files $uri $uri/ /index.php;
}

和阻止.htaccess的:

location ~ \.htaccess {
    deny all;
}