架構選擇(ELK VS EFK)
ELK
咱們首先介紹一下傳統的日誌監控方案。其中,ELK Stack 是咱們最熟悉不過的架構。所謂ELK,分別指Elastic公司的Elasticsearch、Logstash、Kibana。在比較舊的ELK架構中,Logstash身兼日誌的採集、過濾兩職。但因爲Logstash基於JVM,性能有必定限制,所以,目前業界更推薦使用Go語言開發FIiebeat代替Logstash的採集功能,Logstash只做爲了日誌過濾的中間件。java
最多見的ELK架構以下:node
如上圖所示,各角色功能以下:docker
多個Filebeat在各個業務端進行日誌採集,而後上傳至Logstash
多個Logstash節點並行(負載均衡,不做爲集羣),對日誌記錄進行過濾處理,而後上傳至Elasticsearch集羣
多個Elasticsearch構成集羣服務,提供日誌的索引和存儲能力
Kibana負責對Elasticsearch中的日誌數據進行檢索、分析
固然,在該架構中,根據業務特色,還能夠加入某些中間件,如Redis、Kafak等:json
如上圖所示,Kafka集羣做爲消息緩衝隊列,能夠下降大量FIlebeat對Logstash的併發訪問壓力。後端
EFK
目前,在K8S的日誌監控解決方案中,EFK也是較經常使用的架構。所謂的EFK,即Elasticsearch + Fluentd + Kibana。在該架構中,Fluentd做爲日誌採集客戶端。但我我的認爲,相對於Filebeat,Fluentd並無突出的優點。而且,因爲同屬於Elastic公司,Filebeat能夠更好的兼容其產品棧。所以,在K8S上,我仍然推薦ELK架構。api
日誌採集方式
肯定使用ELK+Filebeat做爲架構後,咱們還須要明確Filebeat採集K8S集羣日誌的方式,這也是本文的重點。官方文檔中提到了三種採集方式,這裏簡單介紹一下:網絡
方式1:Node級日誌代理
在每一個節點(即宿主機)上能夠獨立運行一個Node級日誌代理,一般的實現方式爲DaemonSet。用戶應用只須要將日誌寫到標準輸出,Docker 的日誌驅動會將每一個容器的標準輸出收集並寫入到主機文件系統,這樣Node級日誌代理就能夠將日誌統一收集並上傳。另外,可使用K8S的logrotate或Docker 的log-opt 選項負責日誌的輪轉。架構
Docker默認的日誌驅動(LogDriver)是json-driver,其會將日誌以JSON文件的方式存儲。全部容器輸出到控制檯的日誌,都會以*-json.log的命名方式保存在/var/lib/docker/containers/目錄下。對於Docker日誌驅動的具體介紹,請參考官方文檔。另外,除了收集Docker容器日誌,通常建議同時收集K8S自身的日誌以及宿主機的全部系統日誌,其位置都在var/log下。併發
因此,簡單來講,本方式就是在每一個node上各運行一個日誌代理容器,對本節點/var/log和 /var/lib/docker/containers/兩個目錄下的日誌進行採集,而後彙總到elasticsearch集羣,最後經過kibana展現。app
方式2:伴生容器(sidecar container)做爲日誌代理
建立一個伴生容器(也可稱做日誌容器),與應用程序容器在處於同一個Pod中。同時伴生容器內部運行一個獨立的、專門爲收集應用日誌的代理,常見的有Logstash、Fluentd 、Filebeat等。日誌容器經過共享卷能夠得到應用容器的日誌,而後進行上傳。
方式3:應用直接上傳日誌
應用程序容器直接經過網絡鏈接上傳日誌到後端,這是最簡單的方式。
對比
其中,相對來講,方式1在業界使用更爲普遍,而且官方也更爲推薦。所以,最終咱們採用ELK+Filebeat架構,並基於方式1,以下:
準備操做
DaemonSet概念介紹
在搭建前,咱們先簡單介紹一下方式1中提到的DaemonSet,這也是一個重要的概念:
DaemonSet可以讓全部(或者一些特定)的Node節點運行同一個pod。當節點加入到kubernetes集羣中,pod會被(DaemonSet)調度到該節點上運行,當節點從kubernetes集羣中被移除,被(DaemonSet)調度的pod會被移除,若是刪除DaemonSet,全部跟這個DaemonSet相關的pods都會被刪除。
所以,咱們可使用DaemonSet來部署Filebeat。這樣,每當集羣加入一個新的節點,該節點就會自動建立一個Filebeat守護進程,並有且只有一個。
另外,因爲篇幅限制,本文只介紹如何經過基於DaemonSet的Filebeat來收集K8S集羣的日誌,而非介紹如何在K8S上搭建一個ELK集羣。同時,日誌記錄將直接上傳至Elasticsearch中,而不經過Logstash,而且本文假設Elasticsearch集羣已提早搭建完畢可直接使用。
清楚了本文的側重點後,好,走你~
官方Filebeat部署腳本介紹
這裏,咱們將基於Elastic官方提供的Filebeat部署腳本進行部署,以下所示:
---
apiVersion: v1
kind: ConfigMap
metadata:
name: filebeat-config
namespace: kube-system
labels:
k8s-app: filebeat
kubernetes.io/cluster-service: "true"
data:
filebeat.yml: |-
filebeat.config:
prospectors:
# Mounted `filebeat-prospectors` configmap:
path: ${path.config}/prospectors.d/*.yml
# Reload prospectors configs as they change:
reload.enabled: false
modules:
path: ${path.config}/modules.d/*.yml
# Reload module configs as they change:
reload.enabled: false
processors:
- add_cloud_metadata:
cloud.id: ${ELASTIC_CLOUD_ID}
cloud.auth: ${ELASTIC_CLOUD_AUTH}
output.elasticsearch:
hosts: ['${ELASTICSEARCH_HOST:elasticsearch}:${ELASTICSEARCH_PORT:9200}']
username: ${ELASTICSEARCH_USERNAME}
password: ${ELASTICSEARCH_PASSWORD}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: filebeat-prospectors
namespace: kube-system
labels:
k8s-app: filebeat
kubernetes.io/cluster-service: "true"
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
kubernetes.io/cluster-service: "true"
spec:
template:
metadata:
labels:
k8s-app: filebeat
kubernetes.io/cluster-service: "true"
spec:
serviceAccountName: filebeat
terminationGracePeriodSeconds: 30
containers:
- name: filebeat
image: docker.elastic.co/beats/filebeat:6.2.4
args: [
"-c", "/etc/filebeat.yml",
"-e",
]
env:
- name: ELASTICSEARCH_HOST
value: elasticsearch
- name: ELASTICSEARCH_PORT
value: "9200"
- name: ELASTICSEARCH_USERNAME
value: elastic
- name: ELASTICSEARCH_PASSWORD
value: changeme
- name: ELASTIC_CLOUD_ID
value:
- name: ELASTIC_CLOUD_AUTH
value:
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: prospectors
mountPath: /usr/share/filebeat/prospectors.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: prospectors
configMap:
defaultMode: 0600
name: filebeat-prospectors
- name: data
emptyDir: {}
---
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
---
如上,看起來彷佛挺複雜,能夠分爲以下幾個部分:
ConfigMap
DaemonSet
ClusterRoleBinding
ClusterRole
ServiceAccount
ConfigMap
咱們先重點關注一下DaemonSet的volumeMounts和volumes,以瞭解ConfigMap的掛載方式:
volumeMounts:
- name: config
mountPath: /etc/filebeat.yml
readOnly: true
subPath: filebeat.yml
- name: prospectors
mountPath: /usr/share/filebeat/prospectors.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: prospectors
configMap:
defaultMode: 0600
name: filebeat-prospectors
- name: data
emptyDir: {}
如上,volumeMounts包括四個部分,解釋以下:
config
filebeat-config這個Configmap會生成一個filebeat.yml文件,其會被掛載爲Filebeat的配置文件/etc/filebeat.yml
prospectors
prospectors這個Configmap會生成一個kubernetes.yml文件,其會被掛載到路徑/usr/share/filebeat/prospectors.d下,並被filebeat.yml引用
data
Filebeat自身的數據掛載爲emptyDir: {}
varlibdockercontainers
K8S集羣的日誌都存儲在/var/lib/docker/containers,Filebeat將從該路徑進行收集
瞭解了ConfigMap的掛載方式後,如今,咱們分析第一個ConfigMap:
---
apiVersion: v1
kind: ConfigMap
metadata:
name: filebeat-config
namespace: kube-system
labels:
k8s-app: filebeat
kubernetes.io/cluster-service: "true"
data:
filebeat.yml: |-
filebeat.config:
prospectors:
# Mounted `filebeat-prospectors` configmap:
path: ${path.config}/prospectors.d/*.yml
# Reload prospectors configs as they change:
reload.enabled: false
modules:
path: ${path.config}/modules.d/*.yml
# Reload module configs as they change:
reload.enabled: false
processors:
- add_cloud_metadata:
cloud.id: ${ELASTIC_CLOUD_ID}
cloud.auth: ${ELASTIC_CLOUD_AUTH}
output.elasticsearch:
hosts: ['${ELASTICSEARCH_HOST:elasticsearch}:${ELASTICSEARCH_PORT:9200}']
username: ${ELASTICSEARCH_USERNAME}
password: ${ELASTICSEARCH_PASSWORD}
咱們知道,Configmap的每一個key都會生成一個同名的文件,所以這裏會建立一個配置文件filebeat.yml文件,其內容中的環境變量將由DaemonSet中的env部分定義。
在filebeat.yml中,能夠看到Filebeat的一個重要組件: prospectors(採礦者),其主要用來指定從哪些文件中採集數據。這裏,prospectors並無直接指定目標文件,而是間接的引用路徑:${path.config}/prospectors.d/*.yml,由前面可知,該路徑中的yml文件由第二個ConfigMap定義:
---
apiVersion: v1
kind: ConfigMap
metadata:
name: filebeat-prospectors
namespace: kube-system
labels:
k8s-app: filebeat
kubernetes.io/cluster-service: "true"
data:
kubernetes.yml: |-
- type: docker
containers.ids:
- "*"
processors:
- add_kubernetes_metadata:
in_cluster: true
如上,type指定了prospectors的類型爲docker,表示收集本機的docker日誌。containers.ids爲*表示監聽全部容器。type除了docker,通常使用更多的是log,能夠直接指定任何路徑上的日誌文件,參見官方文檔。
部署步驟
介紹完Filebeat的部署腳本後,咱們開始真正的部署過程。
1.部署Filebeat
官方配置文件沒法直接使用,須要咱們定製。首先,修改DaemonSet中的環境變量env:
env:
- name: ELASTICSEARCH_HOST
value: "X.X.X.X"
- name: ELASTICSEARCH_PORT
value: "9200"
- name: ELASTICSEARCH_USERNAME
value:
- name: ELASTICSEARCH_PASSWORD
value:
- name: ELASTIC_CLOUD_ID
value:
- name: ELASTIC_CLOUD_AUTH
value:
如上,ELASTICSEARCH_HOST指定爲Elasticsearch集羣的入口地址,端口ELASTICSEARCH_PORT爲默認的9200;因爲個人集羣沒有加密,所以ELASTICSEARCH_USERNAME和ELASTICSEARCH_PASSWORD所有留空,你們能夠酌情修改;其餘保持默認。
同時,還須要註釋掉第一個ConfigMap中output.elasticsearch的用戶名和密碼:
output.elasticsearch:
hosts: ['${ELASTICSEARCH_HOST:elasticsearch}:${ELASTICSEARCH_PORT:9200}']
#username: ${ELASTICSEARCH_USERNAME}
#password: ${ELASTICSEARCH_PASSWORD}
其次,還須要修改第二個ConfigMap的data部分爲:
data:
kubernetes.yml: |-
- type: log
enabled: true
paths:
- /var/log/*.log
- type: docker
containers.ids:
- "*"
processors:
- add_kubernetes_metadata:
in_cluster: true
如上,type: docker的配置能夠對K8S上全部Docker容器產生的日誌進行收集。另外,爲了收集宿主機系統日誌和K8S自身日誌,咱們還須要獲取/var/log/*.log。
修改並建立完畢後,查看DaemonSet信息,以下圖所示:
[root@k8s-node1 filebeat]# kubectl get ds -n kube-system
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
calico-etcd 1 1 1 1 1 node-role.kubernetes.io/master= 5d
calico-node 3 3 3 3 3 <none> 5d
filebeat 2 2 0 2 0 <none> 24s
kube-proxy 3 3 3 3 3 <none> 5d
查看pod信息,每一個節點都會啓動一個filebeat容器:
filebeat-hr5vq 1/1 Running 1 3m 192.168.169.223 k8s-node2
filebeat-khzzj 1/1 Running 1 3m 192.168.108.7 k8s-node3
filebeat-rsnbl 1/1 Running 0 3m 192.168.36.126 k8s-node1
2.部署Kibana
參考官方示例,咱們按需修改成以下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: kibana-logging
namespace: kube-system
labels:
k8s-app: kibana-logging
spec:
replicas: 1
selector:
matchLabels:
k8s-app: kibana-logging
template:
metadata:
labels:
k8s-app: kibana-logging
spec:
containers:
- name: kibana-logging
image: docker.elastic.co/kibana/kibana:6.2.4
resources:
# need more cpu upon initialization, therefore burstable class
limits:
cpu: 1000m
requests:
cpu: 100m
env:
- name: ELASTICSEARCH_URL
value: http://X.X.X.X:9200
ports:
- containerPort: 5601
name: ui
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: kibana-logging
namespace: kube-system
labels:
k8s-app: kibana-logging
spec:
type: NodePort
ports:
- port: 5601
targetPort: 5601
selector:
k8s-app: kibana-logging
如上,Kibana的版本爲6.2.4,而且必定要與Filebeat、Elasticsearch保持一致。另外,注意將Deployment中env的環境變量ELASTICSEARCH_URL,修改成本身的Elasticsearch集羣地址。
這裏咱們使用了Service暴露了NodePort,固然也可使用Ingress。
3.訪問Kibana
好了,如今咱們能夠經過NodeIp:NodePort或Ingress方式來訪問Kibana。在配置Elasticsearch索引前綴後,便可檢索日誌:
如上,能夠看到K8S中各個容器的日誌,固然也包括宿主機的系統日誌。
4.測試應用日誌
至此,咱們經過Filebeat成功獲取了K8S上的容器日誌以及系統日誌。但在實際中,咱們更關注的是應用程序的業務日誌。這裏,咱們編寫一個簡單的JAVA項目來測試一下。
測試代碼
只是簡單的循環輸出遞增序列:
logback.xml
appender指定爲STDOUT便可:
Dockerfile
可使用gradle將項目發佈爲tar包,而後拷貝到java:9-re鏡像中。在build鏡像後,記得別忘記上傳至本身的倉庫中:
K8S部署腳本
執行該腳本便可完成測試項目的部署:
輸出日誌
咱們能夠去/var/lib/docker/containers/下查看測試項目輸出的json格式日誌:
在Dashborad中,也能夠查看標準輸出的日誌:
好了,咱們已經成功的經過Filebeat上傳了自定義的應用程序日誌,收工~