搭建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字段

最后顯示:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM