本文档将介绍一种Docker日志收集工具Log-Pilot,结合Elasticsearch和Kibana等工具,形成一套适用于Kubernetes环境下的一站式日志解决方案。

前提条件

  • 您已经开通容器服务,并创建了一个Kubernetes集群。本示例中,创建的Kubernetes集群位于华东1地域。
  • 请务必确保到ElasticSearch集群网络可达。

背景信息

开发者在面对Kubernetes分布式集群下的日志需求时,常常会感到头疼,既有容器自身特性的原因,也有现有日志采集工具的桎梏,主要包括:
  • 容器本身特性:
    • 采集目标多:容器本身的特性导致采集目标多,需要采集容器内日志、容器stdout。对于容器内部的文件日志采集,现在并没有一个很好的工具能够去动态发现采集。针对每种数据源都有对应的采集软件,但缺乏一站式的工具。
    • 弹性伸缩难:Kubernetes是分布式的集群,服务、环境的弹性伸缩对于日志采集带来了很大的困难,无法像传统虚拟机环境下那样,事先配置好日志的采集路径等信息,采集的动态性以及数据完整性是非常大的挑战。
  • 现有日志工具的一些缺陷:
    • 缺乏动态配置的能力。目前的采集工具都需要事先手动配置好日志采集方式和路径等信息,因为它无法自动感知到容器的生命周期变化或者动态漂移,所以它无法动态地去配置。
    • 日志采集重复或丢失的问题。因为现在的一些采集工具基本上是通过 tail 的方式来进行日志采集的,那么这里就可能存在两个方面的问题:一个是可能导致日志丢失,比如采集工具在重启的过程中,而应用依然在写日志,那么就有可能导致这个窗口期的日志丢失;而对于这种情况一般保守的做法就是,默认往前多采集1M日志或2M的日志,那么这就又会可能引起日志采集重复的问题。
    • 未明确标记日志源。因为一个应用可能有很多个容器,输出的应用日志也是一样的,那么当我们将所有应用日志收集到统一日志存储后端时,在搜索日志的时候,我们就无法明确这条日志具体是哪一个节点上的哪一个应用容器产生的。

Log-Pilot介绍

log-Pilot是一个智能容器日志采集工具,它不仅能够高效便捷地将容器日志采集输出到多种存储日志后端,同时还能够动态地发现和采集容器内部的日志文件。

针对前面提出的日志采集难题,Log-Pilot通过声明式配置实现强大的容器事件管理,可同时获取容器标准输出和内部文件日志,解决了动态伸缩问题,此外,Log-Pilot具有自动发现机制、CheckPoint及句柄保持的机制、自动日志数据打标、有效应对动态配置、日志重复和丢失以及日志源标记等问题。

目前log-pilot在Github完全开源,项目地址是github.com/AliyunContainerService/log-pilot 。您可以深入了解更多实现原理。

针对容器日志的声明式配置

Log-Pilot支持容器事件管理,它能够动态地监听容器的事件变化,然后依据容器的标签来进行解析,生成日志采集配置文件,然后交由采集插件来进行日志采集。

在Kubernetes下,Log-Pilot可以依据环境变量 aliyun_logs_$name = $path 动态地生成日志采集配置文件,其中包含两个变量:

  • $name是我们自定义的一个字符串,它在不同的场景下指代不同的含义,在本场景中,将日志采集到ElasticSearch的时候,这个$name表示的是Index。
  • 另一个是$path,支持两种输入形式,stdout和容器内部日志文件的路径,对应日志标准输出和容器内的日志文件。
    • 第一种约定关键字stdout表示的是采集容器的标准输出日志,如本例中我们要采集tomcat容器日志,那么我们通过配置标签aliyun.logs.catalina=stdout 来采集tomcat标准输出日志。
    • 第二种是容器内部日志文件的路径,也支持通配符的方式,通过配置环境变量 aliyun_logs_access=/usr/local/tomcat/logs/*.log来采集tomcat容器内部的日志。当然如果你不想使用aliyun这个关键字,Log-Pilot也提供了环境变量PILOT_LOG_PREFIX可以指定自己的声明式日志配置前缀,比如 PILOT_LOG_PREFIX: "aliyun,custom"

此外,Log-Pilot还支持多种日志解析格式,通过 aliyun_logs_$name_format=<format> 标签就可以告诉Log-Pilot在采集日志的时候,同时以什么样的格式来解析日志记录,支持的格式包括:none、json、csv、nginx、apache2和regxp。

Log-Pilot同时支持自定义tag,我们可以在环境变量里配置 aliyun_logs_$name_tags="K1=V1,K2=V2",那么在采集日志的时候也会将K1=V1和K2=V2采集到容器的日志输出中。自定义tag可帮助您给日志产生的环境打上tag,方便进行日志统计、日志路由和日志过滤。

日志采集模式

本文档采用node方式进行部署,通过在每台机器上部署一个Log-Pilot实例,收集机器上所有Docker应用日志。

该方案跟在每个Pod中都部署一个logging容器的模式相比,最明显的优势就是占用资源较少,在集群规模比较大的情况下表现出的优势越明显,这也是社区推荐的一种模式。
日志采集模式

步骤1 部署ElasticSearch集群

请参见创建阿里云Elasticsearch实例完成ElasticSearch集群的部署,完成后,进行下一步操作。
说明 默认情况下,阿里云ElasticSearch集群(简称ES集群)当新增的文档发现没有索引时,不允许自动创建索引,而Log-Pilot在自动采集容器日志时需要依据日志采集配置来自动创建文档索引,因此我们这里需要开启自动创建索引功能。

您可通过以下操作,开启自动创建索引功能。

  1. 登录阿里云Elasticsearch控制台
  2. 实例列表页面,单击实例ID/名称链接。
  3. 单击左侧导航栏的ES集群配置,进入ES集群配置页面。在YML文件配置区域框右侧单击修改配置,弹出YMAL参数配置页面。
  4. 自动创建索引选择允许自动创建索引,勾选该操作会重启实例,请确认后操作。,然后单击确认
    部署ES集群

步骤2 部署log-pilot组件

为降低节点的资源消耗,Log-Pilot 组件采用的是DaemonSet方式部署到每个集群的Node节点上,部Log-Pilot日志采集工具,操作如下:

  1. 登录容器服务管理控制台
  2. 在Kubernetes菜单下,单击左侧导航栏中的应用 > 无状态,进入无状态页面。
    部署组件
  3. 单击右上角的使用模板创建,进入使用模板创建页面。
  4. 选择需要部署log-pilot组件的集群,并选择kube-system作为命名空间
  5. 示例模板选择自定义,并将以下内容复制到模板中,单击创建
    模板部署
    apiVersion: extensions/v1beta1
    kind: DaemonSet
    metadata:
      name: log-pilot
      labels:
        app: log-pilot
      # 设置期望部署的namespace
      namespace: kube-system
    spec:
      updateStrategy:
        type: RollingUpdate
      template:
        metadata:
          labels:
            app: log-pilot
          annotations:
            scheduler.alpha.kubernetes.io/critical-pod: ''
        spec:
          # 是否允许部署到Master节点上
          tolerations:
          - key: node-role.kubernetes.io/master
            effect: NoSchedule
          containers:
          - name: log-pilot
            # 版本请参考https://github.com/AliyunContainerService/log-pilot/releases
            image: registry.cn-hangzhou.aliyuncs.com/acs/log-pilot:0.9.6-filebeat
            resources:
              limits:
                memory: 500Mi
              requests:
                cpu: 200m
                memory: 200Mi
            env:
              - name: "NODE_NAME"
                valueFrom:
                  fieldRef:
                    fieldPath: spec.nodeName
              - name: "LOGGING_OUTPUT"
                value: "elasticsearch"
              # 请确保集群到ES网络可达
              - name: "ELASTICSEARCH_HOSTS"
                value: "{es_endpoint}:{es_port}"
              # 配置ES访问权限
              - name: "ELASTICSEARCH_USER"
                value: "{es_username}"
              - name: "ELASTICSEARCH_PASSWORD"
                value: "{es_password}"
            volumeMounts:
            - name: sock
              mountPath: /var/run/docker.sock
            - name: root
              mountPath: /host
              readOnly: true
            - name: varlib
              mountPath: /var/lib/filebeat
            - name: varlog
              mountPath: /var/log/filebeat
            - name: localtime
              mountPath: /etc/localtime
              readOnly: true
            livenessProbe:
              failureThreshold: 3
              exec:
                command:
                - /pilot/healthz
              initialDelaySeconds: 10
              periodSeconds: 10
              successThreshold: 1
              timeoutSeconds: 2
            securityContext:
              capabilities:
                add:
                - SYS_ADMIN
          terminationGracePeriodSeconds: 30
          volumes:
          - name: sock
            hostPath:
              path: /var/run/docker.sock
          - name: root
            hostPath:
              path: /
          - name: varlib
            hostPath:
              path: /var/lib/filebeat
              type: DirectoryOrCreate
          - name: varlog
            hostPath:
              path: /var/log/filebeat
              type: DirectoryOrCreate
          - name: localtime
            hostPath:
              path: /etc/localtime
    说明 参数说明:
    • {es_endpoint}:ES集群的访问地址。
      • 如果Kubernetes集群和部署的ES集群在同一个VPC下,则该地址为ES集群基本信息中的内网地址
      • 如果Kubernetes集群和部署的ES集群不在同一个VPC下,则该地址为ES集群基本信息中的公网地址
    • {es_port}:ES集群的访问端口,一般是9200。
    • {es_username}:访问ES集群的用户名。默认用户名elastic。
    • {es_password}:访问ES集群的用户密码。即为创建ES集群时,设置的用户密码。
  6. 在容器服务控制台左侧导航栏,单击应用 > 容器组进入容器组(Pod)页面。
  7. 选择目标集群,命名空间为kube-system,看到以下图所示时,表示运行正常。
    部署完成
    说明 您也可以通过kubectl连接Kubernetes集群,执行如下命令来查看Log-Pilot组件的运行状态。
    ~ kubectl apply -f log-pilot.yml
    daemonset.extensions "log-pilot" created
    ~ kubectl -n kube-system get pod | grep log-pilot
    log-pilot-458nj 1/1 Running 0 23s
    log-pilot-8ld4n 1/1 Running 0 23s
    log-pilot-b4kqv 1/1 Running 0 23s
    log-pilot-gd588 1/1 Running 0 23s
    log-pilot-k2ttk 1/1 Running 0 23s

步骤3 采集日志

这里以部署一个Tomcat为例,通过创建Deploment应用配置日志采集(配置方式同样适用StatefulSet),来测试日志是否能正常采集、索引和显示。

  1. 在Kubernetes菜单下,单击左侧导航栏中的应用 > 无状态,进入无状态页面。
  2. 单击右上角的使用模板创建,进入使用模板创建页面。
  3. 选择集群k8s-cluster(必须和Log-Pilot组件部署在同一集群下),并选择目标命名空间
  4. 示例模板选择自定义,并将以下内容复制到模板中,单击创建
    编排模板如下:
    apiVersion: v1
    kind: Pod
    metadata:
      name: tomcat
    spec:
      containers:
      - name: tomcat
        image: "tomcat:7.0"
        env:
        # 1、stdout为约定关键字,表示采集标准输出日志
        # 2、配置标准输出日志采集到ES的catalina索引下
        - name: aliyun_logs_catalina
          value: "stdout"
        # 1、配置采集容器内文件日志,支持通配符
        # 2、配置该日志采集到ES的access索引下
        - name: aliyun_logs_access
          value: "/usr/local/tomcat/logs/catalina.*.log"
        # 容器内文件日志路径需要配置emptyDir
        volumeMounts:
          - name: tomcat-log
            mountPath: /usr/local/tomcat/logs
      volumes:
        - name: tomcat-log
          emptyDir: {}
    说明 在上面的编排中,通过在Pod中定义环境变量的方式,动态地生成日志采集配置文件,环境变量的具体说明如下:
    • aliyun_logs_catalina=stdout表示要收集容器的stdout日志。
    • aliyun_logs_access=/usr/local/tomcat/logs/catalina.*.log 表示要收集容器内 /usr/local/tomcat/logs/ 目录下所有名字匹配 catalina.*.log 的文件日志。

    在本方案的Elasticsearch场景下,环境变量中的 $name 表示Index,本例中 $name即是catalina和access 。

  5. 选择应用 > 容器组,选择部署 Tomcat 的集群cluster-k8s和命名空间default,在目标Tomcat右侧选择更多 > 日志
    采集日志
    容器组 - tomcat页面的日志页签下,看到如下内容时,表示Tomca部署成功。部署tomcat
  6. 阿里云ES控制台,单击Kibana控制台,登录Kibana。
  7. 单击Management,在Configure an index pattern区域输入access-*,单击Create
    创建索引
  8. 可选:如果您需要创建多个索引,按照如下步骤操作。
    1. 单击Management,在Kibana区域单击Index Patterns
    2. 单击Create Index Patterns,在Index name or pattern中输入目标索引(索引格式为XXX-*),单击Create
  9. 单击Discover,您可以看到Tomcat的日志被采集到了指定的ES集群中。
    采集日志到ES
    说明 Log-Pilot默认采集日志到ES集群时会自动创建格式为index-yyyy.MM.dd的索引。

我们可以看到只需要通过上面简单的配置就可以很方便地将Kubernetes集群中Pod的标准输出日志和容器内文件日志采集到ES集群中。通过这个方案,我们能有效应对分布式Kubernetes集群日志需求,可以帮助提升运维和运营效率,保障系统持续稳定运行。