• kubernets轻量 contain log 日志收集技巧


    首先这里要收集的日志是容器的日志,而不是集群状态的日志

    要完成的三个点,收集,监控,报警,收集是基础,监控和报警可以基于收集的日志来作,这篇主要实现收集

    不想看字的可以直接看代码,一共没几行,尽量用调用本地方法实现,有工夫的可以改写成shell脚本

    https://github.com/cclient/kubernetes-filebeat-collector

    官方的收集方案

    https://kubernetes.io/docs/tasks/debug-application-cluster/logging-elasticsearch-kibana/

    一些基础的总结方案,文章时间比较早

    https://jimmysong.io/kubernetes-handbook/practice/app-log-collection.html

    官方的方案限制较多,因各种原因放弃

    文内的原因是

    ```

    Kubernetes官方提供了EFK的日志收集解决方案,但是这种方案并不适合所有的业务场景,它本身就有一些局限性,例如:

    • 所有日志都必须是out前台输出,真实业务场景中无法保证所有日志都在前台输出
    • 只能有一个日志输出文件,而真实业务场景中往往有多个日志输出文件
    • Fluentd并不是常用的日志收集工具,我们更习惯用logstash,现使用filebeat替代
    • 我们已经有自己的ELK集群且有专人维护,没有必要再在kubernetes上做一个日志收集服务

    基于以上几个原因,我们决定使用自己的ELK集群。

    ```

    个人不采用官方收集的原因基本类似,既要收集out前台输出,也要收集映射目录的多个输出文件,个人也不喜欢Fluentd,太重,整体较封闭。

    基本实现方式

    编号方案优点缺点
    1 每个app的镜像中都集成日志收集组件 部署方便,kubernetes的yaml文件无须特别配置,可以为每个app自定义日志收集配置 强耦合,不方便应用和日志收集组件升级和维护且会导致镜像过大
    2 单独创建一个日志收集组件跟app的容器一起运行在同一个pod中 低耦合,扩展性强,方便维护和升级 需要对kubernetes的yaml文件进行单独配置,略显繁琐
    3 将所有的Pod的日志都挂载到宿主机上,每台主机上单独起一个日志收集Pod 完全解耦,性能最高,管理起来最方便 需要统一日志收集规则,目录和输出方式

    文内的需求选择的是方案2

    文内和我的需有区别,但不管需求如何

    方案1,2都引入了更多的复杂度,需要较多的额外工作,区别只是引入时机,1是build image时和2是deploy时

    1,2从设计上就可以排除,太不优雅,以开发语言来说,侵入性太高,3是类似aop性质的能减少侵入,只有在3难以实现的情况下才考虑,实际上3并不难实现,早前纯docker集群就部署过类似的方案,稍改改就能适用于kubernetes

    而且这些方案的设计,都只考虑到了,收集的是自定义日志文件,映射外部目录下的文件,不论在容器内,容器外读取,都只监听相应目录下的文件,都不收集 docker 默认的json file,即 docker logs --tail 100 -f contain_name 这类日志的数据

    docker logs 操作上比较方便,早先设计docker 日志收集时,虽官方支持不少日志插件

    因为`The docker logs command is not available for drivers other than json-file and journald.`

    https://docs.docker.com/config/containers/logging/configure/#supported-logging-drivers

    过去一直采用默认的json-file,收集时,直接监听 /var/lib/docker/containers/*/*-json.log

    早期文件日志收集采用过logstash,修改数据比Fluentd方便,日志收集,没有太多的修改过滤需求,可以切到更轻量的filebeat。

    但缺点也很明显,这些日志只是容器的日志,日志本身并不含有容器的相关信息,一种实现应用时就把应用信息写到日志里,虽然没有容器信息,应用信息也足够识别。这些实际也引入项目开发的侵入性。

    实际上,docker早期就有基本的容器信息,我们只要把这个容器信息,在收集时,附加到原始日志中即可。

    很多人想到附加信息,会考虑从kube-api,或是从etcd读取,这样复杂度过高,基本都放弃了。

    实际上,docker 包括contain 在内都可以附加 lable 信息,而这些都可直接从本地文件读取 

    https://docs.docker.com/config/labels-custom-metadata/#key-format-recommendations

    kuberetes 不支持为contain打label,但官方自已会加上一部分,这些lable基本够用了。

    需求和简介描述完毕,以下是实现

    看一眼config文件即知道实现方式,docker版本不同,文件名可能不一样,印象里早期版本是config.json,内容也没现在的全,早期版本不保证适用本文内容

    cat /var/lib/docker/containers/caed8938198152778e3715f4bd3c00795f40c812d2cdb87dadfe8b0bb058390f/config.v2.json
     
    caed8938198152778e3715f4bd3c00795f40c812d2cdb87dadfe8b0bb058390f是容器id,了解docker 应该知道 docker ps 第一列就是这个id的首几位,随便输一个本机的即可
    {
        "StreamConfig": {},
        "State": {
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "RemovalInProgress": false,
            "Dead": false,
            "Pid": 15201,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2018-07-23T00:51:31.821704515Z",
            "FinishedAt": "2018-07-23T00:51:31.573484432Z",
            "Health": null
        },
        "ID": "caed8938198152778e3715f4bd3c00795f40c812d2cdb87dadfe8b0bb058390f",
        "Created": "2018-07-20T09:05:21.881001304Z",
        "Managed": false,
        "Path": "/app/app",
        "Args": [],
        "Config": {
            "Hostname": "consume-577cd986c7-8mk4h",
            "Domainname": "",
            "User": "0",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443",
                "TUICE_PERSONAL_WECHAT_PORT_8883_TCP_PORT=8883",
                "KUBERNETES_SERVICE_HOST=10.96.0.1",
                "KUBERNETES_PORT=tcp://10.96.0.1:443",
                "TUICE_PERSONAL_WECHAT_PORT=tcp://10.109.89.240:8883",
                "TUICE_PERSONAL_WECHAT_PORT_8883_TCP=tcp://10.109.89.240:8883",
                "KUBERNETES_SERVICE_PORT=443",
                "KUBERNETES_SERVICE_PORT_HTTPS=443",
                "KUBERNETES_PORT_443_TCP_PORT=443",
                "KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1",
                "TUICE_PERSONAL_WECHAT_PORT_8883_TCP_ADDR=10.109.89.240",
                "KUBERNETES_PORT_443_TCP_PROTO=tcp",
                "TUICE_PERSONAL_WECHAT_SERVICE_HOST=10.109.89.240",
                "TUICE_PERSONAL_WECHAT_SERVICE_PORT=8883",
                "TUICE_PERSONAL_WECHAT_SERVICE_PORT_SERVER=8883",
                "TUICE_PERSONAL_WECHAT_PORT_8883_TCP_PROTO=tcp",
                "PATH=/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "GOLANG_VERSION=1.8",
                "GOLANG_SRC_URL=https://golang.org/dl/go1.8.src.tar.gz",
                "GOLANG_SRC_SHA256=406865f587b44be7092f206d73fc1de252600b79b3cacc587b74b5ef5c623596",
                "GOPATH=/app",
                "RUN=pro"
            ],
            "Cmd": null,
            "Healthcheck": {
                "Test": [
                    "NONE"
                ]
            },
            "Image": "hub.docker.admaster.co/social_base/consume@sha256:483cd48b4b1e2ca53846f11fe953695bcceea59542b77f09044d30644cf3235f",
            "Volumes": null,
            "WorkingDir": "/app",
            "Entrypoint": [
                "/app/app"
            ],
            "OnBuild": null,
            "Labels": {
                "annotation.io.kubernetes.container.hash": "e1a52ef6",
                "annotation.io.kubernetes.container.restartCount": "0",
                "annotation.io.kubernetes.container.terminationMessagePath": "/dev/termination-log",
                "annotation.io.kubernetes.container.terminationMessagePolicy": "File",
                "annotation.io.kubernetes.pod.terminationGracePeriod": "30",
                "io.kubernetes.container.logpath": "/var/log/pods/00bea8a1-8bfc-11e8-b709-f01fafd51338/consume/0.log",
                "io.kubernetes.container.name": "consume",
                "io.kubernetes.docker.type": "container",
                "io.kubernetes.pod.name": "consume-577cd986c7-8mk4h",
                "io.kubernetes.pod.namespace": "default",
                "io.kubernetes.pod.uid": "00bea8a1-8bfc-11e8-b709-f01fafd51338",
                "io.kubernetes.sandbox.id": "fda1d939f5b41e31ca5c5214b4d78b38691d438eeb52d22d4460d49a2a820c1f"
            }
        },
        "Image": "sha256:85c9ca987b6fd310ce1019c28031b670da4d6705e70f1807f17727d48aa4aef4",
        "NetworkSettings": {
            "Bridge": "",
            "SandboxID": "",
            "HairpinMode": false,
            "LinkLocalIPv6Address": "",
            "LinkLocalIPv6PrefixLen": 0,
            "Networks": null,
            "Service": null,
            "Ports": null,
            "SandboxKey": "",
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "IsAnonymousEndpoint": false,
            "HasSwarmEndpoint": false
        },
        "LogPath": "/var/lib/docker/containers/caed8938198152778e3715f4bd3c00795f40c812d2cdb87dadfe8b0bb058390f/caed8938198152778e3715f4bd3c00795f40c812d2cdb87dadfe8b0bb058390f-json.log",
        "Name": "/k8s_consume_consume-577cd986c7-8mk4h_default_00bea8a1-8bfc-11e8-b709-f01fafd51338_0",
        "Driver": "overlay",
        "MountLabel": "",
        "ProcessLabel": "",
        "RestartCount": 0,
        "HasBeenStartedBefore": true,
        "HasBeenManuallyStopped": false,
        "MountPoints": {
            "/app/conf": {
                "Source": "/var/lib/kubelet/pods/00bea8a1-8bfc-11e8-b709-f01fafd51338/volumes/kubernetes.io~configmap/conf",
                "Destination": "/app/conf",
                "RW": false,
                "Name": "",
                "Driver": "",
                "Type": "bind",
                "Relabel": "ro",
                "Propagation": "rprivate",
                "Spec": {
                    "Type": "bind",
                    "Source": "/var/lib/kubelet/pods/00bea8a1-8bfc-11e8-b709-f01fafd51338/volumes/kubernetes.io~configmap/conf",
                    "Target": "/app/conf",
                    "ReadOnly": true
                }
            },
            "/dev/termination-log": {
                "Source": "/var/lib/kubelet/pods/00bea8a1-8bfc-11e8-b709-f01fafd51338/containers/consume/fc3f9ebf",
                "Destination": "/dev/termination-log",
                "RW": true,
                "Name": "",
                "Driver": "",
                "Type": "bind",
                "Propagation": "rprivate",
                "Spec": {
                    "Type": "bind",
                    "Source": "/var/lib/kubelet/pods/00bea8a1-8bfc-11e8-b709-f01fafd51338/containers/consume/fc3f9ebf",
                    "Target": "/dev/termination-log"
                }
            },
            "/etc/hosts": {
                "Source": "/var/lib/kubelet/pods/00bea8a1-8bfc-11e8-b709-f01fafd51338/etc-hosts",
                "Destination": "/etc/hosts",
                "RW": true,
                "Name": "",
                "Driver": "",
                "Type": "bind",
                "Propagation": "rprivate",
                "Spec": {
                    "Type": "bind",
                    "Source": "/var/lib/kubelet/pods/00bea8a1-8bfc-11e8-b709-f01fafd51338/etc-hosts",
                    "Target": "/etc/hosts"
                }
            },
            "/var/log/contain": {
                "Source": "/var/lib/kubelet/pods/00bea8a1-8bfc-11e8-b709-f01fafd51338/volumes/kubernetes.io~local-volume/pv-3",
                "Destination": "/var/log/contain",
                "RW": true,
                "Name": "",
                "Driver": "",
                "Type": "bind",
                "Propagation": "rprivate",
                "Spec": {
                    "Type": "bind",
                    "Source": "/var/lib/kubelet/pods/00bea8a1-8bfc-11e8-b709-f01fafd51338/volumes/kubernetes.io~local-volume/pv-3",
                    "Target": "/var/log/contain"
                }
            },
            "/tmp/nbbsdownload": {
                "Source": "/var/lib/kubelet/pods/00bea8a1-8bfc-11e8-b709-f01fafd51338/volumes/kubernetes.io~local-volume/pv-3-6",
                "Destination": "/tmp/nbbsdownload",
                "RW": true,
                "Name": "",
                "Driver": "",
                "Type": "bind",
                "Propagation": "rprivate",
                "Spec": {
                    "Type": "bind",
                    "Source": "/var/lib/kubelet/pods/00bea8a1-8bfc-11e8-b709-f01fafd51338/volumes/kubernetes.io~local-volume/pv-3-6",
                    "Target": "/tmp/nbbsdownload"
                }
            },
            "/var/run/secrets/kubernetes.io/serviceaccount": {
                "Source": "/var/lib/kubelet/pods/00bea8a1-8bfc-11e8-b709-f01fafd51338/volumes/kubernetes.io~secret/default-token-g6gr9",
                "Destination": "/var/run/secrets/kubernetes.io/serviceaccount",
                "RW": false,
                "Name": "",
                "Driver": "",
                "Type": "bind",
                "Relabel": "ro",
                "Propagation": "rprivate",
                "Spec": {
                    "Type": "bind",
                    "Source": "/var/lib/kubelet/pods/00bea8a1-8bfc-11e8-b709-f01fafd51338/volumes/kubernetes.io~secret/default-token-g6gr9",
                    "Target": "/var/run/secrets/kubernetes.io/serviceaccount",
                    "ReadOnly": true
                }
            }
        },
        "SecretReferences": null,
        "AppArmorProfile": "",
        "HostnamePath": "/var/lib/docker/containers/fda1d939f5b41e31ca5c5214b4d78b38691d438eeb52d22d4460d49a2a820c1f/hostname",
        "HostsPath": "/var/lib/kubelet/pods/00bea8a1-8bfc-11e8-b709-f01fafd51338/etc-hosts",
        "ShmPath": "/var/lib/docker/containers/fda1d939f5b41e31ca5c5214b4d78b38691d438eeb52d22d4460d49a2a820c1f/shm",
        "ResolvConfPath": "/var/lib/docker/containers/fda1d939f5b41e31ca5c5214b4d78b38691d438eeb52d22d4460d49a2a820c1f/resolv.conf",
        "SeccompProfile": "unconfined",
        "NoNewPrivileges": false
    }
    View Code

    如果是k8s启动的contain,会有k8s相应的lable,应用这部分数据

    {
                "annotation.io.kubernetes.container.hash": "e1a52ef6",
                "annotation.io.kubernetes.container.restartCount": "0",
                "annotation.io.kubernetes.container.terminationMessagePath": "/dev/termination-log",
                "annotation.io.kubernetes.container.terminationMessagePolicy": "File",
                "annotation.io.kubernetes.pod.terminationGracePeriod": "30",
                "io.kubernetes.container.logpath": "/var/log/pods/00bea8a1-8bfc-11e8-b709-f01fafd51338/consume/0.log",
                "io.kubernetes.container.name": "consume",
                "io.kubernetes.docker.type": "container",
                "io.kubernetes.pod.name": "consume-577cd986c7-8mk4h",
                "io.kubernetes.pod.namespace": "default",
                "io.kubernetes.pod.uid": "00bea8a1-8bfc-11e8-b709-f01fafd51338",
                "io.kubernetes.sandbox.id": "fda1d939f5b41e31ca5c5214b4d78babcd1d438eeb52d22d4460d49a2a820c1f"
    }

    docker的原始日志json file 文件 是

    /var/lib/docker/containers/caed8938198152778e3715f4bd3c00795f40c812d2cdb87dadfe8b0bb058390f/caed8938198152778e3715f4bd3c00795f40c812d2cdb87dadfe8b0bb058390f-json.log

    收集这个文件,再把label里的相应数据加上,就是k8s的容器日志了,说穿了很简单,只是很多人不清楚这几个文件的存在罢了。

    这里收集的是docker out的日志,另一种普遍的作法是,映射一个本地路径,把文件写在这个目录下。

    在容器内执行,收集容器内路径下的日志,或在宿主机执行,收集容器外路径下的日志。

    这里最好能和docker out的收集方式统一,用两套完全不可接受

    实际实现也很简单,以上的信息里还有这一部分

    {
            "/var/log/contain": {
                "Source": "/var/lib/kubelet/pods/00bea8a1-8bfc-11e8-b709-f01fafd51338/volumes/kubernetes.io~local-volume/pv-3",
                "Destination": "/var/log/contain",
                "RW": true,
                "Name": "",
                "Driver": "",
                "Type": "bind",
                "Propagation": "rprivate",
                "Spec": {
                    "Type": "bind",
                    "Source": "/var/lib/kubelet/pods/00bea8a1-8bfc-11e8-b709-f01fafd51338/volumes/kubernetes.io~local-volume/pv-3",
                    "Target": "/var/log/contain"
                }
            }
    }

    这里mount的映射信息,

    /var/log/contain是容器内路径,
    /var/lib/kubelet/pods/00bea8a1-8bfc-11e8-b709-f01fafd51338/volumes/kubernetes.io~local-volume/pv-3是宿主机路径

    把这个

    /var/lib/kubelet/pods/00bea8a1-8bfc-11e8-b709-f01fafd51338/volumes/kubernetes.io~local-volume/pv-3目录也加到收集监控里便ok了

    思路已经通了

    最后我们要的是类似这样的一个配置文件(不论是不是filebeat,需要的数据就是这些)

    补充一点,看下方输出就明白

     ls -alh /var/log/pods/00bea8a1-8bfc-11e8-b709-f01fafd51338/consume/0.log
    lrwxrwxrwx 1 root root 165 Jul 20 17:05 /var/log/pods/00bea8a1-8bfc-11e8-b709-f01fafd51338/consume/0.log -> /var/lib/docker/containers/caed8938198152778e3715f4bd3c00795f40c812d2cdb87dadfe8b0bb058390f/caed8938198152778e3715f4bd3c00795f40c812d2cdb87dadfe8b0bb058390f-json.log
     
    filebeat.inputs:
    - type: log
      paths:
        - /var/lib/docker/containers/caed8938198152778e3715f4bd3c00795f40c812d2cdb87dadfe8b0bb058390f/caed8938198152778e3715f4bd3c00795f40c812d2cdb87dadfe8b0bb058390f-json.log
        - /var/lib/kubelet/pods/00bea8a1-8bfc-11e8-b709-f01fafd51338/volumes/kubernetes.io~local-volume/pv-3/*.log
      fields:
        namespace: "default"
        name: "consume"
        pod_name: "consume-577cd986c7-8mk4h"
    output.elasticsearch:
      hosts: ["localhost:9200"]
    setup.kibana:
      host: "localhost:5601"

    定时扫描/var/lib/docker/containers/,读取每个contain的config.v2.json,构造配置文件,启动收集进程

    因为contain id包含hash值,同名冲突的概率几乎0零,因此也不用考虑任何冲突的问题,每个k8s node独立部署一个脚本即可。

    目前实现的方式是用脚本加定时器,实时性稍微差了些,够用了,有工夫的可以优化成文件监听。

  • 相关阅读:
    oracle 10g 共享服务器搭建
    Oracle:10053事件简述
    Asktom Oracle: Partition table and index .
    Oracle shared server配置
    Oracle:物化视图语法
    Asktom:Thanks for the question regarding "consistent gets Very puzzling".
    oracle嵌套表整理的学习资料
    非空闲等待事件之:db file sequential read(转)
    Asktom Oracle:How to multiplex single row into multiple rows
    Oracle:Not exists
  • 原文地址:https://www.cnblogs.com/zihunqingxin/p/9380509.html
Copyright © 2020-2023  润新知