1胳嘲、統(tǒng)一日志管理的整體方案
通過應(yīng)用和系統(tǒng)日志可以了解Kubernetes集群內(nèi)所發(fā)生的事情绣否,對于調(diào)試問題和監(jiān)視集群活動來說日志非常有用。對于大部分的應(yīng)用來說侍匙,都會具有某種日志機(jī)制成翩。因此觅捆,大多數(shù)容器引擎同樣被設(shè)計成支持某種日志機(jī)制。對于容器化應(yīng)用程序來說麻敌,最簡單和最易接受的日志記錄方法是將日志內(nèi)容寫入到標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯誤流惠拭。
但是,容器引擎或運行時提供的本地功能通常不足以支撐完整的日志記錄解決方案庸论。例如职辅,如果一個容器崩潰、一個Pod被驅(qū)逐聂示、或者一個Node死亡域携,應(yīng)用相關(guān)者可能仍然需要訪問應(yīng)用程序的日志。因此鱼喉,日志應(yīng)該具有獨立于Node秀鞭、Pod或者容器的單獨存儲和生命周期,這個概念被稱為群集級日志記錄扛禽。群集級日志記錄需要一個獨立的后端來存儲锋边、分析和查詢?nèi)罩尽ubernetes本身并沒有為日志數(shù)據(jù)提供原生的存儲解決方案编曼,但可以將許多現(xiàn)有的日志記錄解決方案集成到Kubernetes集群中豆巨。在Kubernetes中,有三個層次的日志:
- 基礎(chǔ)日志
- Node級別的日志
- 群集級別的日志架構(gòu)
1.1 基礎(chǔ)日志
kubernetes基礎(chǔ)日志即將日志數(shù)據(jù)輸出到標(biāo)準(zhǔn)輸出流掐场,可以使用kubectl logs命令獲取容器日志信息往扔。如果Pod中有多個容器,可以通過將容器名稱附加到命令來指定要訪問哪個容器的日志熊户。例如萍膛,在Kubernetes集群中的devops命名空間下有一個名稱為nexus3-f5b7fc55c-hq5v7的Pod,就可以通過如下的命令獲取日志:
$ kubectl logs nexus3-f5b7fc55c-hq5v7 --namespace=devops
1.2 Node級別的日志
容器化應(yīng)用寫入到stdout和stderr的所有內(nèi)容都是由容器引擎處理和重定向的嚷堡。例如蝗罗,docker容器引擎會將這兩個流重定向到日志記錄驅(qū)動,在Kubernetes中該日志驅(qū)動被配置為以json格式寫入文件。docker json日志記錄驅(qū)動將每一行視為單獨的消息串塑。當(dāng)使用docker日志記錄驅(qū)動時沼琉,并不支持多行消息,因此需要在日志代理級別或更高級別上處理多行消息拟赊。
默認(rèn)情況下刺桃,如果容器重新啟動粹淋,kubectl將會保留一個已終止的容器及其日志吸祟。如果從Node中驅(qū)逐Pod,那么Pod中所有相應(yīng)的容器也會連同它們的日志一起被驅(qū)逐桃移。Node級別的日志中的一個重要考慮是實現(xiàn)日志旋轉(zhuǎn)屋匕,這樣日志不會消耗Node上的所有可用存儲。Kubernetes目前不負(fù)責(zé)旋轉(zhuǎn)日志借杰,部署工具應(yīng)該建立一個解決方案來解決這個問題过吻。
在Kubernetes中有兩種類型的系統(tǒng)組件:運行在容器中的組件和不在容器中運行的組件。例如:
- Kubernetes調(diào)度器和kube-proxy在容器中運行蔗衡。
- kubelet和容器運行時纤虽,例如docker,不在容器中運行绞惦。
在帶有systemd的機(jī)器上逼纸,kubelet和容器運行時寫入journaId。如果systemd不存在济蝉,它們會在/var/log目錄中寫入.log文件杰刽。在容器中的系統(tǒng)組件總是繞過默認(rèn)的日志記錄機(jī)制,寫入到/var/log目錄王滤,它們使用golg日志庫贺嫂。可以找到日志記錄中開發(fā)文檔中那些組件記錄嚴(yán)重性的約定雁乡。
類似于容器日志第喳,在/var/log目錄中的系統(tǒng)組件日志應(yīng)該被旋轉(zhuǎn)。這些日志被配置為每天由logrotate進(jìn)行旋轉(zhuǎn)踱稍,或者當(dāng)大小超過100mb時進(jìn)行旋轉(zhuǎn)墩弯。
1.3 集群級別的日志架構(gòu)
Kubernetes本身沒有為群集級別日志記錄提供原生解決方案,但有幾種常見的方法可以采用:
- 使用運行在每個Node上的Node級別的日志記錄代理寞射;
- 在應(yīng)用Pod中包含一個用于日志記錄的sidecar渔工。
- 將日志直接從應(yīng)用內(nèi)推到后端。
經(jīng)過綜合考慮桥温,本文采用通過在每個Node上包括Node級別的日志記錄代理來實現(xiàn)群集級別日志記錄引矩。日志記錄代理暴露日志或?qū)⑷罩就扑偷胶蠖说膶S霉ぞ摺Mǔ#琹ogging-agent是一個容器旺韭,此容器能夠訪問該Node上的所有應(yīng)用程序容器的日志文件氛谜。
因為日志記錄必須在每個Node上運行,所以通常將它作為DaemonSet副本区端、或一個清單Pod或Node上的專用本機(jī)進(jìn)程值漫。然而,后兩種方法后續(xù)將會被放棄织盼。使用Node級別日志記錄代理是Kubernetes集群最常見和最受歡迎的方法杨何,因為它只為每個節(jié)點創(chuàng)建一個代理,并且不需要對節(jié)點上運行的應(yīng)用程序進(jìn)行任何更改沥邻。但是危虱,Node級別日志記錄僅適用于應(yīng)用程序的標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯誤。
Kubernetes本身并沒有指定日志記錄代理唐全,但是有兩個可選的日志記錄代理與Kubernetes版本打包發(fā)布:和谷歌云平臺一起使用的Stackdriver和Elasticsearch埃跷,兩者都使用自定義配置的fluentd作為Node上的代理。在本文的方案中邮利,Logging-agent 采用 Fluentd弥雹,而 Logging Backend 采用 Elasticsearch,前端展示采用Grafana延届。即通過 Fluentd 作為 Logging-agent 收集日志剪勿,并推送給后端的Elasticsearch;Grafana從Elasticsearch中獲取日志祷愉,并進(jìn)行統(tǒng)一的展示窗宦。
2、安裝統(tǒng)一日志管理的組件
在本文中采用使用Node日志記錄代理的方面進(jìn)行Kubernetes的統(tǒng)一日志管理二鳄,相關(guān)的工具采用:
- 日志記錄代理(logging-agent):日志記錄代理用于從容器中獲取日志信息赴涵,使用Fluentd;
- 日志記錄后臺(Logging-Backend):日志記錄后臺用于處理日志記錄代理推送過來的日志订讼,使用Elasticsearch髓窜;
- 日志記錄展示:日志記錄展示用于向用戶顯示統(tǒng)一的日志信息,使用Kibana欺殿。
在Kubernetes中通過了Elasticsearch 附加組件寄纵,此組件包括Elasticsearch、Fluentd和Kibana脖苏。Elasticsearch是一種負(fù)責(zé)存儲日志并允許查詢的搜索引擎程拭。Fluentd從Kubernetes中獲取日志消息,并發(fā)送到Elasticsearch棍潘;而Kibana是一個圖形界面恃鞋,用于查看和查詢存儲在Elasticsearch中的日志崖媚。在安裝部署之前,對于環(huán)境的要求如下:
2.1 安裝部署Elasticsearch
Elasticsearch是一個基于Apache Lucene(TM)的開源搜索和數(shù)據(jù)分析引擎引擎恤浪,Elasticsearch使用Java進(jìn)行開發(fā)畅哑,并使用Lucene作為其核心實現(xiàn)所有索引和搜索的功能。它的目的是通過簡單的RESTful API
來隱藏Lucene的復(fù)雜性水由,從而讓全文搜索變得簡單荠呐。Elasticsearch不僅僅是Lucene和全文搜索,它還提供如下的能力:
- 分布式的實時文件存儲砂客,每個字段都被索引并可被搜索泥张;
- 分布式的實時分析搜索引擎;
- 可以擴(kuò)展到上百臺服務(wù)器鞭盟,處理PB級結(jié)構(gòu)化或非結(jié)構(gòu)化數(shù)據(jù)圾结。
在Elasticsearch中瑰剃,包含多個索引(Index)齿诉,相應(yīng)的每個索引可以包含多個類型(Type),這些不同的類型每個都可以存儲多個文檔(Document)晌姚,每個文檔又有多個屬性粤剧。索引 (index) 類似于傳統(tǒng)關(guān)系數(shù)據(jù)庫中的一個數(shù)據(jù)庫,是一個存儲關(guān)系型文檔的地方挥唠。Elasticsearch 使用的是標(biāo)準(zhǔn)的 RESTful API 和 JSON抵恋。此外,還構(gòu)建和維護(hù)了很多其他語言的客戶端宝磨,例如 Java, Python, .NET, 和 PHP弧关。
下面是Elasticsearch的YAML配置文件,在此配置文件中唤锉,定義了一個名稱為elasticsearch-logging的ServiceAccount世囊,并授予其能夠?qū)γ臻g、服務(wù)和端點讀取的訪問權(quán)限窿祥;并以StatefulSet類型部署Elasticsearch株憾。
# RBAC authn and authz
apiVersion: v1
kind: ServiceAccount metadata: name: elasticsearch-logging namespace: kube-system
labels: k8s-app: elasticsearch-logging
kubernetes.io/cluster-service: "true" addonmanager.kubernetes.io/mode: Reconcile --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1
metadata: name: elasticsearch-logging
labels: k8s-app: elasticsearch-logging
kubernetes.io/cluster-service: "true" 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
kubernetes.io/cluster-service: "true" 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: v6.2.5 kubernetes.io/cluster-service: "true" addonmanager.kubernetes.io/mode: Reconcile spec: serviceName: elasticsearch-logging
replicas: 2 selector: matchLabels: k8s-app: elasticsearch-logging
version: v6.2.5 template: metadata: labels: k8s-app: elasticsearch-logging
version: v6.2.5 kubernetes.io/cluster-service: "true" spec: serviceAccountName: elasticsearch-logging
containers: - image: k8s.gcr.io/elasticsearch:v6.2.5 name: elasticsearch-logging
resources: # need more cpu upon initialization, therefore burstable class limits: cpu: 1000m requests: cpu: 100m ports: - containerPort: 9200 name: db
protocol: TCP - containerPort: 9300 name: transport
protocol: TCP
volumeMounts: - name: elasticsearch-logging
mountPath: /data
env: - 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
通過執(zhí)行如下的命令部署Elasticsearch:
$ kubectl create -f {path}/es-statefulset.yaml
下面Elasticsearch的代理服務(wù)YAML配置文件,代理服務(wù)暴露的端口為9200晒衩。
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
通過執(zhí)行如下的命令部署Elasticsearch的代理服務(wù):
$ kubectl create -f {path}/es-service.yaml
2.2 安裝部署Fluentd
Fluentd是一個開源數(shù)據(jù)收集器嗤瞎,通過它能對數(shù)據(jù)進(jìn)行統(tǒng)一收集和消費,能夠更好地使用和理解數(shù)據(jù)听系。Fluentd將數(shù)據(jù)結(jié)構(gòu)化為JSON贝奇,從而能夠統(tǒng)一處理日志數(shù)據(jù),包括:收集靠胜、過濾掉瞳、緩存和輸出届惋。Fluentd是一個基于插件體系的架構(gòu),包括輸入插件菠赚、輸出插件脑豹、過濾插件、解析插件衡查、格式化插件瘩欺、緩存插件和存儲插件,通過插件可以擴(kuò)展和更好的使用Fluentd拌牲。
Fluentd的整體處理過程如下俱饿,通過Input插件獲取數(shù)據(jù),并通過Engine進(jìn)行數(shù)據(jù)的過濾塌忽、解析拍埠、格式化和緩存,最后通過Output插件將數(shù)據(jù)輸出給特定的終端土居。
在本文中枣购, Fluentd 作為 Logging-agent 進(jìn)行日志收集,并將收集到的日子推送給后端的Elasticsearch擦耀。對于Kubernetes來說棉圈,DaemonSet確保所有(或一些)Node會運行一個Pod副本。因此眷蜓,F(xiàn)luentd被部署為DaemonSet分瘾,它將在每個節(jié)點上生成一個pod,以讀取由kubelet吁系,容器運行時和容器生成的日志德召,并將它們發(fā)送到Elasticsearch。為了使Fluentd能夠工作汽纤,每個Node都必須標(biāo)記beta.kubernetes.io/fluentd-ds-ready=true上岗。
下面是Fluentd的ConfigMap配置文件,此文件定義了Fluentd所獲取的日志數(shù)據(jù)源冒版,以及將這些日志數(shù)據(jù)輸出到Elasticsearch中液茎。
kind: ConfigMap
apiVersion: v1
metadata: name: fluentd-es-config-v0.1.4 namespace: kube-system
labels: addonmanager.kubernetes.io/mode: Reconcile data: system.conf: |- <system> root_dir /tmp/fluentd-buffers/ </system>
containers.input.conf: |-
<source>
@id fluentd-containers.log
@type tail
path /var/log/containers/*.log
pos_file /var/log/es-containers.log.pos
time_format %Y-%m-%dT%H:%M:%S.%NZ
tag raw.kubernetes.*
read_from_head true
<parse>
@type multi_format
<pattern>
format json
time_key time
time_format %Y-%m-%dT%H:%M:%S.%NZ
</pattern>
<pattern>
format /^(?<time>.+) (?<stream>stdout|stderr) [^ ]* (?<log>.*)$/
time_format %Y-%m-%dT%H:%M:%S.%N%:z
</pattern>
</parse>
</source>
# Detect exceptions in the log output and forward them as one log entry.
<match raw.kubernetes.**>
@id raw.kubernetes
@type detect_exceptions
remove_tag_prefix raw
message log
stream stream
multiline_flush_interval 5
max_bytes 500000
max_lines 1000
</match>
output.conf: |-
# Enriches records with Kubernetes metadata
<filter kubernetes.**>
@type kubernetes_metadata
</filter>
<match **>
@id elasticsearch
@type elasticsearch
@log_level info
include_tag_key true
host elasticsearch-logging
port 9200
logstash_format true
<buffer>
@type file
path /var/log/fluentd-buffers/kubernetes.system.buffer
flush_mode interval
retry_type exponential_backoff
flush_thread_count 2
flush_interval 5s
retry_forever
retry_max_interval 30
chunk_limit_size 2M
queue_limit_length 8
overflow_action block
</buffer>
</match>
通過執(zhí)行如下的命令創(chuàng)建Fluentd的ConfigMap:
$ kubectl create -f {path}/fluentd-es-configmap.yaml
Fluentd本身的YAML配置文件如下所示:
apiVersion: v1
kind: ServiceAccount metadata: name: fluentd-es namespace: kube-system
labels: k8s-app: fluentd-es
kubernetes.io/cluster-service: "true" addonmanager.kubernetes.io/mode: Reconcile --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1
metadata: name: fluentd-es
labels: k8s-app: fluentd-es
kubernetes.io/cluster-service: "true" addonmanager.kubernetes.io/mode: Reconcile rules: - apiGroups: - "" resources: - "namespaces" - "pods" verbs: - "get" - "watch" - "list" --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1
metadata: name: fluentd-es
labels: k8s-app: fluentd-es
kubernetes.io/cluster-service: "true" addonmanager.kubernetes.io/mode: Reconcile subjects: - kind: ServiceAccount name: fluentd-es namespace: kube-system
apiGroup: "" roleRef: kind: ClusterRole name: fluentd-es
apiGroup: "" --- apiVersion: apps/v1
kind: DaemonSet metadata: name: fluentd-es-v2.2.0 namespace: kube-system
labels: k8s-app: fluentd-es
version: v2.2.0 kubernetes.io/cluster-service: "true" addonmanager.kubernetes.io/mode: Reconcile spec: selector: matchLabels: k8s-app: fluentd-es
version: v2.2.0 template: metadata: labels: k8s-app: fluentd-es
kubernetes.io/cluster-service: "true" version: v2.2.0 # This annotation ensures that fluentd does not get evicted if the node # supports critical pod annotation based priority scheme. # Note that this does not guarantee admission on the nodes (#40573). annotations: scheduler.alpha.kubernetes.io/critical-pod: '' seccomp.security.alpha.kubernetes.io/pod: 'docker/default' spec: priorityClassName: system-node-critical
serviceAccountName: fluentd-es
containers: - name: fluentd-es
image: k8s.gcr.io/fluentd-elasticsearch:v2.2.0 env: - name: FLUENTD_ARGS
value: --no-supervisor -q
resources: limits: memory: 500Mi requests: cpu: 100m memory: 200Mi volumeMounts: - name: varlog
mountPath: /var/log - name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true - name: config-volume
mountPath: /etc/fluent/config.d
nodeSelector: beta.kubernetes.io/fluentd-ds-ready: "true" terminationGracePeriodSeconds: 30 volumes: - name: varlog
hostPath: path: /var/log - name: varlibdockercontainers
hostPath: path: /var/lib/docker/containers - name: config-volume
configMap: name: fluentd-es-config-v0.1.4
通過執(zhí)行如下的命令部署Fluentd:
$ kubectl create -f {path}/fluentd-es-ds.yaml
2.3 安裝部署Kibana
Kibana是一個開源的分析與可視化平臺,被設(shè)計用于和Elasticsearch一起使用的辞嗡。通過kibana可以搜索捆等、查看和交互存放在Elasticsearch中的數(shù)據(jù),利用各種不同的圖表续室、表格和地圖等栋烤,Kibana能夠?qū)?shù)據(jù)進(jìn)行分析與可視化。Kibana部署的YAML如下所示挺狰,通過環(huán)境變量ELASTICSEARCH_URL明郭,指定所獲取日志數(shù)據(jù)的Elasticsearch服務(wù)买窟,此處為:http://elasticsearch-logging:9200,elasticsearch.cattle-logging是elasticsearch在Kubernetes中代理服務(wù)的名稱薯定。在Fluented配置文件中始绍,有下面的一些關(guān)鍵指令:
- source指令確定輸入源。
- match指令確定輸出目標(biāo)话侄。
- filter指令確定事件處理管道亏推。
- system指令設(shè)置系統(tǒng)范圍的配置。
- label指令將輸出和過濾器分組以進(jìn)行內(nèi)部路由
- @include指令包含其他文件年堆。
apiVersion: apps/v1
kind: Deployment
metadata:
name: kibana-logging n
amespace: kube-system
labels: k8s-app: kibana-logging
kubernetes.io/cluster-service: "true" 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.2.4 resources: # need more cpu upon initialization, therefore burstable class limits: cpu: 1000m requests: cpu: 100m env: - name: ELASTICSEARCH_URL
value: http://elasticsearch-logging:9200 ports: - containerPort: 5601 name: ui
protocol: TCP
通過執(zhí)行如下的命令部署Kibana的代理服務(wù):
bash$ kubectl create -f {path}/kibana-deployment.yaml
下面Kibana的代理服務(wù)YAML配置文件吞杭,代理服務(wù)的類型為NodePort。
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: type: NodePort ports: - port: 5601 protocol: TCP
targetPort: ui
selector: k8s-app: kibana-logging
通過執(zhí)行如下的命令部署Kibana的代理服務(wù):
$ kubectl create -f {path}/kibana-service.yaml
3变丧、日志數(shù)據(jù)展示
通過如下命令獲取Kibana的對外暴露的端口:
$ kubectl get svc --namespace=kube-system
從輸出的信息可以知道芽狗,kibana對外暴露的端口為30471,因此在Kubernetes集群外可以通過:http://{NodeIP}:30471 訪問kibana痒蓬。
通過點擊“Discover”童擎,就能夠?qū)崟r看看從容器中獲取到的日志信息:
參考資料
1.《Logging Architecture》地址:https://kubernetes.io/docs/concepts/cluster-administration/logging/
2.《Kubernetes Logging with Fluentd》地址:https://docs.fluentd.org/v0.12/articles/kubernetes-fluentd
3.《Quickstart Guide》地址:https://docs.fluentd.org/v0.12/articles/quickstart
4.《fluentd-elasticsearch》地址:https://github.com/kubernetes/kubernetes/tree/master/cluster/addons/fluentd-elasticsearch
5.《Elasticsearch》地址:https://www.elastic.co/products/elasticsearch
6.《What is Fluentd?》地址:https://www.fluentd.org/architecture
7.《Configuration File Syntax》地址:https://docs.fluentd.org/v1.0/articles/config-file
8.《A Practical Introduction to Elasticsearch》地址:https://www.elastic.co/blog/a-practical-introduction-to-elasticsearch
Memo
本文轉(zhuǎn)載至: https://www.kubernetes.org.cn/4278.html