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

前提条件

  • 您已经开通容器服务,并创建了一个Kubernetes集群。具体步骤,请参见创建Kubernetes托管版集群

    本示例中,创建的Kubernetes集群位于华东1地域。

  • 请务必确保到Elasticsearch集群网络的可连通性。

背景信息

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

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集群版本需低于7.x.x。有关如何部署Elasticsearch集群,请参见创建阿里云Elasticsearch实例。默认情况下,阿里云Elasticsearch集群当新增的文档发现没有索引时,不允许自动创建索引,而Log-pilot在自动采集容器日志时需要依据日志采集配置来自动创建文档索引,因此我们这里需要开启自动创建索引功能。

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

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

步骤2部署Log-pilot组件

为降低节点的资源消耗,Log-pilot组件采用的是DaemonSet方式部署到每个集群的Node节点上。

  1. 登录容器服务管理控制台
  2. 在控制台左侧导航栏中,单击集群
  3. 集群列表页面中,单击目标集群名称或者目标集群右侧操作列下的详情
  4. 在集群管理页左侧导航栏中,单击工作负载
  5. 选择kube-system作为命名空间
  6. 无状态页签下,单击右上角的使用模板创建,进入使用模板创建页面。
  7. 示例模板选择自定义,并将以下内容复制到模板中,单击创建
    apiVersion: apps/v1
    kind: DaemonSet
    metadata:
      name: log-pilot
      labels:
        app: log-pilot
      # 设置期望部署的namespace。
      namespace: kube-system
    spec:
      selector:
        matchLabels:
          app: log-pilot
      updateStrategy:
        type: RollingUpdate
      template:
        metadata:
          labels:
            app: log-pilot
          annotations:
            scheduler.alpha.kubernetes.io/critical-pod: ''
        spec:
          # 是否允许部署到Master节点上tolerations。
          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集群的访问地址。
    {es_port}
    • 如果Kubernetes集群和部署的ES集群在同一个VPC下,则该地址为ES集群基本信息中的内网地址
    • 如果Kubernetes集群和部署的ES集群不在同一个VPC下,则该地址为ES集群基本信息中的公网地址
    {es_username} 访问ES集群的用户名。即为创建ES集群时,设置的用户名。
    {es_password} 访问ES集群的用户密码。即为创建ES集群时,设置的用户密码。
  8. 工作负载页面,选择守护进程集页签。
    选择目标集群,命名空间为kube-system,看到以下图所示时,表示运行正常。pilot
说明 您也可以通过kubectl连接Kubernetes集群。使用上述YAML模板创建DaemonSet,并执行以下命令查看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为例,通过创建Deployment应用配置日志采集(配置方式同样适用StatefulSet),测试日志是否能正常采集、索引和显示。

  1. 登录容器服务管理控制台
  2. 在控制台左侧导航栏中,单击集群
  3. 集群列表页面中,单击目标集群名称或者目标集群右侧操作列下的详情
    说明 目标集群必须和Log-pilot组件部署在同一集群下。
  4. 在集群管理页左侧导航栏中,单击工作负载
  5. 选择目标命名空间无状态页签。
  6. 单击右上角的使用模板创建,进入使用模板创建页面。
  7. 示例模板选择自定义,并将以下内容复制到模板中,单击创建
    编排模板如下。
    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。

  8. 工作负载页面,选择容器组页签,选择部署集群的命名空间default,单击tomcat名称。
    容器组 - tomcat页面的日志页签下,看到以下内容时,表示Tomcat部署成功。部署tomcat
  9. 阿里云ES控制台,登录Kibana控制台。详情请参见登录Kibana
  10. 单击Management,然后单击Index Patterns,单击Create index...,在Create index pattern区域输入access-*,单击Next step
    创建索引
  11. Time Filter field name下拉框中,选择@timestamp,然后单击Create index pattern
  12. 可选:如果您需要创建多个索引,按照以下步骤操作。
    1. 单击Management,在Kibana区域单击Index Patterns
    2. 单击Create Index Patterns,在Index pattern中输入目标索引(索引格式为XXX-*),单击Next stop,然后单击Create

执行结果

在Kibana控制台左侧导航栏单击Discover,您可以看到Tomcat的日志被采集到了指定的ES集群中。采集日志到ES
说明 Log-pilot默认采集日志到ES集群时会自动创建格式为index-yyyy.MM.dd的索引。

可以看到通过以上的配置可以方便地将Kubernetes集群中Pod的标准输出日志和容器内文件日志采集到ES集群中。利用开源组件Log-pilot能有效应对分布式Kubernetes集群日志需求,帮助提升运维和运营效率,保障系统持续稳定运行。