k8sEfk快速搭建

1.前言

ELK 相信大家都很熟悉

  • ElasticSearch,分布式搜索和分析引擎。
  • Logstash,实时传输能力的数据收集引擎。
  • Kibana,为 Elasticsearch 提供了分析和可视化的 Web 平台。它可以在 Elasticsearch 的索引中查找,交互数据,并生成各种维度表格、图形。

这里我们把Logstash 替换成 Fluentd,进行部署

1.1 logstash 和 fluentd 对比

名称 优点 缺点
logstash Logstash 主要的有点就是它的灵活性,主要因为它有很多插件,详细的文档以及直白的配置格式让它可以在多种场景下应用。我们基本上可以在网上找到很多资源,几乎可以处理任何问题 效能上表现略逊,大数据量的情况下会是个问题
fluentd 效能好,轻量 灵活性差

1.2 flutend 相关说明

1.2.1 flutend 介绍

Fluentd 是一个高效的日志聚合器,是用 Ruby 编写的,并且可以很好地扩展。对于大部分企业来说,Fluentd 足够高效并且消耗的资源相对较少,另外一个工具Fluent-bit更轻量级,占用资源更少,但是插件相对 Fluentd 来说不够丰富,所以整体来说,Fluentd 更加成熟,使用更加广泛,所以我们这里也同样使用 Fluentd 来作为日志收集工具

1.2.2 工作原理

Fluentd 通过一组给定的数据源抓取日志数据,处理后(转换成结构化的数据格式)将它们转发给其他服务,比如 Elasticsearch、对象存储等等。Fluentd 支持超过300个日志存储和分析服务,所以在这方面是非常灵活的。主要运行步骤如下:

  • 首先 Fluentd 从多个日志源获取数据
  • 结构化并且标记这些数据
  • 然后根据匹配的标签将数据发送到多个目标服务去
1.2.3 架构图

k8sEfk快速搭建_第1张图片

2.部署

本次搭建的环境是测试环境,一个主节点,四个工作节点

  • Kubernetes:v1.17.3
  • Kibana镜像 :kibana:7.6.2
  • Elasticsearch镜像:elasticsearch:7.6.2
  • Fluentd 镜像:willdockerhub/fluentd-elasticsearch:v2.3.2

2.1 Elasticsearch 相关

2.1.1 es-statefulset.yaml配置

apiVersion: apps/v1
kind: StatefulSet
metadata:
 name: es
 namespace: logging
spec:
 serviceName: elasticsearch
 replicas: 3
 selector:
   matchLabels:
     app: elasticsearch
 template:
   metadata:
     labels: 
       app: elasticsearch
   spec:
     nodeSelector:
       es: log
     initContainers:
     - name: increase-vm-max-map
       image: busybox
       command: ["sysctl", "-w", "vm.max_map_count=262144"]
       securityContext:
         privileged: true
     - name: increase-fd-ulimit
       image: busybox
       command: ["sh", "-c", "ulimit -n 65536"]
       securityContext:
         privileged: true
     containers:
     - name: elasticsearch
       image: elasticsearch:7.6.2
       ports:
       - name: rest
         containerPort: 9200
       - name: inter
         containerPort: 9300
       resources:
         limits:
           cpu: 1000m
         requests:
           cpu: 1000m
       volumeMounts:
       - name: elasticsearch-logging
         mountPath: /usr/share/elasticsearch/data
       env:
       - name: cluster.name
         value: k8s-logs
       - name: node.name
         valueFrom:
           fieldRef:
             fieldPath: metadata.name
       - name: cluster.initial_master_nodes
         value: "es-0,es-1,es-2"
       - name: discovery.zen.minimum_master_nodes
         value: "2"
       - name: discovery.seed_hosts
         value: "elasticsearch"
       - name: ES_JAVA_OPTS
         value: "-Xms512m -Xmx512m"
       - name: network.host
         value: "0.0.0.0"
 volumeClaimTemplates:
 - metadata:
     name: elasticsearch-logging
     annotations:
       volume.beta.kubernetes.io/storage-class: course-nfs-storage
   spec:
     accessModes: [ "ReadWriteOnce" ]
     resources:
       requests:
         storage: 1Gi      

关于volumeClaimTemplates相关的内容,我是参考 简书的一个博客

2.1.2 es-svc.yaml配置

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

2.1.3 安装es相关配置文件

$ kubectl create -f es-statefulset.yaml -n logging
$ kubectl create -f es-svc.yaml -n logging

$ kubectl get pods -n logging
NAME                      READY   STATUS    RESTARTS   AGE
es-0                      1/1     Running   0          6h9m
es-1                      1/1     Running   0          6h9m
es-2                      1/1     Running   0          6h8m

ps node label

$ kubectl get svc -n logging
NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGE
elasticsearch   ClusterIP   None                    9200/TCP,9300/TCP   6h17m

2.1.4 测试es

$ kubectl port-forward es-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

正常应该是可以看到该信息的

2.2 Kibana 相关

2.2.1 kibana.yaml配置文件

apiVersion: v1
kind: Service
metadata:
  name: kibana
  namespace: logging
  labels:
    app: kibana
spec:
  ports:
  - port: 5601
  type: NodePort
  selector:
    app: kibana

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kibana
  namespace: logging
  labels:
    app: kibana
spec:
  selector:
    matchLabels:
      app: kibana
  template:
    metadata:
      labels:
        app: kibana
    spec:
      nodeSelector:
        es: log
      containers:
      - name: kibana
        image: kibana:7.6.2
        resources:
          limits:
            cpu: 1000m
          requests:
            cpu: 1000m
        env:
        - name: ELASTICSEARCH_HOSTS
          value: http://elasticsearch:9200
        ports:
        - containerPort: 5601

2.2.2 部署kibana相关yaml

$ kubectl create -f kibana.yaml -n logging

$ kubectl get pods -n logging
NAME                      READY   STATUS    RESTARTS   AGE
es-0                      1/1     Running   0          6h9m
es-1                      1/1     Running   0          6h9m
es-2                      1/1     Running   0          6h8m
kibana-7fc6d9dcbf-9twkd   1/1     Running   0          5h22m

$ kubectl get svc -n logging
NAME                    TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
elasticsearch   ClusterIP   None                    9200/TCP,9300/TCP   6h9m
kibana          NodePort    10.108.169.43           5601:32355/TCP      5h14m

2.2.5 测试kibana

在浏览器中输入 访问 10.104.61.249:32355 即可打开kibana 即可

2.3 flutend相关

2.3.1 fluentd-es-configmap.yaml

  
kind: ConfigMap
apiVersion: v1
metadata:
  name: fluentd-es-config-v0.2.0
  namespace: logging
  labels:
    addonmanager.kubernetes.io/mode: Reconcile
data:
  system.conf: |-
    
      root_dir /tmp/fluentd-buffers/
    
  containers.input.conf: |-
    # This configuration file for Fluentd / td-agent is used
    # to watch changes to Docker log files. The kubelet creates symlinks that
    # capture the pod name, namespace, container name & Docker container ID
    # to the docker logs for pods in the /var/log/containers directory on the host.
    # If running this fluentd configuration in a Docker container, the /var/log
    # directory should be mounted in the container.
    #
    # These logs are then submitted to Elasticsearch which assumes the
    # installation of the fluent-plugin-elasticsearch & the
    # fluent-plugin-kubernetes_metadata_filter plugins.
    # See https://github.com/uken/fluent-plugin-elasticsearch &
    # https://github.com/fabric8io/fluent-plugin-kubernetes_metadata_filter for
    # more information about the plugins.
    #
    # Example
    # =======
    # A line in the Docker log file might look like this JSON:
    #
    # {"log":"2014/09/25 21:15:03 Got request with path wombat\n",
    #  "stream":"stderr",
    #   "time":"2014-09-25T21:15:03.499185026Z"}
    #
    # The time_format specification below makes sure we properly
    # parse the time format produced by Docker. This will be
    # submitted to Elasticsearch and should appear like:
    # $ curl 'http://elasticsearch-logging:9200/_search?pretty'
    # ...
    # {
    #      "_index" : "logstash-2014.09.25",
    #      "_type" : "fluentd",
    #      "_id" : "VBrbor2QTuGpsQyTCdfzqA",
    #      "_score" : 1.0,
    #      "_source":{"log":"2014/09/25 22:45:50 Got request with path wombat\n",
    #                 "stream":"stderr","tag":"docker.container.all",
    #                 "@timestamp":"2014-09-25T22:45:50+00:00"}
    #    },
    # ...
    #
    # The Kubernetes fluentd plugin is used to write the Kubernetes metadata to the log
    # record & add labels to the log record if properly configured. This enables users
    # to filter & search logs on any metadata.
    # For example a Docker container's logs might be in the directory:
    #
    #  /var/lib/docker/containers/997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b
    #
    # and in the file:
    #
    #  997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b-json.log
    #
    # where 997599971ee6... is the Docker ID of the running container.
    # The Kubernetes kubelet makes a symbolic link to this file on the host machine
    # in the /var/log/containers directory which includes the pod name and the Kubernetes
    # container name:
    #
    #    synthetic-logger-0.25lps-pod_default_synth-lgr-997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b.log
    #    ->
    #    /var/lib/docker/containers/997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b/997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b-json.log
    #
    # The /var/log directory on the host is mapped to the /var/log directory in the container
    # running this instance of Fluentd and we end up collecting the file:
    #
    #   /var/log/containers/synthetic-logger-0.25lps-pod_default_synth-lgr-997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b.log
    #
    # This results in the tag:
    #
    #  var.log.containers.synthetic-logger-0.25lps-pod_default_synth-lgr-997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b.log
    #
    # The Kubernetes fluentd plugin is used to extract the namespace, pod name & container name
    # which are added to the log message as a kubernetes field object & the Docker container ID
    # is also added under the docker field object.
    # The final tag is:
    #
    #   kubernetes.var.log.containers.synthetic-logger-0.25lps-pod_default_synth-lgr-997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b.log
    #
    # And the final log record look like:
    #
    # {
    #   "log":"2014/09/25 21:15:03 Got request with path wombat\n",
    #   "stream":"stderr",
    #   "time":"2014-09-25T21:15:03.499185026Z",
    #   "kubernetes": {
    #     "namespace": "default",
    #     "pod_name": "synthetic-logger-0.25lps-pod",
    #     "container_name": "synth-lgr"
    #   },
    #   "docker": {
    #     "container_id": "997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b"
    #   }
    # }
    #
    # This makes it easier for users to search for logs by pod name or by
    # the name of the Kubernetes container regardless of how many times the
    # Kubernetes pod has been restarted (resulting in a several Docker container IDs).
    # Json Log Example:
    # {"log":"[info:2016-02-16T16:04:05.930-08:00] Some log text here\n","stream":"stdout","time":"2016-02-17T00:04:05.931087621Z"}
    # CRI Log Example:
    # 2016-02-17T00:04:05.931087621Z stdout F [info:2016-02-16T16:04:05.930-08:00] Some log text here
    
      @id fluentd-containers.log
      @type tail
      path /var/log/containers/*.log
      pos_file /var/log/es-containers.log.pos
      tag raw.kubernetes.*
      read_from_head true
      
        @type multi_format
        
          format json
          time_key time
          time_format %Y-%m-%dT%H:%M:%S.%NZ
        
        
          format /^(?
      
    
    # Detect exceptions in the log output and forward them as one log entry.
    
      @id raw.kubernetes
      @type detect_exceptions
      remove_tag_prefix raw
      message log
      stream stream
      multiline_flush_interval 5
      max_bytes 500000
      max_lines 1000
    
    # Concatenate multi-line logs
    
      @id filter_concat
      @type concat
      key message
      multiline_end_regexp /\n$/
      separator ""
    
    # Enriches records with Kubernetes metadata
    
      @id filter_kubernetes_metadata
      @type kubernetes_metadata
    
    
    
      @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,$.kubernetes.pod_name,$.stream,$.tag
    
   
    
      @id filter_log
      @type grep
      
        key $.kubernetes.labels.logging
        pattern ^true$
      
        
    # Fixes json fields in Elasticsearch
    
      @id filter_parser
      @type parser
      key_name log
      reserve_data true
      remove_key_name_field true
      
        @type multi_format
        
          format json
        
        
          format none
        
      
    
  system.input.conf: |-
    # Example:
    # 2015-12-21 23:17:22,066 [salt.state       ][INFO    ] Completed state [net.ipv4.ip_forward] at time 23:17:22.066081
    
      @id minion
      @type tail
      format /^(?

2.3.2 fluentd-es-ds.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: fluentd-es
  namespace: logging
  labels:
    k8s-app: fluentd-es
    addonmanager.kubernetes.io/mode: Reconcile
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: fluentd-es
  labels:
    k8s-app: fluentd-es
    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
    addonmanager.kubernetes.io/mode: Reconcile
subjects:
- kind: ServiceAccount
  name: fluentd-es
  namespace: logging
  apiGroup: ""
roleRef:
  kind: ClusterRole
  name: fluentd-es
  apiGroup: ""
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd-es-v2.3.2
  namespace: logging
  labels:
    k8s-app: fluentd-es
    version: v2.3.2
    addonmanager.kubernetes.io/mode: Reconcile
spec:
  selector:
    matchLabels:
      k8s-app: fluentd-es
      version: v2.3.2
  template:
    metadata:
      labels:
        k8s-app: fluentd-es
        version: v2.3.2
      # 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:
        seccomp.security.alpha.kubernetes.io/pod: 'docker/default'
    spec:
      priorityClassName: system-node-critical
      serviceAccountName: fluentd-es
      containers:
      - name: fluentd-es
        image: willdockerhub/fluentd-elasticsearch:v2.3.2
        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: /home/docker/data/containers
          readOnly: true
        - name: config-volume
          mountPath: /etc/fluent/config.d
        ports:
        - containerPort: 24231
          name: prometheus
          protocol: TCP
        livenessProbe:
          tcpSocket:
            port: prometheus
          initialDelaySeconds: 5
          timeoutSeconds: 10
        readinessProbe:
          tcpSocket:
            port: prometheus
          initialDelaySeconds: 5
          timeoutSeconds: 10
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /home/docker/data/containers
      - name: config-volume
        configMap:
          name: fluentd-es-config-v0.2.0

为了能够灵活控制哪些节点的日志可以被收集,所以这里还添加了一个 nodSelector 属性

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

想采集节点的日志,那么我们就需要给节点打上上面的标签 我是在所有的节点打上标签

$ kubectl label nodes node名 beta.kubernetes.io/fluentd-ds-ready=true

$ kubectl get nodes --show-labels
NAME              STATUS   ROLES    AGE   VERSION   LABELS
v10-104-141-164   Ready       69d   v1.17.3   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/fluentd-ds-ready=true,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=v10-104-141-164,kubernetes.io/os=linux
v10-104-61-249    Ready    master   69d   v1.17.3   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/fluentd-ds-ready=true,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=v10-104-61-249,kubernetes.io/os=linux,node-role.kubernetes.io/master=
v10-104-61-251    Ready       69d   v1.17.3   app=ingress,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/fluentd-ds-ready=true,beta.kubernetes.io/os=linux,es=log,kubernetes.io/arch=amd64,kubernetes.io/hostname=v10-104-61-251,kubernetes.io/os=linux
v10-104-61-252    Ready       69d   v1.17.3   app=ingress,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/fluentd-ds-ready=true,beta.kubernetes.io/os=linux,es=log,kubernetes.io/arch=amd64,kubernetes.io/hostname=v10-104-61-252,kubernetes.io/os=linux
v10-104-61-253    Ready       69d   v1.17.3   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/fluentd-ds-ready=true,beta.kubernetes.io/os=linux,es=log,kubernetes.io/arch=amd64,kubernetes.io/hostname=v10-104-61-253,kubernetes.io/os=linux

这里需要注意的是,docker的根目录:

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

所以上面要获取 docker 的容器目录需要更改成/home/docker/data/containers,这个地方非常重要,当然如果你没有更改 docker 根目录则使用默认的/var/lib/docker/containers目录即可

2.3.3 部署fluentd

$ kubectl create -f fluentd-es-configmap.yaml -n logging
$ kubectl create -f fluentd-es-ds.yaml -n logging
$ kubectl get pods -n logging
NAME                      READY   STATUS    RESTARTS   AGE
es-0                      1/1     Running   0          6h14m
es-1                      1/1     Running   0          6h13m
es-2                      1/1     Running   0          6h13m
fluentd-es-v2.3.2-64phl   1/1     Running   0          5h18m
fluentd-es-v2.3.2-fqtpf   1/1     Running   0          5h18m
fluentd-es-v2.3.2-gjk72   1/1     Running   0          5h18m
fluentd-es-v2.3.2-m8bv5   1/1     Running   0          5h18m
fluentd-es-v2.3.2-rb6z6   1/1     Running   0          5h18m
kibana-7fc6d9dcbf-9twkd   1/1     Running   0          5h26m

2.3.4 测试fluentd

部署一个简单的测试应用,新建 counter.yaml 文件,文件内容如下

apiVersion: v1
kind: Pod
metadata:
  name: counter
  labels:
    logging: "true"  # 一定要具有该标签才会被采集 在fluentd配置文件中有过滤
spec:
  containers:
  - name: count
    image: busybox
    args: [/bin/sh, -c,
            'i=0; while true; do echo "$i: $(date)"; i=$((i+1)); sleep 1; done']

k8sEfk快速搭建_第2张图片
k8sEfk快速搭建_第3张图片

到这里,基本上efk就可以使用了

参考资料:

阳明博客
github单节点的简单项目
快速搭建博客
k8s yaml文件详解
nfs相关资料

项目地址

我这边把这次搭建的相关资料全部放在 项目资料,欢迎大家批评指正。
因为是快速搭建,可能还有之后要完善的地方,后续还会继续更新。

你可能感兴趣的:(k8s)