k8s-搭建 EFK 日誌系統

搭建 EFK 日誌系統

你們介紹了 Kubernetes 集羣中的幾種日誌收集方案,Kubernetes 中比較流行的日誌收集解決方案是 Elasticsearch、Fluentd 和 Kibana(EFK)技術棧,也是官方如今比較推薦的一種方案。html

Elasticsearch 是一個實時的、分佈式的可擴展的搜索引擎,容許進行全文、結構化搜索,它一般用於索引和搜索大量日誌數據,也可用於搜索許多不一樣類型的文檔。node

Elasticsearch 一般與 Kibana 一塊兒部署,Kibana 是 Elasticsearch 的一個功能強大的數據可視化 Dashboard,Kibana 容許你經過 web 界面來瀏覽 Elasticsearch 日誌數據。linux

Fluentd是一個流行的開源數據收集器,咱們將在 Kubernetes 集羣節點上安裝 Fluentd,經過獲取容器日誌文件、過濾和轉換日誌數據,而後將數據傳遞到 Elasticsearch 集羣,在該集羣中對其進行索引和存儲。web

咱們先來配置啓動一個可擴展的 Elasticsearch 集羣,而後在 Kubernetes 集羣中建立一個 Kibana 應用,最後經過 DaemonSet 來運行 Fluentd,以便它在每一個 Kubernetes 工做節點上均可以運行一個 Pod。正則表達式

建立 Elasticsearch 集羣

在建立 Elasticsearch 集羣以前,咱們先建立一個命名空間,咱們將在其中安裝全部日誌相關的資源對象。docker

新建一個 kube-logging.yaml 文件:shell

apiVersion: v1 kind: Namespace metadata: name: logging 

而後經過 kubectl 建立該資源清單,建立一個名爲 logging 的 namespace:json

$ kubectl create -f kube-logging.yaml
namespace/logging created
$ kubectl get ns
NAME           STATUS    AGE
default        Active    244d
istio-system   Active    100d
kube-ops       Active    179d
kube-public    Active    244d
kube-system    Active    244d
logging        Active    4h
monitoring     Active    35d

如今建立了一個命名空間來存放咱們的日誌相關資源,接下來能夠部署 EFK 相關組件,首先開始部署一個3節點的 Elasticsearch 集羣。後端

這裏咱們使用3個 Elasticsearch Pod 來避免高可用下多節點集羣中出現的「腦裂」問題,當一個或多個節點沒法與其餘節點通訊時會產生「腦裂」,可能會出現幾個主節點。api

瞭解更多 Elasticsearch 集羣腦裂問題,能夠查看文檔https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-node.html#split-brain

一個關鍵點是您應該設置參數discover.zen.minimum_master_nodes=N/2+1,其中N是 Elasticsearch 集羣中符合主節點的節點數,好比咱們這裏3個節點,意味着N應該設置爲2。這樣,若是一個節點暫時與集羣斷開鏈接,則另外兩個節點能夠選擇一個新的主節點,而且集羣能夠在最後一個節點嘗試從新加入時繼續運行,在擴展 Elasticsearch 集羣時,必定要記住這個參數。

首先建立一個名爲 elasticsearch 的無頭服務,新建文件 elasticsearch-svc.yaml,文件內容以下:

kind: Service apiVersion: v1 metadata: name: elasticsearch namespace: logging labels: app: elasticsearch spec: selector: app: elasticsearch clusterIP: None ports: - port: 9200 name: rest - port: 9300 name: inter-node 

定義了一個名爲 elasticsearch 的 Service,指定標籤app=elasticsearch,當咱們將 Elasticsearch StatefulSet 與此服務關聯時,服務將返回帶有標籤app=elasticsearch的 Elasticsearch Pods 的 DNS A 記錄,而後設置clusterIP=None,將該服務設置成無頭服務。最後,咱們分別定義端口9200、9300,分別用於與 REST API 交互,以及用於節點間通訊。

使用 kubectl 直接建立上面的服務資源對象:

$ kubectl create -f elasticsearch-svc.yaml
service/elasticsearch created
$ kubectl get services --namespace=logging Output NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE elasticsearch ClusterIP None <none> 9200/TCP,9300/TCP 26s 

如今咱們已經爲 Pod 設置了無頭服務和一個穩定的域名.elasticsearch.logging.svc.cluster.local,接下來咱們經過 StatefulSet 來建立具體的 Elasticsearch 的 Pod 應用。

Kubernetes StatefulSet 容許咱們爲 Pod 分配一個穩定的標識和持久化存儲,Elasticsearch 須要穩定的存儲來保證 Pod 在從新調度或者重啓後的數據依然不變,因此須要使用 StatefulSet 來管理 Pod。

要了解更多關於 StaefulSet 的信息,能夠查看官網關於 StatefulSet 的相關文檔:https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/

新建名爲 elasticsearch-statefulset.yaml 的資源清單文件,首先粘貼下面內容:

apiVersion: apps/v1 kind: StatefulSet metadata: name: es-cluster namespace: logging spec: serviceName: elasticsearch replicas: 3 selector: matchLabels: app: elasticsearch template: metadata: labels: app: elasticsearch 

該內容中,咱們定義了一個名爲 es-cluster 的 StatefulSet 對象,而後定義serviceName=elasticsearch和前面建立的 Service 相關聯,這能夠確保使用如下 DNS 地址訪問 StatefulSet 中的每個 Pod:es-cluster-[0,1,2].elasticsearch.logging.svc.cluster.local,其中[0,1,2]對應於已分配的 Pod 序號。

而後指定3個副本,將 matchLabels 設置爲app=elasticsearch,因此 Pod 的模板部分.spec.template.metadata.lables也必須包含app=elasticsearch標籤。

而後定義 Pod 模板部份內容:

... spec: containers: - name: elasticsearch image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.4.3 resources: limits: cpu: 1000m requests: cpu: 100m ports: - containerPort: 9200 name: rest protocol: TCP - containerPort: 9300 name: inter-node protocol: TCP volumeMounts: - name: data mountPath: /usr/share/elasticsearch/data env: - name: cluster.name value: k8s-logs - name: node.name valueFrom: fieldRef: fieldPath: metadata.name - name: discovery.zen.ping.unicast.hosts value: "es-cluster-0.elasticsearch,es-cluster-1.elasticsearch,es-cluster-2.elasticsearch" - name: discovery.zen.minimum_master_nodes value: "2" - name: ES_JAVA_OPTS value: "-Xms512m -Xmx512m" 

該部分是定義 StatefulSet 中的 Pod,咱們這裏使用一個-oss後綴的鏡像,該鏡像是 Elasticsearch 的開源版本,若是你想使用包含X-Pack之類的版本,能夠去掉該後綴。而後暴露了9200和9300兩個端口,注意名稱要和上面定義的 Service 保持一致。而後經過 volumeMount 聲明瞭數據持久化目錄,下面咱們再來定義 VolumeClaims。最後就是咱們在容器中設置的一些環境變量了:

  • cluster.name:Elasticsearch 集羣的名稱,咱們這裏命名成 k8s-logs。
  • node.name:節點的名稱,經過metadata.name來獲取。這將解析爲 es-cluster-[0,1,2],取決於節點的指定順序。
  • discovery.zen.ping.unicast.hosts:此字段用於設置在 Elasticsearch 集羣中節點相互鏈接的發現方法。咱們使用 unicastdiscovery 方式,它爲咱們的集羣指定了一個靜態主機列表。因爲咱們以前配置的無頭服務,咱們的 Pod 具備惟一的 DNS 域es-cluster-[0,1,2].elasticsearch.logging.svc.cluster.local,所以咱們相應地設置此變量。因爲都在同一個 namespace 下面,因此咱們能夠將其縮短爲es-cluster-[0,1,2].elasticsearch。要了解有關 Elasticsearch 發現的更多信息,請參閱 Elasticsearch 官方文檔:https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-discovery.html
  • discovery.zen.minimum_master_nodes:咱們將其設置爲(N/2) + 1N是咱們的羣集中符合主節點的節點的數量。咱們有3個 Elasticsearch 節點,所以咱們將此值設置爲2(向下舍入到最接近的整數)。要了解有關此參數的更多信息,請參閱官方 Elasticsearch 文檔:https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-node.html#split-brain
  • ES_JAVA_OPTS:這裏咱們設置爲-Xms512m -Xmx512m,告訴JVM使用512 MB的最小和最大堆。您應該根據羣集的資源可用性和需求調整這些參數。要了解更多信息,請參閱設置堆大小的相關文檔:https://www.elastic.co/guide/en/elasticsearch/reference/current/heap-size.html

接下來添加關於 initContainer 的內容:

... initContainers: - name: fix-permissions image: busybox command: ["sh", "-c", "chown -R 1000:1000 /usr/share/elasticsearch/data"] securityContext: privileged: true volumeMounts: - name: data mountPath: /usr/share/elasticsearch/data - name: increase-vm-max-map image: busybox command: ["sysctl", "-w", "vm.max_map_count=262144"] securityContext: privileged: true - name: increase-fd-ulimit image: busybox command: ["sh", "-c", "ulimit -n 65536"] securityContext: privileged: true 

這裏咱們定義了幾個在主應用程序以前運行的 Init 容器,這些初始容器按照定義的順序依次執行,執行完成後纔會啓動主應用容器。

第一個名爲 fix-permissions 的容器用來運行 chown 命令,將 Elasticsearch 數據目錄的用戶和組更改成1000:1000(Elasticsearch 用戶的 UID)。由於默認狀況下,Kubernetes 用 root 用戶掛載數據目錄,這會使得 Elasticsearch 沒法方法該數據目錄,能夠參考 Elasticsearch 生產中的一些默認注意事項相關文檔說明:https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html#_notes_for_production_use_and_defaults

第二個名爲 increase-vm-max-map 的容器用來增長操做系統對mmap計數的限制,默認狀況下該值可能過低,致使內存不足的錯誤,要了解更多關於該設置的信息,能夠查看 Elasticsearch 官方文檔說明:https://www.elastic.co/guide/en/elasticsearch/reference/current/vm-max-map-count.html

最後一個初始化容器是用來執行ulimit命令增長打開文件描述符的最大數量的。

此外 Elastisearch Notes for Production Use 文檔還提到了因爲性能緣由最好禁用 swap,固然對於 Kubernetes 集羣而言,最好也是禁用 swap 分區的。

如今咱們已經定義了主應用容器和它以前運行的 Init Containers 來調整一些必要的系統參數,接下來咱們能夠添加數據目錄的持久化相關的配置,在 StatefulSet 中,使用 volumeClaimTemplates 來定義 volume 模板便可:

... volumeClaimTemplates: - metadata: name: data labels: app: elasticsearch spec: accessModes: [ "ReadWriteOnce" ] storageClassName: es-data-db resources: requests: storage: 50Gi 

咱們這裏使用 volumeClaimTemplates 來定義持久化模板,Kubernetes 會使用它爲 Pod 建立 PersistentVolume,設置訪問模式爲ReadWriteOnce,這意味着它只能被 mount 到單個節點上進行讀寫,而後最重要的是使用了一個名爲 es-data-db 的 StorageClass 對象,因此咱們須要提早建立該對象,咱們這裏使用的 NFS 做爲存儲後端,因此須要安裝一個對應的 provisioner 驅動,前面關於 StorageClass 的課程中已經和你們介紹過方法,新建一個 elasticsearch-storageclass.yaml 的文件,文件內容以下:

apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: es-data-db provisioner: fuseim.pri/ifs # 該值須要和 provisioner 配置的保持一致 

最後,咱們指定了每一個 PersistentVolume 的大小爲 50GB,咱們能夠根據本身的實際須要進行調整該值。最後,完整的 Elasticsearch StatefulSet 資源清單文件內容以下:

apiVersion: apps/v1 kind: StatefulSet metadata: name: es-cluster namespace: logging spec: serviceName: elasticsearch replicas: 3 selector: matchLabels: app: elasticsearch template: metadata: labels: app: elasticsearch spec: containers: - name: elasticsearch image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.4.3 resources: limits: cpu: 1000m requests: cpu: 100m ports: - containerPort: 9200 name: rest protocol: TCP - containerPort: 9300 name: inter-node protocol: TCP volumeMounts: - name: data mountPath: /usr/share/elasticsearch/data env: - name: cluster.name value: k8s-logs - name: node.name valueFrom: fieldRef: fieldPath: metadata.name - name: discovery.zen.ping.unicast.hosts value: "es-cluster-0.elasticsearch,es-cluster-1.elasticsearch,es-cluster-2.elasticsearch" - name: discovery.zen.minimum_master_nodes value: "2" - name: ES_JAVA_OPTS value: "-Xms512m -Xmx512m" initContainers: - name: fix-permissions image: busybox command: ["sh", "-c", "chown -R 1000:1000 /usr/share/elasticsearch/data"] securityContext: privileged: true volumeMounts: - name: data mountPath: /usr/share/elasticsearch/data - name: increase-vm-max-map image: busybox command: ["sysctl", "-w", "vm.max_map_count=262144"] securityContext: privileged: true - name: increase-fd-ulimit image: busybox command: ["sh", "-c", "ulimit -n 65536"] securityContext: privileged: true volumeClaimTemplates: - metadata: name: data labels: app: elasticsearch spec: accessModes: [ "ReadWriteOnce" ] storageClassName: es-data-db resources: requests: storage: 100Gi 

如今直接使用 kubectl 工具部署便可:

$ kubectl create -f elasticsearch-storageclass.yaml
storageclass.storage.k8s.io "es-data-db" created $ kubectl create -f elasticsearch-statefulset.yaml statefulset.apps/es-cluster created 

添加成功後,能夠看到 logging 命名空間下面的全部的資源對象:

$ kubectl get sts -n logging
NAME         DESIRED   CURRENT   AGE
es-cluster   3         3         20h
$ kubectl get pods -n logging
NAME                      READY     STATUS    RESTARTS   AGE
es-cluster-0              1/1       Running   0          20h
es-cluster-1              1/1       Running   0          20h
es-cluster-2              1/1       Running   0          20h
$ kubectl get svc -n logging
NAME            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S) AGE elasticsearch ClusterIP None <none> 9200/TCP,9300/TCP 20h 

Pods 部署完成後,咱們能夠經過請求一個 REST API 來檢查 Elasticsearch 集羣是否正常運行。使用下面的命令將本地端口9200轉發到 Elasticsearch 節點(如es-cluster-0)對應的端口:

$ kubectl port-forward es-cluster-0 9200:9200 --namespace=logging Forwarding from 127.0.0.1:9200 -> 9200 Forwarding from [::1]:9200 -> 9200 

而後,在另外的終端窗口中,執行以下請求:

$ curl http://localhost:9200/_cluster/state?pretty 

正常來講,應該會看到相似於以下的信息:

{ "cluster_name" : "k8s-logs", "compressed_size_in_bytes" : 348, "cluster_uuid" : "QD06dK7CQgids-GQZooNVw", "version" : 3, "state_uuid" : "mjNIWXAzQVuxNNOQ7xR-qg", "master_node" : "IdM5B7cUQWqFgIHXBp0JDg", "blocks" : { }, "nodes" : { "u7DoTpMmSCixOoictzHItA" : { "name" : "es-cluster-1", "ephemeral_id" : "ZlBflnXKRMC4RvEACHIVdg", "transport_address" : "10.244.4.191:9300", "attributes" : { } }, "IdM5B7cUQWqFgIHXBp0JDg" : { "name" : "es-cluster-0", "ephemeral_id" : "JTk1FDdFQuWbSFAtBxdxAQ", "transport_address" : "10.244.2.215:9300", "attributes" : { } }, "R8E7xcSUSbGbgrhAdyAKmQ" : { "name" : "es-cluster-2", "ephemeral_id" : "9wv6ke71Qqy9vk2LgJTqaA", "transport_address" : "10.244.40.4:9300", "attributes" : { } } }, ... 

看到上面的信息就代表咱們名爲 k8s-logs 的 Elasticsearch 集羣成功建立了3個節點:es-cluster-0,es-cluster-1,和es-cluster-2,當前主節點是 es-cluster-0。

建立 Kibana 服務

Elasticsearch 集羣啓動成功了,接下來咱們能夠來部署 Kibana 服務,新建一個名爲 kibana.yaml 的文件,對應的文件內容以下:

apiVersion: v1 kind: Service metadata: name: kibana namespace: logging labels: app: kibana spec: ports: - port: 5601 type: NodePort selector: app: kibana --- apiVersion: apps/v1 kind: Deployment metadata: name: kibana namespace: logging labels: app: kibana spec: selector: matchLabels: app: kibana template: metadata: labels: app: kibana spec: containers: - name: kibana image: docker.elastic.co/kibana/kibana-oss:6.4.3 resources: limits: cpu: 1000m requests: cpu: 100m env: - name: ELASTICSEARCH_URL value: http://elasticsearch:9200 ports: - containerPort: 5601 

上面咱們定義了兩個資源對象,一個 Service 和 Deployment,爲了測試方便,咱們將 Service 設置爲了 NodePort 類型,Kibana Pod 中配置都比較簡單,惟一須要注意的是咱們使用 ELASTICSEARCH_URL 這個環境變量來設置Elasticsearch 集羣的端點和端口,直接使用 Kubernetes DNS 便可,此端點對應服務名稱爲 elasticsearch,因爲是一個 headless service,因此該域將解析爲3個 Elasticsearch Pod 的 IP 地址列表。

配置完成後,直接使用 kubectl 工具建立:

$ kubectl create -f kibana.yaml
service/kibana created
deployment.apps/kibana created

建立完成後,能夠查看 Kibana Pod 的運行狀態:

$ kubectl get pods --namespace=logging NAME READY STATUS RESTARTS AGE es-cluster-0 1/1 Running 0 20h es-cluster-1 1/1 Running 0 20h es-cluster-2 1/1 Running 0 20h kibana-7558d4dc4d-5mqdz 1/1 Running 0 20h $ kubectl get svc --namespace=logging NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE elasticsearch ClusterIP None <none> 9200/TCP,9300/TCP 20h kibana NodePort 10.105.208.253 <none> 5601:31816/TCP 20h 

若是 Pod 已是 Running 狀態了,證實應用已經部署成功了,而後能夠經過 NodePort 來訪問 Kibana 這個服務,在瀏覽器中打開http://<任意節點IP>:31816便可,若是看到以下歡迎界面證實 Kibana 已經成功部署到了 Kubernetes集羣之中。

kibana welcomekibana welcome

部署 Fluentd

Fluentd 是一個高效的日誌聚合器,是用 Ruby 編寫的,而且能夠很好地擴展。對於大部分企業來講,Fluentd 足夠高效而且消耗的資源相對較少,另一個工具Fluent-bit更輕量級,佔用資源更少,可是插件相對 Fluentd 來講不夠豐富,因此總體來講,Fluentd 更加成熟,使用更加普遍,因此咱們這裏也一樣使用 Fluentd 來做爲日誌收集工具。

工做原理

Fluentd 經過一組給定的數據源抓取日誌數據,處理後(轉換成結構化的數據格式)將它們轉發給其餘服務,好比 Elasticsearch、對象存儲等等。Fluentd 支持超過300個日誌存儲和分析服務,因此在這方面是很是靈活的。主要運行步驟以下:

  • 首先 Fluentd 從多個日誌源獲取數據
  • 結構化而且標記這些數據
  • 而後根據匹配的標籤將數據發送到多個目標服務去

fluentd 架構fluentd 架構

配置

通常來講咱們是經過一個配置文件來告訴 Fluentd 如何採集、處理數據的,下面簡單和你們介紹下 Fluentd 的配置方法。

日誌源配置

好比咱們這裏爲了收集 Kubernetes 節點上的全部容器日誌,就須要作以下的日誌源配置:

<source> @id fluentd-containers.log @type tail path /var/log/containers/*.log pos_file /var/log/fluentd-containers.log.pos time_format %Y-%m-%dT%H:%M:%S.%NZ tag raw.kubernetes.* format json read_from_head true </source> 

上面配置部分參數說明以下:

  • id:表示引用該日誌源的惟一標識符,該標識可用於進一步過濾和路由結構化日誌數據
  • type:Fluentd 內置的指令,tail表示 Fluentd 從上次讀取的位置經過 tail 不斷獲取數據,另一個是http表示經過一個 GET 請求來收集數據。
  • path:tail類型下的特定參數,告訴 Fluentd 採集/var/log/containers目錄下的全部日誌,這是 docker 在 Kubernetes 節點上用來存儲運行容器 stdout 輸出日誌數據的目錄。
  • pos_file:檢查點,若是 Fluentd 程序從新啓動了,它將使用此文件中的位置來恢復日誌數據收集。
  • tag:用來將日誌源與目標或者過濾器匹配的自定義字符串,Fluentd 匹配源/目標標籤來路由日誌數據。

路由配置

上面是日誌源的配置,接下來看看如何將日誌數據發送到 Elasticsearch:

<match **> @id elasticsearch @type elasticsearch @log_level info include_tag_key true type_name fluentd host "#{ENV['OUTPUT_HOST']}" port "#{ENV['OUTPUT_PORT']}" 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 "#{ENV['OUTPUT_BUFFER_CHUNK_LIMIT']}" queue_limit_length "#{ENV['OUTPUT_BUFFER_QUEUE_LIMIT']}" overflow_action block </buffer> 
  • match:標識一個目標標籤,後面是一個匹配日誌源的正則表達式,咱們這裏想要捕獲全部的日誌並將它們發送給 Elasticsearch,因此須要配置成**
  • id:目標的一個惟一標識符。
  • type:支持的輸出插件標識符,咱們這裏要輸出到 Elasticsearch,因此配置成 elasticsearch,這是 Fluentd 的一個內置插件。
  • log_level:指定要捕獲的日誌級別,咱們這裏配置成info,表示任何該級別或者該級別以上(INFO、WARNING、ERROR)的日誌都將被路由到 Elsasticsearch。
  • host/port:定義 Elasticsearch 的地址,也能夠配置認證信息,咱們的 Elasticsearch 不須要認證,因此這裏直接指定 host 和 port 便可。
  • logstash_format:Elasticsearch 服務對日誌數據構建反向索引進行搜索,將 logstash_format 設置爲true,Fluentd 將會以 logstash 格式來轉發結構化的日誌數據。
  • Buffer: Fluentd 容許在目標不可用時進行緩存,好比,若是網絡出現故障或者 Elasticsearch 不可用的時候。緩衝區配置也有助於下降磁盤的 IO。

安裝

要收集 Kubernetes 集羣的日誌,直接用 DasemonSet 控制器來部署 Fluentd 應用,這樣,它就能夠從 Kubernetes 節點上採集日誌,確保在集羣中的每一個節點上始終運行一個 Fluentd 容器。固然能夠直接使用 Helm 來進行一鍵安裝,爲了可以瞭解更多實現細節,咱們這裏仍是採用手動方法來進行安裝。

首先,咱們經過 ConfigMap 對象來指定 Fluentd 配置文件,新建 fluentd-configmap.yaml 文件,文件內容以下:

kind: ConfigMap apiVersion: v1 metadata: name: fluentd-config namespace: logging 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 localtime tag raw.kubernetes.* format json read_from_head true </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> system.input.conf: |- # Logs from systemd-journal for interesting services. <source> @id journald-docker @type systemd filters [{ "_SYSTEMD_UNIT": "docker.service" }] <storage> @type local persistent true </storage> read_from_head true tag docker </source> <source> @id journald-kubelet @type systemd filters [{ "_SYSTEMD_UNIT": "kubelet.service" }] <storage> @type local persistent true </storage> read_from_head true tag kubelet </source> forward.input.conf: |- # Takes the messages sent over TCP <source> @type forward </source> 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 port 9200 logstash_format true request_timeout 30s <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> 

上面配置文件中咱們配置了 docker 容器日誌目錄以及 docker、kubelet 應用的日誌的收集,收集到數據通過處理後發送到 elasticsearch:9200 服務。

而後新建一個 fluentd-daemonset.yaml 的文件,文件內容以下:

apiVersion: v1 kind: ServiceAccount metadata: name: fluentd-es namespace: logging 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: logging apiGroup: "" roleRef: kind: ClusterRole name: fluentd-es apiGroup: "" --- apiVersion: apps/v1 kind: DaemonSet metadata: name: fluentd-es namespace: logging labels: k8s-app: fluentd-es version: v2.0.4 kubernetes.io/cluster-service: "true" addonmanager.kubernetes.io/mode: Reconcile spec: selector: matchLabels: k8s-app: fluentd-es version: v2.0.4 template: metadata: labels: k8s-app: fluentd-es kubernetes.io/cluster-service: "true" version: v2.0.4 # 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: '' spec: serviceAccountName: fluentd-es containers: - name: fluentd-es image: cnych/fluentd-elasticsearch:v2.0.4 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: /data/docker/containers readOnly: true - name: config-volume mountPath: /etc/fluent/config.d nodeSelector: beta.kubernetes.io/fluentd-ds-ready: "true" tolerations: - key: node-role.kubernetes.io/master operator: Exists effect: NoSchedule terminationGracePeriodSeconds: 30 volumes: - name: varlog hostPath: path: /var/log - name: varlibdockercontainers hostPath: path: /data/docker/containers - name: config-volume configMap: name: fluentd-config 

咱們將上面建立的 fluentd-config 這個 ConfigMap 對象經過 volumes 掛載到了 Fluentd 容器中,另外爲了可以靈活控制哪些節點的日誌能夠被收集,因此咱們這裏還添加了一個 nodSelector 屬性:

nodeSelector: beta.kubernetes.io/fluentd-ds-ready: "true" 

意思就是要想採集節點的日誌,那麼咱們就須要給節點打上上面的標籤,好比咱們這裏3個節點都打上了該標籤:

$ kubectl get nodes --show-labels
NAME      STATUS    ROLES     AGE       VERSION   LABELS
master    Ready     master    245d      v1.10.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/fluentd-ds-ready=true,beta.kubernetes.io/os=linux,kubernetes.io/hostname=master,node-role.kubernetes.io/master= node02 Ready <none> 165d v1.10.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/fluentd-ds-ready=true,beta.kubernetes.io/os=linux,com=youdianzhishi,course=k8s,kubernetes.io/hostname=node02 node03 Ready <none> 225d v1.10.0 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/fluentd-ds-ready=true,beta.kubernetes.io/os=linux,jnlp=haimaxy,kubernetes.io/hostname=node03 

另外因爲咱們的集羣使用的是 kubeadm 搭建的,默認狀況下 master 節點有污點,因此要想也收集 master 節點的日誌,則須要添加上容忍:

tolerations: - key: node-role.kubernetes.io/master operator: Exists effect: NoSchedule 

另外須要注意的地方是,我這裏的測試環境更改了 docker 的根目錄:

$ docker info
... Docker Root Dir: /data/docker ... 

因此上面要獲取 docker 的容器目錄須要更改爲/data/docker/containers,這個地方很是重要,固然若是你沒有更改 docker 根目錄則使用默認的/var/lib/docker/containers目錄便可。

分別建立上面的 ConfigMap 對象和 DaemonSet:

$ kubectl create -f fluentd-configmap.yaml
configmap "fluentd-config" created $ kubectl create -f fluentd-daemonset.yaml serviceaccount "fluentd-es" created clusterrole.rbac.authorization.k8s.io "fluentd-es" created clusterrolebinding.rbac.authorization.k8s.io "fluentd-es" created daemonset.apps "fluentd-es" created 

建立完成後,查看對應的 Pods 列表,檢查是否部署成功:

$ kubectl get pods -n logging
NAME                      READY     STATUS    RESTARTS   AGE
es-cluster-0              1/1       Running   0          1d
es-cluster-1              1/1       Running   0          1d
es-cluster-2              1/1       Running   0          1d
fluentd-es-2z9jg          1/1       Running   1          35s
fluentd-es-6dfdd          1/1       Running   0          35s
fluentd-es-bfkg7          1/1       Running   0          35s
kibana-7558d4dc4d-5mqdz   1/1       Running   0          1d

Fluentd 啓動成功後,咱們能夠前往 Kibana 的 Dashboard 頁面中,點擊左側的Discover,能夠看到以下配置頁面:

create indexcreate index

在這裏能夠配置咱們須要的 Elasticsearch 索引,前面 Fluentd 配置文件中咱們採集的日誌使用的是 logstash 格式,這裏只須要在文本框中輸入logstash-*便可匹配到 Elasticsearch 集羣中的全部日誌數據,而後點擊下一步,進入如下頁面:

index configindex config

在該頁面中配置使用哪一個字段按時間過濾日誌數據,在下拉列表中,選擇@timestamp字段,而後點擊Create index pattern,建立完成後,點擊左側導航菜單中的Discover,而後就能夠看到一些直方圖和最近採集到的日誌數據了:

log datalog data

測試

如今咱們來將上一節課的計數器應用部署到集羣中,並在 Kibana 中來查找該日誌數據。

新建 counter.yaml 文件,文件內容以下:

apiVersion: v1 kind: Pod metadata: name: counter spec: containers: - name: count image: busybox args: [/bin/sh, -c, 'i=0; while true; do echo "$i: $(date)"; i=$((i+1)); sleep 1; done'] 

該 Pod 只是簡單將日誌信息打印到 stdout,因此正常來講 Fluentd 會收集到這個日誌數據,在 Kibana 中也就能夠找到對應的日誌數據了,使用 kubectl 工具建立該 Pod:

$ kubectl create -f counter.yaml

Pod 建立並運行後,回到 Kibana Dashboard 頁面,在上面的Discover頁面搜索欄中輸入kubernetes.pod_name:counter,就能夠過濾 Pod 名爲 counter 的日誌數據:

counter log datacounter log data

咱們也能夠經過其餘元數據來過濾日誌數據,好比 您能夠單擊任何日誌條目以查看其餘元數據,如容器名稱,Kubernetes 節點,命名空間等。

相關文章
相關標籤/搜索