分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > 运营维护

kubernets轻量 contain log 日志收集技巧

发布时间:2023-09-06 02:06责任编辑:顾先生关键词:暂无标签

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

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

不想看字的可以直接看代码,一共没几行,尽量用调用本地方法实现,有工夫的可以改写成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独立部署一个脚本即可。

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

kubernets轻量 contain log 日志收集技巧

原文地址:https://www.cnblogs.com/zihunqingxin/p/9380509.html

知识推荐

我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved