跳到主要内容
JackSparrow414
返回

使用Filebeat和AWS CloudWatch Logs将EC2上的Tomcat的access_log传送到ELK

本文导航

展开本文导航

背景和方案选择

最近一直在搞公司的日志系统,目前日志系统初步有两大部分:

  1. Tomcat的catalina.out
  2. Tomcat的access_log

机器都在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账号

AWS注册成功之后,会有一年的免费套餐使用

创建EC2实例

在EC2控制台上找到EC2的文档 AWS EC2 控制台中的文档链接入口 按照官方文档,启动EC2实例即可。

注意事项

  1. 要提前创建key pairs,检查是否存在kye pairs
  2. 要提前创建VPC,检查是否存在默认的VPC,如果没有,点击创建默认的VPCAWS VPC 控制台 Actions 菜单中的 Create Default VPC 选项
  3. 要提前创建security group,检查是否存在security group。要特别注意Inbound rules中HTTP,HTTPS,SSH端口的暴露配置,否则有可能无法通过EC2 console连接
  4. 上述三项都确认存在,则按照官方文档开始创建EC2,创建完毕之后测试是否能够通过EC2 console连接到实例。
  5. 连接成功之后,查看IAM下是否有名字ec2-user的用户(因为EC2实例默认的用户名字是ec2-user),如果没有的话,为当前EC2实例创建一个.详细步骤参考下列文档AWS EC2 入门文档中的创建 IAM 用户组与用户步骤 记得创建完用户之后,保存用户的access key和secret key

在EC2实例上安装aws-cloudwatch-agent

按照官方文档一步一步来即可 AWS 文档中的 CloudWatch agent 下载与命令行配置说明

注意事项

  1. 先下载agent和创建用于AWS CoudWatch Logs的IAM Role
  2. 接着去创建agent所需的文件,创建配置文件时有一个collectd的选项,选择no即可,其他的选项默认即可。到第二段配置的时候,会问你想要监控哪些日志文件,输入文件路径即可
  3. 最后才是将刚刚创建好的IAM Role Attach到EC2实例上,并启动agent

官方文档有点跳跃 AWS 文档中在 EC2 与本地服务器安装 CloudWatch agent 的入口

测试aws-cloudwatch-agent是否可用

在EC2实例上启动agent之后,修改agent监控的文件的内容,在AWS CLoudWatch控制台查看,是否有新增的log group和log stream。二者的名字是配置agent时设置的 CloudWatch Log groups 控制台显示日志组列表 如果无法发送到日志到AWS CloudWatch,则检查agent的日志和查阅对应的官方文档

请确保上面的所有流程都正确并且可正常在控制台看到新加的日志再进行下一步

使用Docker Compose部署ELK

部署单节点的ELK可快速使用Github上非常受欢迎的docker-elk项目。 非常简单,代码一拉,改改配置或者都不需要改配置,就可以启动ELK的docker环境。 详细步骤请参考该项目的文档。

进行下一步时,务必确定docker环境的ELK三个组件都正常启动并能访问

使用Docker Compose部署Filebeat

因为上述官方主要提供ELK的docker compose,虽然filebeat也作为extension提供了。但是我自己测试的时候是使用单独的Filebeat,当然最终在产品环境部署的时候肯定是和上面的docker-compose.yml一起的。

mkdir filebeat
cd filebeat

在filebeat文件下建立三个文件

  1. docker-compose文件
    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
  2. filebeat配置文件filebeat.docker.yml
    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"]
  3. 环境变量文件.env 这里配置的就是ec2-user的access_key和secret_key
    AWS_ACCESS_KEY_ID=
    AWS_SECRET_ACCESS_KEY=

配置文件说明

docker-compose.yml说明

  1. 第一个docker-compose文件中,关于networks的配置做一下说明。因为我们ELK的docker compose环境已经部署好了,它有自己的专属网络。我们此时想要在另外一个docker compose中访问此网络而不需要再自己创建网络,那么就需要做两个事情。第一:在具体service下的networks声明那里的名字要是ELK的docker compose的网络名字。第二:在根节点的networks那里要加一个external的参数并设置为true。更多详细内容参考Compose中external解释
  2. service下network的名字是否修改要看你的docker-compose.yml所在的文件目录,我这里所在的文件目录是elk,然后网络名字是elk,所以docker默认创建出来的网络是elk_elk,如果你的文件目录不是elk,务必要修改正确。关于docker compose中网络的命名,参考Docker Compose官方文档有关NetWork的配置和Compose文件书写规范关于Network一节
  3. 再扩展一下,如果是docker swarm的话,其他docker容器想要访问swarm的网络的话,需要在swarm中添加attachable参数。详情参考Compose文件书写规范关于Network一节和Docker Engine中关于Network的说明

filebeat配置文件说明

input配置AWS CloudWatch插件

具体配置项含义参考官方文档

注意事项

Filebeat本质上是使用AWS CloudWatch Logs的API进行拉取。关于API的使用可以参考CloudWatch Logs CLI, 所以在制定方案的时候要把API的调用次数考虑进去。AWS CloudWatch Logs中叫Service quotas。 见Filebeat AWS CloudWatch插件api-sleep参数和上述Service quotas文档中的FilterLogEvents中的具体限制

output配置

因为经过上面的配置,filebeat已经可以访问ELK的网络了。所以这里输出的host直接写ELK的docker-compose.yml中logstash的配置名即可。 注意:filebeat只能配置一个output,不能配置多个output

processors的配置

这里就比较灵活了,看实际情况中Tomcat中关于access_log的配置格式是什么样的。以我的Tomcat配置的access_log为例说明

Tomcat设置access_log输出格式

在Tomat的server.xml中配置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 &quot;%r&quot; %s %b &quot;%{Referer}i&quot; &quot;%{User-Agent}i&quot;"
			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中配置access_log

具体配置项的名字在 Spring Boot Application Properties 文档中的服务器日志配置项 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并可以正常拉取日志

测试方法

  1. 可以先将filebeat的输出改为输出到控制台,启动filebeat,并确定无报错
  2. 在EC2中向监控的日志添加内容。例如加一条本地Tomcat的access_log
  3. 查看AWS CloudWatch控制台的log group下的log stream是否有刚才新添加的内容。正常情况下,一般2秒左右就可以在观察到有新的日志产生
  4. 查看filebeat日志,是否打印出刚刚在EC2实例中添加的日志,例如使用如下命令
    docker logs --tail 10 -f filebeat

配置Logstash

下面根据上面的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}"
               }
        }
}

配置文件说明

  1. 建议所有input都加入专属的tags,方便在filter中同其他input的处理隔离,因为实际情况中,很可能Logstash有多个input,每个input的处理逻辑都不相同。所以最好是用tags区分开
  2. 这里直接使用Elasticsearch自带的ecs-logstash的index template
  3. 在access_log中,将私有IP地址打上标记,并不去解析IP,因为这些IP解析不出来,还会报错
  4. 如果是正常的IP则使用logstash的geoip插件解析
  5. 对于access_log中的user agent使用logstash的useragent插件解析
  6. 使用logstash的date插件将access_log中的时间戳由字符串转为@timestamp字段
  7. 使用logstash的mutate插件将字段重命名为符合ECS规范的字段
  8. 为了便于查看具体的index,index命名时加入EC2的服务器名字以区分各个服务器的access_log,通过使用logstash的@metadata来达到此目的

完整流程测试

  1. 重启logstash,并确保无错误
  2. 将filebeat的输出配置改为logstash,并重启filebeat并确保无错误
  3. 在EC2中向监控的日志文件添加一些日志
  4. 在Kibana中查看是否有新的ecs-logstash-*的index产生

故障排除思路

如果进行每一步时都按照博客中说的步骤进行验证正确了之后才进行的下一步。那么最终完整流程应该非常顺利的串起来。 如果最终日志没发送到ES中,提供几个排查的思路

  1. 在Logstash中的output中加入stdout打印一下,看看是否真的收到了filebeat推送过了的日志
  2. 如果上一步没问题,则观察ES的日志,看看是否有错误产生.例如
    docker logs --tail 5 -f elasticsearch

备注

  1. 上述很多配置都需要仔细阅读对应的官方文档,这篇博文更多的是整体的思路分享而不是手把手教学
  2. AWS所有的文档我这里没有给出链接,因为都可以登录到具体服务的控制台,在控制台页面那里仔细找找,就可以找到对应文档的入口点
  3. 上述配置文件如果真的看懂了,在实际解决问题中,应该经过简单修改或者照葫芦画瓢就可以基本满足你的实际业务需要

分享本文:

继续阅读本系列

Elasticsearch 与 ELK 实战

  1. ELK环境搭建+入门使用
  2. Elasticsearch查询
  3. Elasticsearch 常用操作、对接Logstash以及处理本地IP、ECS规范(干货满满)
  4. ELK生成PEM格式的ca证书、设置Https并使用Elasticsearch Java Client连接
  5. ElasticSearch Java API的使用
  6. 使用Filebeat和AWS CloudWatch Logs将EC2上的Tomcat的access_log传送到ELK正在阅读
  7. 使用Filebeat和AWS CloudWatch Logs将EC2上的Tomcat的access_log传送到Elasticsearch中并使用ILM完成日志的自动管理
  8. 跟着官方文档一步一步搭建Elastic Stack(3节点ES集群+Kibana+Filebeat+Metricbeat)+ 零停机由单节点切换到集群解决方案
  9. 在业务开发中使用ElasticSearch的指导手册+附真实业务优化ElasticSearch场景
  10. 脚本自动化创建AWS EC2实例+安装ElasticSearch和Kibana+集成OpenTelemetry监控
  11. 使用Elasticsearch代替数据库like以加快查询的各种技术方案+实现细节

评论

欢迎提问、纠错和分享经验。发表评论需要登录 GitHub;中英文版本共享评论。

评论仅在正式网站开放。