IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    使用Filebeat输送Docker容器的日志

    bigwhite发表于 2016-03-25 03:05:30
    love 0

    今天我们来说说Docker容器日志。

    一、容器日志输出的旧疾及能力演进

    Docker容器在默认情况下会将打印到stdout、stderr的 日志数据存储在本地磁盘上,默认位置为/var/lib/docker/containers/{ContainerId} /{ContainerId}-json.log。在老版本Docker中,这种日志记录方式经常被诟病,诸如:日志大小无限制、无法 Rotate(轮转)、无日志基本管理能力以及性能糟糕等。针对这些旧疾,Docker一直试图在演进中完善和解决。

    记忆中好像是在Docker 1.8版本中,Docker增加了对json-file型(默认)log driver的rotate功能,我们可通过max-size和max-file两个–log-opt来配置。比如:我们启动一个nginx容器,采用 json-file日志引擎,每个log文件限制最大为1k,轮转的日志个数为5个:

    $docker run -d --log-driver=json-file --log-opt max-size=1k --log-opt max-file=5 --name webserver -p 9988:80 nginx
    50f100e7ea4d5b4931f144f9eac12b6a05e56579583d7a0322b250004b68ae72
    $ sudo ls -l /var/lib/docker/containers/50f100e7ea4d5b4931f144f9eac12b6a05e56579583d7a0322b250004b68ae72
    总用量 44
    -rw-r--r-- 1 root root  226  3月 24 14:39 50f100e7ea4d5b4931f144f9eac12b6a05e56579583d7a0322b250004b68ae72-json.log
    -rw-r--r-- 1 root root 1129  3月 24 14:39 50f100e7ea4d5b4931f144f9eac12b6a05e56579583d7a0322b250004b68ae72-json.log.1
    -rw-r--r-- 1 root root 1130  3月 24 14:39 50f100e7ea4d5b4931f144f9eac12b6a05e56579583d7a0322b250004b68ae72-json.log.2
    -rw-r--r-- 1 root root 1129  3月 24 14:39 50f100e7ea4d5b4931f144f9eac12b6a05e56579583d7a0322b250004b68ae72-json.log.3
    -rw-r--r-- 1 root root 1129  3月 24 14:39 50f100e7ea4d5b4931f144f9eac12b6a05e56579583d7a0322b250004b68ae72-json.log.4
    ... ...
    

    有了rotate,我们就不必担心某个container的日志暴涨而将同host的其他container拖死了。不过对于日志的管理目前也仅仅演进到如此,很多需求还得依靠第三方工具和方案来解决。

    另外当前Docker容器日志的写入性能依旧糟糕,如果对此敏感,可以用volume机制来解决,即 关闭容器内应用的标准输出、错误(–log-driver=none),直接将日志写到某mounted volume中的某个文件中。下面是bare metal裸机原生写日志文件、volume方式写日志文件以及docker默认写json文件的性能简单对比:

    我们用dd这个小工具,以go1.6.linux-amd64.tar.gz这个 85MB的文件作为输入,结果如下:(环境ubuntu 12.04 docker 1.9.1)

    1、bare metal
    dd if=~/.bin/go1.6.linux-amd64.tar.gz of=./go.bin
    记录了165623+1 的读入
    记录了165623+1 的写出
    84799480字节(85 MB)已复制,0.426716 秒,199 MB/秒
    
    2、通过挂在本地volume
    $ docker run --rm -it -v /home1/tonybai/testdd/volume:/testdd ubuntu:14.04 dd if=/testdd/go1.6.linux-amd64.tar.gz of=/testdd/go.bin
    165623+1 records in
    165623+1 records out
    84799480 bytes (85 MB) copied, 0.3753 s, 226 MB/s
    
    3、docker default
    $docker run  -v /home1/tonybai/testdd/volume:/testdd ubuntu:14.04 dd if=/testdd/go1.6.linux-amd64.tar.gz 2>&1 1>/dev/null
    
    165623+1 records in
    165623+1 records out
    84799480 bytes (85 MB) copied, 5.97732 s, 14.2 MB/s
    
    $ sudo ls -lh /var/lib/docker/containers/d4b5e6aae3968f68e5081414ad95c6308fa91808b44b415a03040403af5a4713/                                              d4b5e6aae3968f68e5081414ad95c6308fa91808b44b415a03040403af5a4713-json.log
    -rw------- 1 root root 331M  3月 24 18:05 /var/lib/docker/containers/d4b5e6aae3968f68e5081414ad95c6308fa91808b44b415a03040403af5a4713/                                          d4b5e6aae3968f68e5081414ad95c6308fa91808b44b415a03040403af5a4713-json.log
    

    可以看出,默认情况下,Docker写入json的速度是挂载volume方式的十分之一还低。主要原因是Docker容器的标准输出、 标准错误都会被Docker Daemon接管,并由Daemon写入json log文件,因此Daemon就成为了日志写入的瓶颈。

    二、容器日志的集中管理

    日志的管理需求由来已久,无论是传统遗留系统,还是互联网应用或服务,日志在运维和运营中的作用不可小觑。尤其是现在被普遍采用的集中日志管理实践,对Docker的日志管理提出了新的要求。上面提到随着Docker的演进,Docker的logging已有所改善,增加了多种log driver的支持(比如syslog、fluentd等),为容器日志的集中管理方案提供了多样性。

    目前国内很多企业采用ELK方案(当然ELK方案不仅仅局限于Docker了),即ElasticSearch + Logstash + Kibana,Logstash负责从各个节点收集、过滤、分析和处理日志,ElasticSearch负责存储、索引和查找日志;Kibana负责以图形化界面展示日志处理结果。但Docker Container如何做本地日志管理、如何将本地最新的日志输送给Logstash没有标准方案,你可以用fluentd agent也可以使用logspout。ELK方案中也有自己的用于客户端节点日志输送的工具,以前称为logstash-forwarder:

    node1 (logstash-forwarder) ------>
    
    node2 (logstash-forwarder) ------>   logstash server --> ElasticSearch
    
    node3 (logstash-forwarder) ------>
    

    现在Elastic.co使用beats系列产品替代logstash-forwarder,针对日志输送这块,对应的beats产品是filebeat,使用filebeat后,前面的集中日志方案结构就变成了:

    node1 (filebeat) ------>
    
    node2 (filebeat) ------>   [logstash server] --> ElasticSearch
    
    node3 (filebeat) ------>
    

    我们看到logstash server是一个可选的中间环节,使用filebeat后,你可以将client node上的最新日志直接发送给ElasticSearch,而无需经过logstash这一环节。当然如果你对源日志有过滤、清洗、分析等需求时,logstash依旧是你的得力助手。这里我们暂不用logstash,而是直接将日志发给ElasticSearch做存储和索引。

    三、使用Filebeat输出容器日志的步骤

    测试环境示意图如下:(ubuntu 14.04 + docker 1.9.1)

    node1 (10.10.126.101 nginx container +  filebeat) ------>   server 10.10.105.71 (ElasticSearch + kibana)
    

    这里的所有程序均以容器形式安装和运行。

    1、安装elasticsearch和kibana

    elasticsearch和kibana都有官方Docker image。

    安装elasticsearch:

    $ docker pull elasticsearch
    Using default tag: latest
    latest: Pulling from library/elasticsearch
    ...
    

    执行env,查看版本:

    $ docker exec  elasticsearch env
    
    ... ...
    ELASTICSEARCH_MAJOR=2.2
    ELASTICSEARCH_VERSION=2.2.1
    ELASTICSEARCH_REPO_BASE=http://packages.elasticsearch.org/elasticsearch/2.x/debian
    ... ...
    

    安装kibana:

    $ docker pull kibana
    Using default tag: latest
    latest: Pulling from library/kibana
    ... ...
    

    我们查看一下当前images列表:

    REPOSITORY                             TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
    elasticsearch                          latest              c6b6bed19c45        8 days ago          347.1 MB
    kibana                                 latest              d2c9c3cfc682        12 days ago         295.4 MB
    ... ...
    

    2、启动es和kibana,验证服务启动ok

    启动ES:

    $ sudo mkdir -p /data/elasticsearch
    $  docker run -d --name elasticsearch -p 9200:9200 -v /data/elasticsearch:/usr/share/elasticsearch/data elasticsearch
    4288b4db18af8575961faf940a1dc634fe30857bb184fb45611136b7bd3ffb7d
    

    查看服务启动情况:

    $ docker ps
    CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                              NAMES
    4288b4db18af        elasticsearch       "/docker-entrypoint.s"   21 seconds ago      Up 10 seconds       0.0.0.0:9200->9200/tcp, 9300/tcp   elasticsearch
    

    启动日志如下:

    $ docker logs elasticsearch
    [2016-03-24 11:00:29,289][INFO ][node                     ] [Katherine Reynolds] version[2.2.1], pid[1], build[d045fc2/2016-03-09T09:38:54Z]
    [2016-03-24 11:00:29,291][INFO ][node                     ] [Katherine Reynolds] initializing ...
    [2016-03-24 11:00:29,863][INFO ][plugins                  ] [Katherine Reynolds] modules [lang-expression, lang-groovy], plugins [], sites []
    [2016-03-24 11:00:29,894][INFO ][env                      ] [Katherine Reynolds] using [1] data paths, mounts [[/usr/share/elasticsearch/data (/dev/disk/by-uuid/f577c0bc-665b-431b-96e0-e3536dc11469)]], net usable_space [114.5gb], net total_space [130.4gb], spins? [possibly], types [ext4]
    [2016-03-24 11:00:29,894][INFO ][env                      ] [Katherine Reynolds] heap size [990.7mb], compressed ordinary object pointers [true]
    [2016-03-24 11:00:31,881][INFO ][node                     ] [Katherine Reynolds] initialized
    [2016-03-24 11:00:31,881][INFO ][node                     ] [Katherine Reynolds] starting ...
    [2016-03-24 11:00:31,993][INFO ][transport                ] [Katherine Reynolds] publish_address {172.17.0.1:9300}, bound_addresses {[::]:9300}
    [2016-03-24 11:00:32,004][INFO ][discovery                ] [Katherine Reynolds] elasticsearch/D7hV_RcHQa275Xc7if1Kkg
    [2016-03-24 11:00:35,058][INFO ][cluster.service          ] [Katherine Reynolds] new_master {Katherine Reynolds}{D7hV_RcHQa275Xc7if1Kkg}{172.17.0.1}{172.17.0.1:9300}, reason: zen-disco-join(elected_as_master, [0] joins received)
    [2016-03-24 11:00:35,075][INFO ][http                     ] [Katherine Reynolds] publish_address {172.17.0.1:9200}, bound_addresses {[::]:9200}
    [2016-03-24 11:00:35,076][INFO ][node                     ] [Katherine Reynolds] started
    [2016-03-24 11:00:35,144][INFO ][gateway                  ] [Katherine Reynolds] recovered [0] indices into cluster_state
    

    启动kibana:

    启动kibana容器需要提供一个环境变量参数,即ES的服务地址和端口:

    $docker run -d --name kibana -e ELASTICSEARCH_URL="http://10.10.105.72:9200"  -p 5601:5601 kibana
    

    要验证kibana是否启动ok,可以通过浏览器打开:http://10.10.105.72:5601,如果web页面正常显示,并且http://10.10.105.72:5601/status页面中有”Status: Green”字样,说明Kibana启动ok了。

    3、安装和配置filebeat

    在安装filebeat前,我们先启动一个测试用webserver,部署在10.10.126.101上,用于产生日志数据:

    $ docker run -d --log-driver=json-file --log-opt max-size=1k --log-opt max-file=5 --name webserver -p 9988:80 nginx
    50f100e7ea4d5b4931f144f9eac12b6a05e56579583d7a0322b250004b68ae72
    

    Filebeat没有官方image版本,docker hub上star数量最多的是prima/filebeat这个库中的image,我们就打算使用这个了,pull过程这里就不赘述了:

    $docker run --rm prima/filebeat env
    ... ...
    FILEBEAT_VERSION=1.1.2
    ... ...
    

    可以看到这个库中的filebeat image使用的filebeat版本是最新的。

    我们接下来来看run:

    $ docker run --rm prima/filebeat
    Loading config file error: Failed to read /filebeat.yml: open /filebeat.yml: no such file or directory. Exiting.
    

    看来Filebeat需要做一些配置,我们得来查看一下Filebeat的官方manual。

    Filebeat需要一个filebeat.yml配置文件,用于配置log来源以及log输送的目的地,我们参考manual给出一个适合我们的配置:

    filebeat:
      # List of prospectors to fetch data.
      prospectors:
        # Each - is a prospector. Below are the prospector specific configurations
        -
          # Paths that should be crawled and fetched. Glob based paths.
          # For each file found under this path, a harvester is started.
          paths:
              - "/var/lib/docker/containers/*/*.log"
            #- c:\programdata\elasticsearch\logs\*
    
          # Type of the files. Based on this the way the file is read is decided.
          # The different types cannot be mixed in one prospector
          #
          # Possible options are:
          # * log: Reads every line of the log file (default)
          # * stdin: Reads the standard in
          input_type: log
    
    # Configure what outputs to use when sending the data collected by the beat.
    # Multiple outputs may be used.
    output:
      ### Elasticsearch as output
      elasticsearch:
        # Array of hosts to connect to.
        hosts: ["10.10.105.72:9200"]
    

    我们采集/var/lib/docker/containers/*/*.log,即filebeat所在节点的所有容器的日志。输出的位置是我们ElasticSearch的服务地址,这里我们直接将log输送给ES,而不通过Logstash中转。

    再启动之前,我们还需要向ES提交一个filebeat index template,以便让Es知道filebeat输出的日志数据都包含哪些属性和字段。filebeat.template.json这个模板文件不用我们编写,filebeat官方提供,我们可以在github.com上找到它。

    加载这个模板到ES:

    $ curl -XPUT 'http://10.10.105.72:9200/_template/filebeat?pretty' -d@/home1/tonybai/filebeat.template.json
    {
      "acknowledged" : true
    }
    

    如果看到curl的返回结果是true,那么说明加载ok了。

    接下来,我们启动filebeat容器:

    $ docker run -d --name filebeat -v /home1/tonybai/filebeat.yml:/filebeat.yml prima/filebeat
    f93497ea816e5c4015e69376f98e791ca02b91a20145ee1366e4c15f6a706c10
    

    我们到Kibana中看看是否能收到容器的日志。使用Kibana时,需要添加一个新的index pattern。按照manual中的要求,对于filebeat输送的日志,我们的index name or pattern应该填写为:”filebeat-“,不过我在kibana中添加default index :filebeat- 一直失败,下面那个按钮一直是灰色的,并提示:“Unable to fetch mapping. Do you have indices matching the pattern”。

    在filebeat的forum中找寻问题答案,有人提示:看看ElasticSearch中是否有filebeat传输来的日志。于是查看ElasticSearch日志以及通过ElasticSearch提供的API做了一番查询,发现filebeat根本没有日志传输过来。

    回过头仔细想来,wow,居然没给filebeat容器挂在/var/lib/docker/containers目录,那么filebeat就没有权限访问容器日志,自然不会有日志传输到ES了,下面的输出也证实了这一点:

    $ docker exec filebeat ls /var/lib/docker/containers
    ls: cannot access /var/lib/docker/containers: No such file or directory
    

    于是修改filebeat启动参数:

    $docker run -d  --name filebeat -v /home1/tonybai/filebeat.yml:/filebeat.yml -v /var/lib/docker/containers:/var/lib/docker/containers prima/filebeat
    

    一段时间后,我们就可以在Kibana上成功创建filebeat-* index pattern并看到filebeat输送过来的日志了。

    © 2016, bigwhite. 版权所有.



沪ICP备19023445号-2号
友情链接