在 Kubernetes 上搭建 EFK 日志收集系统(理论)

一、日志收集选型

Kubernetes 中比较流行的日志收集解决方案是 Elasticsearch、Fluentd 和 Kibana(EFK)技术栈,也是官方现在比较推荐的一种方案。

  • EFK(es、fluentd、kibana)

Fluentd:
作为一个很好的 Logstash 替代品,Fluentd 是 DevOps 的最爱,特别是对于 Kubernetes 部署,因为它具有丰富的插件库。与 Logstash 一样,它可以将数据结构化为 JSON,并涉及日志数据处理的所有方面:收集、解析、缓冲和输出跨各种来源和目的地的数据。

主要特点

  • 与库和Kubernetes的良好集成
  • 大量的内置插件,很容易编写新的

chatgpt选择

现在你是架构师,做k8s日志收集的技术选型,请从 filebeat、Logstash 和 Fluentd选择一个采集工具,并给出理由 。

作为架构师,我建议选择 Fluentd 作为 Kubernetes 日志收集工具。以下是一些理由:

成熟稳定:Fluentd 是一个成熟且广泛使用的开源项目,已经在许多生产环境中得到验证。它有一个活跃的社区支持,并且有大量的插件和扩展可用。

灵活性和可定制性:Fluentd 提供了高度灵活的配置选项,可以根据需求进行定制。你可以通过配置文件定义日志的输入、过滤和输出规则,以适应各种复杂的日志处理场景。

多种输入和输出插件:Fluentd 支持多种输入和输出插件,可以与各种存储和分析系统集成。无论是将日志发送到 Elasticsearch、Kafka、S3 还是其他目标,都可以通过简单的配置实现。

跨平台支持:Fluentd 可以在不同的操作系统和云平台上运行,包括 Linux、Windows、Docker 和 Kubernetes。这使得它非常适合在多样化的环境中进行日志收集和处理。

社区支持和文档资源丰富:Fluentd 有一个活跃的社区,提供了大量的文档、教程和示例代码,可以帮助你快速上手和解决问题。

当然,选择适合的日志收集工具还要考虑你的具体需求和环境。如果你已经使用 Elastic Stack 或希望与 Elasticsearch 和 Kibana 集成,Logstash 也是一个不错的选择。而 Filebeat 则更适合简单地收集和发送日志文件。

综上所述,基于 Fluentd 的稳定性、灵活性和丰富的插件生态系统,我建议选择 Fluentd 作为 Kubernetes 日志收集工具。

二、k8s 集群应该采集哪些日志?

K8S 系统的组件日志:apiserver、scheduler、kubelet
K8S 集群里面部署的应用程序日志

在 Kubernetes 集群上运行多个服务和应用程序时,日志收集系统可以帮助你快速分类和分析由Pod 生成的大量日志数据。

Kubernetes 中比较流行的日志收集解决方案是 Elasticsearch、Fluentd 和 Kibana(EFK)技术栈,也是官方推荐的一种方案。
在 k8s 集群中应用一般有如下日志输出方式:
直接遵循 docker 官方建议把日志输出到标准输出或者标准错误输出输出日志到容器内指定目录中,应用直接发送日志给日志收集系统。

我们构建的带有elasticsearch插件的fluentd以DaemonSet的形式部署在每一个节点上,主要收集如下几种日志:

  • containers log: /var/log/containers/*.log
  • docker log: /var/log/docker.log
  • kubelet log: /var/log/kubelet.log
  • kube-proxy log: /var/log/kube-proxy.log
  • kube-apiserver log: /var/log/kube-apiserver.log
  • kube-controller-manager log: /var/log/kube-controller-manager.log
  • kube-scheduler log: /var/log/kube-scheduler.log

三、实践

1、创建pv

创建 pv 文件

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-nfs
    namespace: logging
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: nfs-es
  nfs:
    path: /data/nfs_data
    server: 172.26.40.165

第二步:执行yaml文件创建此PV;注意PV的成功跟后端存储正不正常没什么关系。

 > kubectl apply -f es-pv.yaml

2、创建pvc

创建一个PVC挂载到Pod#
注意:pvc要和Pod在同一个命名空间,而pv不需要;因为都是在默认空间,就没指定,需要注意

第一步:编写一个PVC的yaml文件,es-pvc.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: es-pv-claim
  namespace: logging
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 3Gi  # 小于等于pv
  storageClassName: nfs-es

第二步:执行yaml文件创建PVC;从下面这个状态来看已经绑定成功了,绑定了pv-nfs,类型为nfs-es,都是前面指定的。

> kubectl apply -f es-pvc.yaml
> [root@master-165 k8sYaml]# kubectl get pv -n logging
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                                         STORAGECLASS   REASON   AGE
pv-nfs                                     5Gi        RWO            Recycle          Bound    logging/es-pv-claim                           nfs-es                  15m

[root@master-165 k8sYaml]# kubectl get pvc -n logging
NAME          STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
es-pv-claim   Bound    pv-nfs   5Gi        RWO            nfs-es         5m8s

第三步,创建pod,
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: arm64v8/elasticsearch:7.17.10
        ports:
        - name: rest
          containerPort: 9200
        - name: inter
          containerPort: 9300
        resources:
          limits:
            cpu: 1000m
          requests:
            cpu: 1000m
        volumeMounts:
        - name: data-storage
          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"

由于系统是 arm架构的,所以需要拉取arm架构的镜像:

# https://hub.docker.com/r/arm64v8/elasticsearch/
# docker pull arm64v8/elasticsearch:7.17.10

给部署节点打标签:

# ①、查看集群节点标签,确保node有这个labe
kubectl get nodes --show-labels | grep 'es=log'

# ②、添加集群节点标签(如果该节点不存在该标签,则需要手动添加)
kubectl label nodes master-165 es=log
kubectl label nodes node-166 es=log
kubectl label nodes bide-167 es=log

部署:

$ kubectl create -f es-statefulset.yaml
statefulset.apps/es created

$ kubectl get pods -n logging

添加成功后,可以看到 logging 命名空间下面的所有的资源对象:

root@master-165 k8sYaml]# kubectl get pods -n logging -o wide
NAME   READY   STATUS             RESTARTS   AGE     IP               NODE         NOMINATED NODE   READINESS GATES
es-0   1/1     Running            0          12m     10.244.69.156    master-165   <none>           <none>
es-1   0/1     CrashLoopBackOff   3          3m19s   10.244.110.105   node-166     <none>           <none>
es-2   1/1     Running            0          94s     10.244.27.16     bide-167     <none>           <none>

# 查看日志
kubectl describe pod es-1 -n logging

单节点部署es

创建hostpath挂载目录:

# 在宿主机/节点创建
mkdir -p /data/es-data

# 赋予权限,否则启动会报错
chmod 777 /data/es-data

es.yaml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: es-pv
spec:
  capacity:
    storage: 5Gi
  accessModes:
  - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  storageClassName: es-host
  hostPath:
    path: /data/es-data
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: es-pvc
  namespace: logging
spec:
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 5Gi
  storageClassName: es-host
---

apiVersion: v1
kind: ConfigMap
metadata:
  name: es
  namespace: logging
data:
  elasticsearch.yml: |
    cluster.name: my-cluster
    node.name: node-1
    node.max_local_storage_nodes: 3
    network.host: 0.0.0.0
    http.port: 9200
    discovery.seed_hosts: ["127.0.0.1", "[::1]"]
    cluster.initial_master_nodes: ["node-1"]
    http.cors.enabled: true
    http.cors.allow-origin: /.*/
---

apiVersion: apps/v1
kind: Deployment
metadata:
  name: elasticsearch
  namespace: logging
spec:
  selector:
    matchLabels:
      name: elasticsearch
  replicas: 1
  template:
    metadata:
      labels:
        name: elasticsearch
    spec:
      initContainers:
      - name: init-sysctl
        image: busybox
        command:
        - sysctl
        - -w
        - vm.max_map_count=262144
        securityContext:
          privileged: true
      containers:
      - name: elasticsearch
        image: arm64v8/elasticsearch:7.17.10
        imagePullPolicy: IfNotPresent
        resources:
          limits:
            cpu: 1000m
            memory: 2Gi
          requests:
            cpu: 100m
            memory: 1Gi
        env:
        - name: ES_JAVA_OPTS
          value: -Xms512m -Xmx512m
        ports:
        - containerPort: 9200
        - containerPort: 9300
        volumeMounts:
        - name: elasticsearch-data
          mountPath: /usr/share/elasticsearch/data/
        - name: es-config
          mountPath: /usr/share/elasticsearch/config/elasticsearch.yml
          subPath: elasticsearch.yml
      volumes:
      - name: elasticsearch-data
        persistentVolumeClaim:
          claimName: es-pvc
        # hostPath:
          # path: /data/es
      - name: es-config
        configMap:
          name: es

---

apiVersion: v1
kind: Service
metadata:
  name: elasticsearch
  namespace: logging
  labels:
    name: elasticsearch
spec:
  type: NodePort
  ports:
  - name:  web-9200
    port: 9200
    targetPort: 9200
    protocol: TCP
    nodePort: 30105
  - name:  web-9300
    port: 9300
    targetPort: 9300
    protocol: TCP
    nodePort: 30106
  selector:
    name: elasticsearch

验证是否部署成功:

[root@master-165 k8sYaml]# kubectl get pods -n logging -o wide
NAME                             READY   STATUS    RESTARTS   AGE     IP              NODE         NOMINATED NODE   READINESS GATES
elasticsearch-5867d7b46f-2vd8n   1/1     Running   6          9m50s   10.244.69.162   master-165   <none>           <none>
[root@master-165 k8sYaml]# curl 10.244.69.162:9200
{
  "name" : "node-1",
  "cluster_name" : "my-cluster",
  "cluster_uuid" : "EIGaunmRQ2-ZqA3YgKQ-DQ",
  "version" : {
    "number" : "7.17.10",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "fecd68e3150eda0c307ab9a9d7557f5d5fd71349",
    "build_date" : "2023-04-23T05:33:18.138275597Z",
    "build_snapshot" : false,
    "lucene_version" : "8.11.1",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}
[root@master-165 k8sYaml]# 

看到上面的信息就表明我们名为my-cluster 的 Elasticsearch 集群成功创建了1个节点:node-1;


相关文章:
在 Kubernetes 上搭建 EFK 日志收集系统
详解 K8s 日志采集最佳实践
kubernetes 日志架构
9款日志采集和管理工具对比,选型必备!
K8S 生产实践-12-共享存储-PV、PVC 和 StorageClass

为者常成,行者常至