赞
踩
最近一直在搞公司的日志系统,目前日志系统初步有两大部分:
机器都在AWS上,这两部分的日志都要发送到Docker搭建的ELK环境中。
对于第一点,使用Log4j2即可将应用日志实时传送到ELK中,详情参考使用Log4j2以ECS规范的格式将日志发送到Logstash中
对于第二点,方案则是:通过AWS CloudWatch Logs收集各个EC2 实例上的Tomcat的acceess_log,通过Filebeat拉取AWS CloudWatch Logs里收集到的日志,然后发送到ELK中。
注意:AWS CloudWatch 是一个很大的模块,里面既可以收集各种指标,也可以收集日志。详情参考AWS CloudWatch官方文档
AWS注册成功之后,会有一年的免费套餐使用
在EC2控制台上找到EC2的文档
按照官方文档,启动EC2实例即可。
按照官方文档一步一步来即可
官方文档有点跳跃
在EC2实例上启动agent之后,修改agent监控的文件的内容,在AWS CLoudWatch控制台查看,是否有新增的log group和log stream。二者的名字是配置agent时设置的
如果无法发送到日志到AWS CloudWatch,则检查agent的日志和查阅对应的官方文档
请确保上面的所有流程都正确并且可正常在控制台看到新加的日志再进行下一步
部署单节点的ELK可快速使用Github上非常受欢迎的docker-elk项目。
非常简单,代码一拉,改改配置或者都不需要改配置,就可以启动ELK的docker环境。
详细步骤请参考该项目的文档。
进行下一步时,务必确定docker环境的ELK三个组件都正常启动并能访问
因为上述官方主要提供ELK的docker compose,虽然filebeat也作为extension提供了。但是我自己测试的时候是使用单独的Filebeat,当然最终在产品环境部署的时候肯定是和上面的docker-compose.yml一起的。
mkdir filebeat
cd filebeat
在filebeat文件下建立三个文件
version: "3.7" services: filebeat-aws: image: docker.elastic.co/beats/filebeat:这里的版本号和上面ELK保持一致 container_name: filebeat user: root env_file: - .env volumes: - type: bind source: ./filebeat.docker.yml target: /usr/share/filebeat/filebeat.yml - type: bind source: /var/lib/docker/containers target: /var/lib/docker/containers read_only: true - type: bind source: /var/run/docker.sock target: /var/run/docker.sock read_only: true networks: - elk_elk networks: elk_elk: external: true
filebeat.inputs: - type: aws-cloudwatch enabled: true log_group_arn: 替换成自己的log_group_arn scan_frequency: 15s start_position: end access_key_id: ${AWS_ACCESS_KEY_ID} secret_access_key: ${AWS_SECRET_ACCESS_KEY} logging.metrics.enabled: false processors: - dissect: tokenizer: '%{clientIp} - - [%{access_timestamp}] %{response_time|integer} %{session_id} "%{http_request_method} %{url_path} %{http_version}" %{status_code|integer} %{http_request_body_bytes|integer} "%{http_request_referrer}" "%{user_agent_original}"' field: "message" target_prefix: "" ignore_failure: false - timestamp: field: "access_timestamp" layouts: - '2006-01-02T15:04:05Z' - '2006-01-02T15:04:05.999Z' - '2006-01-02T15:04:05.999-07:00' test: - '2019-06-22T16:33:51Z' - '2019-11-18T04:59:51.123Z' - '2020-08-03T07:10:20.123456+02:00' - drop_fields: fields: ["agent","log","cloud","event","message"] ignore_missing: true #output.console: # pretty: true output.logstash: hosts: ["logstash:5044"]
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
Filebeat本质上是使用AWS CloudWatch Logs的API进行拉取。关于API的使用可以参考CloudWatch Logs CLI, 所以在制定方案的时候要把API的调用次数考虑进去。AWS CloudWatch Logs中叫Service quotas。 见Filebeat AWS CloudWatch插件api-sleep参数和上述Service quotas文档中的FilterLogEvents中的具体限制
因为经过上面的配置,filebeat已经可以访问ELK的网络了。所以这里输出的host直接写ELK的docker-compose.yml中logstash的配置名即可。
注意:filebeat只能配置一个output,不能配置多个output
这里就比较灵活了,看实际情况中Tomcat中关于access_log的配置格式是什么样的。以我的Tomcat配置的access_log为例说明
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="access_log" suffix=".txt"
pattern="%h %l %u [%{yyyy-MM-dd'T'HH:mm:ss.SSSZ}t] %D %S "%r" %s %b "%{Referer}i" "%{User-Agent}i""
renameOnRotate="true"
requestAttributesEnabled="true" />
上面各个配置的具体含义参考Tomcat官方文档关于Access Log的配置
过滤掉局域网IP
<Valve className="org.apache.catalina.valves.RemoteIpValve"
internalProxies="172\.16\.\d{1,3}\.\d{1,3}" />
各项配置的具体含义参考Access_Control下的RemoteIp的配置
具体配置项的名字在
Spring Boot文档 > Application Properties > Server Properties下查看
server:
port: 18081
servlet:
context-path: /pool2
tomcat:
accesslog:
enabled: true
pattern: "%h %l %u [%{yyyy-MM-dd'T'HH:mm:ss.SSSZ}t] %D %S '%r' %s %b '%{Referer}i' '%{User-Agent}i'"
directory: /tmp/logs/tomcat
prefix: access_log
basedir: /var
需要指定basedir,否则tomcat的access_log也是不能生成的.详细文章参考这位博主的分享
对于RemoteIp的配置则是使用默认的,Spring Boot的默认值是
10\.\d{1,3}\.\d{1,3}\.\d{1,3}|192\.168\.\d{1,3}\.\d{1,3}|169\.254\.\d{1,3}\.\d{1,3}|127\.\d{1,3}\.\d{1,3}\.\d{1,3}|172\.1[6-9]{1}\.\d{1,3}\.\d{1,3}|172\.2[0-9]{1}\.\d{1,3}\.\d{1,3}|172\.3[0-1]{1}\.\d{1,3}\.\d{1,3}|0:0:0:0:0:0:0:1|::1
进入下一步之前,务必确保filebeat能够正常连接到AWS CloudWatch Log并可以正常拉取日志
docker logs --tail 10 -f filebeat
下面根据上面的access_log的格式给出一个示例配置文件
input { beats { port => 5044 client_inactivity_timeout => 3600 codec => "json" tags => [aws_access_log] } } filter { if "aws_access_log" in [tags] { # handle internal ip if [clientIp] =~ "^10.0.*|^192.168.*|^172.16.*|^127.0.*|0.0.0.0|^0:0:0:0*" { mutate { add_tag => ["private internets"] } } # convert ip to IP type from String Type if [clientIp] and "private internets" not in [tags] { geoip { source => "clientIp" target => "[client][geo]" } } useragent { source => 'user_agent_original' } date { match => ["access_timestamp", "yyyy-MM-dd'T'HH:mm:ss.SSSZ"] } mutate { add_field => { "[@metadata][server_name]" => "%{[awscloudwatch][log_stream]}" } rename => { "clientIp" => "[client][ip]" "status_code" => "[http][response][status_code]" "http_request_method" => "[http][request][method]" "http_request_referrer" => "[http][request][referrer]" "http_request_body_bytes" => "[http][request][body][bytes]" "http_version" => "[http][version]" "url_path" => "[url][path]" "log.file.path" => "[log][file][path]" "user_agent_original" => "[user_agent][original]" "ecs.version" => "[ecs][version]" } remove_field => ["[input]","[host]","access_timestamp"] } if [client][geo]{ mutate { remove_field => ["[client][geo][ip]"] } } } } output { if "aws_access_log" in [tags] { elasticsearch { hosts => "elasticsearch:9200" user => "logstash_internal" password => "${LOGSTASH_INTERNAL_PASSWORD}" index => "ecs-logstash-aws-access-log-%{[@metadata][server_name]}-%{+YYYY.MM.dd}" } } }
如果进行每一步时都按照博客中说的步骤进行验证正确了之后才进行的下一步。那么最终完整流程应该非常顺利的串起来。
如果最终日志没发送到ES中,提供几个排查的思路
docker logs --tail 5 -f elasticsearch
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。