原生Kibana + Clickhouse构建日志平台

776 阅读5分钟

背景介绍

作为线上定位问题和排查故障的重要手段,日志在可观测领域有着不可替代的作用。因此,日志系统需要追求稳定性、性能、成本、易用性、可扩展性等关键点。

目前公司的日志系统是基于ELK的,支持云主机日志采集、容器日志采集和特殊分类日志的综合采集等功能。但是随着公司的业务发展,日志应用场景逐渐遇到了一些瓶颈:

数据增长和处理需求增加:业务的不断扩张和数据量的增加,原有的日志系统无法满足现有的数据处理需求。数据处理速度变慢,存储空间不足等问题。

数据质量和可靠性要求提高:日志数据对于公司业务和运维至关重要,因此数据质量和可靠性要求越来越高。原有的日志系统存在日志丢失、日志收集慢等问题,需要进行改进。

现状: 运行了多套ES 集群,需要的硬件和维护成本很高,通过扩容的方法去满足业务场景,ES集群会太大会变动不稳定,创建独立集群,也需要更高成本,两者都会使得成本和维护工作量剧增。

鉴于这些问题,开始探索新的日志系统架构,以彻底解决上面的问题。

关键需求

  1. 功能
    高效支持聚合查询
    支持多区域和跨租户查询
  2. 效率和维护
    降低成本,并能处理 10 倍规模的问题
    提高可靠性,简化操作
  3. 从 ELK 平台透明迁移
    兼容性:能够无缝迁移现有的ELK平台的数据,无需进行大量修改
    最好用户可以继续使用类Kibana 来交互分析日志
  4. 增强日志系统的完整性
    高性能采集器:采用高性能的采集器,提高日志收集的速度,降低数据采集延迟。
    并行处理:采用并行处理的方式,同时处理多个日志数据流,提高数据处理速度和效率。

新架构体系

日志存储选择clickhouse的原因

  1. 具有高写入吞吐量
  2. 具有高吞吐量的单个大查询能力
  3. 服务器成本更低
  4. 更稳定,运维成本更低
  5. ClickHouse 采用 SQL 语法,比 ES 的 DSL 更加简单,学习成本更低。

在原生Kibana跟ElasticSearch之间新增一层Proxy,该Proxy来做ElasticSearch跟ClickHouse的语法转换,如图:

image.png

CKibana 该Proxy负责将图表请求转换成ClickHouse语法查询到ClickHouse结果后模拟成ElasticSearch响应返回给Kibana,这样就可以直接在原生Kibana上面展示ClickHouse中的数据

CKibana: github.com/TongchengOp…

K8S on CKibana部署

准备环境

  1. Kibana: 用来提供给业务做UI展示
  2. ElasticSearch: 用来做kibana元数据存储+查询缓存等高级特性
  3. ClickHouse: 日志数据存储
  4. CKibana: 提供Proxy等高级功能,实现让用户直接在原生Kibana上查询ClickHouse数据

部署ElasticSearch

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: elasticsearch
spec:
  serviceName: elasticsearch
  replicas: 1
  selector:
    matchLabels:
      app: elasticsearch
  template:
    metadata:
      labels:
        app: elasticsearch
    spec:
      containers:
      - name: elasticsearch
        image: docker.elastic.co/elasticsearch/elasticsearch:8.12.0
        env:
        - name: discovery.type
          value: single-node
        - name: cluster.name
          value: my-cluster
        - name: xpack.security.enabled
          value: "true"
        - name: network.host
          value: "0.0.0.0"
        - name: ES_JAVA_OPTS
          value: "-Xms512m -Xmx512m"
        ports:
        - containerPort: 9200
---
apiVersion: v1
kind: Service
metadata:
  name: elasticsearch
spec:
  type: NodePort
  ports:
  - port: 9200
    targetPort: 9200
    nodePort: 30200
  selector:
    app: elasticsearch

部署 elasticsearch:

kubectl apply -f elasticsearch.yaml

部署完之后通过如下命令查看 pod 启动情况:

kubectl get pod -n default

查找到 pod 后,通过如下命令进入 elasticsearch 的 pod:

kubectl exec -it elasticsearch-0 -n default -- bash

部署完成后进入 pod 后执行如下命令,根据提示设置初始密码

/usr/share/elasticsearch/bin/elasticsearch-setup-passwords interactive

部署ckibana

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ckibana
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ckibana
  template:
    metadata:
      labels:
        app: ckibana
    spec:
      containers:
      - name: ckibana
        image: ting001/ckibana:1.0.5
        args: [ "java", "-jar","-Dspring.config.location=/wd/application.yaml","app.jar" ]
        imagePullPolicy: IfNotPresent
        resources:
          limits:
            cpu: "5"
            memory: 5Gi
          requests:
            cpu: "1"
            memory: 1Gi
        volumeMounts:
        - name: ckibana-application-config
          mountPath: /wd/application.yaml
          subPath: application.yaml
      volumes:
      - name: ckibana-application-config
        configMap:
          name: ckibana-application-config
      tolerations:
      - effect: NoSchedule
        key: log
        operator: Exists

---
apiVersion: v1
kind: Service
metadata:
  name: ckibana
  namespace: default
  labels:
    app: ckibana
spec:
  selector:
    app: ckibana
  ports:
  - port: 8080
    protocol: TCP
    targetPort: 8080

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: ckibana-application-config
  namespace: default
data:
  application.yaml: |-
    spring:
      application:
        name: ckibana
    server:
      port: 8080
    logging:
      config: classpath:logback-spring.xml
      file:
        path: logs
    metadata-config:
      hosts: http://elasticsearch.default.svc:9200
      headers:
        Authorization: Basic ZWxhc3RpYzpwYXNzd29yZA==  # echo -ne "elastic:password" | base64

部署 ckibana:

kubectl apply -f ckibana.yaml

部署完之后通过如下命令查看 pod 启动情况:

kubectl get pod -n default

查找到 pod 后,通过如下命令进入 ckibana 的 pod:

kubectl exec -it ckibana-5fb6dc98fb-kp5zl -n default -- bash

部署完成后进入 pod 后配置ck连接信息

全量更新配置接口

    curl --location 'localhost:8080/config/all' \
    --header 'Content-Type: application/json' \
    --data '{
        "proxy":{
            "roundAbleMinPeriod":120000,
            "maxTimeRange":86400000,
            "enableMonitoring":true,
            "blackIndexList":[],
            "whiteIndexList":[
                "index1"
            ],
            "es":{
              "host": "http://elasticsearch.default.svc:9200",
              "headers": {
                "Authorization": "yourEsClusterAuthorization"
              }
            },
            "ck":{
                "url": "ip:Port",
                "user": "yourUserName",
                "pass": "yourPass",
                "defaultCkDatabase": "yourCkDatabaseName"
            }
        }
    }'

获取配置接口

    curl --location 'localhost:8080/config/all'

部署kibana

apiVersion: apps/v1
kind: Deployment
metadata:
  name: kibana
spec:
  replicas: 1
  selector:
    matchLabels:
      app: kibana
  template:
    metadata:
      labels:
        app: kibana
    spec:
      containers:
      - name: kibana
        image: docker.elastic.co/kibana/kibana:8.12.0
        env:
        - name: ELASTICSEARCH_HOSTS
          value: "http://ckibana.default.svc:8080"
        - name: NODE_OPTIONS
          value: "--max-old-space-size=1800"
        volumeMounts:
        - name: kibana-config
          mountPath: /usr/share/kibana/config/kibana.yml
          readOnly: true
          subPath: kibana.yml
        ports:
        - containerPort: 5601
      volumes:
      - name: kibana-config
        configMap:
          name: kibana
---
kind: Service
apiVersion: v1
metadata:
  name: kibana
spec:
  selector:
    app: kibana
  ports:
  - protocol: TCP
    port: 5601
    targetPort: 5601
    nodePort: 30011
  type: NodePort

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: kibana
  labels:
    k8s-app: kibana
data:
  kibana.yml: |-
    server.name: kibana
    server.host: "0.0.0.0"
    elasticsearch.hosts: [ "http://ckibana.default.svc:8080" ]
    elasticsearch.username: "kibana"
    elasticsearch.password: "password"
    elasticsearch.ssl.verificationMode: none
    monitoring.ui.container.elasticsearch.enabled: true

部署 kibana:

kubectl apply -f kibana.yaml

部署完之后通过如下命令查看 pod 启动情况:

kubectl get pod -n default

访问kibana

image.png

配置index pattern

image.png

进行查询

image.png

更多配置请参考:tongchengopensource.github.io/ckibana-doc…