前言
k8s的集群日志收集结构,一般分为下面几种:
- 集群节点级模式,节点程序收集系统(k8s和容器)底层日志,并传输到日志系统。
- 伪边车容器模式,边车容器程序只负责将共享卷里的文件日志读取并传输到节点的stdout和stderr,然后节点级Pod收集器获取日志并传输到日志系统。
- 边车容器模式,边车容器程序通过共享卷从应用程序那收集日志,然后传输到日志系统。
- 应用容器模式,主容器程序直接将日志流式传输到日志系统。
k8s的日志收集分两部分:
- 自身日志,通过集群节点级模式收集
- 应用日志,根据情况选择收集方式
自身日志
采用daemonset方式在每一个节点上部署一个日志收集程序。对节点的日志目录采集:
- 节点级日志:/var/log
应用日志
集群节点级模式(模式1)
采用daemonset方式在每一个节点上部署一个日志收集程序。对节点的日志目录采集:
- 应用级日志:/var/lib/docker/containers
例如:官方的示例EFK,fluentd(节点级收集器)=》elasticsearch =》 kibana
✨这种方式,需要主容器程序将日志输出到stdout和stderr。
伪边车容器模式(模式2)
前提:主容器程序需要将日志以文件形式放在共享卷中。
通过边车容器程序将日志文件重新读取并输出到stdout,比如用tail -n+1 -f log.file
;
然后通过集群节点级模式的方式收集日志。
💥相比于模式1,主容器程序写入一次日志文件,同时边车容器程序又重新读取文件日志,对磁盘负担增大。
✨这种方式,目的是解决日志文件在本地,但又不想给每一个pod部署一个日志收集程序。毕竟日志收集程序占用资源多。
总的来说是:磁盘压力大,CPU和内存额外开销小。
一个Pod例子:
apiVersion: v1
kind: Pod
metadata:
name: counter
spec:
containers:
- name: count
image: busybox
args:
- /bin/sh
- -c
- >
i=0;
while true;do
echo "$i: $(date)" >> /var/log/1.log;
echo "$(date) INFO $i" >> /var/log/2.log;
i=$((i+1));
sleep 1;
done
volumeMounts:
- name: varlog
mountPath: /var/log
- name: count-log-1
image: busybox
args: [/bin/sh, -c, 'tail -n+1 -f /var/log/1.log']
volumeMounts:
- name: varlog
mountPath: /var/log
- name: count-log-2
image: busybox
args: [/bin/sh, -c, 'tail -n+1 -f /var/log/2.log']
volumeMounts:
- name: varlog
mountPath: /var/log
volumes:
- name: varlog
emptyDir: {}
边车容器模式(模式3)
前提:主容器程序需要将日志以文件形式放在共享卷(例如通过emtyDir共享日志目录)。
通过边车容器程序将文件日志重新读取并发送到远程日志系统,例如用filebeat;
💥相比于模式1,主容器程序写入一次日志文件,同时边车容器程序又重新读取文件日志,对磁盘负担增大。
💥相比于模式2,无需将日志在写入到节点级,磁盘负担压力小了50%。但filebeat程序CPU和内存消耗大。
每一个运行的pod中添加一个日志收集容器filebeat,通过共享卷共享日志目录收集应用日志,将收集后的日志数据传输到logstash
logstash 通过 filebeat 配置的标签创建不同的索引
filebeat示例
节点级DaemonSet类型filebeat,收集k8s节点日志并传输到kafka
https://www.elastic.co/guide/en/beats/filebeat/7.17/running-on-kubernetes.html
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: k8s-logs
namespace: kube-system
spec:
selector:
matchLabels:
project: k8s
app: filebeat
template:
metadata:
labels:
project: k8s
app: filebeat
spec:
hostAliases:
- ip: "10.200.16.51"
hostnames:
- "data01"
- ip: "10.200.16.52"
hostnames:
- "data02"
- ip: "10.200.16.53"
hostnames:
- "data03"
containers:
- name: filebeat
image: docker.elastic.co/beats/filebeat:7.17.1
args: [
"-c", "/etc/filebeat.yml",
"-e",
]
resources:
requests:
cpu: 100m
memory: 100Mi
limits:
cpu: 500m
memory: 500Mi
securityContext:
runAsUser: 0
volumeMounts:
- name: filebeat-config
mountPath: /etc/filebeat.yml
subPath: filebeat.yml
- name: k8s-logs
mountPath: /messages
volumes:
- name: filebeat-config
configMap:
name: k8s-logs-filebeat-config
- name: k8s-logs
hostPath:
path: /var/log/messages
type: File
---
apiVersion: v1
kind: ConfigMap
metadata:
name: k8s-logs-filebeat-config
namespace: kube-system
data:
filebeat.yml: |-
filebeat.shutdown_timeout: 5s
close_removed: true
clean_removed: true
filebeat.inputs:
- type: log
enabled: true
paths:
- /messages
tags: ["k8s","messages"]
fields:
log_topic: zz.it.elk.k8s.messages
fields_under_root: true
output.kafka:
hosts: ["data01:8123", "data01:8123", "data01:8123"]
username: elk
password: 123456
sasl.mechanism: 'SCRAM-SHA-256'
topic: '%{[log_topic]}'
partition.round_robin:
reachable_only: false
required_acks: 1
compression: gzip
max_message_bytes: 1000000
logstash 配置
input {
beats {
port => 5044
}
}
filter {
}
output {
elasticsearch {
hosts => ["http://xxx:9200"]
index => "k8s-syslog-%{+YYYY.MM.dd}"
}
}
# 调试用
stdout {
codec => rubydebug
}
节点级filebeat
- type: log
paths:
- /messages
fields:
app: k8s
type: module
fields_under_root: true
output.logstash:
hosts: ['xxx:5044']