docker中ELK+x做日志聚合的几种方案

日志聚合解决的主要痛点为分布式架构下,查看散落各处日志的不便。
笔者的另一篇博客《ELK on docker-compose》已介绍了搭建一个ELK,并给出了一个docker-compose.yml
无论容器场景还是非容器场景,日志聚合均已十分成熟,可搜到很多材料。本文列举几种docker下日志聚合的方案并做个简单比较,没有关注Beats这种通用工具,更关注利用dockerlog-driver机制对接ELK,不需要程序输出日志到文件,而是输出到标准输出stdout和标准错误stderr

dcoker中几种日志聚合方案简述

Beats系列当然可以使用,和不使用docker时方案没有太大出入,建议将其封入基础镜像;利用dockerlog-driver机制,没必要将日志输出到文件,因为默认的机制将程序的标准输出stdout和标准错误stderr持久化了,而且可以通过log-driver机制将日志接入ELK
具体来说,容器默认的log-driverjson-file,位置/var/lib/docker/containers/<容器ID>/<容器ID>-json.log

$ docker info | grep 'Logging Driver'
Logging Driver: json-file

而本文关注的几种方案利用其他log-driver,几种方案的简介、对比如下:

候选工具 简介
syslog 容器log-driver=syslog,容器的stdoutstderr先转发给linux宿主机上的rsyslogrsyslog再转发给logstashsyslog需要修改linux中的配置,不便于实践everything as code
gelf 容器log-driver=syslog,再指定要发送给logstashgelf
fluentd-pilot 理念很有趣,启动一个fluentd-pilot容器,容器不需要指定日志发送的位置,而是通过--label指定特殊标签,github上不够活跃

gelf方案

修改logstash的配置,

docker run -p 12201:12201/udp --link af64b395a5c1:elasticsearch docker.elastic.co/logstash/logstash:6.7.1 logstash -e 'input { gelf{} }  output { elasticsearch {  hosts => ["elasticsearch:9200"] } }'

主要为input部分使用gelf

input {
  gelf{}
}

日志为

$ docker run -p 12201:12201/udp --link af64b395a5c1:elasticsearch docker.elastic.co/logstash/logstash:6.7.1 logstash -e 'input { gelf{} }  output { elasticsearch {  hosts => ["elasticsearch:9200"] } }'
Sending Logstash logs to /usr/share/logstash/logs which is now configured via log4j2.properties
[2019-04-08T01:27:17,093][INFO ][logstash.setting.writabledirectory] Creating directory {:setting=>"path.queue", :path=>"/usr/share/logstash/data/queue"}
[2019-04-08T01:27:17,108][INFO ][logstash.setting.writabledirectory] Creating directory {:setting=>"path.dead_letter_queue", :path=>"/usr/share/logstash/data/dead_letter_queue"}
[2019-04-08T01:27:17,116][WARN ][logstash.runner          ] Deprecated setting `xpack.monitoring.elasticsearch.url` please use `xpack.monitoring.elasticsearch.hosts`
[2019-04-08T01:27:17,500][WARN ][logstash.config.source.multilocal] Ignoring the 'pipelines.yml' file because modules or command line options are specified
[2019-04-08T01:27:17,516][INFO ][logstash.runner          ] Starting Logstash {"logstash.version"=>"6.7.1"}
[2019-04-08T01:27:17,566][INFO ][logstash.agent           ] No persistent UUID file found. Generating new UUID {:uuid=>"4a577feb-363e-4a1b-b96a-4eb4bd528c44", :path=>"/usr/share/logstash/data/uuid"}
[2019-04-08T01:27:18,129][WARN ][logstash.monitoringextension.pipelineregisterhook] xpack.monitoring.enabled has not been defined, but found elasticsearch configuration. Please explicitly set `xpack.monitoring.enabled: true` in logstash.yml
[2019-04-08T01:27:19,020][INFO ][logstash.licensechecker.licensereader] Elasticsearch pool URLs updated {:changes=>{:removed=>[], :added=>[http://elasticsearch:9200/]}}
[2019-04-08T01:27:19,195][WARN ][logstash.licensechecker.licensereader] Restored connection to ES instance {:url=>"http://elasticsearch:9200/"}
[2019-04-08T01:27:19,280][INFO ][logstash.licensechecker.licensereader] ES Output version determined {:es_version=>6}
[2019-04-08T01:27:19,288][WARN ][logstash.licensechecker.licensereader] Detected a 6.x and above cluster: the `type` event field won't be used to determine the document _type {:es_version=>6}
[2019-04-08T01:27:19,437][INFO ][logstash.monitoring.internalpipelinesource] Monitoring License OK
[2019-04-08T01:27:19,439][INFO ][logstash.monitoring.internalpipelinesource] Validated license for monitoring. Enabling monitoring pipeline.
[2019-04-08T01:27:23,298][INFO ][logstash.pipeline        ] Starting pipeline {:pipeline_id=>"main", "pipeline.workers"=>8, "pipeline.batch.size"=>125, "pipeline.batch.delay"=>50}
[2019-04-08T01:27:23,352][INFO ][logstash.outputs.elasticsearch] Elasticsearch pool URLs updated {:changes=>{:removed=>[], :added=>[http://elasticsearch:9200/]}}
[2019-04-08T01:27:23,419][WARN ][logstash.outputs.elasticsearch] Restored connection to ES instance {:url=>"http://elasticsearch:9200/"}
[2019-04-08T01:27:23,431][INFO ][logstash.outputs.elasticsearch] ES Output version determined {:es_version=>6}
[2019-04-08T01:27:23,432][WARN ][logstash.outputs.elasticsearch] Detected a 6.x and above cluster: the `type` event field won't be used to determine the document _type {:es_version=>6}
[2019-04-08T01:27:23,456][INFO ][logstash.outputs.elasticsearch] New Elasticsearch output {:class=>"LogStash::Outputs::ElasticSearch", :hosts=>["//elasticsearch:9200"]}
[2019-04-08T01:27:23,465][INFO ][logstash.outputs.elasticsearch] Using default mapping template
[2019-04-08T01:27:23,490][INFO ][logstash.outputs.elasticsearch] Attempting to install template {:manage_template=>{"template"=>"logstash-*", "version"=>60001, "settings"=>{"index.refresh_interval"=>"5s"}, "mappings"=>{"_default_"=>{"dynamic_templates"=>[{"message_field"=>{"path_match"=>"message", "match_mapping_type"=>"string", "mapping"=>{"type"=>"text", "norms"=>false}}}, {"string_fields"=>{"match"=>"*", "match_mapping_type"=>"string", "mapping"=>{"type"=>"text", "norms"=>false, "fields"=>{"keyword"=>{"type"=>"keyword", "ignore_above"=>256}}}}}], "properties"=>{"@timestamp"=>{"type"=>"date"}, "@version"=>{"type"=>"keyword"}, "geoip"=>{"dynamic"=>true, "properties"=>{"ip"=>{"type"=>"ip"}, "location"=>{"type"=>"geo_point"}, "latitude"=>{"type"=>"half_float"}, "longitude"=>{"type"=>"half_float"}}}}}}}}
[2019-04-08T01:27:23,518][INFO ][logstash.outputs.elasticsearch] Installing elasticsearch template to _template/logstash
[2019-04-08T01:27:23,549][INFO ][logstash.pipeline        ] Pipeline started successfully {:pipeline_id=>"main", :thread=>"#<Thread:0x3e3ac2e9 run>"}
[2019-04-08T01:27:23,565][INFO ][logstash.inputs.gelf     ] Starting gelf listener (udp) ... {:address=>"0.0.0.0:12201"}
[2019-04-08T01:27:23,627][INFO ][logstash.agent           ] Pipelines running {:count=>1, :running_pipelines=>[:main], :non_running_pipelines=>[]}
[2019-04-08T01:27:23,933][WARN ][logstash.outputs.elasticsearch] You are using a deprecated config setting "document_type" set in elasticsearch. Deprecated settings will continue to work, but are scheduled for removal from logstash in the future. Document types are being deprecated in Elasticsearch 6.0, and removed entirely in 7.0. You should avoid this feature If you have any questions about this, please visit the #logstash channel on freenode irc. {:name=>"document_type", :plugin=><LogStash::Outputs::ElasticSearch bulk_path=>"/_xpack/monitoring/_bulk?system_id=logstash&system_api_version=6&interval=1s", hosts=>[http://elasticsearch:9200], sniffing=>false, manage_template=>false, id=>"79d6d565e50fe66f5b522da92c3f148f42013ca30f089ab756df0e5573e82c9c", document_type=>"%{[@metadata][document_type]}", enable_metric=>true, codec=><LogStash::Codecs::Plain id=>"plain_6869d230-c856-4978-8174-4a45318a9c76", enable_metric=>true, charset=>"UTF-8">, workers=>1, template_name=>"logstash", template_overwrite=>false, doc_as_upsert=>false, script_type=>"inline", script_lang=>"painless", script_var_name=>"event", scripted_upsert=>false, retry_initial_interval=>2, retry_max_interval=>64, retry_on_conflict=>1, ilm_enabled=>false, ilm_rollover_alias=>"logstash", ilm_pattern=>"{now/d}-000001", ilm_policy=>"logstash-policy", action=>"index", ssl_certificate_verification=>true, sniffing_delay=>5, timeout=>60, pool_max=>1000, pool_max_per_route=>100, resurrect_delay=>5, validate_after_inactivity=>10000, http_compression=>false>}
[2019-04-08T01:27:23,940][INFO ][logstash.pipeline        ] Starting pipeline {:pipeline_id=>".monitoring-logstash", "pipeline.workers"=>1, "pipeline.batch.size"=>2, "pipeline.batch.delay"=>50}
[2019-04-08T01:27:23,955][INFO ][logstash.outputs.elasticsearch] Elasticsearch pool URLs updated {:changes=>{:removed=>[], :added=>[http://elasticsearch:9200/]}}
[2019-04-08T01:27:23,961][WARN ][logstash.outputs.elasticsearch] Restored connection to ES instance {:url=>"http://elasticsearch:9200/"}
[2019-04-08T01:27:23,966][INFO ][logstash.outputs.elasticsearch] ES Output version determined {:es_version=>6}
[2019-04-08T01:27:23,966][WARN ][logstash.outputs.elasticsearch] Detected a 6.x and above cluster: the `type` event field won't be used to determine the document _type {:es_version=>6}
[2019-04-08T01:27:23,969][INFO ][logstash.outputs.elasticsearch] New Elasticsearch output {:class=>"LogStash::Outputs::ElasticSearch", :hosts=>["http://elasticsearch:9200"]}
[2019-04-08T01:27:24,003][INFO ][logstash.pipeline        ] Pipeline started successfully {:pipeline_id=>".monitoring-logstash", :thread=>"#<Thread:0x3a25e8f1 sleep>"}
[2019-04-08T01:27:24,011][INFO ][logstash.agent           ] Pipelines running {:count=>2, :running_pipelines=>[:".monitoring-logstash", :main], :non_running_pipelines=>[]}
[2019-04-08T01:27:24,284][INFO ][logstash.agent           ] Successfully started Logstash API endpoint {:port=>9600}

此时,随便启动一个容器,将其log指向此处

docker run --log-driver gelf --log-opt gelf-address=udp://<换用宿主机ip>:12201 --rm alpine echo hello world

注意其中的--log-driver gelf --log-opt gelf-address=udp://<换用宿主机ip>:12201
kibana即可看到收集效果

利用gelf做日志聚合

elasticsearch收到的json形如:

{
  "_index": "logstash-2019.04.09",
  "_type": "doc",
  "_id": "IfvVAGoBIccTKo5H-R20",
  "_version": 1,
  "_score": null,
  "_source": {
    "@version": "1",
    "container_name": "compassionate_wilson",
    "tag": "b5711ba0d851",
    "message": "hello world",
    "@timestamp": "2019-04-09T06:43:03.692Z",
    "command": "echo hello world",
    "created": "2019-04-09T06:43:03.113896143Z",
    "source_host": "1.203.181.118",
    "version": "1.1",
    "image_id": "sha256:caf27325b298a6730837023a8a342699c8b7b388b8d878966b064a1320043019",
    "image_name": "alpine",
    "container_id": "b5711ba0d8519f8a95a8335bc84c27bb87324dcbcb1f43285a7d80644797231b",
    "level": 6,
    "host": "dk-mi13"
  },
  "fields": {
    "created": [
      "2019-04-09T06:43:03.113Z"
    ],
    "@timestamp": [
      "2019-04-09T06:43:03.692Z"
    ]
  },
  "sort": [
    1554792183692
  ]
}

fluentd-pilot方案

使用fluentd-pilot,可接elasticsearch,启动命令如下:

$ docker run --rm -it \
     -v /var/run/docker.sock:/var/run/docker.sock \
     -v /etc/localtime:/etc/localtime \
     -v /:/host:ro \
     --cap-add SYS_ADMIN \
     -e LOGGING_OUTPUT=elasticsearch \
     -e ELASTICSEARCH_HOST=1.203.181.43 \
     -e ELASTICSEARCH_PORT=9200 \
     registry.cn-hangzhou.aliyuncs.com/acs/log-pilot:0.9.6-fluentd

此时,同样启动一个示例应用,命令如下:

docker run --rm -p 10080:8080 \
--label aliyun.logs.catalina=stdout \
tomcat:9.0.17-jre11

elasticsearch收到的json形如:

{
  "_index": "catalina-2019.04.09",
  "_type": "fluentd",
  "_id": "IPvTAGoBIccTKo5Hdh2P",
  "_version": 1,
  "_score": null,
  "_source": {
    "log": "09-Apr-2019 06:39:19.879 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in [684] milliseconds\n",
    "stream": "stderr",
    "time": "2019-04-09T06:39:19.880086298Z",
    "host": "cb3e36b53aec",
    "index": "catalina",
    "topic": "catalina",
    "docker_container": "sad_gagarin"
  },
  "fields": {
    "time": [
      "2019-04-09T06:39:19.880Z"
    ]
  },
  "sort": [
    1554791959880
  ]
}

上述命令试验成功,下述命令失败:

docker run --label aliyun.logs.some-index=stdout --rm alpine echo hello world

发现一点,查到的日志均是stderr里过来的

查到的日志均来自stderr

对比发现,对于tomcatfluentd-pilot方式下收集到的日志和gelf一样

docker-compose交付基础设施

根据上述内容,新建文件docker-compose.yml,内容如下:

version: '2'
services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:6.7.1
    ports:
      - "9200:9200"
      - "9300:9300"
    environment:
      discovery.type: single-node
  kibana:
    image: docker.elastic.co/kibana/kibana:6.7.1
    depends_on:
      - elasticsearch
    ports:
      - "5601:5601"
  logstash:
    image: docker.elastic.co/logstash/logstash:6.7.1
    depends_on:
      - elasticsearch
    ports:
      - "9600:9600"
      - "12201:12201/udp"
    entrypoint: logstash -e 'input { gelf{} }  output { elasticsearch {  hosts => ["elasticsearch:9200"] } }'
  fluentd-pilot:
    image: registry.cn-hangzhou.aliyuncs.com/acs/log-pilot:0.9.6-fluentd
    depends_on:
      - elasticsearch
    cap_add:
      - SYS_ADMIN
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /:/host:ro
    environment:
      FLUENTD_OUTPUT: elasticsearch
      ELASTICSEARCH_HOST: elasticsearch
      ELASTICSEARCH_PORT: '9200'

在同一层目录下执行如下命令

docker-compose up
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,099评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,473评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,229评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,570评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,427评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,335评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,737评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,392评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,693评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,730评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,512评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,349评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,750评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,017评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,290评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,706评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,904评论 2 335

推荐阅读更多精彩内容