云原生架构下的日志平台方案
作者简介
Ford, 云原生布道师,云原生实验室(CloudnativeLab.COM)创始人 专注于云计算领域数年,目前主要从事容器云平台的建设,推进各类基础设施服务的云原生化,乐于研发效能建设、产品驱动模式探索和敏捷高效的产品研发团队打造,ServiceMesh拥护者,持续交付、敏捷实践者。
一、云原生架构下的日志系统特点
伴随公司近年来持续高速增长的业务发展,以及软件架构的微服务化,在水平和垂直双向扩展后线上运行的应用成倍增长。原本基于单体应用(Monolithic)场景下,使用tail、grep、awk的日志查询和日志分析方式已捉襟见肘、无法应对云原生架构下成倍增长的应用日志量和分布式项目的复杂运行环境挑战。
在公司转型云原生架构的过程中,复杂的动态集群环境下,可观测性(Observability)对于快速定位和诊断问题,已上升到公司整体层面了。日志作为重要的三大(logs, metrics and traces)可监测要素之一尤为重要。同时日志系统提供的也不再局限于应用系统的诊断,还包括业务、运营、BI、审计、安全等领域,日志平台最终的目标是实现公司在云原生架构下各个方面的数字化、智能化。
图:三大可监测要素:logs, metrics and traces
云原生架构下的日志方案比基于物理机、虚拟机场景的日志架构设计有较大的差异,比如:
1、动态的日志环境,在 kubernetes 集群环境下,应用的弹性伸缩、Pod的销毁和漂移、working节点的上线和下线都是常态,这种情况下日志的存在是瞬间的,伴随着Pod的销毁和漂移日志会被动销毁,因此日志数据需要被实时采集到集中式的存储设备中,同时对于日志采集器在此动态和复杂环境下的扩展性和适配性有新的要求。
2、资源消耗,在原有的传统ELK架构中,基于 JDK 的 Logstash 和 Filebeat 预期分别会消耗500M、12M左右的内存,在微服务、云原生的架构下,服务通常都会拆的很小,因此数据采集对于服务自身的资源消耗要尽可能的少。
3、日志平台的运维代价,运维一套动态环境下的日志采集和日志管理平台是复杂和繁琐的,日志平台应该SaaS话,作为底层基础设施,可一键部署和动态适配。
4、便捷的日志分析、日志系统最核心的功能是问题排查,问题排查的速度直接决定了事故响应速度、损失大小。一套可视化、高性能、智能分析的功能可以帮助用户快速定位问题。
二、云原生架构下的日志系统设计
2.1 方案选型
- 云原生架构下的日志采集解决方案
编号 | 方案 | 优点 | 缺点 |
---|---|---|---|
1 | 每个app的镜像中都集成日志收集组件,如logback-redis-appender | 部署方便,kubernetes的yaml文件无须特别配置,可以灵活的为每个app自定义日志采集规则 | 强耦合,应用侵入式,不方便应用和日志收集组件升级和维护且会导致镜像过大 |
2 | app的Pod内单独创建一个日志采集容器跟app的容器一起运行 | 低耦合,扩展性强,方便维护和升级 | 需要对 kubernetes 的yaml文件进行单独配置,略显繁琐 |
3 | 以 DaemonSet 方式在每个工作节点上启动一个日志采集的Pod, 将所有的Pod的日志都挂载到宿主机上 | 完全解耦,性能最高,管理起来最方便 | 需要统一日志收集规则,目录和输出方式 |
综合以上优缺点,我们选择使用方案三。
该方案在扩展性、资源消耗、部署和后期维护方面都能做到均衡,因此选择该方案。
以下整理各方案的架构图:
图:方案1,应用内置采集组件,异步采集
图:方案2,Pod伴侣容器,Sidercar模式
图:方案3,宿主机统一采集方案
2.2 方案实施及验证
2.2.1 方案说明
在集群启动时以 DaemonSet 方式在每个机器启动一个 Fluent-bit agent,收集日志然后发送给 Elasticsearch。实现方式是每个agent挂载目录 /var/log/containers/
使用 Fluent-bit 的tail插件扫描每个容器日志文件,直接发送给 Elasticsearch。
而 /var/log/containers/ 的日志实际映射自 kubernetes 节点上的容器日志,如下图所示:
图:节点在/var/log/containers/目录下的文件链接路径
fluent-bit 监听及Input配置
代码语言:txt复制 @INCLUDE input-kubernetes.conf
@INCLUDE filter-kubernetes.conf
@INCLUDE output-elasticsearch.conf
input-kubernetes.conf: |
[INPUT]
Name tail
Tag kube.*
Path /var/log/containers/*.log
Parser docker
DB /var/log/flb_kube.db
Mem_Buf_Limit 5MB
Skip_Long_Lines On
Refresh_Interval 10
采集agent本身也是基于 kubernetes 集群部署的,当集群节点扩容时,由 kube-scheduler 执行新节点的 fluent-bit agent 的自动完成部署。
Elasticsearch 和 Kibana 目前使用的云供应商的服务,自带 X-pack 插件,支持商业版才有的权限管理功能。
2.2.2 实施
1、fluent-bit采集器配置(server, input, filters and output)
2、fluent-bit在kubernetes集群中的RABC权限创建
- fluent-bit-service-account.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
代码语言:txt复制name: fluent-bit
代码语言:txt复制namespace: logging
代码语言:txt复制
- fluent-bit-role.yaml
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
代码语言:txt复制name: fluent-bit-read
rules:
- apiGroups: "" resources: - namespaces - pods verbs: "get", "list", "watch"
- fluent-bit-role-binding.yaml
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
代码语言:txt复制name: fluent-bit-read
roleRef:
代码语言:txt复制apiGroup: rbac.authorization.k8s.io
代码语言:txt复制kind: ClusterRole
代码语言:txt复制name: fluent-bit-read
subjects:
- kind: ServiceAccount name: fluent-bit namespace: logging
3、Fluent-bit在kubernetes集群节点以Daemonset方式部署
- fluent-bit-ds.yaml
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
代码语言:txt复制name: fluent-bit
代码语言:txt复制namespace: logging
代码语言:txt复制labels:
代码语言:txt复制 k8s-app: fluent-bit-logging
代码语言:txt复制 version: v1
代码语言:txt复制 kubernetes.io/cluster-service: "true"
spec:
代码语言:txt复制template:
代码语言:txt复制 metadata:
代码语言:txt复制 labels:
代码语言:txt复制 k8s-app: fluent-bit-logging
代码语言:txt复制 version: v1
代码语言:txt复制 kubernetes.io/cluster-service: "true"
代码语言:txt复制 annotations:
代码语言:txt复制 prometheus.io/scrape: "true"
代码语言:txt复制 prometheus.io/port: "2020"
代码语言:txt复制 prometheus.io/path: /api/v1/metrics/prometheus
代码语言:txt复制 spec:
代码语言:txt复制 containers:
代码语言:txt复制 - name: fluent-bit
image: fluent/fluent-bit:1.2.1
imagePullPolicy: Always
ports:
- containerPort: 2020
env:
- name: FLUENT_ELASTICSEARCH_HOST
value: "elasticsearch"
- name: FLUENT_ELASTICSEARCH_PORT
value: "9200"
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
- name: fluent-bit-config
mountPath: /fluent-bit/etc/
terminationGracePeriodSeconds: 10
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
- name: fluent-bit-config
configMap:
name: fluent-bit-config
serviceAccountName: fluent-bit
tolerations:
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
- operator: "Exists"
effect: "NoExecute"
- operator: "Exists"
effect: "NoSchedule"
2.3 验证日志平台
- 简单查询
- 分词查询
- 精准查询
- 复合查询 (AND OR)
- 基于字段的查询
- 项目过滤
- 机器、节点过滤
- 正则查询
- 区间查询
- 查询上下文
- 日志列表显示定制
- 时间选择
图:日志查询 - 复合查询 (AND OR)
图:日志查询-查询上下文
2.4 集群审计日志采集
方案中的,Fluent-bit支持采集kubernetes集群的事件审计日志,在kube-apiserver操作导致的状态变更的都会产生相应日志,如下 kubernetes-audit-policy.yaml 定义了收集那些审计日志,只需要在kube-api启动文件中引用此配置,通过使用 --audit-policy-file
。
- kubernetes-audit-policy.yamlapiVersion: audit.k8s.io/v1 # This is required.
kind: PolicyDon't generate audit events for all requests in RequestReceived stage.omitStages:
- "RequestReceived" rules:Log pod changes at RequestResponse level
- level: RequestResponse
resources:
- group: ""# Resource "pods" doesn't match requests to any subresource of pods, # which is consistent with the RBAC policy. resources: ["pods"]Log "pods/log", "pods/status" at Metadata level
- level: Metadata
resources:
- group: "" resources: "pods/log", "pods/status"
Don't log requests to a configmap called "controller-leader"
- level: None
resources:
- group: "" resources: "configmaps" resourceNames: "controller-leader"
Don't log watch requests by the "system:kube-proxy" on endpoints or services
- level: None
users: "system:kube-proxy"
verbs: "watch"
resources:
- group: "" # core API group resources: "endpoints", "services"
Don't log authenticated requests to certain non-resource URL paths.
- level: None
userGroups: "system:authenticated"
nonResourceURLs:
- "/api*" # Wildcard matching.
- "/version"
Log the request body of configmap changes in kube-system.
- level: Request
resources:
- group: "" # core API group resources: "configmaps"This rule only applies to resources in the "kube-system" namespace.The empty string "" can be used to select non-namespaced resources.namespaces: "kube-system"
Log configmap and secret changes in all other namespaces at the Metadata level.
- level: Metadata
resources:
- group: "" # core API group resources: "secrets", "configmaps"
Log all other resources in core and extensions at the Request level.
- level: Request
resources:
- group: "" # core API group
- group: "extensions" # Version of group should NOT be included.
A catch-all rule to log all other requests at the Metadata level.
- level: MetadataLong-running requests like watches that fall under this rule will notgenerate an audit event in RequestReceived.omitStages:
- "RequestReceived"
图:kubernetes集群审计日志
三、总结
随着云原生架构下日益复杂的分布式系统,日志比较分散,应用监控和排查问题都比较困难,同时效率还低下,本文中kubernetes集群下的集中式日志平台就是为了解决这个问题。将集群日志、应用日志,安全日志收集、检索、统计、分析以及对日志信息的 Web 管理等集中化管控,实现了快速问题排查、高效解决问题的重要的途径。
在生产部署时,可以根据业务系统容量来确定是否引入 Kafaka 队列,线下环境也可以不引入 Kafaka 队列,简单部署,后续需要扩展再接入 Kafaka 队列。
本文中Elasticsearch 和 Kibana 使用的云厂商的服务,线下开发环境,考虑成本节约的因素可以使用 helm 快速构建,参考如下:
- 使用 helm 快速部署 Elasticsearch
helm repo add incubator https://kubernetes-charts-incubator.storage.googleapis.com/
helm install --name elasticsearch stable/elasticsearch
代码语言:txt复制 --set master.persistence.enabled=false
代码语言:txt复制 --set data.persistence.enabled=false
代码语言:txt复制 --namespace logging
代码语言:txt复制
- 使用 helm 快速部署 Kibana
helm install --name kibana stable/kibana
代码语言:txt复制 --set env.ELASTICSEARCH_URL=http://elasticsearch-client:9200
代码语言:txt复制 --namespace logging
代码语言:txt复制
四、参考文档
https://github.com/fluent/fluentd
https://kubernetes.io/docs/tasks/debug-application-cluster/audit/
https://github.com/kubernetes/kubernetes/tree/master/cluster/addons/fluentd-elasticsearch
https://github.com/fluent/fluent-bit
https://github.com/anjia0532/gcr.io_mirror