网站首页 > 技术文章 正文
1 系统环境说明
- Elasticsearch:v6.7.2
- Log-pilot:0.9.7-filebeat
- Kibana-oss:6.7.2 # oss代表开源版本
- Kubernetes v1.14.2
Log-Pilot目前为止还不支持Elasticsearch、Kibana-oss组件v7.x以上版本,具体表现为Log-Pilot容器内部filebeat收集日志报错,提示格式不对之类的,具体查看https://github.com/AliyunContainerService/log-pilot/issues/235
2 背景信息
开发者在面对 Kubernetes 分布式集群下的日志需求时,常常会感到头疼,既有容器自身特性的原因,也有现有日志采集工具的桎梏,主要包括:
- 容器本身特性:
- 采集目标多:容器本身的特性导致采集目标多,需要采集容器内日志、容器 stdout。对于容器内部的文件日志采集,现在并没有一个很好的工具能够去动态发现采集。针对每种数据源都有对应的采集软件,但缺乏一站式的工具。
- 弹性伸缩难:Kubernetes 是分布式的集群,服务、环境的弹性伸缩对于日志采集带来了很大的困难,无法像传统虚拟机环境下那样,事先配置好日志的采集路径等信息,采集的动态性以及数据完整性是非常大的挑战。
- 现有日志工具的一些缺陷:
- 缺乏动态配置的能力。目前的采集工具都需要事先手动配置好日志采集方式和路径等信息,因为它无法能够自动感知到容器的生命周期变化或者动态漂移,所以它无法动态地去配置。
- 日志采集重复或丢失的问题。因为现在的一些采集工具基本上是通过 tail 的方式来进行日志采集的,那么这里就可能存在两个方面的问题:一个是可能导致日志丢失,比如采集工具在重启的过程中,而应用依然在写日志,那么就有可能导致这个窗口期的日志丢失;而对于这种情况一般保守的做法就是,默认往前多采集 1M 日志或 2M 的日志,那么这就又会可能引起日志采集重复的问题。
- 未明确标记日志源。因为一个应用可能有很多个容器,输出的应用日志也是一样的,那么当我们将所有应用日志收集到统一日志存储后端时,在搜索日志的时候,我们就无法明确这条日志具体是哪一个节点上的哪一个应用容器产生的。
本文档将介绍一种 Docker 日志收集工具 Log-Pilot,结合 Elasticsearch 和 Kibana 等工具,形成一套适用于 Kubernetes 环境下的一站式日志解决方案。
2.1 组件介绍
这里重点介绍下log-Pilot收集工具
log-Pilot 是一个智能容器日志采集工具,它不仅能够高效便捷地将容器日志采集输出到多种存储日志后端,同时还能够动态地发现和采集容器内部的日志文件。
针对前面提出的日志采集难题,Log-Pilot 通过声明式配置实现强大的容器事件管理,可同时获取容器标准输出和内部文件日志,解决了动态伸缩问题,此外,Log-Pilot 具有自动发现机制,CheckPoint 及句柄保持的机制,自动日志数据打标,有效应对动态配置、日志重复和丢失以及日志源标记等问题。
目前 log-pilot 在 Github 完全开源,项目地址是 https://github.com/AliyunContainerService/log-pilot 。您可以深入了解更多实现原理。
2.2 针对容器日志的声明式配置
Log-Pilot 支持容器事件管理,它能够动态地监听容器的事件变化,然后依据容器的标签来进行解析,生成日志采集配置文件,然后交由采集插件来进行日志采集。
在 Kubernetes 下,Log-Pilot 可以依据环境变量 aliyun_logs_$name = $path 动态地生成日志采集配置文件,其中包含两个变量:
- $name 是我们自定义的一个字符串,它在不同的场景下指代不同的含义,在本场景中,将日志采集到 ElasticSearch 的时候,这个 $name 表示的是 Index。
- 另一个是 $path,支持两种输入形式,stdout 和容器内部日志文件的路径,对应日志标准输出和容器内的日志文件。
- 第一种约定关键字 stdout 表示的是采集容器的标准输出日志,如本例中我们要采集 tomcat 容器日志,那么我们通过配置标签 aliyun.logs.catalina=stdout 来采集 tomcat 标准输出日志。
- 第二种是容器内部日志文件的路径,也支持通配符的方式,通过配置环境变量 aliyun_logs_access=/usr/local/tomcat/logs/*.log来采集 tomcat 容器内部的日志。当然如果你不想使用 aliyun 这个关键字,Log-Pilot 也提供了环境变量 PILOT_LOG_PREFIX 可以指定自己的声明式日志配置前缀,比如 PILOT_LOG_PREFIX: "aliyun,custom"。
此外,Log-Pilot 还支持多种日志解析格式,通过 aliyun_logs_$name_format=<format> 标签就可以告诉 Log-Pilot 在采集日志的时候,同时以什么样的格式来解析日志记录,支持的格式包括:none、json、csv、nginx、apache2 和 regxp。
Log-Pilot 同时支持自定义 tag,我们可以在环境变量里配置 aliyun_logs_$name_tags="K1=V1,K2=V2",那么在采集日志的时候也会将 K1=V1 和 K2=V2 采集到容器的日志输出中。自定义 tag 可帮助您给日志产生的环境打上 tag,方便进行日志统计、日志路由和日志过滤。
3 日志架构
需要采集的日志分为2类:
- 标准输出日志,例如nginx、tomcat、pod类打印到前台的日志等
- 应用文件日志,例如php程序日志、业务代码输出的日志等
4 日志收集
4.1 部署Elasticsearch
准备es-service.yaml文件
apiVersion: v1
kind: Service
metadata:
name: elasticsearch-logging
namespace: kube-system
labels:
k8s-app: elasticsearch-logging
kubernetes.io/cluster-service: "true"
addonmanager.kubernetes.io/mode: Reconcile
kubernetes.io/name: "Elasticsearch"
spec:
ports:
- port: 9200
protocol: TCP
targetPort: db
selector:
k8s-app: elasticsearch-logging准备es-statefulset.yaml文件
# RBAC authn and authz
apiVersion: v1
kind: ServiceAccount
metadata:
name: elasticsearch-logging
namespace: kube-system
labels:
k8s-app: elasticsearch-logging
addonmanager.kubernetes.io/mode: Reconcile
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: elasticsearch-logging
labels:
k8s-app: elasticsearch-logging
addonmanager.kubernetes.io/mode: Reconcile
rules:
- apiGroups:
- ""
resources:
- "services"
- "namespaces"
- "endpoints"
verbs:
- "get"
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: kube-system
name: elasticsearch-logging
labels:
k8s-app: elasticsearch-logging
addonmanager.kubernetes.io/mode: Reconcile
subjects:
- kind: ServiceAccount
name: elasticsearch-logging
namespace: kube-system
apiGroup: ""
roleRef:
kind: ClusterRole
name: elasticsearch-logging
apiGroup: ""
---
# Elasticsearch deployment itself
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: elasticsearch-logging
namespace: kube-system
labels:
k8s-app: elasticsearch-logging
version: v7.2.0
addonmanager.kubernetes.io/mode: Reconcile
spec:
serviceName: elasticsearch-logging
replicas: 3
selector:
matchLabels:
k8s-app: elasticsearch-logging
version: v7.2.0
template:
metadata:
labels:
k8s-app: elasticsearch-logging
version: v7.2.0
spec:
tolerations:
- effect: NoSchedule
key: node-role.kubernetes.io/master
serviceAccountName: elasticsearch-logging
containers:
- image: quay.io/fluentd_elasticsearch/elasticsearch:v6.7.2
name: elasticsearch-logging
imagePullPolicy: Always
resources:
# need more cpu upon initialization, therefore burstable class
limits:
cpu: 1000m
memory: 3Gi
requests:
cpu: 100m
memory: 3Gi
ports:
- containerPort: 9200
name: db
protocol: TCP
- containerPort: 9300
name: transport
protocol: TCP
securityContext:
capabilities:
add:
- IPC_LOCK
- SYS_RESOURCE
volumeMounts:
- name: elasticsearch-logging
mountPath: /data
env:
- name: "http.host"
value: "0.0.0.0"
- name: "network.host"
value: "_eth0_"
- name: "bootstrap.memory_lock"
value: "false"
- name: "cluster.name"
value: "docker-cluster"
- name: "NAMESPACE"
valueFrom:
fieldRef:
fieldPath: metadata.namespace
#volumes:
#- name: elasticsearch-logging
# emptyDir: {}
# Elasticsearch requires vm.max_map_count to be at least 262144.
# If your OS already sets up this number to a higher value, feel free
# to remove this init container.
initContainers:
- image: alpine:3.6
command: ["/sbin/sysctl", "-w", "vm.max_map_count=262144"]
name: elasticsearch-logging-init
securityContext:
privileged: true
volumeClaimTemplates:
- metadata:
name: elasticsearch-logging
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: prod-sc
resources:
requests:
storage: 8Gi数据持久化我这里使用了storeageclass后端ceph分布式存储;当然也可以选择nfs作为存储,或者默认数据存储跟随pod的生命周期随之释放,如果选择默认把上面“#volumes:”那段注释打开,下面的volumeClaimTemplates那段注释了就行了
4.2 部署Kibana
准备kibana-service.yaml文件
apiVersion: v1
kind: Service
metadata:
name: kibana-logging
namespace: kube-system
labels:
k8s-app: kibana-logging
kubernetes.io/cluster-service: "true"
addonmanager.kubernetes.io/mode: Reconcile
kubernetes.io/name: "Kibana"
spec:
ports:
- port: 5601
protocol: TCP
targetPort: ui
selector:
k8s-app: kibana-logging准备kibana-deployment.yaml文件
apiVersion: apps/v1
kind: Deployment
metadata:
name: kibana-logging
namespace: kube-system
labels:
k8s-app: kibana-logging
addonmanager.kubernetes.io/mode: Reconcile
spec:
replicas: 1
selector:
matchLabels:
k8s-app: kibana-logging
template:
metadata:
labels:
k8s-app: kibana-logging
annotations:
seccomp.security.alpha.kubernetes.io/pod: 'docker/default'
spec:
containers:
- name: kibana-logging
image: docker.elastic.co/kibana/kibana-oss:6.7.2
securityContext:
runAsNonRoot: true
runAsUser: 1000
resources:
# need more cpu upon initialization, therefore burstable class
limits:
cpu: 1000m
requests:
cpu: 100m
env:
- name: ELASTICSEARCH_HOSTS
value: http://elasticsearch-logging:9200
- name: SERVER_NAME
value: kibana-logging
#- name: SERVER_BASEPATH
# value: /api/v1/namespaces/kube-system/services/kibana-logging/proxy
- name: SERVER_REWRITEBASEPATH
value: "false"
ports:
- containerPort: 5601
name: ui
protocol: TCP
livenessProbe:
httpGet:
path: /api/status
port: ui
initialDelaySeconds: 5
timeoutSeconds: 10
readinessProbe:
httpGet:
path: /api/status
port: ui
initialDelaySeconds: 5
timeoutSeconds: 104.3 部署Log-pilot
准备log-pilot.yaml文件
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
name: log-pilot
namespace: kube-system
labels:
k8s-app: log-pilot
spec:
updateStrategy:
type: RollingUpdate
template:
metadata:
labels:
k8s-app: log-pilot
spec:
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule
serviceAccountName: elasticsearch-logging
containers:
- name: log-pilot
image: registry.cn-hangzhou.aliyuncs.com/acs/log-pilot:0.9.7-filebeat
env:
- name: "LOGGING_OUTPUT"
value: "elasticsearch"
- name: "ELASTICSEARCH_HOSTS"
value: "elasticsearch-logging:9200"
- name: "NODE_NAME"
valueFrom:
fieldRef:
fieldPath: spec.nodeName
volumeMounts:
- name: sock
mountPath: /var/run/docker.sock
- name: logs
mountPath: /var/log/filebeat
- name: state
mountPath: /var/lib/filebeat
- name: root
mountPath: /host
readOnly: true
- name: localtime
mountPath: /etc/localtime
securityContext:
capabilities:
add:
- SYS_ADMIN
terminationGracePeriodSeconds: 30
volumes:
- name: sock
hostPath:
path: /var/run/docker.sock
- name: logs
hostPath:
path: /var/log/filebeat
- name: state
hostPath:
path: /var/lib/filebeat
- name: root
hostPath:
path: /
- name: localtime
hostPath:
path: /etc/localtimeLOGGING_OUTPUT:填写后端存储日志的服务类型,这里使用elasticsearch
ELASTICSEARCH_HOSTS:这里填写elasticsearch地址和端口
4.4 部署日志组件yaml文件
上面已经准备了各组件的yaml文件,这里开始部署到k8s环境中
$ ls -l 总用量 20 -rw-r--r-- 1 root root 382 12月 3 18:08 es-service.yaml -rw-r--r-- 1 root root 3157 12月 4 11:33 es-statefulset.yaml -rw-r--r-- 1 root root 1537 12月 4 11:52 kibana-deployment.yaml -rw-r--r-- 1 root root 354 12月 3 18:17 kibana-service.yaml -rw-r--r-- 1 root root 1636 12月 3 18:15 log-pilot.yaml $ kubectl apply -f .
查看pod启动情况
$ kubectl get pods -n kube-system | grep -E "ela|log-pilot|kiba" elasticsearch-logging-0 1/1 Running 0 12d elasticsearch-logging-1 1/1 Running 0 12d elasticsearch-logging-2 1/1 Running 0 12d kibana-logging-96c5c69b5-hmfcf 1/1 Running 0 12d log-pilot-fm85f 1/1 Running 0 12d log-pilot-tfr7k 1/1 Running 0 12d
到目前为止,日志收集ELK已经部署完成,也都启动正常;但是还是无法收集到想要的日志,前面介绍Log-Pilot时候有提到Log-Pilot是声明式的配置,Log-Pilot 支持容器事件管理,它能够动态地监听容器的事件变化,然后依据容器的标签来进行解析,生成日志采集配置文件,然后交由采集插件来进行日志采集。
所以我们需要在需要收集日志的pod声明配置容器标签,来让Log-Pilot动态监听日志的变化,下面来配置标准输出日志的pod和应用日志的pod
4.5 配置pod日志标签
4.5.1 配置标准输出ingress
配置ingress-controller.yaml文件
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-ingress-controller
namespace: kube-system
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
spec:
......
spec:
......
containers:
......
env:
......
- name: aliyun_logs_ingress
value: "stdout"aliyun_logs_ingress: 声明变量为标准输出“stdout”,前面针对容器日志的声明式配置也有详细的说明
4.5.2 配置应用日志goproxy
配置proxy_deployment.yml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: proxy-test
spec:
......
spec:
containers:
.......
env:
- name: aliyun_logs_goproxy
value: "stdout"
- name: aliyun_logs_access
value: "/data/modules/proxy_sandbox/log/*.log"aliyun_logs_ingress: 声明变量为标准输出“stdout”,前面针对容器日志的声明式配置也有详细的说明
aliyun_logs_access: 声明变量为应用日志路径“/data/modules/proxy_sandbox/log/*.log”,支持模糊匹配,这里的路径指的是容器内日志文件的路径
以上声明基本满足业务需求,如果需要更复杂的使用方式,可以参见Log-pilot官方说明 https://github.com/AliyunContainerService/log-pilot
配置修改完成,最后从新部署下ingress和goproxy两个pod即可
5 通过Kibana查看日志
第一次打开,需要创建索引
创建标准输出日志的索引
创建应用日志的索引
这里看到我们已经创建了2个不同索引
为了更好的区分应用日志,可以根据不同的应用,声明不同的标签就可,标签就是索引;在 Kubernetes 下,Log-Pilot 可以依据pod环境变量 aliyun_logs_$name = $path 动态地生成日志采集配置文件,$name 是我们自定义的一个字符串,将日志采集到ElasticSearch 的时候,这个 $name 表示的是 Index。
猜你喜欢
- 2024-10-19 Node-RED系列(六):Node-RED解析节点的使用
- 2024-10-19 越南指数行情数据API接口(越南指数股票最新行情)
- 2024-10-19 Pinot 架构分析(pod架构)
- 2024-10-19 大模型开发者实战揭秘:SFT指令微调数据构建的全方位指南
- 2024-10-19 27K star!这款开源可视利器帮你一眼看穿JSON
- 2024-10-19 linux-shell命令处理json数据(linux检查json格式)
- 2024-10-19 MongoDB常用特性一览(mongodb4.2新特性)
- 2024-10-19 轻量级的原生JavaScript的Excel插件——JExcel
- 2024-10-19 5万字长文!搞定Spark方方面面(五)
- 2024-10-19 越南指数清单列表数据API接口(越南指数清单列表数据api接口在哪)
- 最近发表
- 标签列表
-
- cmd/c (90)
- c++中::是什么意思 (84)
- 标签用于 (71)
- 主键只能有一个吗 (77)
- c#console.writeline不显示 (95)
- pythoncase语句 (88)
- es6includes (74)
- sqlset (76)
- apt-getinstall-y (100)
- node_modules怎么生成 (87)
- chromepost (71)
- flexdirection (73)
- c++int转char (80)
- mysqlany_value (79)
- static函数和普通函数 (84)
- el-date-picker开始日期早于结束日期 (76)
- js判断是否是json字符串 (75)
- c语言min函数头文件 (77)
- asynccallback (87)
- localstorage.removeitem (77)
- vector线程安全吗 (73)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- 无效的列索引 (74)
