日志系统的重要度不言而喻,开发、测试、用户体验都等都与其密切相关。最近帮助客户完成了容器化日志收集系统的部署,踩了很多坑。本文以k8s下ELKB日志采集方案为例,详细介绍各个组件功能及日志采集过程中遇到的一些问题。
日志包含系统日志、应用服务日志、访问日志等,以往若需要查看日志需登录系统设备,通过vim、cat 等方式查看,操作繁琐、定位问题困难。费事费力还不能达到理想的效果。因此需要一个集中的统一日志管理分析平台,该系统具有分析统计聚合等功能,并具有用户友好的可视化界面。开源实时分析日志ELK应用而生。
ELK是elasticsearch、logstash、kibana三个系统的首字母组合 ,是业界用来搭理海量日志分析平台的绝佳选择。其中elasticsearch是一个开源的分布式搜索引擎,其底层基于lucene实现,主打分布式的近实时文件存储、分析和检索, 是当前流行的企业级搜索引擎。后面准备写一篇es及lucene底层原理解析。
logstash是常见的日志收集中间件,通过它可以轻松的对目标日志进行收集和处理,仅需要简单的配置即可完成部署流程。
Kibana提供了强大的数据可视化和报表统计能力,是针对es restfulApi的上层应用,能清晰的发现当前es集群中的索引构建情况及索引状态。
传统的elk的日志收集体系架构,直接将系统日志输出至logstash,缺点是Logstash是重量级日志收集server,占用cpu资源高且内存占用高。故此在容器化部署过程中通常会采用filebeat/flunt等轻量级监控软件来进行日志初始收集。下面介绍几种常见的日志收集方案。
Beat + Elasticsearch + Kibana
这种方案比较适用于测试小型输入流,或用作demo演示,无法直接应用于真是业务场景中。该方法通过filebeat直接对接es,并通过kibana来可视化日志输出。
Beat + logstash + Elasticsearch + Kibana
相较于上方案,该方法在满足了小业务流量的日志收集需求,一定程度上解决了ELK中Logstash的不足,但是随着Beats 收集的每秒数据量越来越大,Logstash 可能无法承载这么大量日志的处理,面对超大型流量突袭,极有可能对logstash及es造成极大的业务压力,最终导致系统崩盘。
Beat + kafka + logstash + Elasticsearch + Kibana
随着 Beats 收集的每秒数据量越来越大,Logstash 可能无法承载这么大量日志的处理。虽然可以增加 Logstash 节点数量,提高每秒数据的处理速度,但是仍需考虑可能 Elasticsearch 无法承载这么大量的日志的写入。此时,我们可以考虑 引入消息队列 ,进行缓存:
容器化部署流程架构如下图所示:
kubernetes支持两种部署模式:deployment模式及daemonset模式。其中daemonset模式为在每个k8s节点下配置一个单一容器。本文采用daemonset的方式配置为每个k8s节点部署一个logging-agent(filebeat),考虑到filebeat轻量级及与k8s兼容的api访问能力,在部署过程中注意需要指定filebeat用户认证。
首先来看filebeat常见的配置方式, 以收集k8s默认日志为例:
一般而言,k8s中filebeat部署大致必须包含有几个部分:
为了降低耦合度及后续的维护难度,创建filebeat容器的时候将filebeat.yml配置文件以configmap的方式实现。用来指定filebeat内部配置(如监控目录、输出类型、是否包含k8s元数据等)
在filebeat.inputs下可以定义监控日志的路径,必须包含有type及paths字段
- apiVersion: v1
kind: ConfigMap # 指定类型为configmap
metadata: # 定义filebete元数据
name: filebeat-config
labels:
k8s-app: filebeat
kubernetes.io/cluster-service: "true"
app: filebeat-config
data: # 定义processor (包含哪些元数据、监控路径、输出类型)
filebeat.yml: |
processors:
- add_cloud_metadata:
filebeat.modules: # 定义filebeatModule
- module: system
filebeat.inputs: # 定义收集日志路径
- type: log
paths: # 定义收集路径为k8s的标准输出
- /var/log/containers/*.log
symlinks: true
# json.message_key: log
# json.keys_under_root: true
output.elasticsearch: # 表示直接输出之es
hosts: ['es-single:9200']
logging.level: info
使用daemonset方式部署filebeat, 其中有两个较为重要的参数:volumnMounts及volumn,分别指定了filebeat及容器节点的映射路径,其字段为一一对应。
- apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
name: filebeat
labels:
k8s-app: filebeat
kubernetes.io/cluster-service: "true"
spec:
template:
metadata:
name: filebeat
labels:
app: filebeat
k8s-app: filebeat
kubernetes.io/cluster-service: "true"
spec:
containers:
- image: docker.elastic.co/beats/filebeat:6.4.0
name: filebeat
args: [
"-c", "/home/filebeat-config/filebeat.yml",
"-e",
]
securityContext:
runAsUser: 0
volumeMounts:
- name: filebeat-storage
mountPath: /var/log/containers
- name: varlogpods
mountPath: /var/log/pods
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
- name: "filebeat-volume"
mountPath: "/home/filebeat-config"
nodeSelector:
role: front
volumes:
- name: filebeat-storage
hostPath:
path: /var/log/containers
- name: varlogpods
hostPath:
path: /var/log/pods
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
- name: filebeat-volume
configMap:
name: filebeat-config
daemonset方式filebeat部署脚步如下,注意在在后指定了filebeat用户认证
apiVersion: v1
items:
- apiVersion: v1
kind: ConfigMap
metadata:
name: filebeat-config
labels:
k8s-app: filebeat
kubernetes.io/cluster-service: "true"
app: filebeat-config
data:
filebeat.yml: |
processors:
- add_cloud_metadata:
filebeat.modules:
- module: system
filebeat.inputs:
- type: log
paths:
- /var/log/containers/*.log
symlinks: true
# json.message_key: log
# json.keys_under_root: true
output.elasticsearch:
hosts: ['es-single:9200']
logging.level: info
- apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
name: filebeat
labels:
k8s-app: filebeat
kubernetes.io/cluster-service: "true"
spec:
template:
metadata:
name: filebeat
labels:
app: filebeat
k8s-app: filebeat
kubernetes.io/cluster-service: "true"
spec:
containers:
- image: docker.elastic.co/beats/filebeat:6.4.0
name: filebeat
args: [
"-c", "/home/filebeat-config/filebeat.yml",
"-e",
]
securityContext:
runAsUser: 0
volumeMounts:
- name: filebeat-storage
mountPath: /var/log/containers
- name: varlogpods
mountPath: /var/log/pods
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
- name: "filebeat-volume"
mountPath: "/home/filebeat-config"
nodeSelector:
role: front
volumes:
- name: filebeat-storage
hostPath:
path: /var/log/containers
- name: varlogpods
hostPath:
path: /var/log/pods
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
- name: filebeat-volume
configMap:
name: filebeat-config
- apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: filebeat
subjects:
- kind: ServiceAccount
name: filebeat
namespace: default
roleRef:
kind: ClusterRole
name: filebeat
apiGroup: rbac.authorization.k8s.io
- apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: filebeat
labels:
k8s-app: filebeat
rules:
- apiGroups: [""] # "" indicates the core API group
resources:
- namespaces
- pods
verbs:
- get
- watch
- list
- apiVersion: v1
kind: ServiceAccount
metadata:
name: filebeat
namespace: default
labels:
k8s-app: filebeat
部署成功之后可以在pod里查看相应filebeat容器信息。
查看容器控件命令:
kubectl get pod -n {namespace} -owide
进入容器命令:
kubectl exec -ti filebeat-xxx -n {namespace} bash
logstash可以选择安装包部署或yaml方式部署,流程较为统一且十分简单。但在部署过程中需要注意几个点:
logstash的标准配置文件样例:
input{
stdout{}
}
filter{
grok{
match => {"message" => "content"}
}
mutate{
remove_field => ["timestamp"]
}
}
output{
elasticsearch {
hosts => "localhost:9200"
index => "nginx-access-log-%{+YYYY.MM.dd}"
}
}
es及kafka是常用的日志中间件,在k8s部署过程中可以采用deployment部署来实现流程化部署,也可采用阿里云、华为云等云厂商提供的中间件服务来实现简单易用的服务部署使用,下面提供了常用的部署yaml。
kafka.yaml
version: '3'
services:
zookeeper:
image: zookeeper:latest
container_name: zookeeper
volumes:
- /Users/home/docker/kafka/zookeeper/data:/data
- /Users/home/docker/kafka/zookeeper/datalog:/datalog
ports:
- 2181:2181
restart: always
kafka:
image: docker.io/kafka
container_name: kafka
volumes:
- /Users/home/docker/kafka/data:/kafka
ports:
- 9092:9092
environment:
KAFKA_ADVERTISED_HOST_NAME: 192.168.3.3
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_PORT: 9092
KAFKA_LOG_RETENTION_HOURS: 120
KAFKA_MESSAGE_MAX_BYTES: 10000000
KAFKA_REPLICA_FETCH_MAX_BYTES: 10000000
KAFKA_GROUP_MAX_SESSION_TIMEOUT_MS: 60000
KAFKA_NUM_PARTITIONS: 3
KAFKA_DELETE_RETENTION_MS: 1000
restart: always
kafka-manager:
image: kafkamanager/kafka-manager
container_name: kafka-manager
environment:
ZK_HOSTS: 192.168.3.3
ports:
- 9001:9000
restart: always
es.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: es-deployment
namespace: my-namespace
labels:
app: es-deployment
spec:
replicas: 1
selector:
matchLabels:
app: es-pod
template:
metadata:
labels:
app: es-pod
spec:
nodeSelector:
deploy.elk: "true"
restartPolicy: Always
containers:
- name: tsale-server-container
image: "10.68.60.103:5000/elasticsearch:7.8.0"
ports:
- containerPort: 9200
env:
- name: node.name
value: "es01"
- name: cluster.name
value: "tsale-sit2-es"
- name: discovery.seed_hosts
value: "10.68.60.111"
- name: cluster.initial_master_nodes
value: "es01"
- name: bootstrap.memory_lock
value: "false"
- name: ES_JAVA_OPTS
value: "-Xms512m -Xmx1g"
resources:
limits:
memory: "1G"
cpu: "1"
requests:
memory: "512Mi"
cpu: "500m"
volumeMounts:
- mountPath: "/usr/share/elasticsearch/data"
name: "es-data-volume"
- mountPath: "/usr/share/elasticsearch/logs"
name: "es-logs-volume"
imagePullSecrets:
- name: regcred
volumes:
- name: "es-data-volume"
hostPath:
path: "/opt/apps-mgr/es/data"
type: DirectoryOrCreate
- name: "es-logs-volume"
hostPath:
path: "/opt/apps-mgr/es/logs"
type: DirectoryOrCreate
日志收集 | 说明 |
---|---|
Logstash | logstash是常见的日志收集、处理方案,提供了非常强大的日志处理功能,能通过grok等工具轻松实现日志过滤和拆分。但是相较于其他日志收集agent而言,logstash会消耗更多的系统资源,对节点配置提出了更高的要求 |
Fluentd | Logstash 和 Fluentd 都具有收集并处理 log 的能力,功能上二者旗鼓相当, Fluentd 抽象性做得更好,对用户屏蔽了底层细节的繁琐。 |
Filebeat | Filebeats 是一个轻量级的收集本地 log 数据的方案,从官方对其的评价可以看出 Filebeats 功能比较单一,它仅仅只能收集本地的 log,但并不能对收集到的 Log 做什么处理,所以通常 Filebeats 通常需要将收集到的 log 发送到 Logstash 做进一步的处理。但是也正因为其轻量级的特点,适合作为节点内部的日志监控插件 |
1.为何要用kafka?如若不用会产生哪些问题,用kafka的好处及带来的问题
在以往的beat中还不支持MQ, 仅支持es/logstash的output。在后续的更新中,filebeat提供了对于mq的支持。这种架构比较适合于日志规模比较庞大的情况。但由于 Logstash 日志解析节点和 Elasticsearch 的负荷比较重,可将他们配置为集群模式,以分担负荷。引入消息队列,均衡了网络传输,从而降低了网络闭塞,尤其是丢失数据的可能性,但依然存在 Logstash 占用系统资源过多的问题。
如果在filebeat中需要配置多个日志收集路径,每种日志都有不同的结构,如何做好分词?
在configMap中可以配置多个日志收集路径,如果遇到按podId命名的文件夹,可以采用**(多级目录) / *(单目录)来适配。实例如下:
filebeat.inputs:
- type: log
paths:
- /var/log/containers/*.log
- type: log
paths:
- /var/lib/pod/**/kubernetes~io-empty/*.log
对于分词,可以在logstash中通过grok采用正则过滤,实例格式为(?
filter {
grok {
match => {
"message" => [
"\[(?(%{MONTHNUM}/%{MONTHDAY}/%{YEAR})\s+%{TIME}\s+%{WORD})\]\s+%{BASE16NUM}\s+(?([\w|\S]+))\s+%{WORD:LogLevel}\s+(?[\w|\W]*)"
]
}
remove_field => ["message"]
}
}
监控中间件能否不使用filebeat,有没有其他中间可以替代,区别又是什么?
同上
中间件的底层原理(ES、logstash、kafka),关注如何才能用的更好,性能更优。
该问题涉及非常深入,见后续更新
Filebeat 如何读取多个日志目录(收集标准输出、spring日志、nginx访问日志等)?
主要涉及几个part:
filebeat.inputs:
- type: log
paths:
- /var/log/containers/*.log
- type: log
paths:
- /var/lib/pod/**/kubernetes~io-empty/*.log
volumeMounts:
- name: filebeat-storage
mountPath: /var/log/containers
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
volumes:
- name: filebeat-storage
hostPath:
path: /var/log/containers
- name: varlogpods
Filebeat 如何区分不同日志来源?
通过指定field
filebeat.inputs:
- type: log
paths:
- /var/log/containers/*.log
- fields:
logtype: container
如何配置 Logstash 与 Elasticsearch 集群通信?
最简单的做法是把集群中所有的 Elasticsearch 节点的 IP 或者是 hostname 信息都在 hosts 中配上(它支持数组)。但是如果集群比较大,或者是集群节点变动频繁的话,还需要维护这个 hosts 值,不太方便。比较推荐的做法是只配集群中某个节点的信息,可以是 client 节点,也可以是 master 节点或者是 data 节点。因为不管是哪个节点,都知道该它所在集群的信息(集群规模,各节点角色)。这样,Logstash 与任意节点通信时都会先拿到集群信息,然后再决定应该给哪个节点发送数据输出请求
output{
elasticsearch{
user => "test-logstash"
password => "xxxxx"
hosts => ["http://es-ip1:9200","http://es-ip2:9200","http://es-ip3:9200"]
index => "testLog-%{+YYYYMMdd}"
ilm_enabled => false
manage_template => false
}
}
4.filebeat如何多行日志合并展示(如java错误日志,输出栈信息)
通过配置multiline*
filebeat.inputs:
- type: log
enable: true
paths:
- /var/lib/kubelet/*.log
multiline.type: pattern
multiline.pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}'
multiline.negate: true
multiline.match: after
5.如何输出k8s节点信息(日志调试使用pod信息)
指定processors,在其中增加kubernets元数据
processors:
- add_kubernetes_metadata:
default_indexers.enabled: false
default_matchers.enabled: false
indexers:
- pod_uid:
matchers:
- logs_path:
logs_path: '/var/lib/kubelet/pods/'
resource_type: 'pod'
kafka为何与filebeat不通
检查是否同一vpc网络,两者安全组出入要开放9092(kafka)
安全组配置设定
在同一vpc下,注意开放9200(es)、9300(kibana)、9092&9011(kafka)、5044(logstash)
es写入为何突然停止
注意检查写入容量是否充足,es默认配置下,存储达80%则转变为只读
kibana索引pattern配置完成,日志为何不输出
注意刷新pattern,先配置,再刷新,区分索引的时候用时间戳