一份接地氣的Kubernetes日志方案
微服務(wù)應(yīng)用的日志鏈路一般比較長(zhǎng),包含以下環(huán)節(jié):日志收集 → 日志緩沖 → 日志過(guò)濾清洗 → 日志存儲(chǔ) → 日志展示。每個(gè)環(huán)節(jié)都有多種對(duì)應(yīng)的組件去解決,這樣的結(jié)果就是業(yè)內(nèi)組合出了多種整體解決方案。
以前我的微服務(wù)部署在IDC機(jī)房虛擬機(jī)時(shí),采用的是ELK(Elasticsearch、Logstash、Kibana)方案,這也是通用的微服務(wù)應(yīng)用的日志解決方案。幾年前我們的應(yīng)用部署整體切到Kubernetes后,我依舊采用了這套方案。
下面介紹Kubernetes場(chǎng)景下基于ELK的日志解決方案。整體思路:Filebeat -> Kafka -> Logstash -> Elasticsearch -> Kibana。
1、日志數(shù)據(jù)流轉(zhuǎn)
日志數(shù)據(jù)流轉(zhuǎn)見(jiàn)下圖:
2、日志采集
2.1、容器日志在哪兒
首先得有個(gè)概念:容器只是K8S集群Node上的一個(gè)進(jìn)程。要在K8S集群機(jī)器上找到此Docker進(jìn)程,然后進(jìn)入到對(duì)應(yīng)的文件夾里查看日志文件。
一般情況下,容器的日志存放在宿主機(jī)上的這個(gè)目錄下/var/lib/docker/containers/:
# 日志在宿主機(jī)的這個(gè)文件夾下
cd /var/lib/docker/containers
# 用這個(gè)命令可以查找對(duì)應(yīng)的日志文件
find /var/lib/docker/containers -name "*-json.log"
進(jìn)入到/var/lib/docker/containers/下,看到的是一堆毫無(wú)規(guī)律的文件夾。
看到這些毫無(wú)規(guī)律的文件夾名稱(chēng),會(huì)一下子有點(diǎn)懵,但是仔細(xì)看看,其實(shí)這些碼是對(duì)應(yīng)的Docker容器的id。繼續(xù)通過(guò)名稱(chēng)查看容器id。
# docker命令查看容器
docker ps -a
找到了容器id之后,可以看到用容器id的前幾位,可以完全匹配到,日志文件夾名稱(chēng)的前幾位。docker ps 顯示的容器id只是顯示了整個(gè)id的前幾位。
進(jìn)入到日志文件夾后,就可以看到具體的json日志文件了。
至此已經(jīng)知道日志文件存放的位置了。當(dāng)然啦,要控制好日志級(jí)別,還要做好日志清理任務(wù),否則大量的日志會(huì)導(dǎo)致磁盤(pán)空間不夠。Pod銷(xiāo)毀之后,日志文件也會(huì)被銷(xiāo)毀的。
文件找到了接下來(lái),就看怎么采集日志了。
2.2、日志采集工具
日志采集工具有多種,本文采用Filebeat作為日志采集工具。
Filebeat是用于轉(zhuǎn)發(fā)和匯總?cè)罩九c文件的輕量級(jí)傳送程序。作為服務(wù)器上的代理安裝,F(xiàn)ilebeat會(huì)監(jiān)視你指定的日志文件或位置。然后收集日志事件,并將它們轉(zhuǎn)發(fā)到Elasticsearch或Logstash或Kafka。官方文檔顯示的工作流程如下:
Filebeat的主要優(yōu)勢(shì)有:
- 輕量級(jí)并且易使用
- 免費(fèi)開(kāi)源
- 資源使用率低
- 良好的性能
2.3、日志如何采集
日志采集工具選型確定之后,接下來(lái)就是如何采集了。
K8S部署的場(chǎng)景下,想要收集每臺(tái)Node下的容器日志,需要采用Deamonset控制器自動(dòng)部署,這樣每次新增節(jié)點(diǎn)時(shí),會(huì)自動(dòng)部署Filebeat的Pod。每臺(tái)Node自動(dòng)安裝好Filebeat后,每臺(tái)Node上的日志會(huì)被自動(dòng)采集,然后輸出到Kafka。
Filebeat大致的編排yaml如下:
apiVersion: v1
kind: ConfigMap
metadata:
name: filebeat-config
namespace: ops-monit
labels:
k8s-app: filebeat
data:
filebeat.yml: |-
filebeat.inputs:
- type: container #因?yàn)槭遣杉娜萜魅罩?,所以這里要用container 不能用 log,否則拿不到容器日志
enable: true
stream: stdout #只取stdout日志
paths:
- /var/log/containers/*demo*.log #采集了demo環(huán)境的所有日志
processors:
- add_kubernetes_metadata: # 增加kubernetes的屬性
in_cluster: true
host: ${NODE_NAME}
matchers:
- logs_path:
logs_path: "/var/log/containers/"
- drop_event:
when:
contains:
message: "INFO"
- drop_event:
when:
contains:
message: "DEBUG"
# 配置多行顯示
multiline.type: pattern
multiline.pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}'
multiline.negate: true
multiline.match: after
fields:
logtype: applog
output.kafka:
hosts: ['172.10.10.10:9092','172.10.10.11:9092','172.10.10.12:9092']
topic: 'topic-bizlog'
partition.round_robin:
reachable_only: false
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: filebeat
namespace: ops-monit
labels:
k8s-app: filebeat
spec:
selector:
matchLabels:
k8s-app: filebeat
template:
metadata:
labels:
k8s-app: filebeat
spec:
serviceAccountName: filebeat
terminationGracePeriodSeconds: 30
dnsPolicy: ClusterFirstWithHostNet
containers:
- name: filebeat
image: elastic/filebeat:7.12.1
args: [
"-c", "/etc/filebeat.yml",
"-e",
]
env:
- name: ELASTICSEARCH_HOST
value: "172.10.20.10"
- name: ELASTICSEARCH_PORT
value: "9200"
- name: ELASTICSEARCH_USERNAME
value:
- name: ELASTICSEARCH_PASSWORD
value:
- name: ELASTIC_CLOUD_ID
value:
- name: ELASTIC_CLOUD_AUTH
value:
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
securityContext:
runAsUser: 0
# If using Red Hat OpenShift uncomment this:
# privileged: true
resources:
limits:
cpu: 3000m
memory: 2000Mi
requests:
cpu: 500m
memory: 100Mi
volumeMounts:
- name: timezone
mountPath: /etc/localtime
- name: config
mountPath: /etc/filebeat.yml
readOnly: true
subPath: filebeat.yml
- name: data
mountPath: /usr/share/filebeat/data
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
- name: varlog
mountPath: /var/log
volumes:
- name: timezone
hostPath:
path: /usr/share/zoneinfo/Asia/Shanghai
- name: config
configMap:
defaultMode: 0640
name: filebeat-config
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
- name: varlog
hostPath:
path: /var/log
- name: data
hostPath:
path: /var/lib/filebeat-data
type: DirectoryOrCreate
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: filebeat
namespace: ops-monit
subjects:
- kind: ServiceAccount
name: filebeat
namespace: ops-monit
roleRef:
kind: ClusterRole
name: filebeat
apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: filebeat
namespace: ops-monit
labels:
k8s-app: filebeat
rules:
- apiGroups: [""] # "" indicates the core API group
resources:
- namespaces
- pods
- nodes
verbs:
- get
- watch
- list
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: filebeat
namespace: ops-monit
labels:
k8s-app: filebeat
---
3、日志緩沖、過(guò)濾清洗、存儲(chǔ)、展示
3.1、緩沖
Kafka是一個(gè)消息處理引擎,這里采用Kafka作為日志數(shù)據(jù)的緩沖工具。采用Kafka有2個(gè)用途:
- 作為緩沖,防止日志量太大導(dǎo)致下游來(lái)不及消費(fèi),所以要加入消息緩沖這一層。這一層必不可少。
- Kafka消息可以被別的應(yīng)用監(jiān)聽(tīng)消費(fèi),過(guò)濾輸出到一些告警信息到企微、釘釘、郵件等。
3.2、過(guò)濾清洗和轉(zhuǎn)發(fā)
Logstash 是一個(gè)日志收集和處理引擎,它帶有各種各樣的插件,能夠從各種來(lái)源攝取數(shù)據(jù)。并且可以對(duì)數(shù)據(jù)進(jìn)行轉(zhuǎn)換,然后轉(zhuǎn)發(fā)到目的地。我這里采用Logstash作為日志攝取、過(guò)濾、清洗、轉(zhuǎn)發(fā)的工具。
這是一個(gè)大概的Logstash Conf文件,文件的內(nèi)容分3塊:input 、filter 、output。
input {
kafka {
bootstrap_servers=>"172.10.7.79:9092"
topics=>["topic-bizlogs"]
codec => "json"
}
}
filter{
mutate{
split => ["message", "|"]
add_field => { "log_time" => "%{[message][0]}"}
add_field => { "level" => "%{[message][1]}"}
add_field => { "class" => "%{[message][2]}"}
add_field => { "line" => "%{[message][3]}"}
add_field => { "thread" => "%{[message][4]}"}
add_field => { "log_message" => "%{[message][5]}"}
add_field => { "env" => "%{[kubernetes][namespace]}"}
add_field => { "podName" => "%{[kubernetes][pod][name]}"}
add_field => { "podId" => "%{[kubernetes][pod][uid]}"}
add_field => { "image" => "%{[container][image][name]}"}
add_field => { "imageId" => "%{[container][id]}"}
add_field => { "nodeId" => "%{[kubernetes][node][uid]}"}
add_field => { "nodeName" => "%{[kubernetes][node][name]}"}
add_field => { "nodeHostName" => "%{[kubernetes][node][hostname]}"}
add_field => { "logPath" => "%{[log][file][path]}"}
add_field => { "appName" => "%{[kubernetes][labels][app]}"}
remove_field => ["agent","fields","input","ecs","host","@version","kubernetes","stream","log","container"]
}
}
output{
elasticsearch{
hosts=>["172.11.4.82:9200"]
index => "%{appName}‐%{+YYYY.MM.dd}"
}
}
3.3、存儲(chǔ)和搜索
Elasticsearch是一個(gè)可擴(kuò)展的搜索引擎,這里采用Elasticsearch作為日志存儲(chǔ)搜索工具。
3.4、展示
采用Kibana為日志構(gòu)建可視化的UI。
4、總結(jié)
本文主要介紹Kubernetes場(chǎng)景下比較接地氣好落地的,基于ELK的日志解決方案。整體思路:Filebeat -> Kafka -> Logstash -> Elasticsearch -> Kibana。
本文沒(méi)有介紹Kafka、Logstash、Elasticsearch、Kibana的安裝,只提及了一些配置文件,安裝過(guò)程讀者自行查閱資料搭建。