Kubernetes EFK 日志收集

2023年 5月 4日 61.9k 0

本次使用的架构为stdout删除日志,通过fluentd 监听/var/log/container目录进行采集。 这是目前比较通用的收集日志架构,当然fluentd也可以更换存filebeat

日志收集架构
Kubernetes集群本身不提供收集日志的解决方案,目前基于ELK日志收集的方案主要有三种

  • 在节点运行一个agent收集日志
  • 在Pod中包含一个sidecar容器来收集日志 (可以参考下面的文章)
  • K8s容器日志实时收集FileBeat+ES+Kibana

    新闻联播老司机

  • 19年4月4日
  • 喜欢:0
  • 浏览:5.4k
  • 直接通过应用程序将日志信息推送到采集后端 (例kafka,es等)
  • 节点级别的日志记录

    节点日志采集
    image_1e78iqakepn317qu1490hsoups9.png-36.7kB
    通过在每个节点上运行一个日志收集的Agent来采集数据,日志采集agent是一种专用工具,用于将日志数据推送到统一的后端。一般来说,这种agent用一个容器来运行,可以访问该节点上所有应用程序容器的日志文件所在目录
    由于这种agent必须在每个节点上运行,所以需要使用DaemonSet控制器运行该应用程序。在节点运行agent,不需要对节点上运行的应用程序进行更改,对应用程序没有任何侵入性,但是这种方法也仅仅适合于日志输出到stdout和stderr的应用程序日志。容器情况下就会把这些日志输出到宿主机上的一个JSON文件之中,同样也可以通过docker log或者kubectl logs查看对应的日志,但是如果是直接写入到文件中,则并不是输出到stdout中并不能通过命令行查看到
    优点: 每个Node仅需部署一个日志收集程序,资源消耗少,对应用无入侵
    因为使用stdout的方式,只需要在宿主机上收集每个容器中的日志/var/log和/var/lib/docker/containers (目录要根据docker info中的dir进行修改)
    容器会将日志转化为JSON格式,是docker中的配置起的作用。
    配置如下

    [root@k8s-01 ~]# docker info
    ...
     Logging Driver: json-file
    ...
    

    集群级日志架构

    以sidecar容器收集日志,如果我们的应用程序的日志是输出到容器中的某个日志文件的话,使用agent节点的方式是无法采集到的
    对于容器输出到文件的方式可以直接在Pod中启动另外一个sidecar容器(例如filebeat)
    使用sidecar有两种方式

  • 通过sidecar容器将应用程序中的日志进行重定向打印,把日志输出到stdout或者stderr
  • 使用sidecar运行日志采集agent
  • 我个人认为,第一种方案和第二种方案都有优缺点,首先说第一种方案缺点是要重定向日志,并且如果日志分的比较细,比如Tomcat启动日志和业务日志是分开的,用第一种就没有优势。第二种缺点就是浪费资源,因为每次启动除了业务日志,还有sidecar日志要跟随业务启动。
    第一种方式
    image_1e78l7jv0be1e9933gaohalqm.png-36kB
    使用sidecar将应用程序日志重定向就可以在接入前面agent采集自动获取日志信息,不需要在配置其他。虽然这个方式可以通过sidecar将应用程序日志转化为stdout输出,但是有一个明显的缺点,就是日志不仅会在原容器文件保留,还会通过stdout输出后占用磁盘空间,这样无形中就增加了一倍磁盘空间
    下面实例是通过sidecar将容器本地的日志重定向到stdout中

    apiVersion: v1
    kind: Pod
    metadata:
      name: counter
    spec:
      containers:
      - name: count
        image: busybox
        args:
        - /bin/sh
        - -c
        - >
          i=0;
          while true;
          do
            echo "$(date) INFO $i" >> /var/log/1.log;     #模拟Tomcat将日志追加到文件中
            i=$((i+1));
            sleep 1;
          done
        volumeMounts:
        - name: varlog
          mountPath: /var/log
      - name: count-log-1
        image: busybox
        args: [/bin/sh, -c, 'tail -n+1 -f /var/log/1.log']   #使用tail -f 打印日志
        volumeMounts:
        - name: varlog
          mountPath: /var/log
      volumes:
      - name: varlog
        emptyDir: {}
    

    创建完成后,我们就可以通过stdout查看到日志

    [root@k8s-01 test]# kubectl logs counter count-log-1 
    Fri May  1 18:33:26 UTC 2020 INFO 0
    Fri May  1 18:33:27 UTC 2020 INFO 1
    Fri May  1 18:33:28 UTC 2020 INFO 2
    Fri May  1 18:33:29 UTC 2020 INFO 3
    Fri May  1 18:33:30 UTC 2020 INFO 4
    Fri May  1 18:33:31 UTC 2020 INFO 5
    Fri May  1 18:33:32 UTC 2020 INFO 6
    

    这时候我们可以在对应Pod宿主机上找到日志文件路径

    [root@k8s-04 2db38c31797ef8830486739d7098ee81261fd36b32b6c6959a0f1cf014ab7f68]# pwd
    /var/lib/docker/containers/2db38c31797ef8830486739d7098ee81261fd36b32b6c6959a0f1cf014ab7f68
    
    
    #前面的日志路径,对应的就是容器ID
    
    [root@k8s-04 2db38c31797ef8830486739d7098ee81261fd36b32b6c6959a0f1cf014ab7f68]# docker ps
    CONTAINER ID        IMAGE                                                        COMMAND                  CREATED             STATUS              PORTS               NAMES
    2db38c31797e        busybox                                                      "/bin/sh -c 'tail -n…"   6 minutes ago       Up 6 minutes                            k8s_count-log-1_counter_default_8c341c58-ec9c-4266-a190-779d514fadf0_0
    77b4c4933cd1        busybox                                                      "/bin/sh -c 'i=0; wh…"   6 minutes ago       Up 6 minutes                            k8s_count_counter_default_8c341c58-ec9c-4266-a190-779d514fadf0_0
    0c7b3e8d7e48        registry.cn-beijing.aliyuncs.com/abcdocker/pause-amd64:3.1   "/pause"                 6 minutes ago       Up 6 minutes                            k8s_POD_counter_default_8c341c58-ec9c-4266-a190-779d514fadf0_0
    

    第二种方式
    image_1e78lncc67go1d7d150s1h1r1ok713.png-24.5kB
    如果觉得第一种过于麻烦,会保留多个日志则可以使用以sidecar运行日志收集直接将日志推送到es中。
    但是sidecar容器中运行日志采集代理程序会导致大量资源消耗,因为每个Pod都需要启动2个容器,业务容器和采集代理程序,另外无法使用kubectl logs命令来访问这些日志,因为他们不受kubelet控制
    下面是Kubernetes官方的一个fluentd的配置文件实例,使用Configmap保存配置文件,通过Pod中启动sidecar容器代理采集agent将日志进行收集然后上传,当然我们也可以使用logstash、filebeat等代替

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: fluentd-config
    data:
      fluentd.conf: |
        <source>
          type tail
          format none
          path /var/log/1.log
          pos_file /var/log/1.log.pos
          tag count.format1
        </source>
    
        <source>
          type tail
          format none
          path /var/log/2.log
          pos_file /var/log/2.log.pos
          tag count.format2
        </source>
    
        <match **>
          type google_cloud
        </match>
    

    上面的配置文件是收集原文件/var/log/1.log和/var/log/2.log的日志数据,然后通过google_cloud这个插件将数据推送到Stackdriver后端去 (也可以推送到es和kafka)
    上面是创建了fluentd的配置文件,下面在Deployment应用中添加2个容器即可

    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)" >> /var/log/1.log;
            echo "$(date) INFO $i" >> /var/log/2.log;
            i=$((i+1));
            sleep 1;
          done
        volumeMounts:
        - name: varlog
          mountPath: /var/log
      - name: count-agent
        image: k8s.gcr.io/fluentd-gcp:1.30
        env:
        - name: FLUENTD_ARGS
          value: -c /etc/fluentd-config/fluentd.conf
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: config-volume
          mountPath: /etc/fluentd-config
      volumes:
      - name: varlog
        emptyDir: {}
      - name: config-volume
        configMap:
          name: fluentd-config
    

    Pod创建完成后,容器count-agent就会从count容器中的日志进行收集然后上传。

    通过应用程序收集日志

    image_1e78m9qkd15j116ne1jt21t5813tt1g.png-18.6kB
    除了上面通过宿主机运行agent采集和在容器中创建sidecar外,还有一种方案就是在代码层面实现,通过代码层面直接将日志输出到对应的后端存储
    更多内容可以参阅官方文档https://v1-15.docs.kubernetes.io/zh/docs/concepts/cluster-administration/logging/
    传统架构图
    image_1e7j4k0mbnku1vlaic18bu1r2s9.png-51.7kB
    本次使用stdout的方式获取日志

  • Kubernetes 1.18
  • Elasticsearch 7.6.2
  • Fluend 3.0.1
  • Kibana 7.6.2
  • 本次环境项目地址: https://github.com/kubernetes/kubernetes/tree/master/cluster/addons/fluentd-elasticsearch
    本次使用镜像均同步至阿里云

    docker.elastic.co/elasticsearch/elasticsearch:7.6.2
    docker.elastic.co/kibana/kibana:7.6.2
    quay.io/fluentd_elasticsearch/fluentd:v3.0.1
    
    同步如下 (有需要直接pull tag标记,或者将yaml中镜像修改下方即可)
    registry.cn-beijing.aliyuncs.com/abcdocker/fluentd:v3.0.1
    registry.cn-beijing.aliyuncs.com/abcdocker/elasticsearch:7.6.2
    registry.cn-beijing.aliyuncs.com/abcdocker/kibana:7.6.2
    

    安装Elasticsearch

    如果已经有ES集群,这里可以不用安装。或者您不想在Kubernetes集群内安装ES,可以直接参考下面的文章。直接在宿主机上安装,和在kubernetes效果一样的。
    https://i4t.com/3552.html
    Kubernetes StatefulSet允许我们为Pod分配一个稳定的标识和持久化存储,Elasticsearch需要稳定的存储来保证Pod在重新调度或者重启后的数据依旧不变,所以要试用StatefulSet来管理Pod
    我这里使用NFS进行模式后端存储,当然使用Ceph或者其他存储也是可以的

    #创建命名空间
    kubectl create namespace elk
    
    
    #使用StatefulSet创建ES集群
    wget down.i4t.com/kubernetes/es/es.yaml
    kubectl apply -f es.yaml
    

    创建es svc

    #这里的service使用无头服务
    wget down.i4t.com/kubernetes/es/es-svc.yaml
    kubect apply -f es-svc.yaml
    

    es.yaml配置文件参数解释

    [root@k8s-01 elk]# cat es.yaml 
    apiVersion: apps/v1
    kind: StatefulSet   #使用statefulset创建Pod
    metadata:
      name: es-test          #pod名称,使用statefulSet创建的Pod是有序号有顺序的
      namespace: elk    #命名空间
    spec:
      serviceName: elasticsearch  #与svc相关联,这可以确保使用以下DNS地址访问Statefulset中的每个pod (es-cluster-[0,1,2].elasticsearch.elk.svc.cluster.local)
      replicas: 3   #副本数量
      selector:
        matchLabels:
          app: elasticsearch    #和pod template配置的labels相匹配
      template:
        metadata:
          labels: 
            app: elasticsearch
        spec:
          #nodeSelector:    #如果需要匹配几台对应的节点可以添加nodeSelect
          #  es: log
          initContainers:   #容器初始化前的操作
          - name: increase-vm-max-map   
            image: busybox
            imagePullPolicy: IfNotPresent
            command: ["sysctl", "-w", "vm.max_map_count=262144"]  #添加mmap计数限制,太低可能造成内存不足的错误
            securityContext:    #仅应用到指定的容器上,并且不会影响Volume
              privileged: true  #运行特权容器
          - name: increase-fd-ulimit
            image: busybox
            imagePullPolicy: IfNotPresent
            command: ["sh", "-c", "ulimit -n 65536"] #修改文件描述符最大数量
            securityContext:
              privileged: true
          containers:
          - name: elasticsearch
            image: docker.elastic.co/elasticsearch/elasticsearch:7.6.2
            imagePullPolicy: IfNotPresent
            ports:
            - name: rest
              containerPort: 9200   #Restapi
            - name: inter
              containerPort: 9300  #节点通信
            resources:
              limits:
                cpu: 1000m   #限制cpu (虚拟环境可以修改小一点)
              requests:
                cpu: 1000m
            volumeMounts:   #容器挂载
            - name: data    #名称
              mountPath: /usr/share/elasticsearch/data  #挂载点
            env:    #设置相关环境变量
            - name: cluster.name  #es集群的名称
              value: k8s-logs
            - name: node.name   #节点名称
              valueFrom:  #通过匹配上面metadata.name来当节点的名称
                fieldRef:
                  fieldPath: metadata.name
            - name: cluster.initial_master_nodes #初始化集群引导,需要是pod的名称
              value: "es-test-0,es-test-1,es-test-2"
            - name: discovery.zen.minimum_master_nodes  #节点数量,高可用集群至少3个主节点,其中2个至少不仅投票节点。https://blog.csdn.net/zuodaoyong/article/details/104719508
              value: "2"
            - name: discovery.seed_hosts #用于es集群中节点互相连接发现
              value: "elasticsearch"
            - name: ES_JAVA_OPTS  #设置Java的内存参数
              value: "-Xms512m -Xmx512m"
            - name: network.host   
              value: "0.0.0.0"
      volumeClaimTemplates:   #定义持久化模板
      - metadata:
          name: data
          labels:
            app: elasticsearch
        spec:
          accessModes: [ "ReadWriteOnce" ]  #访问模式
          storageClassName: managed-nfs-storage  #storageclass对象  (这里我使用nfs创建的storageclass,当然也可以修改为ceph或者本地存储)
          resources:
            requests:
              storage: 30Gi    #pv资源大小
    

    es镜像比较大,如果下载比较慢可以通过我的镜像直接load导入

    wget http://down.i4t.com/kubernetes/es/es.tar
    

    相关参数文档,请阅读官网
    https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html
    文章中引用了NFS和storageclass,需要的可以前往
    StorageClass 配置文档

    持久化存储 StorageClass

    新闻联播老司机

  • 20年2月1日
  • 喜欢:0
  • 浏览:2.2k
  • NFS按照参考

    Kubernetes PV与PVC

    新闻联播老司机

  • 20年1月31日
  • 喜欢:0
  • 浏览:4.9k
  • 查看Pod IP测试Pod是否正常

    [root@k8s-01 elk]# kubectl get pod -n elk -o wide
    NAME   READY   STATUS    RESTARTS   AGE     IP             NODE     NOMINATED NODE   READINESS GATES
    es-test-0   1/1     Running   7          4h5m    172.30.120.2   k8s-01              
    es-test-1   1/1     Running   0          6h20m   172.30.144.3   k8s-05              
    es-test-2   1/1     Running   0          6h19m   172.30.248.3   k8s-04              
    

    我们还可以查看一下pv和pvc是否正常创建和绑定

    [root@k8s-01 ~]# kubectl  get pv,pvc -n elk
    NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                     STORAGECLASS          REASON   AGE
    persistentvolume/pvc-3d622c74-74d5-4f75-934c-bfcdfc22f647   30Gi       RWO            Delete           Bound    elk/data-es-abcdocker-0   managed-nfs-storage            23h
    persistentvolume/pvc-c83d1aad-031d-4b5b-90fd-1467f4fd9997   30Gi       RWO            Delete           Bound    elk/data-es-abcdocker-1   managed-nfs-storage            23h
    persistentvolume/pvc-f85a272a-3e4f-4686-b15f-208008412320   30Gi       RWO            Delete           Bound    elk/data-es-abcdocker-2   managed-nfs-storage            23h
    
    NAME                                        STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS          AGE
    persistentvolumeclaim/data-es-abcdocker-0   Bound    pvc-3d622c74-74d5-4f75-934c-bfcdfc22f647   30Gi       RWO            managed-nfs-storage   23h
    persistentvolumeclaim/data-es-abcdocker-1   Bound    pvc-c83d1aad-031d-4b5b-90fd-1467f4fd9997   30Gi       RWO            managed-nfs-storage   23h
    persistentvolumeclaim/data-es-abcdocker-2   Bound    pvc-f85a272a-3e4f-4686-b15f-208008412320   30Gi       RWO            managed-nfs-storage   23h
    [root@k8s-01 ~]#
    

    使用无头服务创建service
    解释: Headless Service 无头服务 该服务不会分配ClusterIP,也不会通过kube-proxy做反向代理和负载均衡。而是通过DNS提供稳定的网络ID来访问,DNS会将headless service的后端直接解析为PodIP列表,主要提供StatefulSet使用

    [root@k8s-01 elk]# cat es-svc.yaml 
    kind: Service
    apiVersion: v1
    metadata:
      name: elasticsearch
      namespace: elk
      labels:
        app: elasticsearch
    spec:
      selector:
        app: elasticsearch
      clusterIP: None
      ports:
        - port: 9200
          name: rest
        - port: 9300
          name: inter-node
    

    svc参数解释
    指定匹配标签app=elasticsearch,当我们将elasticsearch statefulset与此服务关联时,服务将返回标签带有app=elasticsearch的Elasticsearch Pod的DNS A记录
    查看创建完毕后的pod和svc

    [root@k8s-01 elk]# kubectl apply -f es-svc.yaml 
    [root@k8s-01 elk]# kubectl get all -n elk
    NAME       READY   STATUS    RESTARTS   AGE
    pod/es-test-0   1/1     Running   7          4h8m
    pod/es-test-1   1/1     Running   0          6h23m
    pod/es-test-2   1/1     Running   0          6h22m
    
    NAME                    TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)             AGE
    service/elasticsearch   ClusterIP   None                 9200/TCP,9300/TCP   12m
    
    NAME                  READY   AGE
    statefulset.apps/es   3/3     6h24m
    

    es设置了无头服务和一个稳定的域名.elasticsearch.elk.svc.cluster.local
    接下来可以获取到当前Pod节点的信息

    [root@k8s-01 elk]# curl 172.30.248.3:9200
    {
      "name" : "es-test-2",
      "cluster_name" : "k8s-logs",
      "cluster_uuid" : "UKHhz9ECQaCU3FN-t_sNKA",
      "version" : {
        "number" : "7.6.2",
        "build_flavor" : "default",
        "build_type" : "docker",
        "build_hash" : "ef48eb35cf30adf4db14086e8aabd07ef6fb113f",
        "build_date" : "2020-03-26T06:34:37.794943Z",
        "build_snapshot" : false,
        "lucene_version" : "8.4.0",
        "minimum_wire_compatibility_version" : "6.8.0",
        "minimum_index_compatibility_version" : "6.0.0-beta1"
      },
      "tagline" : "You Know, for Search"
    }
    

    查看集群状态

    $ curl 172.30.248.3:9200/_cluster/state?pretty |less
    
    {
      "cluster_name" : "k8s-logs",
      "cluster_uuid" : "4IXFctNTT0-KONKlD2K-0Q",
      "version" : 40,
      "state_uuid" : "GUTDz1iKQayYyI_4AHHGIA",
      "master_node" : "SSOXZ5n5TFSpL12UtHhX-w",
      "blocks" : { },
      "nodes" : {
        "LA8ToV02TFOrFHGwVyzpeg" : {
          "name" : "es-abcdocker-1",
          "ephemeral_id" : "FYJWZKTrTg6aPRXaefFM8w",
          "transport_address" : "10.244.0.6:9300",
          "attributes" : {
            "ml.machine_memory" : "4128923648",
            "ml.max_open_jobs" : "20",
            "xpack.installed" : "true"
          }
        },
        "tBVCVr4ATzO5juMtgdTtSA" : {
          "name" : "es-abcdocker-0",
          "ephemeral_id" : "65TM1xFXRaGZISaNaV5mFA",
          "transport_address" : "10.244.2.11:9300",
          "attributes" : {
            "ml.machine_memory" : "4128923648",
            "ml.max_open_jobs" : "20",
            "xpack.installed" : "true"
          }
        },
        "SSOXZ5n5TFSpL12UtHhX-w" : {
          "name" : "es-abcdocker-2",
          "ephemeral_id" : "rQp2Bo0-RtWqG2scbGsc_w",
          "transport_address" : "10.244.1.5:9300",
          "attributes" : {
            "ml.machine_memory" : "4128923648",
            "xpack.installed" : "true",
            "ml.max_open_jobs" : "20"
          }
        }
      },
      "metadata" : {
        "cluster_uuid" : "4IXFctNTT0-KONKlD2K-0Q",
    ...
    
    #可以查看到当前集群的数据结构
    

    通过_cat/nodes?v查看集群中所有的节点,这里我们可以看到master节点是es-test-1 (es-test-1为pod名称)

    [root@k8s-01 ~]# curl 10.244.1.5:9200/_cat/nodes?v
    ip          heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
    10.244.0.6            13          97   3    0.01    0.05     0.07 dilm      -      es-abcdocker-1
    10.244.2.11           10          97   2    0.00    0.03     0.00 dilm      -      es-abcdocker-0
    10.244.1.5             7          97   2    0.24    0.17     0.11 dilm      *      es-abcdocker-2
    

    高可用性(HA)群集至少需要三个主节点,其中至少两个不是仅投票节点。即使其中一个节点发生故障,这样的群集也将能够选举一个主节点。

    创建Kibana服务

    和ES一样,kibana也是可以不在kubernetes集群中安装,同样也可以安装在集群外,集群外安装可以查阅之前的ELK文章,这里不过多说明。本次环境在kubernetes中安装

    ELK 二进制安装并收集nginx日志

    新闻联播老司机

  • 19年2月15日
  • 喜欢:0
  • 浏览:2k
  • [root@k8s-01 elk]# cat kibana.yaml 
    apiVersion: v1
    kind: Service
    metadata:
      name: kibana
      namespace: elk
      labels:
        app: kibana
    spec:
      ports:
      - port: 5601
      type: NodePort
      selector:
        app: kibana
    
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: kibana
      namespace: elk
      labels:
        app: kibana
    spec:
      selector:
        matchLabels:
          app: kibana
      template:
        metadata:
          labels:
            app: kibana
        spec:
          containers:
          - name: kibana
            image: docker.elastic.co/kibana/kibana:7.6.2
            resources:
              limits:
                cpu: 200m
              requests:
                cpu: 200m
            env:
            - name: ELASTICSEARCH_HOSTS
              value: http://elasticsearch:9200    #由于是一个headless service,所以该域将解析为3个 Elasticsearch Pod的IP地址列表
            ports:
            - containerPort: 5601
    

    创建Kibana

    [root@k8s-01 elk]# kubectl apply -f  kibana.yaml 
    service/kibana created
    deployment.apps/kibana created
    

    查看创建Pod状态

    [root@k8s-01 elk]# kubectl get pod,svc -n elk
    NAME                          READY   STATUS    RESTARTS   AGE
    pod/es-test-0                 1/1     Running   0          61m
    pod/es-test-1                 1/1     Running   0          62m
    pod/es-test-2                 1/1     Running   0          62m
    pod/kibana-8686d977d7-t9dc5   1/1     Running   0          74m
    
    NAME                    TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)             AGE
    service/elasticsearch   ClusterIP   None                   9200/TCP,9300/TCP   74m
    service/kibana          NodePort    10.254.252.9           5601:31882/TCP      74m
    

    访问Kibana
    任意集群节点:31882
    image_1e7inqabi1vet4n1k5p130la3h9.png-1641.1kB

    Fluentd

    采集日志客户端采用Fluentd来进行采集日志,使用ConfigMap的形式来存储Fluentd的配置。
    这次环境我们日志格式为stdout的方式,日志输出到每个宿主机的/var/log/containers/下面,所以我们Fluentd安装使用DaemonSet方式即可。

    # 参数解释
    
    kind: ConfigMap
    apiVersion: v1
    metadata:
      name: fluentd-config            
      namespace: elk                    #flunentd所属命名空间
    data:
      system.conf: |-
        <system>
          root_dir /tmp/fluentd-buffers/      #系统日志信息
        </system>
      containers.input.conf: |-              #容器输入日志配置信息
    
    
    #日志源配置
        <source>
          @id fluentd-containers.log         #表示引用该日志源的唯一标志符
          @type tail                         #Fluentd内置命令,tail表示Fluentd从上次读取的位置通过tail不断获取数据
          path /var/log/containers/*.log     #日志路径 (docker stdout默认输出路径,如果修改docker引擎配置这里也需要修改)
          pos_file /var/log/es-containers.log.pos  #检查点,将从文件中获取上一次检查数据的时间
          time_format %Y-%m-%dT%H:%M:%S.%NZ   #时间格式
          localtime
          tag raw.kubernetes.*               #用来将日志源与目标或者过滤器匹配的自定义字符串,Fluentd匹配源/目标标签
          format json                        #JSON解析器
          read_from_head true    
        </source>
    
    
    路由配置
       
         <match **>             #标示一个目标,后面是一个匹配日志源的正则表达式,获取所有日志*.*并且全部发送给ES
          @id elasticsearch      #ID唯一标示符
          @type elasticsearch    #支持的输出插件标示符 (本选项为内置命令,除了输出到es,还可以输出到kafka、logstash等)
          @log_level info        #日志级别
          include_tag_key true    
          host elasticsearch    #es地址 (由于我们es为无头服务,地址就是elasticsearch)
          port 9200             #es端口
          logstash_format true    # es服务队日志数据构建反索引进行搜索,设置为true,fluentd将会以logstash格式来转发构建的日志数据
          request_timeout    30s   #超时时间
    
           <buffer>               #缓存配置,fluentd允许在目录不可用时进行缓存
            @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>
    

    创建Fluentd配置文件,文件内容如下

    cat >> fluentd-configmap.yaml <<EOF
    kind: ConfigMap
    apiVersion: v1
    metadata:
      name: fluentd-config
      namespace: elk
    data:
      system.conf: |-
        <system>
          root_dir /tmp/fluentd-buffers/
        </system>
      containers.input.conf: |-
        <source>
          @id fluentd-containers.log
          @type tail                              # Fluentd 内置的输入方式,其原理是不停地从源文件中获取新的日志。
          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 /^(?.+) (?stdout|stderr) [^ ]* (?.*)$/
              time_format %Y-%m-%dT%H:%M:%S.%N%:z
            </pattern>
          </parse>
        </source>
        # 在日志输出中检测异常,并将其作为一条日志转发 
        # 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                       
          stream stream                     #stdout输出
          multiline_flush_interval 5
          max_bytes 500000                   #异常信息最大字节数
          max_lines 1000                     #异常信息最大行数
         </match>
    
          <filter **># 拼接日志
          @id filter_concat
          @type concat                # Fluentd Filter 插件,用于连接多个事件中分隔的多行日志。
          key message
          multiline_end_regexp /n$/  # 以换行符“n”拼接
          separator ""
         </filter>
    
        # 添加 Kubernetes metadata 数据
        <filter kubernetes.**>
          @id filter_kubernetes_metadata      #将获取到的日志转化,添加pod信息、命名空间,labels标签等
          @type kubernetes_metadata
        </filter>
    
        # JSON格式转换,如果原来是json格式,将把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>
    
        # 只保留具有logging=true标签的Pod日志,如果添加只会收集带有logging=true标签的pod
        #<filter kubernetes.**>
        #  @id filter_log
        #  @type grep
        #  <regexp>
        #    key $.kubernetes.labels.logging
        #    pattern ^true$
        #  </regexp>
        #</filter>
    
      ###### 监听配置,一般用于日志聚合用 ######
      forward.input.conf: |-
        # 监听通过TCP发送的消息
        <source>
          @id forward
          @type forward
        </source>
    
      output.conf: |-
        <match **>
          @id elasticsearch
          @type elasticsearch
          @log_level info
          include_tag_key true
          host elasticsearch
          port 9200
          logstash_format true
          logstash_prefix k8s  # 设置 index 前缀为 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>
    EOF
    

    创建fluentd configmap,检查configmap状态

    [root@k8s-01 ~]# kubectl  apply -f fluentd-configmap.yaml
    configmap/fluentd-config created
    
    [root@k8s-01 ~]# kubectl  get configmap  -n elk
    NAME             DATA   AGE
    fluentd-config   4      7s
    

    接下来创建fluentd-daemonset

    cat >fluentd-ds.yaml <<EOF
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: fluentd-es
      namespace: elk
      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: elk
      apiGroup: ""
    roleRef:
      kind: ClusterRole
      name: fluentd-es
      apiGroup: ""
    ---
    apiVersion: apps/v1
    kind: DaemonSet
    metadata:
      name: fluentd-es
      namespace: elk
      labels:
        k8s-app: fluentd-es
    spec:
      selector:
        matchLabels:
          k8s-app: fluentd-es
      template:
        metadata:
          labels:
            k8s-app: fluentd-es
            kubernetes.io/cluster-service: "true"
          # 此注释确保如果节点被驱逐,fluentd不会被驱逐,支持关键的基于 pod 注释的优先级方案。
          annotations:
            scheduler.alpha.kubernetes.io/critical-pod: ''
        spec:
          serviceAccountName: fluentd-es
          containers:
          - name: fluentd-es
            image: quay.io/fluentd_elasticsearch/fluentd:v3.0.1
            env:
            - name: FLUENTD_ARGS
              value: --no-supervisor -q
            resources:
              limits:
                memory: 500Mi
              requests:
                cpu: 100m
                memory: 200Mi
            volumeMounts:
            - name: varlog
              mountPath: /var/log
            - name: varlibdockercontainers
              mountPath: /var/lib/docker/containers      #docker日志存放路径 (默认情况下)
              readOnly: true
            - name: config-volume
              mountPath: /etc/fluent/config.d
          #nodeSelector:    #这里可以设置需要打标签的节点,我们目前只有3台都需要采集,所以可以注释这个标签
          #  beta.kubernetes.io/fluentd-ds-ready: "true"
          tolerations:
          - operator: Exists
          terminationGracePeriodSeconds: 30
          volumes:
          - name: varlog
            hostPath:
              path: /var/log
          - name: varlibdockercontainers
            hostPath:
              path: /var/lib/docker/containers
          - name: config-volume
            configMap:
              name: fluentd-config
    EOF
    

    接下来创建

    kubectl apply -f fluentd-ds.yaml
    

    我们等待一会可以在es中看到fluentd收集的日志索引

    [root@k8s-01 ~]# curl 10.244.0.6:9200/_cat/indices
    green open .kibana_task_manager_1   X54N-pZATVmvH7ZbWTgQRw 1 1  2 2  40.3kb  20.1kb
    green open .apm-agent-configuration NDxFtJcnTl-RjZEpRYnGIA 1 1  0 0    566b    283b
    green open .kibana_1                DhJEAzOGTHqyOOLCoflmzw 1 1 10 1  62.1kb    31kb
    green open k8s-2020.07.15           fqUmr9QeRMq4O29eZHV8LA 1 1 42 0 618.1kb 312.9kb
    
    #其中命名为k8s-*的为我们下一步需要在kibana中添加的选项
    

    访问kibana

    [root@k8s-01 ~]# kubectl  get svc -n elk
    NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGE
    elasticsearch   ClusterIP   None                    9200/TCP,9300/TCP   2d2h
    kibana          NodePort    10.105.190.52           5601:31978/TCP      2d2h
    
    #在浏览器中访问任意节点IP:31978端口
    

    进入到kibana中,我们配置索引
    设置-->索引-->配置索引
    22222.png-221.9kB
    这里已经获取到我们在es中查看的索引了,点击下一步
    image_1ed932r18lg21135168emu6c01e.png-91kB
    在该页面中配置使用哪个字段按时间过滤日志数据,在下拉列表中,选择@timestamp字段,然后点击Create index pattern,创建完成后,点击左侧导航菜单中的Discover,然后就可以看到一些直方图和最近采集到的日志数据了
    image_1ed9354gp1m1qhi9p6qirf9ev1r.png-122.3kB
    添加完成后,我们点击左边Discover就可以看到数据
    image_1ed938ikopvdcdl9ebhhljs28.png-133.1kB
    这边还可以筛选过滤一下,只看几个字段的数据
    image_1ed93oo4spti18pni19p4418o02l.png-299.4kB
    测试数据

    cat<<EOF | kubectl apply -f -
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx
    spec:
      selector:
        matchLabels:
          app: nginx
      template:
        metadata:
          labels:
            app: nginx
        spec:
          containers:
          - image: nginx:alpine
            name: nginx
            ports:
            - containerPort: 80
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: nginx
    spec:
      selector:
        app: nginx
      type: NodePort
      ports:
        - protocol: TCP
          port: 80
          targetPort: 80
          nodePort: 30001
    

    接下来我们直接访问宿主机的IP:30001端口,查看nginx日志

    [root@k8s-01 ~]# kubectl  get svc
    NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
    kubernetes   ClusterIP   10.96.0.1                443/TCP        2d6h
    nginx        NodePort    10.102.132.199           80:30001/TCP   2d6h
    

    image_1ed943erih6l3e0urg1bf7it43f.png-128.5kB

    相关文章:

    1. Kubernetes 1.14 二进制集群安装
    2. Kuerbernetes 1.11 集群二进制安装
    3. Kubenetes 1.13.5 集群二进制安装
    4. CentOS 7 ETCD集群配置大全

    相关文章

    KubeSphere 部署向量数据库 Milvus 实战指南
    探索 Kubernetes 持久化存储之 Longhorn 初窥门径
    征服 Docker 镜像访问限制!KubeSphere v3.4.1 成功部署全攻略
    那些年在 Terraform 上吃到的糖和踩过的坑
    无需 Kubernetes 测试 Kubernetes 网络实现
    Kubernetes v1.31 中的移除和主要变更

    发布评论