kubernetes運維之使用elk Stack收集k8s平臺日志
目錄:
一、收集哪些日志
? k8s系統(tǒng)的組件日志 比如kubectl get cs下面的組件
master節(jié)點上的controller-manager,scheduler,apiserver
node節(jié)點上的kubelet,kube-proxy
? k8s Cluster里面部署的應(yīng)用程序日志
這里就形成一個數(shù)據(jù)流,選呢就是Beats,后面是Logstach,后面是ES,后面是kibana,所以這個技術(shù)棧是非常完善的。而beats系列的組件也很多,比如對網(wǎng)絡(luò)數(shù)據(jù)的采集,對日志文件的采集,對windos事件的采集,運行時間的采集,而elk這個棧呢,不僅僅可以采集數(shù)據(jù)日志,還能采集性能資源的指標(biāo),比如CPU,內(nèi)存,網(wǎng)絡(luò),這里呢主要使用Filebeat收集日志文件。監(jiān)控呢就找專業(yè)的去做,比如prometheus。
話說回來,容器的日志怎么收集?
方案一:Node上部署一個日志收集程序
? DaemonSet方式部署日志收集程序
? 對本節(jié)點/var/log和 /var/lib/docker/containers/
兩個目錄下的日志進(jìn)行采集
? Pod中容器日志目錄掛載到宿主機(jī)統(tǒng)一目錄上
也就是my-pod是容器,然后標(biāo)準(zhǔn)輸入輸出到控制臺了,時間上這里是被docker接管了,落地到一個具體的文件中了,docker會接管標(biāo)準(zhǔn)輸出與標(biāo)準(zhǔn)錯誤輸出,然后寫到一個日志里,這里會起一個日志采集的agent,去采集這個日志,而這個張圖,大概意思就是在你每個的node上部署一個日志采集器,然后采集pod日志,標(biāo)準(zhǔn)輸入輸出的目錄,默認(rèn)是在 /var/lib/docker/containers/ 下面,這個下面是運行當(dāng)前的容器的讀寫層,里面就包含了一個日志,一般就是掛載到分布式存儲上,要是掛載到宿主機(jī)的目錄,也不是很太方便,還要去區(qū)別所有的容器。要是使用分布式存儲,比如器一個pod,直接讓存儲專門存儲日志這個卷,
掛載到容器中的啟動容器中,每起一個都掛這個卷,最終都會落到上面去,這樣會好一點。
方案二:Pod中附加專用日志收集的容器
? 每個運行應(yīng)用程序的Pod中增加一個日志
收集容器,使用emtyDir共享日志目錄讓
日志收集程序讀取到。
第二種呢就是一種sidecar的模式,就是在你sidecar模式中再增加一個專門去處理那就想要的事情的容器,這就稱為一個旁路,也就是當(dāng)前你業(yè)務(wù)旁邊,給你增加一個容器,處理你業(yè)務(wù)的日志的,也就是使用emtyDir讓容器A寫日志目錄,給共享到這個目錄里,也就是共享目錄,也就是在宿主機(jī)的目錄里,然后容器B讀取也是掛載這個數(shù)據(jù)卷,它也能自然讀取當(dāng)前數(shù)據(jù)卷內(nèi)的內(nèi)容,所以這種情況呢就附加個Pod就能獲取到日志了。
方案三:應(yīng)用程序直接推送日志
? 超出Kubernetes范圍
這個小公司會有些,不過也不多,就是應(yīng)用程序在代碼里去修改,直接推送到你遠(yuǎn)程的存儲上,不再輸出到控制臺,本地文件了,直接推,這個就不再k8s范圍之內(nèi)了,這種方案用的有不多,用的最多的就是前兩種
這兩種方案有什么優(yōu)缺點?
像方案一就是需要在每臺node節(jié)點上去部署一個日志采集的agent,資源消耗少,一般不需要應(yīng)用程序的介入,但是它需要通過應(yīng)用程序?qū)懙綐?biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯誤輸出,比如tomcat,日志里面都是很多行,需要合并成一行才能進(jìn)行采集,需要轉(zhuǎn)換成json格式,json在docker方面已經(jīng)多行分開了,所以不能使用多行匹配了,所以很尷尬。
像方案二就是在pod中附加專用日志收集的容器,附加到每個應(yīng)用里面,每個pod再加一個容器,就加一個鏡像,這種方式低耦合,缺點呢,顯而易見,你部署一個應(yīng)用,加一個采集器,部署又一個又加一個采集器,就是你要啟動一套項目,需要加一個容器,就是需要資源的消耗了,并增加運維的維護(hù)成本,不過運維維護(hù)成本也還好,主要增加資源的消耗比較大一點。
第三種方案,就是讓開發(fā)去改就行了。
我們使用的是方案二的,附加一個,這個還是比較好實現(xiàn)的
目前呢我們使用fileBeat去做日志的采集,早期呢都是使用logstach去采集,logstach采集呢占用資源也比較大,本身呢是java寫的,filebeat是Go寫的,資源占用比較小,java它采集的量一大了,之后消耗杠杠的,所以后面官方將logstach采集功能剝離出來,使用GO重寫了一個filebeat,所有現(xiàn)在用建議使用fileBeat。
這個fek里面是filebeat,就是部署filebeat,這里面啟動用對k8s的支持,目前這個適合比較小的數(shù)據(jù)量,自己寫的yaml,如果你的數(shù)據(jù)量達(dá)到20-30g以上,這個單機(jī)的es肯定是很難滿足的,如果你的日志量比較大,不建議你把elk部署到k8s中,建議把你的logstack,es,存儲部署到k8s之外,特別是es,建議部署到集群之外,物理機(jī)去組件集群,kibana可以部署在k8s 中,但是es是有狀態(tài)的部署
為了避免以下yaml出錯,建議拉取我代碼倉庫的代碼
拉取地址:git clone git@gitee.com:zhaocheng172/elk.git
拉代碼的時候請把你的公鑰給我,不然拉不下來
[root@k8s-master elk]# ls
fek k8s-logs.yaml nginx-deployment.yaml tomcat-deployment.yaml
[root@k8s-master elk]# ls
fek k8s-logs.yaml nginx-deployment.yaml tomcat-deployment.yaml
[root@k8s-master elk]# cd fek/
[root@k8s-master fek]# ls
elasticsearch.yaml filebeat-kubernetes.yaml kibana.yaml
[root@k8s-master fek]# cat filebeat-kubernetes.yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
name: filebeat-config
namespace: kube-system
labels:
k8s-app: filebeat
data:
filebeat.yml: |-
filebeat.config:
inputs:
path: ${path.config}/inputs.d/*.yml
reload.enabled: false
modules:
path: ${path.config}/modules.d/*.yml
reload.enabled: false
output.elasticsearch:
hosts: ['${ELASTICSEARCH_HOST:elasticsearch}:${ELASTICSEARCH_PORT:9200}']
---
apiVersion: v1
kind: ConfigMap
metadata:
name: filebeat-inputs
namespace: kube-system
labels:
k8s-app: filebeat
data:
kubernetes.yml: |-
- type: docker
containers.ids:
- "*"
processors:
- add_kubernetes_metadata:
in_cluster: true
---
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
name: filebeat
namespace: kube-system
labels:
k8s-app: filebeat
spec:
template:
metadata:
labels:
k8s-app: filebeat
spec:
serviceAccountName: filebeat
terminationGracePeriodSeconds: 30
containers:
- name: filebeat
image: elastic/filebeat:7.3.1
args: [
"-c", "/etc/filebeat.yml",
"-e",
]
env:
- name: ELASTICSEARCH_HOST
value: elasticsearch
- name: ELASTICSEARCH_PORT
value: "9200"
securityContext:
runAsUser: 0
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 100Mi
volumeMounts:
- name: config
mountPath: /etc/filebeat.yml
readOnly: true
subPath: filebeat.yml
- name: inputs
mountPath: /usr/share/filebeat/inputs.d
readOnly: true
- name: data
mountPath: /usr/share/filebeat/data
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
volumes:
- name: config
configMap:
defaultMode: 0600
name: filebeat-config
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
- name: inputs
configMap:
defaultMode: 0600
name: filebeat-inputs
- name: data
hostPath:
path: /var/lib/filebeat-data
type: DirectoryOrCreate
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: filebeat
subjects:
- kind: ServiceAccount
name: filebeat
namespace: kube-system
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: kube-system
labels:
k8s-app: filebeat
---
[root@k8s-master fek]# cat elasticsearch.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: elasticsearch
namespace: kube-system
labels:
k8s-app: elasticsearch
spec:
serviceName: elasticsearch
selector:
matchLabels:
k8s-app: elasticsearch
template:
metadata:
labels:
k8s-app: elasticsearch
spec:
containers:
- image: elasticsearch:7.3.1
name: elasticsearch
resources:
limits:
cpu: 1
memory: 2Gi
requests:
cpu: 0.5
memory: 500Mi
env:
- name: "discovery.type"
value: "single-node"
- name: ES_JAVA_OPTS
value: "-Xms512m -Xmx2g"
ports:
- containerPort: 9200
name: db
protocol: TCP
volumeMounts:
- name: elasticsearch-data
mountPath: /usr/share/elasticsearch/data
volumeClaimTemplates:
- metadata:
name: elasticsearch-data
spec:
storageClassName: "managed-nfs-storage"
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 20Gi
---
apiVersion: v1
kind: Service
metadata:
name: elasticsearch
namespace: kube-system
spec:
clusterIP: None
ports:
- port: 9200
protocol: TCP
targetPort: db
selector:
k8s-app: elasticsearch
[root@k8s-master fek]# cat kibana.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: kibana
namespace: kube-system
labels:
k8s-app: kibana
spec:
replicas: 1
selector:
matchLabels:
k8s-app: kibana
template:
metadata:
labels:
k8s-app: kibana
spec:
containers:
- name: kibana
image: kibana:7.3.1
resources:
limits:
cpu: 1
memory: 500Mi
requests:
cpu: 0.5
memory: 200Mi
env:
- name: ELASTICSEARCH_HOSTS
value: http://elasticsearch:9200
ports:
- containerPort: 5601
name: ui
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: kibana
namespace: kube-system
spec:
ports:
- port: 5601
protocol: TCP
targetPort: ui
selector:
k8s-app: kibana
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: kibana
namespace: kube-system
spec:
rules:
- host: kibana.ctnrs.com
http:
paths:
- path: /
backend:
serviceName: kibana
servicePort: 5601
[root@k8s-master fek]# kubectl create -f .
[root@k8s-master fek]# kubectl get pod -n kube-system
NAME READY STATUS RESTARTS AGE
alertmanager-5d75d5688f-xw2qg 2/2 Running 0 6h42m
coredns-bccdc95cf-kqxwv 1/1 Running 2 6d6h
coredns-bccdc95cf-nwkbp 1/1 Running 2 6d6h
elasticsearch-0 1/1 Running 0 7m15s
etcd-k8s-master 1/1 Running 1 6d6h
filebeat-8s9cx 1/1 Running 0 7m14s
filebeat-xgdj7 1/1 Running 0 7m14s
grafana-0 1/1 Running 0 21h
kibana-b7d98644-cmg9k 1/1 Running 0 7m15s
kube-apiserver-k8s-master 1/1 Running 1 6d6h
kube-controller-manager-k8s-master 1/1 Running 2 6d6h
kube-flannel-ds-amd64-dc5z9 1/1 Running 1 6d6h
kube-flannel-ds-amd64-jm2jz 1/1 Running 1 6d6h
kube-flannel-ds-amd64-z6tt2 1/1 Running 1 6d6h
kube-proxy-9ltx7 1/1 Running 2 6d6h
kube-proxy-lnzrj 1/1 Running 1 6d6h
kube-proxy-v7dqm 1/1 Running 1 6d6h
kube-scheduler-k8s-master 1/1 Running 2 6d6h
kube-state-metrics-6474469878-lkphv 2/2 Running 0 8h
prometheus-0 2/2 Running 0 5h7m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/elasticsearch ClusterIP None 9200/TCP 46m
service/kibana ClusterIP 10.1.185.95 5601/TCP 46m
service/kube-dns ClusterIP 10.1.0.10 53/UDP,53/TCP,9153/TCP 6d7h
NAME HOSTS ADDRESS PORTS AGE
ingress.extensions/kibana kibana.ctnrs.com 80 46m
然后我們?nèi)ピL問一下,這里我是做的測試所以把它的域名寫到了本地的hosts文件進(jìn)行解析
這里選擇Explore導(dǎo)入自己的數(shù)據(jù)
現(xiàn)在數(shù)據(jù)已經(jīng)收集到了,看一下它的索引有沒有寫入成功
也就是創(chuàng)建的三個yaml文件之后,默認(rèn)的創(chuàng)建filebeat索引,這就像數(shù)據(jù)庫,索引呢查詢到ES里面的數(shù)據(jù)庫,索引匹配呢就是讓kibana拿這個索引匹配查看里面的數(shù)據(jù)。
創(chuàng)建索引的匹配filebeat,這里會幫你去匹配所以有filebeat開頭的,因為它是按每天去存儲這個索引的,也就是每一天都有一個命名,所以打個星就能匹配所有,你訪問這個模式就能查看以這個開頭的數(shù)據(jù)這里還要點一下保存
這里添加一個過濾時間的字段,一般使用這個時間戳
然后點最上面的按鈕就能看到數(shù)據(jù)了。這里默認(rèn)的就是使用這個filebeat,要是多個的話,這里會有一個選擇框,下面的輸出是所有控制臺輸出的
這里會可以看一些相關(guān)的日志,比如命名空間,很多
比如看容器的保存路徑下的容器的日志
還有messages輸出整體的日志內(nèi)容,能看出它采用的哪個命名空間,能看出pod的名稱,node的名稱,也能看出采集的日志
比如單獨看一些想要的日志,通過過濾條件去篩選
我們現(xiàn)在要收集k8s的日志,也就是你的ELK平臺部署起來了,它是一個獨立的系統(tǒng)部署起來了,要是使用set car方式去收集這個日志。
要是采集k8s日志,需要采集
[root@k8s-master elk]# tail /var/log/messages
這個文件,收集這個,我們先編寫一個filebeat,這里就指定你要收集那些文件,這個filebeat這個pod,是不能訪問到宿主機(jī)的,所以我們要通過數(shù)據(jù)卷掛載這個文件,es的索引相當(dāng)于mysql 的db,所以要根據(jù)es里要創(chuàng)建不同的索引,也就是按天,記錄某一個日志,這一天產(chǎn)生的日志放到這個索引中,這個索引呢要自定義名稱,根據(jù)你當(dāng)前部署的業(yè)務(wù)寫一個名稱,這樣為了方面后面做查詢,做一定的分類,別到時候幾個項目了,k8s項目了,php項目了,Tomcat項目了,都在一個索引里面,當(dāng)去查詢也比較困難,所以把他們分成不同的索引,要指定這些的話就需要加這三個參數(shù)
setup.ilm.enabled: false
setup.template.name: "k8s-module"
setup.template.pattern: "k8s-module-*"
下面是通過deemonset部署的filebeat到每個節(jié)點上,通過hostpath將宿主機(jī)上的/var/log/messages/掛載到容器中的/var/log/messages,這樣容器中就有這個日志文件了,就能讀取到了,這就是收集k8s的日志。
[root@k8s-master elk]# cat k8s-logs.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: k8s-logs-filebeat-config
namespace: kube-system
data:
filebeat.yml: |
filebeat.inputs:
- type: log
paths:
- /var/log/messages
fields:
app: k8s
type: module
fields_under_root: true
setup.ilm.enabled: false
setup.template.name: "k8s-module"
setup.template.pattern: "k8s-module-*"
output.elasticsearch:
hosts: ['elasticsearch.kube-system:9200']
index: "k8s-module-%{+yyyy.MM.dd}"
---
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:
containers:
- name: filebeat
image: elastic/filebeat:7.3.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: /var/log/messages
volumes:
- name: k8s-logs
hostPath:
path: /var/log/messages
- name: filebeat-config
configMap:
name: k8s-logs-filebeat-config
[root@k8s-master elk]# kubectl create -f k8s-logs.yaml
[root@k8s-master elk]# kubectl get pod -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-bccdc95cf-kqxwv 1/1 Running 2 6d8h
coredns-bccdc95cf-nwkbp 1/1 Running 2 6d8h
elasticsearch-0 1/1 Running 0 94m
etcd-k8s-master 1/1 Running 1 6d8h
filebeat-8s9cx 1/1 Running 0 94m
filebeat-xgdj7 1/1 Running 0 94m
k8s-logs-5s9kl 1/1 Running 0 37s
k8s-logs-txz4q 1/1 Running 0 37s
kibana-b7d98644-cmg9k 1/1 Running 0 94m
kube-apiserver-k8s-master 1/1 Running 1 6d8h
kube-controller-manager-k8s-master 1/1 Running 2 6d8h
kube-flannel-ds-amd64-dc5z9 1/1 Running 1 6d7h
kube-flannel-ds-amd64-jm2jz 1/1 Running 1 6d7h
kube-flannel-ds-amd64-z6tt2 1/1 Running 1 6d8h
kube-proxy-9ltx7 1/1 Running 2 6d8h
kube-proxy-lnzrj 1/1 Running 1 6d7h
kube-proxy-v7dqm 1/1 Running 1 6d7h
kube-scheduler-k8s-master 1/1 Running 2 6d8h
還是原來的步驟,最后的一個按鈕索引管理這里
這是我們自定義采集的日志
然后在創(chuàng)建一個索引
選擇這個然后保存
創(chuàng)建索引
選擇module-這里就能看到我們var/log/messages的日志
做一個簡單的測試比如這樣
[root@k8s-node1 ~]# echo hello > /var/log/messages
然后接收一下tomcat的日志
一般需要看的話,tomcat前面由nginx去做反向代理,而Tomcat需要看的日志是catalina
jc日志主要調(diào)試的時候需要看。
[root@k8s-master elk]# cat tomcat-deployment.yaml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: tomcat-java-demo
namespace: test
spec:
replicas: 3
selector:
matchLabels:
project: www
app: java-demo
template:
metadata:
labels:
project: www
app: java-demo
spec:
imagePullSecrets:
- name: registry-pull-secret
containers:
- name: tomcat
image: 192.168.30.24/test/tomcat-java-demo:latest
imagePullPolicy: Always
ports:
- containerPort: 8080
name: web
protocol: TCP
resources:
requests:
cpu: 0.5
memory: 1Gi
limits:
cpu: 1
memory: 2Gi
livenessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 60
timeoutSeconds: 20
readinessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 60
timeoutSeconds: 20
volumeMounts:
- name: tomcat-logs
mountPath: /usr/local/tomcat/logs
- name: filebeat
image: elastic/filebeat:7.3.1
args: [
"-c", "/etc/filebeat.yml",
"-e",
]
resources:
limits:
memory: 500Mi
requests:
cpu: 100m
memory: 100Mi
securityContext:
runAsUser: 0
volumeMounts:
- name: filebeat-config
mountPath: /etc/filebeat.yml
subPath: filebeat.yml
- name: tomcat-logs
mountPath: /usr/local/tomcat/logs
volumes:
- name: tomcat-logs
emptyDir: {}
- name: filebeat-config
configMap:
name: filebeat-config
---
apiVersion: v1
kind: ConfigMap
metadata:
name: filebeat-config
namespace: test
data:
filebeat.yml: |-
filebeat.inputs:
- type: log
paths:
- /usr/local/tomcat/logs/catalina.*
fields:
app: www
type: tomcat-catalina
fields_under_root: true
multiline:
pattern: '^\['
negate: true
match: after
setup.ilm.enabled: false
setup.template.name: "tomcat-catalina"
setup.template.pattern: "tomcat-catalina-*"
output.elasticsearch:
hosts: ['elasticsearch.kube-system:9200']
index: "tomcat-catalina-%{+yyyy.MM.dd}"
滾動更新一下,一般要是錯誤日志的話,日志量會增加,肯定需要去解決,又不是訪問日志。另外滾動更新需要一分鐘時間
[root@k8s-master elk]# kubectl get pod -n test
[root@k8s-master elk]# kubectl get pod -n test
NAME READY STATUS RESTARTS AGE
tomcat-java-demo-7ffd4dc7c5-26xjf 2/2 Running 0 5m19s
tomcat-java-demo-7ffd4dc7c5-lwfgr 2/2 Running 0 7m31s
tomcat-java-demo-7ffd4dc7c5-pwj77 2/2 Running 0 8m50s
我們的日志收集到了
然后在創(chuàng)建一個索引
然后也可以去測試一下去容器,比如訪問日志等等,和剛才的測試一樣。
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)cdcxhl.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。