1. 程式人生 > 實用技巧 >搭建EFK日誌系統

搭建EFK日誌系統

Kubernetes中比較流行的日誌收集解決方案是Elasticsearch、Fluentd和 Kibana(EFK)技術棧,也是目前官方比較推薦的一種方案。

Elasticsearch 是一個實時的、分散式的可擴充套件的搜尋引擎,允許進行全文、結構化搜尋,它通常用於索引和搜尋大量日誌資料,也可用於搜尋許多不同型別的文件。

Elasticsearch 通常與 Kibana 一起部署,Kibana 是 Elasticsearch 的一個功能強大的資料視覺化 Dashboard,Kibana 允許你通過 web 介面來瀏覽 Elasticsearch 日誌資料。

Fluentd是一個流行的開源資料收集器,我們將在 Kubernetes 叢集節點上安裝 Fluentd,通過獲取容器日誌檔案、過濾和轉換日誌資料,然後將資料傳遞到 Elasticsearch 叢集,在該叢集中對其進行索引和儲存。

一、建立Elasticsearch叢集

建立一個名稱空間,將在這個namespace中安裝所有日誌相關的資源

kube-logging.yaml

apiVersion: v1
kind: Namespace
metadata:
  name: logging

然後通過kubectl建立該namespace,建立名為logging的名稱空間

# kubectl create -f kube-logging.yaml 
namespace/logging created
​
# kubectl get ns
NAME                   STATUS   AGE
default                Active   31d
kube
-node-lease Active 31d kube-public Active 31d kube-system Active 31d kubernetes-dashboard Active 28d logging Active 7s

使用3個Elasticsearch Pod來避免 高可用下多節點叢集中出現的“腦裂”問題,一個關鍵的是您應該設定引數discover.zen.minimum_master_nodes=N/2+1,其中N是 Elasticsearch 叢集中符合主節點的節點數,比如我們這裡3個節點,意味著N

應該設定為2。

首先建立一個名為elasticsearch的headless服務,新建檔案elasticearch-svc.yaml:

apiVersion: v1
kind: Service
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 svc -n logging
NAME           TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)             AGE
elasticsearch   ClusterIP   None         <none>        9200/TCP,9300/TCP   7s

現在我們已經為 Pod 設定了無頭服務和一個穩定的域名.elasticsearch.logging.svc.cluster.local,接下來我們通過 StatefulSet 來建立具體的 Elasticsearch 的 Pod 應用。

定義elasticsearch的配置檔案,elasticsearch-conf.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: elasticsearch
  namespace: logging
data:
  elasticsearch.yml: |
    cluster.name: k8s-log
    node.name: "${POD_NAME}"
    network.host: 0.0.0.0
    cluster.initial_master_nodes: ["es-cluster-0","es-cluster-1","es-cluster-2"]
    discovery.zen.ping.unicast.hosts: ["es-cluster-0.elasticsearch","es-cluster-1.elasticsearch","es-cluster-2.elasticsearch"]
    xpack.security.enabled: "false"
    bootstrap.system_call_filter: "false"
    discovery.zen.minimum_master_nodes: "2"

建立該configmap

# kubectl create -f elasticsearch-conf.yaml
​
# kubectl get configmap elasticsearch -n logging -o yaml
apiVersion: v1
data:
  elasticsearch.yml: |
    cluster.name: k8s-log
    node.name: "${POD_NAME}"
    network.host: 0.0.0.0
    cluster.initial_master_nodes: ["es-cluster-0","es-cluster-1","es-cluster-2"]
    discovery.zen.ping.unicast.hosts: ["es-cluster-0.elasticsearch","es-cluster-1.elasticsearch","es-cluster-2.elasticsearch"]
    xpack.security.enabled: "false"
    bootstrap.system_call_filter: "false"
    discovery.zen.minimum_master_nodes: "2"
kind: ConfigMap
metadata:
  creationTimestamp: "2020-07-07T01:05:33Z"
  managedFields:
  - apiVersion: v1
    fieldsType: FieldsV1
    fieldsV1:
      f:data:
        .: {}
        f:elasticsearch.yml: {}
    manager: kubectl
    operation: Update
    time: "2020-07-07T01:05:33Z"
  name: elasticsearch
  namespace: logging
  resourceVersion: "5924749"
  selfLink: /api/v1/namespaces/logging/configmaps/elasticsearch
  uid: 2224da1f-78ac-47e7-bcda-814952bbb776
  • 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

Kubernetes StatefulSet 允許我們為 Pod 分配一個穩定的標識和持久化儲存,Elasticsearch 需要穩定的儲存來保證 Pod 在重新排程或者重啟後的資料依然不變,所以需要使用 StatefulSet 來管理 Pod。

新建名為 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: elasticsearch:7.5.0
        imagePullPolicy: IfNotPresent
        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: POD_NAME
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
          - name: ES_JAVA_OPTS
            value: "-Xms512m -Xmx512m"
        volumeMounts:
        - name: elasticsearch-config
          mountPath: /usr/share/elasticsearch/config/elasticsearch.yml
          subPath: elasticsearch.yml 
      volumes:
      - name: elasticsearch-config
        configMap:
          name: elasticsearch

接下來新增關於 initContainer 的內容:

---
      initContainers:
      - name: fix-permissions
        image: registry.cn-hangzhou.aliyuncs.com/google_containers/busybox
        imagePullPolicy: IfNotPresent
        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: registry.cn-hangzhou.aliyuncs.com/google_containers/busybox
        imagePullPolicy: IfNotPresent
        command: ['sysctl','-w','vm.max_map_count=262144']
        securityContext:
          privileged: true
      - name: increase-fd-ulimit
        image: busybox
        command: ['sh','-c','ulimit -n 65535']
        securityContext:
          privileged: true

第一個名為 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 物件,新建一個 elasticsearch-storageclass.yaml 的檔案,檔案內容如下:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: es-data-db
provisioner: kubernetes.io/glusterfs
parameters:
  resturl: http://192.168.10.106:18080

最後,完整的 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: elasticsearch:7.5.0
        imagePullPolicy: IfNotPresent
        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: POD_NAME
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
          - name: ES_JAVA_OPTS
            value: "-Xms512m -Xmx512m"
        volumeMounts:
        - name: elasticsearch-config
          mountPath: /usr/share/elasticsearch/config/elasticsearch.yml
          subPath: elasticsearch.yml 
      volumes:
      - name: elasticsearch-config
        configMap:
          name: elasticsearch
      initContainers:
      - name: fix-permissions
        image: registry.cn-hangzhou.aliyuncs.com/google_containers/busybox
        imagePullPolicy: IfNotPresent
        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: registry.cn-hangzhou.aliyuncs.com/google_containers/busybox
        imagePullPolicy: IfNotPresent
        command: ['sysctl','-w','vm.max_map_count=262144']
        securityContext:
          privileged: true
      - name: increase-fd-ulimit
        image: busybox
        command: ['sh','-c','ulimit -n 65535']
        securityContext:
          privileged: true
  volumeClaimTemplates:
  - metadata:
      name: data
      labels:
        app: elasticsearch
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: es-data-db
      resources:
        requests:
          storage: 2Gi

部署

# 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

檢視

# kubectl get sts -n logging
NAME         READY   AGE
es-cluster   3/3     7m37s
​
# kubectl get pods -n logging
NAME           READY   STATUS    RESTARTS   AGE
es-cluster-0   1/1     Running   0          7m44s
es-cluster-1   1/1     Running   0          6m20s
es-cluster-2   1/1     Running   0          5m1s
​
# kubectl get svc -n logging
NAME                                                     TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGE
elasticserch                                             ClusterIP   None            <none>        9200/TCP,9300/TCP   3h1m

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 | more
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0{
  "cluster_name" : "k8s-log",
  "cluster_uuid" : "iXCjUz9jS_2ndq1VHQhptg",
  "version" : 18,
  "state_uuid" : "BGVTBoDGTSmnM2BXdJVlbw",
  "master_node" : "aL-lsgOIQJ64EMP9T_IRqA",
  "blocks" : { },
  "nodes" : {
    "aL-lsgOIQJ64EMP9T_IRqA" : {
      "name" : "es-cluster-0",
      "ephemeral_id" : "8TGAhf9xQ9id2yqImo7y9A",
      "transport_address" : "10.32.0.2:9300",
      "attributes" : {
        "ml.machine_memory" : "2983755776",
        "xpack.installed" : "true",
        "ml.max_open_jobs" : "20"
      }
    },
    "NbVg-sSYQFuMx0gyyu0ugw" : {
      "name" : "es-cluster-2",
      "ephemeral_id" : "IGDXyo33S5GV779CJgl4fQ",
      "transport_address" : "10.38.0.3:9300",
      "attributes" : {
        "ml.machine_memory" : "2983759872",
        "ml.max_open_jobs" : "20",
        "xpack.installed" : "true"
      }
    },
    "26cjOCdeSwiOIZaNP095Dw" : {
      "name" : "es-cluster-1",
      "ephemeral_id" : "RtSL9qqHQya_m2VAYkTJRA",
      "transport_address" : "10.34.0.1: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:
  selector:
    app: kibana
  type: NodePort
  ports:
  - port: 5601---
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: kibana:7.5.0
        resources:
          limits:
            cpu: 1000m
          requests:
            cpu: 100m
        env:
        - name: ELASTICSEARCH_URL
          value: http://elasticsearch:9200
        ports:
        - containerPort: 5601

建立

# kubectl create -f kibana.yaml
​
# kubectl get svc -n logging
NAME                                                     TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGE
kibana                                                   NodePort    10.97.159.197   <none>        5601:32092/TCP      15m

如果 Pod 已經是 Running 狀態了,證明應用已經部署成功了,然後可以通過 NodePort 來訪問 Kibana 這個服務,在瀏覽器中開啟http://<任意節點IP>:32092即可,如果看到如下歡迎介面證明 Kibana 已經成功部署到了 Kubernetes叢集之中。

部署Fluentd

Fluentd 是一個高效的日誌聚合器,是用 Ruby 編寫的,並且可以很好地擴充套件。對於大部分企業來說,Fluentd 足夠高效並且消耗的資源相對較少,另外一個工具Fluent-bit更輕量級,佔用資源更少,但是外掛相對 Fluentd 來說不夠豐富,所以整體來說,Fluentd 更加成熟,使用更加廣泛,所以我們這裡也同樣使用 Fluentd 來作為日誌收集工具。

工作原理

Fluentd 通過一組給定的資料來源抓取日誌資料,處理後(轉換成結構化的資料格式)將它們轉發給其他服務,比如 Elasticsearch、物件儲存等等。Fluentd 支援超過300個日誌儲存和分析服務,所以在這方面是非常靈活的。主要執行步驟如下:

  • 首先 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 檔案,檔案內容如下:

(這個配置貌似沒效果,直接再daemonset中設定)

apiVersion: v1
kind: ConfigMap
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-docker
        @type systemd
        filters [{ "_SYSTEMD_UNIT": "docker.service" }]
        <storage>
          @type local
          persistent true
        </storage>
        read_from_head true
        tag docker
      </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
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
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"
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
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: rbac.authorization.k8s.io
---
​
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:
      serviceAccount: fluentd-es
      containers:
      - name: fluentd-es
        #image: cnych/fluentd-elasticsearch:v2.0.4
        image: fluent/fluentd-kubernetes-daemonset:v1.4.2-debian-elasticsearch-1.1
        env:
        - name: FLUENTD_ARGS
          value: --no-supervisor -q
        - name:  FLUENT_ELASTICSEARCH_HOST
          value: "elasticsearch.logging.svc.cluster.local"
        - name:  FLUENT_ELASTICSEARCH_PORT
          value: "9200"
        - name: FLUENT_ELASTICSEARCH_SCHEME
          value: "http"
        - name: FLUENTD_SYSTEMD_CONF
          value: disable
        resources:
          limits:
            memory: 500Mi
          requests:
            memory: 100Mi
            cpu: 100m
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
      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: /var/lib/docker/containers

我們將上面建立的 fluentd-config 這個 ConfigMap 物件通過 volumes 掛載到了 Fluentd 容器中,另外為了能夠靈活控制哪些節點的日誌可以被收集,所以我們這裡還添加了一個 nodSelector 屬性:

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

先給kubernetes叢集打標籤

# kubectl label nodes node1 beta.kubernetes.io/fluentd-ds-ready=true
node/node1 labeled
​
#類似所有節點打上label

另外由於我們的叢集使用的是 kubeadm 搭建的,預設情況下 master 節點有汙點,所以要想也收集 master 節點的日誌,則需要新增上容忍:

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

建立flunetd

# 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

啟動後檢視kibana,點選discover

如果建立的index無法儲存,總是要重新建立,建議重啟elasticsearch和kibana

# kubectl scale statefulset es-cluster -n logging --replicas=0
# kubectl scale deployment kibana -n logging --replicas=0
​
# kubectl scale statefulset es-cluster -n logging --replicas=3
# kubectl scale deployment kibana -n logging --replicas=1

新建index pattern: logstash-*, 時間過濾日誌資料,在下拉列表中,選擇@timestamp欄位

最後顯示: