Kubernetes容器日志收集

日志从传统方式演进到容器方式的过程就不详细讲了,可以参考一下这篇文章Docker日志收集最佳实践,由于容器的漂移、自动伸缩等特性,日志收集也就必须使用新的方式来实现,Kubernetes官方给出的方式基本是这三种:原生方式、DaemonSet方式和Sidecar方式。

1.原生方式:使用 kubectl logs 直接在查看本地保留的日志,或者通过docker engine的 log driver 把日志重定向到文件、syslog、fluentd等系统中。
2.DaemonSet方式:在K8S的每个node上部署日志agent,由agent采集所有容器的日志到服务端。
3.Sidecar方式:一个POD中运行一个sidecar的日志agent容器,用于采集该POD主容器产生的日志。
三种方式都有利有弊,没有哪种方式能够完美的解决100%问题的,所以要根据场景来贴合。

一、原生方式

Kubernetes容器日志收集


简单的说,原生方式就是直接使用kubectl logs来查看日志,或者将docker的日志通过日志驱动来打到syslog、journal等去,然后再通过命令来排查,这种方式最好的优势就是简单、资源占用率低等,但是,在多容器、弹性伸缩情况下,日志的排查会十分困难,仅仅适用于刚开始研究Kubernetes的公司吧。不过,原生方式确实其他两种方式的基础,因为它的两种最基础的理念,daemonset和sidecar模式都是基于这两种方式而来的。

1.1 控制台stdout方式

这种方式是daemonset方式的基础。将日志全部输出到控制台,然后docker开启journal,然后就能在/var/log/journal下面看到二进制的journal日志,如果要查看二进制的日志的话,可以使用journalctl来查看日志:journalctl -u docker.service -n 1 --no-pager -o json -o json-pretty

{ "__CURSOR" : "s=113d7df2f5ff4d0985b08222b365c27a;i=1a5744e3;b=05e0fdf6d1814557939e52c0ac7ea76c;m=5cffae4cd4;t=58a452ca82da8;x=29bef852bcd70ae2", "__REALTIME_TIMESTAMP" : "1559404590149032", "__MONOTONIC_TIMESTAMP" : "399426604244", "_BOOT_ID" : "05e0fdf6d1814557939e52c0ac7ea76c", "PRIORITY" : "6", "CONTAINER_ID_FULL" : "f2108df841b1f72684713998c976db72665f353a3b4ea17cd06b5fc5f0b8ae27", "CONTAINER_NAME" : "k8s_controllers_master-controllers-dev4.gcloud.set_kube-system_dcab37be702c9ab6c2b17122c867c74a_1", "CONTAINER_TAG" : "f2108df841b1", "CONTAINER_ID" : "f2108df841b1", "_TRANSPORT" : "journal", "_PID" : "6418", "_UID" : "0", "_GID" : "0", "_COMM" : "dockerd-current", "_EXE" : "/usr/bin/dockerd-current", "_CMDLINE" : "/usr/bin/dockerd-current --add-runtime docker-runc=http://www.likecs.com/usr/libexec/docker/docker-runc-current --default-runtime=docker-runc --exec-opt native.cgroupdriver=systemd --userland-proxy-path=http://www.likecs.com/usr/libexec/docker/docker-proxy-current --init-path=http://www.likecs.com/usr/libexec/docker/docker-init-current --seccomp-profile=http://www.likecs.com/etc/docker/seccomp.json --selinux-enabled=false --log-driver=journald --insecure-registry hub.paas.kjtyun.com --insecure-registry hub.gcloud.lab --insecure-registry 172.30.0.0/16 --log-level=warn --signature-verification=false --max-concurrent-downloads=20 --max-concurrent-uploads=20 --storage-driver devicemapper --storage-opt dm.fs=xfs --storage-opt dm.thinpooldev=http://www.likecs.com/dev/mapper/docker--vg-docker--pool --storage-opt dm.use_deferred_removal=true --storage-opt dm.use_deferred_deletion=true --mtu=1450", "_CAP_EFFECTIVE" : "1fffffffff", "_SYSTEMD_CGROUP" : "/system.slice/docker.service", "_SYSTEMD_UNIT" : "docker.service", "_SYSTEMD_SLICE" : "system.slice", "_MACHINE_ID" : "225adcce13bd233a56ab481df7413e0b", "_HOSTNAME" : "dev4.gcloud.set", "MESSAGE" : "I0601 23:56:30.148153 1 event.go:221] Event(v1.ObjectReference{Kind:\"DaemonSet\", Namespace:\"openshift-monitoring\", Name:\"node-exporter\", UID:\"f6d2bdc1-6658-11e9-aca2-fa163e938959\", APIVersion:\"apps/v1\", ResourceVersion:\"15378688\", FieldPath:\"\"}): type: 'Normal' reason: 'SuccessfulCreate' Created pod: node-exporter-hvrpf", "_SOURCE_REALTIME_TIMESTAMP" : "1559404590148488" }

在上面的json中,_CMDLINE以及其他字段占用量比较大,而且这些没有什么意义,会导致一条简短的日志却被封装成多了几十倍的量,所以的在日志量特别大的情况下,最好进行一下字段的定制,能够减少就减少。
我们一般需要的字段是CONTAINER_NAME以及MESSAGE,通过CONTAINER_NAME可以获取到Kubernetes的namespace和podName,比如CONTAINER_NAME为k8s_controllers_master-controllers-dev4.gcloud.set_kube-system_dcab37be702c9ab6c2b17122c867c74a_1的时候
container name in pod: controllers
pod name: master-controllers-dev4.gcloud.set
namespace: kube-system
pod uid: dcab37be702c9ab6c2b17122c867c74a_1

1.2 新版本的subPathExpr

journal方式算是比较标准的方式,如果采用hostPath方式,能够直接将日志输出这里。这种方式唯一的缺点就是在旧Kubernetes中无法获取到podName,但是最新版的Kubernetes1.14的一些特性subPathExpr,就是可以将目录挂载的时候同时将podName写进目录里,但是这个特性仍旧是alpha版本,谨慎使用。
简单说下实现原理:容器中填写的日志目录,挂载到宿主机的/data/logs/namespace/service_name/$(PodName)/xxx.log里面,如果是sidecar模式,则将改目录挂载到sidecar的收集目录里面进行推送。如果是宿主机安装fluentd模式,则需要匹配编写代码实现识别namespace、service_name、PodName等,然后发送到日志系统。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsxjdp.html