Rsyslog + Logstash 日志传输指南

最近在把公司各个业务线的数据和日志接入到统一的 ELK 平台中,我主要在做 LK 和对接的工作,涉及服务器和网络拓扑相关内容比较多和杂,网上没有太多相关资料,这里跟大家分享一下我的一点经验。


更新记录

  • 2016.08.12: 初稿
  • 2016.11.19: 更新通天塔之日志分析平台系列文章链接

通天塔之日志分析平台系列文章

场景

因为部分历史遗留问题以及业务线的情况,不同后台业务涉及两个云服务提供商在三地的不同机房。出于安全考虑,不同项目组的服务器需要隔离(目前还没有用容器,所以是虚拟机隔离),但是日志需要统一进行收集、统计和展示。因为人员调动,我其实是临时接手这部分业务,所以只能在平稳接入各个业务之后,再考虑架构的演进。

之前的架构基本是『怎么快怎么方便怎么来』,颇有『刀耕火种』的感觉,不过大部分时间还是运转良好的,在新架构完成之前,只能先用这套不太完善的机制进行传输了。

为了描述方便,这里我们虚构两个服务,各自服务器部署配置如下

  • 服务 A:两个机房
    • 机房 1:杭州阿里云,机器 x 3,编号 A-HZ-ALI-01, A-HZ-ALI-02, A-HZ-ALI-03
    • 机房 2:美国 AWS,机器 x 3,编号 A-US-AWS-01, A-US-AWS-02, A-US-AWS-03
  • 服务 B:两个机房
    • 机房 1:杭州阿里云,机器 x 3,编号 B-HZ-ALI-01, B-HZ-ALI-02, B-HZ-ALI-03
    • 机房 2:美国阿里云,机器 x 3,编号 B-US-ALI-01, B-US-ALI-02, B-US-ALI-03

而 Elasticsearch 集群部署在美国 AWS,后面统称 ES。另外还有部分数据需要从 RDS 中获取,统称为 MySQL 任务。

网络

每台服务器都有自己的日志,使用 logrotate 工具按日进行切割,如果服务异常,之前的做法是登录到对应机器上进行日志查看。因为使用 Nginx 做负载均衡的缘故,很可能没办法快速找到对应的日志(现在机器比较少还好,再多就很麻烦了)。所以我们首先需要考虑的是想个办法把各个机器的日志收集到一起。

在具体部署的时候,我们考虑过两种方案,这里简单介绍一下:

第一种,简单粗暴,每台机器均直接把自己的日志发送给 ES。第二种,在每个机房选择一台服务器进行日志汇总,然后再统一发送给 ES。这两种方案各有优劣,我们来简单思考一下:

第一种方案的优劣

  • 劣势
    • 每台机器直接发给 ES 需要大量配置(当然这个可以用自动配置工具完成),一旦需求变动,所有的配置都需要修改
    • 每台机器都需要外网权限,运维难度增加,安全风险增加
    • 没办法利用内网通讯速度快的优势,给线上服务带来较高的带宽压力
  • 优势
    • 避免了单点的问题,即使一台服务器出问题,其他的日志还能正常发送数据

第二种方案的优劣

  • 劣势
    • 汇总服务器是单点,一旦出问题,所有的日志都没有办法正常发送
  • 优势
    • 只需要配置一台机器(其他机器可以通过系统自带的 rsyslog 进行日志传输),即使人工操作也不太麻烦
    • 只有一台服务器强制需要外网权限
    • 内部的日志传输通过内网,速度快

综合上述考虑,我们决定采用第二种解决方案,接下来会介绍如何进行配置。

配置

在开始配置之前一定要确定下面几个事情:

  • 服务器上的对应端口是否有配置好权限
  • 服务器的内网 ip 以及内网互访的权限
  • Logstash 和 Rsyslog 的版本
  • 服务器配置及负载情况

转发机配置

有了这些信息,我们就可以从每个机房挑选一台服务器作为日志汇总和转发机,这里我们选择:A-HZ-ALI-01, A-US-AWS-01, B-HZ-ALI-01, B-US-ALI-01 这四台机器分别接入(后面称为汇总机)。

这里我们使用 Logstash 进行日志汇总转发,各台机器上的 Logstash 配置可以是一样的,因为需要在后台启用该服务,所以需要修改/创建 /etc/logstash/conf.d/logstash.conf 文件,然后使用 sudo service logstash restart 重启服务(或者也可以使用 & 创建后台任务,总之把应用开起来就行)

input {
tcp {
port=> 16666
codec => json_lines
}
}
filter {
geoip {
source => "ip"
fields => ["location", "city_name", "country_name", "country_code2","country_code3", "region_name", "continent_code"]
}
}
output {
elasticsearch {
hosts => ["es.wdxtub.com:80"]
index => "ServiceA-log-%{+YYYY-MM-dd}"
user => "wdxtub"
password => "www.wdxtub.com"
}
}

上面的配置文件的意思是当程序启动时,监听 16666 端口传来的 json 行,根据 ip 地址获取地理信息之后,发送到 es.wdxtub.com:80 这个 ES 集群中,对应的索引是 ServiceA-log-%{+YYYY-MM-dd},这里实际上做了按日分隔(在 Kibana 中只需使用 ServiceA-log-* 就可以汇总所有的 ServiceA 的日志)

日志转发

我们现在已经有了从汇总机到 ES 的通道,现在需要做的就是把各个机器的日志发送到汇总机了,甚至汇总机本机的日志也可以通过这种方式进行(这样就是一个统一的过程了,不因为机器的角色有特殊情况)。这里具体选择的方式是 rsyslog,同一个机房同一个服务的机器的配置文件是一样的,我们在 /etc/rsyslog.d/ 中新建一个 10-file.conf 文件,内容为

# 载入文件读取模块
$ModLoad imfile
$template myFileMonitorTemplate,"%msg%"
# 日志文件
$InputFileName /data/home/service/project/shared/log/lograge_production.log
# 打标签
$InputFileTag service-log:
# 下面这行是用来保存文件状态的
$InputFileStateFile stat1470781524
$InputFileSeverity info
$InputFileFacility local7
# 拉取时间间隔,单位是秒
$InputFilePollInterval 1
$InputFilePersistStateInterval 1
$InputRunFileMonitor
# 两个 @@ 为 tcp 请求
if $syslogtag contains 'service-log' and $syslogfacility-text == 'local7' then @@10.0.1.2:16666;myFileMonitorTemplate
& stop

配置文件最后的地址就是对应转发机的内网 ip 地址,这样我们就建立了每个服务器到转发机的连接。然后可以通过 sudo service rsyslog restart 来重启并加载新的配置文件。因为使用 logrotate,所以还要考虑 rotate 时候的日志传输问题,解决办法是在 /etc/logrotate.d/ 文件夹中新建一个 _machine_name 文件,内容为

/data/home/service/project/current/log/*.log {
daily
size 200M
missingok
rotate 52
compress
delaycompress
notifempty
copytruncate
postrotate
(sudo stop rsyslog && sudo sed -i "s/InputFileStateFile.*/InputFileStateFile stat$(date +'%s')/" /etc/rsyslog.d/10-file.conf && sudo start rsyslog 2>&1) || true
endscript
}

前面的比较常规,主要是 postrotate 之后执行的脚本,大意就是在日志切分之后完成收尾工作,保证不因为 logrotate 导致日志重复发送或者丢失。

至此,我们就建立了服务器 - 转发机 - ES 的完整链路。


更新 rsyslog 的方法,因为要发送到 kafka 需要 v8 的配合

sudo add-apt-repository ppa:adiscon/v8-stable
sudo apt-get update
sudo apt-get install rsyslog

更新之后就可以弄到 kafka 上了

测试

前面的配置看起来不复杂,但是其中任何一步没跑通,整个套路就玩不转。具体有没有什么办法能测试呢?当然要有了,不然真是连错都找不到。我们来看看一步一步如何进行测试。

转发机到 ES

首先是测试配置文件,直接从 logstash 文件夹前台启动 logstash,并查看具体的信息,确定无误后,再放到后台执行。这里可能遇到的问题基本是跟 logstash 版本相关的,要么升级要么改成对应版本的配置语法即可。

常用的命令有

  • sudo lsof -i:16666 查看对应端口的占用情况
  • ifconfig 查看本机的网络信息
  • cat /proc/meminfo 查看本机的内存信息
  • cat /proc/cpuinfo 查看本机的 CPU 信息
  • ps aux | grep logstash 查看 logstash 对应的进程号(可以用于强制关闭)

服务器到转发机

具体配置的时候,这一步是问题最多的,因为 rsyslog 的配置实在是令人抓狂。基本的测试套路是这样的

  • 用 telnet 看看转发机的对应端口开起来没有,如果开起来了,应该是能连通的,不能的话,检查权限与服务配置
  • 可以利用 nc 工具尝试发送单条 log,比如 head -n1 service.log | nc 10.0.1.2 16666,如果卡住,那么说明链路建立有问题,如果能正常发送,就可以看看 ES 到底收到没有了

基本上如果这两步都没问题,大概是可以看到链路建立的。如果还不行,考虑到我对运维这块也不是很熟,就只能自力更生或者抱大腿了。

改进方向

目前这套非常脆弱的处理流程,每天只能祈祷服务器不崩,崩了之后得自己手动重启(或者写一个守护进程,挂了之后重新启动起来)。这种收集的工作,使用业务服务器『推』的模型,不如统一由日志服务器『拉』,如果借由诸如 S3 这样的外部存储,其实是可以做到解耦的。

所以这就衍生出两个套路,以后可以考虑尝试

  1. 使用 fluentd
  2. 改为『拉』模型

具体的以后细写,现在的我,感觉身体被掏空。

总结

搭建数据平台的过程基本把公司的业务线都过了一遍,深深感受到数据收集处理分析的重要性,这样对产品和服务的改进才不是瞎拍脑袋,才能真正把好钢用到刀刃上(现在的云资源浪费还是很严重的)。

不过嘛,慢慢来,一步一个脚印,每天都在进步就好。

参考链接

捧个钱场?