1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        使用 EFKLK 搭建 Kubernetes 日志收集工具棧

        共 57596字,需瀏覽 116分鐘

         ·

        2021-04-29 18:07

        前面大家介紹了 Kubernetes 集群中的幾種日志收集方案,Kubernetes 中比較流行的日志收集解決方案是 Elasticsearch、Fluentd 和 Kibana(EFK)技術棧,也是官方現(xiàn)在比較推薦的一種方案。

        Elasticsearch 是一個實時的、分布式的可擴展的搜索引擎,允許進行全文、結構化搜索,它通常用于索引和搜索大量日志數(shù)據(jù),也可用于搜索許多不同類型的文檔。

        Elasticsearch 通常與 Kibana 一起部署,Kibana 是 Elasticsearch 的一個功能強大的數(shù)據(jù)可視化 Dashboard,Kibana 允許你通過 web 界面來瀏覽 Elasticsearch 日志數(shù)據(jù)。

        Fluentd是一個流行的開源數(shù)據(jù)收集器,我們將在 Kubernetes 集群節(jié)點上安裝 Fluentd,通過獲取容器日志文件、過濾和轉換日志數(shù)據(jù),然后將數(shù)據(jù)傳遞到 Elasticsearch 集群,在該集群中對其進行索引和存儲。

        我們先來配置啟動一個可擴展的 Elasticsearch 集群,然后在 Kubernetes 集群中創(chuàng)建一個 Kibana 應用,最后通過 DaemonSet 來運行 Fluentd,以便它在每個 Kubernetes 工作節(jié)點上都可以運行一個 Pod。

        如果你了解 EFK 的基本原理,只是為了測試可以直接使用 Kubernetes 官方提供的 addon 插件的資源清單,地址:https://github.com/kubernetes/kubernetes/blob/master/cluster/addons/fluentd-elasticsearch/,直接安裝即可。

        1安裝 Elasticsearch 集群

        在創(chuàng)建 Elasticsearch 集群之前,我們先創(chuàng)建一個命名空間,我們將在其中安裝所有日志相關的資源對象。

        kubectl create ns logging

        環(huán)境準備

        ElasticSearch 安裝有最低安裝要求,如果安裝后 Pod 無法正常啟動,請檢查是否符合最低要求的配置,要求如下:

        es 集群要求

        這里我們要安裝的 ES 集群環(huán)境信息如下所示:

        es 集群環(huán)境

        這里我們使用一個 NFS 類型的 StorageClass 來做持久化存儲,當然如果你是線上環(huán)境建議使用 Local PV 或者 Ceph RBD 之類的存儲來持久化 Elasticsearch 的數(shù)據(jù)。

        此外由于 ElasticSearch 7.x 版本默認安裝了 X-Pack 插件,并且部分功能免費,需要我們配置一些安全證書文件。

        1、生成證書文件

        # 運行容器生成證書
        $ docker run --name elastic-certs -i -w /app elasticsearch:7.12.0 /bin/sh -c  \
          "elasticsearch-certutil ca --out /app/elastic-stack-ca.p12 --pass '' && \
            elasticsearch-certutil cert --name security-master --dns \
            security-master --ca /app/elastic-stack-ca.p12 --pass '' --ca-pass '' --out /app/elastic-certificates.p12"
        # 從容器中將生成的證書拷貝出來
        $ docker cp elastic-certs:/app/elastic-certificates.p12 .
        # 刪除容器
        $ docker rm -f elastic-certs
        # 將 pcks12 中的信息分離出來,寫入文件
        $ openssl pkcs12 -nodes -passin pass:'' -in elastic-certificates.p12 -out elastic-certificate.pem

        2、添加證書到 Kubernetes

        # 添加證書
        $ kubectl create secret -n logging generic elastic-certs --from-file=elastic-certificates.p12
        # 設置集群用戶名密碼
        $ kubectl create secret -n logging generic elastic-auth --from-literal=username=elastic --from-literal=password=ydzsio321

        安裝 ES 集群

        首先添加 ELastic 的 Helm 倉庫:

        helm repo add elastic https://helm.elastic.co
        helm repo update

        ElaticSearch 安裝需要安裝三次,分別安裝 Master、Data、Client 節(jié)點,Master 節(jié)點負責集群間的管理工作;Data 節(jié)點負責存儲數(shù)據(jù);Client 節(jié)點負責代理 ElasticSearch Cluster 集群,負載均衡。

        首先使用 helm pull 拉取 Chart 并解壓:

        helm pull elastic/elasticsearch --untar --version 7.12.0
        cd elasticsearch

        在 Chart 目錄下面創(chuàng)建用于 Master 節(jié)點安裝配置的 values 文件:

        # values-master.yaml
        ## 設置集群名稱
        clusterName: "elasticsearch"
        ## 設置節(jié)點名稱
        nodeGroup: "master"

        ## 設置角色
        roles:
          master: "true"
          ingest: "false"
          data: "false"

        # ============鏡像配置============
        ## 指定鏡像與鏡像版本
        image: "elasticsearch"
        imageTag: "7.12.0"
        ## 副本數(shù)
        replicas: 3

        # ============資源配置============
        ## JVM 配置參數(shù)
        esJavaOpts: "-Xmx1g -Xms1g"
        ## 部署資源配置(生成環(huán)境一定要設置大些)
        resources:
          requests:
            cpu: "2000m"
            memory: "2Gi"
          limits:
            cpu: "2000m"
            memory: "2Gi"
        ## 數(shù)據(jù)持久卷配置
        persistence:
          enabled: true
        ## 存儲數(shù)據(jù)大小配置
        volumeClaimTemplate:
          storageClassName: nfs-storage
          accessModes: ["ReadWriteOnce"]
          resources:
            requests:
              storage: 5Gi

        # ============安全配置============
        ## 設置協(xié)議,可配置為 http、https
        protocol: http
        ## 證書掛載配置,這里我們掛入上面創(chuàng)建的證書
        secretMounts:
          - name: elastic-certs
            secretName: elastic-certs
            path: /usr/share/elasticsearch/config/certs

        ## 允許您在/usr/share/elasticsearch/config/中添加任何自定義配置文件,例如 elasticsearch.yml
        ## ElasticSearch 7.x 默認安裝了 x-pack 插件,部分功能免費,這里我們配置下
        ## 下面注掉的部分為配置 https 證書,配置此部分還需要配置 helm 參數(shù) protocol 值改為 https
        esConfig:
          elasticsearch.yml: |
            xpack.security.enabled: true
            xpack.security.transport.ssl.enabled: true
            xpack.security.transport.ssl.verification_mode: certificate
            xpack.security.transport.ssl.keystore.path: /usr/share/elasticsearch/config/certs/elastic-certificates.p12
            xpack.security.transport.ssl.truststore.path: /usr/share/elasticsearch/config/certs/elastic-certificates.p12
            # xpack.security.http.ssl.enabled: true
            # xpack.security.http.ssl.truststore.path: /usr/share/elasticsearch/config/certs/elastic-certificates.p12
            # xpack.security.http.ssl.keystore.path: /usr/share/elasticsearch/config/certs/elastic-certificates.p12
        ## 環(huán)境變量配置,這里引入上面設置的用戶名、密碼 secret 文件
        extraEnvs:
          - name: ELASTIC_USERNAME
            valueFrom:
              secretKeyRef:
                name: elastic-auth
                key: username
          - name: ELASTIC_PASSWORD
            valueFrom:
              secretKeyRef:
                name: elastic-auth
                key: password

        # ============調(diào)度配置============
        ## 設置調(diào)度策略
        ## - hard:只有當有足夠的節(jié)點時 Pod 才會被調(diào)度,并且它們永遠不會出現(xiàn)在同一個節(jié)點上
        ## - soft:盡最大努力調(diào)度
        antiAffinity: "soft"
        tolerations:
          - operator: "Exists" ##容忍全部污點

        然后創(chuàng)建用于 Data 節(jié)點安裝的 values 文件:

        # values-data.yaml
        # ============設置集群名稱============
        ## 設置集群名稱
        clusterName: "elasticsearch"
        ## 設置節(jié)點名稱
        nodeGroup: "data"
        ## 設置角色
        roles:
          master: "false"
          ingest: "true"
          data: "true"

        # ============鏡像配置============
        ## 指定鏡像與鏡像版本
        image: "elasticsearch"
        imageTag: "7.12.0"
        ## 副本數(shù)(建議設置為3,我這里資源不足只用了1個副本)
        replicas: 1

        # ============資源配置============
        ## JVM 配置參數(shù)
        esJavaOpts: "-Xmx1g -Xms1g"
        ## 部署資源配置(生成環(huán)境一定要設置大些)
        resources:
          requests:
            cpu: "1000m"
            memory: "2Gi"
          limits:
            cpu: "1000m"
            memory: "2Gi"
        ## 數(shù)據(jù)持久卷配置
        persistence:
          enabled: true
        ## 存儲數(shù)據(jù)大小配置
        volumeClaimTemplate:
          storageClassName: nfs-storage
          accessModes: ["ReadWriteOnce"]
          resources:
            requests:
              storage: 10Gi

        # ============安全配置============
        ## 設置協(xié)議,可配置為 http、https
        protocol: http
        ## 證書掛載配置,這里我們掛入上面創(chuàng)建的證書
        secretMounts:
          - name: elastic-certs
            secretName: elastic-certs
            path: /usr/share/elasticsearch/config/certs
        ## 允許您在/usr/share/elasticsearch/config/中添加任何自定義配置文件,例如 elasticsearch.yml
        ## ElasticSearch 7.x 默認安裝了 x-pack 插件,部分功能免費,這里我們配置下
        ## 下面注掉的部分為配置 https 證書,配置此部分還需要配置 helm 參數(shù) protocol 值改為 https
        esConfig:
          elasticsearch.yml: |
            xpack.security.enabled: true
            xpack.security.transport.ssl.enabled: true
            xpack.security.transport.ssl.verification_mode: certificate
            xpack.security.transport.ssl.keystore.path: /usr/share/elasticsearch/config/certs/elastic-certificates.p12
            xpack.security.transport.ssl.truststore.path: /usr/share/elasticsearch/config/certs/elastic-certificates.p12
            # xpack.security.http.ssl.enabled: true
            # xpack.security.http.ssl.truststore.path: /usr/share/elasticsearch/config/certs/elastic-certificates.p12
            # xpack.security.http.ssl.keystore.path: /usr/share/elasticsearch/config/certs/elastic-certificates.p12
        ## 環(huán)境變量配置,這里引入上面設置的用戶名、密碼 secret 文件
        extraEnvs:
          - name: ELASTIC_USERNAME
            valueFrom:
              secretKeyRef:
                name: elastic-auth
                key: username
          - name: ELASTIC_PASSWORD
            valueFrom:
              secretKeyRef:
                name: elastic-auth
                key: password

        # ============調(diào)度配置============
        ## 設置調(diào)度策略
        ## - hard:只有當有足夠的節(jié)點時 Pod 才會被調(diào)度,并且它們永遠不會出現(xiàn)在同一個節(jié)點上
        ## - soft:盡最大努力調(diào)度
        antiAffinity: "soft"
        ## 容忍配置
        tolerations:
          - operator: "Exists" ##容忍全部污點

        最后一個是用于創(chuàng)建 Client 節(jié)點的 values 文件:

        # values-client.yaml
        # ============設置集群名稱============
        ## 設置集群名稱
        clusterName: "elasticsearch"
        ## 設置節(jié)點名稱
        nodeGroup: "client"
        ## 設置角色
        roles:
          master: "false"
          ingest: "false"
          data: "false"

        # ============鏡像配置============
        ## 指定鏡像與鏡像版本
        image: "elasticsearch"
        imageTag: "7.12.0"
        ## 副本數(shù)
        replicas: 1

        # ============資源配置============
        ## JVM 配置參數(shù)
        esJavaOpts: "-Xmx1g -Xms1g"
        ## 部署資源配置(生成環(huán)境一定要設置大些)
        resources:
          requests:
            cpu: "1000m"
            memory: "2Gi"
          limits:
            cpu: "1000m"
            memory: "2Gi"
        ## 數(shù)據(jù)持久卷配置
        persistence:
          enabled: false

        # ============安全配置============
        ## 設置協(xié)議,可配置為 http、https
        protocol: http
        ## 證書掛載配置,這里我們掛入上面創(chuàng)建的證書
        secretMounts:
          - name: elastic-certs
            secretName: elastic-certs
            path: /usr/share/elasticsearch/config/certs
        ## 允許您在/usr/share/elasticsearch/config/中添加任何自定義配置文件,例如 elasticsearch.yml
        ## ElasticSearch 7.x 默認安裝了 x-pack 插件,部分功能免費,這里我們配置下
        ## 下面注掉的部分為配置 https 證書,配置此部分還需要配置 helm 參數(shù) protocol 值改為 https
        esConfig:
          elasticsearch.yml: |
            xpack.security.enabled: true
            xpack.security.transport.ssl.enabled: true
            xpack.security.transport.ssl.verification_mode: certificate
            xpack.security.transport.ssl.keystore.path: /usr/share/elasticsearch/config/certs/elastic-certificates.p12
            xpack.security.transport.ssl.truststore.path: /usr/share/elasticsearch/config/certs/elastic-certificates.p12
            # xpack.security.http.ssl.enabled: true
            # xpack.security.http.ssl.truststore.path: /usr/share/elasticsearch/config/certs/elastic-certificates.p12
            # xpack.security.http.ssl.keystore.path: /usr/share/elasticsearch/config/certs/elastic-certificates.p12
        ## 環(huán)境變量配置,這里引入上面設置的用戶名、密碼 secret 文件
        extraEnvs:
          - name: ELASTIC_USERNAME
            valueFrom:
              secretKeyRef:
                name: elastic-auth
                key: username
          - name: ELASTIC_PASSWORD
            valueFrom:
              secretKeyRef:
                name: elastic-auth
                key: password

        # ============Service 配置============
        service:
          type: NodePort
          nodePort: "30200"

        現(xiàn)在用上面的 values 文件來安裝:

        # 安裝 master 節(jié)點
        helm install es-master -f values-master.yaml --namespace logging .
        # 安裝 data 節(jié)點
        helm install es-data -f values-data.yaml --namespace logging .
        # 安裝 client 節(jié)點
        helm install es-client -f values-client.yaml --namespace logging .

        安裝 Kibana

        Elasticsearch 集群安裝完成后接下來配置安裝 Kibana

        使用 helm pull 命令拉取 Kibana Chart 包并解壓:

        helm pull elastic/kibana --untar --version 7.12.0
        cd kibana

        創(chuàng)建用于安裝 Kibana 的 values 文件:

        # values-prod.yaml
        ## 指定鏡像與鏡像版本
        image: "kibana"
        imageTag: "7.12.0"

        ## 配置 ElasticSearch 地址
        elasticsearchHosts: "http://elasticsearch-client:9200"

        # ============環(huán)境變量配置============
        ## 環(huán)境變量配置,這里引入上面設置的用戶名、密碼 secret 文件
        extraEnvs:
          - name: "ELASTICSEARCH_USERNAME"
            valueFrom:
              secretKeyRef:
                name: elastic-auth
                key: username
          - name: "ELASTICSEARCH_PASSWORD"
            valueFrom:
              secretKeyRef:
                name: elastic-auth
                key: password

        # ============資源配置============
        resources:
          requests:
            cpu: "500m"
            memory: "1Gi"
          limits:
            cpu: "500m"
            memory: "1Gi"

        # ============配置 Kibana 參數(shù)============
        ## kibana 配置中添加語言配置,設置 kibana 為中文
        kibanaConfig:
          kibana.yml: |
            i18n.locale: "zh-CN"

        # ============Service 配置============
        service:
          type: NodePort
          nodePort: "30601"

        使用上面的配置直接安裝即可:

        helm install kibana -f values-prod.yaml --namespace logging .

        下面是安裝完成后的 ES 集群和 Kibana 資源:

        [root@node2 ~]# kubectl get pods -n logging
        NAME                            READY   STATUS              RESTARTS   AGE
        elasticsearch-client-0          1/1     Running             0          13m
        elasticsearch-data-0            1/1     Running             0          17m
        elasticsearch-master-0          1/1     Running             0          14m
        elasticsearch-master-1          1/1     Running             0          16m
        elasticsearch-master-2          1/1     Running             0          18m
        kibana-kibana-66f97964b-pmqlq   1/1     Running             0          31s
        [root@node2 ~]# kubectl get svc -n logging
        NAME                            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                         AGE
        elasticsearch-client            NodePort    10.102.35.207   <none>        9200:30200/TCP,9300:30078/TCP   33m
        elasticsearch-client-headless   ClusterIP   None            <none>        9200/TCP,9300/TCP               33m
        elasticsearch-data              ClusterIP   10.97.179.233   <none>        9200/TCP,9300/TCP               37m
        elasticsearch-data-headless     ClusterIP   None            <none>        9200/TCP,9300/TCP               37m
        elasticsearch-master            ClusterIP   10.97.35.120    <none>        9200/TCP,9300/TCP               46m
        elasticsearch-master-headless   ClusterIP   None            <none>        9200/TCP,9300/TCP               46m
        kibana-kibana                   NodePort    10.106.97.8     <none>        5601:30601/TCP                  35s

        上面我們安裝 Kibana 的時候指定了 30601 的 NodePort 端口,所以我們可以從任意節(jié)點 http://IP:30601 來訪問 Kibana。

        Kibana 登錄頁面

        我們可以看到會跳轉到登錄頁面,讓我們輸出用戶名、密碼,這里我們輸入上面配置的用戶名 elastic、密碼 ydzsio321 進行登錄。登錄成功后進入如下所示的 Kibana 主頁:

        Kibana 主頁面

        2部署 Fluentd

        Fluentd 是一個高效的日志聚合器,是用 Ruby 編寫的,并且可以很好地擴展。對于大部分企業(yè)來說,F(xiàn)luentd 足夠高效并且消耗的資源相對較少,另外一個工具Fluent-bit更輕量級,占用資源更少,但是插件相對 Fluentd 來說不夠豐富,所以整體來說,F(xiàn)luentd 更加成熟,使用更加廣泛,所以我們這里也同樣使用 Fluentd 來作為日志收集工具。

        工作原理

        Fluentd 通過一組給定的數(shù)據(jù)源抓取日志數(shù)據(jù),處理后(轉換成結構化的數(shù)據(jù)格式)將它們轉發(fā)給其他服務,比如 Elasticsearch、對象存儲等等。Fluentd 支持超過 300 個日志存儲和分析服務,所以在這方面是非常靈活的。主要運行步驟如下:

        • 首先 Fluentd 從多個日志源獲取數(shù)據(jù)
        • 結構化并且標記這些數(shù)據(jù)
        • 然后根據(jù)匹配的標簽將數(shù)據(jù)發(fā)送到多個目標服務去
        fluentd 架構

        配置

        一般來說我們是通過一個配置文件來告訴 Fluentd 如何采集、處理數(shù)據(jù)的,下面簡單和大家介紹下 Fluentd 的配置方法。

        日志源配置

        比如我們這里為了收集 Kubernetes 節(jié)點上的所有容器日志,就需要做如下的日志源配置:

        <source>
        @id fluentd-containers.log
        @type tail # Fluentd 內(nèi)置的輸入方式,其原理是不停地從源文件中獲取新的日志。
        path /var/log/containers/*.log # 掛載的服務器Docker容器日志地址
        pos_file /var/log/es-containers.log.pos
        tag raw.kubernetes.* # 設置日志標簽
        read_from_head true
        <parse> # 多行格式化成JSON
        @type multi_format # 使用 multi-format-parser 解析器插件
        <pattern>
        format json # 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>

        上面配置部分參數(shù)說明如下:

        • id:表示引用該日志源的唯一標識符,該標識可用于進一步過濾和路由結構化日志數(shù)據(jù)
        • type:Fluentd 內(nèi)置的指令,tail 表示 Fluentd 從上次讀取的位置通過 tail 不斷獲取數(shù)據(jù),另外一個是 http 表示通過一個 GET 請求來收集數(shù)據(jù)。
        • path:tail 類型下的特定參數(shù),告訴 Fluentd 采集 /var/log/containers 目錄下的所有日志,這是 docker 在 Kubernetes 節(jié)點上用來存儲運行容器 stdout 輸出日志數(shù)據(jù)的目錄。
        • pos_file:檢查點,如果 Fluentd 程序重新啟動了,它將使用此文件中的位置來恢復日志數(shù)據(jù)收集。
        • tag:用來將日志源與目標或者過濾器匹配的自定義字符串,F(xiàn)luentd 匹配源/目標標簽來路由日志數(shù)據(jù)。

        路由配置

        上面是日志源的配置,接下來看看如何將日志數(shù)據(jù)發(fā)送到 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>
        • match:標識一個目標標簽,后面是一個匹配日志源的正則表達式,我們這里想要捕獲所有的日志并將它們發(fā)送給 Elasticsearch,所以需要配置成**
        • id:目標的一個唯一標識符。
        • type:支持的輸出插件標識符,我們這里要輸出到 Elasticsearch,所以配置成 elasticsearch,這是 Fluentd 的一個內(nèi)置插件。
        • log_level:指定要捕獲的日志級別,我們這里配置成 info,表示任何該級別或者該級別以上(INFO、WARNING、ERROR)的日志都將被路由到 Elsasticsearch。
        • host/port:定義 Elasticsearch 的地址,也可以配置認證信息,我們的 Elasticsearch 不需要認證,所以這里直接指定 host 和 port 即可。
        • logstash_format:Elasticsearch 服務對日志數(shù)據(jù)構建反向索引進行搜索,將 logstash_format 設置為 true,F(xiàn)luentd 將會以 logstash 格式來轉發(fā)結構化的日志數(shù)據(jù)。
        • Buffer:Fluentd 允許在目標不可用時進行緩存,比如,如果網(wǎng)絡出現(xiàn)故障或者 Elasticsearch 不可用的時候。緩沖區(qū)配置也有助于降低磁盤的 IO。

        過濾

        由于 Kubernetes 集群中應用太多,也還有很多歷史數(shù)據(jù),所以我們可以只將某些應用的日志進行收集,比如我們只采集具有 logging=true 這個 Label 標簽的 Pod 日志,這個時候就需要使用 filter,如下所示:

        # 刪除無用的屬性
        <filter kubernetes.**>
          @type record_transformer
          remove_keys $.docker.container_id,$.kubernetes.container_image_id,$.kubernetes.pod_id,$.kubernetes.namespace_id,$.kubernetes.master_url,$.kubernetes.labels.pod-template-hash
        </filter>
        # 只保留具有l(wèi)ogging=true標簽的Pod日志
        <filter kubernetes.**>
          @id filter_log
          @type grep
          <regexp>
            key $.kubernetes.labels.logging
            pattern ^true$
          </regexp>
        </filter>

        安裝

        要收集 Kubernetes 集群的日志,直接用 DasemonSet 控制器來部署 Fluentd 應用,這樣,它就可以從 Kubernetes 節(jié)點上采集日志,確保在集群中的每個節(jié)點上始終運行一個 Fluentd 容器。當然可以直接使用 Helm 來進行一鍵安裝,為了能夠了解更多實現(xiàn)細節(jié),我們這里還是采用手動方法來進行安裝。

        首先,我們通過 ConfigMap 對象來指定 Fluentd 配置文件,新建 fluentd-configmap.yaml 文件,文件內(nèi)容如下:

        kind: ConfigMap
        apiVersion: v1
        metadata:
          name: fluentd-conf
          namespace: logging
        data:
          # 容器日志
          containers.input.conf: |-
            <source>
              @id fluentd-containers.log
              @type tail                              # Fluentd 內(nèi)置的輸入方式,其原理是不停地從源文件中獲取新的日志
              path /var/log/containers/*.log          # Docker 容器日志路徑
              pos_file /var/log/es-containers.log.pos  # 記錄讀取的位置
              tag raw.kubernetes.*                    # 設置日志標簽
              read_from_head true                     # 從頭讀取
              <parse>                                 # 多行格式化成JSON
                # 可以使用我們介紹過的 multiline 插件實現(xiàn)多行日志
                @type multi_format                    # 使用 multi-format-parser 解析器插件
                <pattern>
                  format json                         # 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>

            # 在日志輸出中檢測異常(多行日志),并將其作為一條日志轉發(fā)
            # https://github.com/GoogleCloudPlatform/fluent-plugin-detect-exceptions
            <match raw.kubernetes.**>           # 匹配tag為raw.kubernetes.**日志信息
              @id raw.kubernetes
              @type detect_exceptions           # 使用detect-exceptions插件處理異常棧信息
              remove_tag_prefix raw             # 移除 raw 前綴
              message log
              multiline_flush_interval 5
            </match>

            <filter **>  # 拼接日志
              @id filter_concat
              @type concat                # Fluentd Filter 插件,用于連接多個日志中分隔的多行日志
              key message
              multiline_end_regexp /\n$/  # 以換行符“\n”拼接
              separator ""
            </filter>

            # 添加 Kubernetes metadata 數(shù)據(jù)
            <filter kubernetes.**>
              @id filter_kubernetes_metadata
              @type kubernetes_metadata
            </filter>

            # 修復 ES 中的 JSON 字段
            # 插件地址:https://github.com/repeatedly/fluent-plugin-multi-format-parser
            <filter kubernetes.**>
              @id filter_parser
              @type parser                # multi-format-parser多格式解析器插件
              key_name log                # 在要解析的日志中指定字段名稱
              reserve_data true           # 在解析結果中保留原始鍵值對
              remove_key_name_field true  # key_name 解析成功后刪除字段
              <parse>
                @type multi_format
                <pattern>
                  format json
                </pattern>
                <pattern>
                  format none
                </pattern>
              </parse>
            </filter>

            # 刪除一些多余的屬性
            <filter kubernetes.**>
              @type record_transformer
              remove_keys $.docker.container_id,$.kubernetes.container_image_id,$.kubernetes.pod_id,$.kubernetes.namespace_id,$.kubernetes.master_url,$.kubernetes.labels.pod-template-hash
            </filter>

            # 只保留具有l(wèi)ogging=true標簽的Pod日志
            <filter kubernetes.**>
              @id filter_log
              @type grep
              <regexp>
                key $.kubernetes.labels.logging
                pattern ^true$
              </regexp>
            </filter>

          ###### 監(jiān)聽配置,一般用于日志聚合用 ######
          forward.input.conf: |-
            # 監(jiān)聽通過TCP發(fā)送的消息
            <source>
              @id forward
              @type forward
            </source>

          output.conf: |-
            <match **>
              @id elasticsearch
              @type elasticsearch
              @log_level info
              include_tag_key true
              host elasticsearch-client
              port 9200
              user elastic # FLUENT_ELASTICSEARCH_USER | FLUENT_ELASTICSEARCH_PASSWORD
              password ydzsio321
              logstash_format true
              logstash_prefix k8s
              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 容器日志目錄,收集到數(shù)據(jù)經(jīng)過處理后發(fā)送到 elasticsearch-client:9200 服務。

        然后新建一個 fluentd-daemonset.yaml 的文件,文件內(nèi)容如下:

        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
          namespace: logging
          labels:
            app: fluentd
            kubernetes.io/cluster-service: "true"
        spec:
          selector:
            matchLabels:
              app: fluentd
          template:
            metadata:
              labels:
                app: fluentd
                kubernetes.io/cluster-service: "true"
            spec:
              tolerations:
                - key: node-role.kubernetes.io/master
                  effect: NoSchedule
              serviceAccountName: fluentd-es
              containers:
                - name: fluentd
                  image: quay.io/fluentd_elasticsearch/fluentd:v3.2.0
                  volumeMounts:
                    - name: fluentconfig
                      mountPath: /etc/fluent/config.d
                    - name: varlog
                      mountPath: /var/log
                    - name: varlibdockercontainers
                      mountPath: /var/lib/docker/containers
                      readOnly: true
              terminationGracePeriodSeconds: 30
              volumes:
                - name: fluentconfig
                  configMap:
                    name: fluentd-conf
                - name: varlog
                  hostPath:
                    path: /var/log
                - name: varlibdockercontainers
                  hostPath:
                    path: /var/lib/docker/containers

        我們將上面創(chuàng)建的 fluentd-config 這個 ConfigMap 對象通過 volumes 掛載到了 Fluentd 容器中,另外為了能夠靈活控制哪些節(jié)點的日志可以被收集,所以我們這里還添加了一個 nodSelector 屬性:

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

        意思就是要想采集節(jié)點的日志,那么我們就需要給節(jié)點打上上面的標簽。

        !!! info "提示" 如果你需要在其他節(jié)點上采集日志,則需要給對應節(jié)點打上標簽,使用如下命令:kubectl label nodes node名 beta.kubernetes.io/fluentd-ds-ready=true。

        另外由于我們的集群使用的是 kubeadm 搭建的,默認情況下 master 節(jié)點有污點,所以如果要想也收集 master 節(jié)點的日志,則需要添加上容忍:

        tolerations:
          - operator: Exists

        另外需要注意的地方是,如果更改了 docker 的根目錄,則在 volumes 和 volumeMount 里面都需要更改,保持一致。

        分別創(chuàng)建上面的 ConfigMap 對象和 DaemonSet:

        $ kubectl create -f fluentd-configmap.yaml
        configmap "fluentd-conf" 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" created

        創(chuàng)建完成后,查看對應的 Pods 列表,檢查是否部署成功:

        $ kubectl get pods -n logging
        NAME                            READY   STATUS    RESTARTS   AGE
        elasticsearch-client-0          1/1     Running   0          64m
        elasticsearch-data-0            1/1     Running   0          65m
        elasticsearch-master-0          1/1     Running   0          73m
        fluentd-5rqbq                   1/1     Running   0          60m
        fluentd-l6mgf                   1/1     Running   0          60m
        fluentd-xmfpg                   1/1     Running   0          60m
        kibana-kibana-66f97964b-mdspc   1/1     Running   0          63m

        Fluentd 啟動成功后,這個時候就可以發(fā)送日志到 ES 了,但是我們這里是過濾了只采集具有 logging=true 標簽的 Pod 日志,所以現(xiàn)在還沒有任何數(shù)據(jù)會被采集。

        下面我們部署一個簡單的測試應用, 新建 counter.yaml 文件,文件內(nèi)容如下:

        apiVersion: v1
        kind: Pod
        metadata:
          name: counter
          labels:
            logging: "true" # 一定要具有該標簽才會被采集
        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 會收集到這個日志數(shù)據(jù),在 Kibana 中也就可以找到對應的日志數(shù)據(jù)了,使用 kubectl 工具創(chuàng)建該 Pod:

        $ kubectl create -f counter.yaml
        $ kubectl get pods
        NAME                             READY   STATUS    RESTARTS   AGE
        counter                          1/1     Running   0          9h

        Pod 創(chuàng)建并運行后,回到 Kibana Dashboard 頁面,點擊左側最下面的 Management -> Stack Management,進入管理頁面,點擊左側 Kibana 下面的 索引模式,點擊 創(chuàng)建索引模式 開始導入索引數(shù)據(jù):

        create index

        在這里可以配置我們需要的 Elasticsearch 索引,前面 Fluentd 配置文件中我們采集的日志使用的是 logstash 格式,定義了一個 k8s 的前綴,所以這里只需要在文本框中輸入 k8s-* 即可匹配到 Elasticsearch 集群中采集的 Kubernetes 集群日志數(shù)據(jù),然后點擊下一步,進入以下頁面:

        index config

        在該頁面中配置使用哪個字段按時間過濾日志數(shù)據(jù),在下拉列表中,選擇@timestamp字段,然后點擊 創(chuàng)建索引模式,創(chuàng)建完成后,點擊左側導航菜單中的 Discover,然后就可以看到一些直方圖和最近采集到的日志數(shù)據(jù)了:

        log data

        現(xiàn)在的數(shù)據(jù)就是上面 Counter 應用的日志,如果還有其他的應用,我們也可以篩選過濾:

        counter log data

        我們也可以通過其他元數(shù)據(jù)來過濾日志數(shù)據(jù),比如您可以單擊任何日志條目以查看其他元數(shù)據(jù),如容器名稱,Kubernetes 節(jié)點,命名空間等。

        3安裝 Kafka

        對于大規(guī)模集群來說,日志數(shù)據(jù)量是非常巨大的,如果直接通過 Fluentd 將日志打入 Elasticsearch,對 ES 來說壓力是非常巨大的,我們可以在中間加一層消息中間件來緩解 ES 的壓力,一般情況下我們會使用 Kafka,然后可以直接使用 kafka-connect-elasticsearch 這樣的工具將數(shù)據(jù)直接打入 ES,也可以在加一層 Logstash 去消費 Kafka 的數(shù)據(jù),然后通過 Logstash 把數(shù)據(jù)存入 ES,這里我們來使用 Logstash 這種模式來對日志收集進行優(yōu)化。

        首先在 Kubernetes 集群中安裝 Kafka,同樣這里使用 Helm 進行安裝:

        helm repo add bitnami https://charts.bitnami.com/bitnami
        helm repo update

        首先使用 helm pull 拉取 Chart 并解壓:

        helm pull bitnami/kafka --untar --version 12.17.5
        cd kafka

        這里面我們指定使用一個 StorageClass 來提供持久化存儲,在 Chart 目錄下面創(chuàng)建用于安裝的 values 文件:

        # values-prod.yaml
        ## Persistence parameters
        ##
        persistence:
          enabled: true
          storageClass: "nfs-storage"
          accessModes:
            - ReadWriteOnce
          size: 5Gi
          ## Mount point for persistence
          mountPath: /bitnami/kafka

        # 配置zk volumes
        zookeeper:
          enabled: true
          persistence:
            enabled: true
            storageClass: "nfs-storage"
            accessModes:
              - ReadWriteOnce
            size: 8Gi

        直接使用上面的 values 文件安裝 kafka:

        $ helm install kafka -f values-prod.yaml --namespace logging .
        Release "kafka" does not exist. Installing it now.
        NAME: kafka
        LAST DEPLOYED: Tue Apr 27 18:46:01 2021
        NAMESPACE: logging
        STATUS: deployed
        REVISION: 1
        TEST SUITE: None
        NOTES:
        ** Please be patient while the chart is being deployed **

        Kafka can be accessed by consumers via port 9092 on the following DNS name from within your cluster:

            kafka.logging.svc.cluster.local

        Each Kafka broker can be accessed by producers via port 9092 on the following DNS name(s) from within your cluster:

            kafka-0.kafka-headless.logging.svc.cluster.local:9092

        To create a pod that you can use as a Kafka client run the following commands:

            kubectl run kafka-client --restart='Never' --image docker.io/bitnami/kafka:2.8.0-debian-10-r0 --namespace logging --command -- sleep infinity
            kubectl exec --tty -i kafka-client --namespace logging -- bash

            PRODUCER:
                kafka-console-producer.sh \

                    --broker-list kafka-0.kafka-headless.logging.svc.cluster.local:9092 \
                    --topic test

            CONSUMER:
                kafka-console-consumer.sh \

                    --bootstrap-server kafka.logging.svc.cluster.local:9092 \
                    --topic test \
                    --from-beginning

        安裝完成后我們可以使用上面的提示來檢查 Kafka 是否正常運行:

        $ kubectl get pods -n logging -l app.kubernetes.io/instance=kafka
        kafka-0             1/1     Running   0          7m58s
        kafka-zookeeper-0   1/1     Running   0          7m58s

        用下面的命令創(chuàng)建一個 Kafka 的測試客戶端 Pod:

        $ kubectl run kafka-client --restart='Never' --image docker.io/bitnami/kafka:2.8.0-debian-10-r0 --namespace logging --command -- sleep infinity
        pod/kafka-client created

        然后啟動一個終端進入容器內(nèi)部生產(chǎn)消息:

        # 生產(chǎn)者
        $ kubectl exec --tty -i kafka-client --namespace logging -- bash
        I have no name!@kafka-client:/$ kafka-console-producer.sh --broker-list kafka-0.kafka-headless.logging.svc.cluster.local:9092 --topic test
        >hello kafka on k8s
        >

        啟動另外一個終端進入容器內(nèi)部消費消息:

        # 消費者
        $ kubectl exec --tty -i kafka-client --namespace logging -- bash
        I have no name!@kafka-client:/$ kafka-console-consumer.sh --bootstrap-server kafka.logging.svc.cluster.local:9092 --topic test --from-beginning
        hello kafka on k8s

        如果在消費端看到了生產(chǎn)的消息數(shù)據(jù)證明我們的 Kafka 已經(jīng)運行成功了。

        4Fluentd 配置 Kafka

        現(xiàn)在有了 Kafka,我們就可以將 Fluentd 的日志數(shù)據(jù)輸出到 Kafka 了,只需要將 Fluentd 配置中的 <match> 更改為使用 Kafka 插件即可,但是在 Fluentd 中輸出到 Kafka,需要使用到 fluent-plugin-kafka 插件,所以需要我們自定義下 Docker 鏡像,最簡單的做法就是在上面 Fluentd 鏡像的基礎上新增 kafka 插件即可,Dockerfile 文件如下所示:

        FROM quay.io/fluentd_elasticsearch/fluentd:v3.2.0
        RUN echo "source 'https://mirrors.tuna.tsinghua.edu.cn/rubygems/'" > Gemfile && gem install bundler
        RUN gem install fluent-plugin-kafka -v 0.16.1 --no-document

        使用上面的 Dockerfile 文件構建一個 Docker 鏡像即可,我這里構建過后的鏡像名為 cnych/fluentd-kafka:v0.16.1。接下來替換 Fluentd 的 Configmap 對象中的 <match> 部分,如下所示:

        # fluentd-configmap.yaml
        kind: ConfigMap
        apiVersion: v1
        metadata:
          name: fluentd-conf
          namespace: logging
        data:
          ......
          output.conf: |-
            <match **>
              @id kafka
              @type kafka2
              @log_level info

              # list of seed brokers
              brokers kafka-0.kafka-headless.logging.svc.cluster.local:9092
              use_event_time true
              # topic settings
              topic_key k8slog
              default_topic messages  # 注意,kafka中消費使用的是這個topic
              # buffer settings
              <buffer k8slog>
                @type file
                path /var/log/td-agent/buffer/td
                flush_interval 3s
              </buffer>
              # data type settings
              <format>
                @type json
              </format>
              # producer settings
              required_acks -1
              compression_codec gzip
            </match>

        然后替換運行的 Fluentd 鏡像:

        # fluentd-daemonset.yaml
        image: cnych/fluentd-kafka:v0.16.1

        直接更新 Fluentd 的 Configmap 與 DaemonSet 資源對象即可:

        kubectl apply -f fluentd-configmap.yaml
        kubectl apply -f fluentd-daemonset.yaml

        更新成功后我們可以使用上面的測試 Kafka 客戶端來驗證是否有日志數(shù)據(jù):

        $ kubectl exec --tty -i kafka-client --namespace logging -- bash
        I have no name!@kafka-client:/$ kafka-console-consumer.sh --bootstrap-server kafka.logging.svc.cluster.local:9092 --topic messages --from-beginning
        {"stream":"stdout","docker":{},"kubernetes":{"container_name":"count","namespace_name":"default","pod_name":"counter","container_image":"busybox:latest","host":"node1","labels":{"logging":"true"}},"message":"43883: Tue Apr 27 12:16:30 UTC 2021\n"}
        ......

        5安裝 Logstash

        雖然數(shù)據(jù)從 Kafka 到 Elasticsearch 的方式多種多樣,我們這里還是采用更加流行的 Logstash 方案,上面我們已經(jīng)將日志從 Fluentd 采集輸出到 Kafka 中去了,接下來我們使用 Logstash 來連接 Kafka 與 Elasticsearch 間的日志數(shù)據(jù)。

        首先使用 helm pull 拉取 Chart 并解壓:

        helm pull elastic/logstash --untar --version 7.12.0
        cd logstash

        同樣在 Chart 根目錄下面創(chuàng)建用于安裝的 Values 文件,如下所示:

        # values-prod.yaml
        fullnameOverride: logstash

        persistence:
          enabled: true

        logstashConfig:
          logstash.yml: |
            http.host: 0.0.0.0
            # 如果啟用了xpack,需要做如下配置
            xpack.monitoring.enabled: true
            xpack.monitoring.elasticsearch.hosts: ["http://elasticsearch-client:9200"]
            xpack.monitoring.elasticsearch.username: "elastic"
            xpack.monitoring.elasticsearch.password: "ydzsio321"

        # 要注意下格式
        logstashPipeline:
          logstash.conf: |
            input { kafka { bootstrap_servers => "kafka-0.kafka-headless.logging.svc.cluster.local:9092" codec => json consumer_threads => 3 topics => ["messages"] } }
            filter {}  # 過濾配置(比如可以刪除key、添加geoip等等)
            output { elasticsearch { hosts => [ "elasticsearch-client:9200" ] user => "elastic" password => "ydzsio321" index => "logstash-k8s-%{+YYYY.MM.dd}" } stdout { codec => rubydebug } }

        volumeClaimTemplate:
          accessModes: ["ReadWriteOnce"]
          storageClassName: nfs-storage
          resources:
            requests:
              storage: 1Gi

        其中最重要的就是通過 logstashPipeline 配置 logstash 數(shù)據(jù)流的處理配置,通過 input 指定日志源 kafka 的配置,通過 output 輸出到 Elasticsearch,同樣直接使用上面的 Values 文件安裝 logstash 即可:

        $ helm upgrade --install logstash -f values-prod.yaml --namespace logging .
        Release "logstash" does not exist. Installing it now.
        NAME: logstash
        LAST DEPLOYED: Tue Apr 27 20:22:45 2021
        NAMESPACE: logging
        STATUS: deployed
        REVISION: 1
        TEST SUITE: None
        NOTES:
        1. Watch all cluster members come up.
          $ kubectl get pods --namespace=logging -l app=logstash -w

        安裝啟動完成后可以查看 logstash 的日志:

        $ logstash kubectl get pods --namespace=logging -l app=logstash
        NAME         READY   STATUS    RESTARTS   AGE
        logstash-0   1/1     Running   0          2m8s
        $ kubectl logs -f logstash-0 -n logging
        ......
        {
        "docker" => {},
        "stream" => "stdout",
        "message" => "46921: Tue Apr 27 13:07:15 UTC 2021\n",
        "kubernetes" => {
                    "host" => "node1",
                  "labels" => {
            "logging" => "true"
        },
                "pod_name" => "counter",
        "container_image" => "busybox:latest",
          "container_name" => "count",
          "namespace_name" => "default"
        },
        "@timestamp" => 2021-04-27T13:07:15.761Z,
        "@version" => "1"
        }

        由于我們啟用了 debug 日志調(diào)試,所以我們可以在 logstash 的日志中看到我們采集的日志消息,到這里證明我們的日志數(shù)據(jù)就獲取成功了。

        現(xiàn)在我們可以登錄到 Kibana 可以看到有如下所示的索引數(shù)據(jù)了:

        然后同樣創(chuàng)建索引模式,匹配上面的索引即可:

        創(chuàng)建完成后就可以前往發(fā)現(xiàn)頁面過濾日志數(shù)據(jù)了:

        到這里我們就實現(xiàn)了一個使用 Fluentd+Kafka+Logstash+Elasticsearch+Kibana 的 Kubernetes 日志收集工具棧,這里我們完整的 Pod 信息如下所示:

        $ kubectl get pods -n logging
        NAME                            READY   STATUS    RESTARTS   AGE
        elasticsearch-client-0          1/1     Running   0          128m
        elasticsearch-data-0            1/1     Running   0          128m
        elasticsearch-master-0          1/1     Running   0          128m
        fluentd-6k52h                   1/1     Running   0          61m
        fluentd-cw72c                   1/1     Running   0          61m
        fluentd-dn4hs                   1/1     Running   0          61m
        kafka-0                         1/1     Running   3          134m
        kafka-client                    1/1     Running   0          125m
        kafka-zookeeper-0               1/1     Running   0          134m
        kibana-kibana-66f97964b-qqjgg   1/1     Running   0          128m
        logstash-0                      1/1     Running   0          13m

        當然在實際的工作項目中還需要我們根據(jù)實際的業(yè)務場景來進行參數(shù)性能調(diào)優(yōu)以及高可用等設置,以達到系統(tǒng)的最優(yōu)性能。


        本文為 《Kubernetes 進階訓練營》課程文檔,需要完整 YAML 文件的可以添加我微信 iEverything 獲取。

        另外我們的平臺所有課程正在進行51優(yōu)惠活動,可以點擊下面圖片了解詳情。


        瀏覽 67
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            午夜Av 入18 在线 | 草在线视频免费观看 | 欧美日韩国产综合一区二区 | 日韩欧美一区二区一幕 | 韩国r级露器官真做av | 国语自产少妇精品视频蜜桃 | 91TS人妖另类精品系列 | 都市乱伦视频 | 欧美久久电影 | 欧美一级亚洲一级 |