FLG (Fluentd, Loki Grafana)


FLG (Fluentd, Loki Grafana) Stack para la observabilidad de aplicaciones

Published on May 14, 2023 by Lucho

post kubernetes devops fluentd loki grafana FLG

6 min READ

FLG

En esta publicación, discutiemos una alternativa para el monitoreo de registros de aplicaciones como seria FLG.

Fluentd es un controlador de registros de código abierto cuya arquitectura conectable lo diferencia de otras alternativas como Logstash, Datadog. Su capa de registro unificada le permite conectarse fácilmente a varias fuentes de datos y destinos de salida. Esto es evidente en la gran cantidad de complementos, filtros, analizadores disponibles para administrar datos/registros de una gran cantidad de fuentes de entrada (por ejemplo, registros de aplicaciones, Syslog, MQTT, Docker, Amazon Cloudwatch, Twitter) y enviarlos a una lista igualmente extensa de salida objetivos (por ejemplo, Elasticsearch, Amazon S3, Google BigQuery, Graphite, Zabbix, Splunk, Sumologic, slack).

Con el siguiente manifiesto se puede desplegar una version sencilla del stack FLG:

---
# source: grafana.yaml
apiVersion: v1
kind: Service
metadata:
  name: grafana
  labels:
    app: grafana
spec:
  type: NodePort
  ports:
    - name: web
      protocol: TCP
      port: 80
      targetPort: 3000
      nodePort: 31000
  selector:
      app: grafana
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: grafana
  labels:
    app: grafana
spec:
  selector:
    matchLabels:
      app: grafana
  replicas: 1
  template:
    metadata:
      labels:
        app: grafana
    spec:
      containers:
      - name: grafana-container
        image: grafana/grafana:latest
        imagePullPolicy: Always
---
# source: loki.yaml
apiVersion: v1
kind: Service
metadata:
  name: loki
  labels:
    app: loki
spec:
  type: ClusterIP
  ports:
    - name: loki
      protocol: TCP
      port: 3100
      targetPort: 3100
  selector:
      app: loki
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: loki
  labels:
    app: loki
spec:
  selector:
    matchLabels:
      app: loki
  replicas: 1
  template:
    metadata:
      labels:
        app: loki
    spec:
      restartPolicy: Always
      containers:
      - name: loki
        image: grafana/loki:latest
        imagePullPolicy: Always
        args: ["-config.file=/etc/loki/local-config.yaml"]
        stdin: true
        tty: true
        ports:
          - containerPort: 3100
---
# component: fluentd.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: fluentd
  labels:
    app: fluentd
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: fluentd
  labels:
    app: fluentd
rules:
- apiGroups:
  - ""
  resources:
  - pods
  - namespaces
  verbs:
  - get
  - list
  - watch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: fluentd
roleRef:
  kind: ClusterRole
  name: fluentd
  apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
  name: fluentd
  namespace: default # <- change to your preferred workspace 
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd
  labels:
    app: fluentd
    kubernetes.io/cluster-service: "true"
spec:
  selector:
    matchLabels:
      app: fluentd
      kubernetes.io/cluster-service: "true"
  template:
    metadata:
      labels:
        app: fluentd
        kubernetes.io/cluster-service: "true"
    spec:
      serviceAccount: fluentd
      serviceAccountName: fluentd
      tolerations:
      - key: node-role.kubernetes.io/master
        effect: NoSchedule
      containers:
      - name: fluentd
        imagePullPolicy: Always
        image: fluent/fluentd-kubernetes-daemonset:v1.4-debian-forward-1
        command: 
          - /bin/sh 
          - '-c'
          - >
            fluent-gem i fluent-plugin-grafana-loki-licence-fix ;
            tini /fluentd/entrypoint.sh;
        resources:
          limits:
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
        - name: config
          mountPath: /fluentd/etc
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers
      - name: config
        configMap:
          name: fluentd-logging
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: fluentd-logging
  labels:
    app: fluentd-logging
data: 
  fluent.conf: |
    <source>
      @type tail
      @id in_tail_container_logs
      path /var/log/containers/*.log
      pos_file /var/log/fluentd-containers.log.pos
      tag kubernetes.*
      read_from_head true

      #<parse>
      #  @type cri
      #  merge_cri_fields false
      #  <parse>
      #    @type json
      #    time_key time
      #    time_format %Y-%m-%dT%H:%M:%S.%L%z
      #  </parse>
      #</parse>

      <parse>
        @type json
        time_format %Y-%m-%dT%H:%M:%S.%NZ
      </parse>
    </source>

    <match fluentd.**>
      @type null
    </match>

    <match kubernetes.var.log.containers.**fluentd**.log>
      @type null
    </match>

    <filter kubernetes.**>
      @type kubernetes_metadata
      @id filter_kube_metadata
    </filter>

    <filter kubernetes.var.log.containers.**>
      @type record_transformer
      enable_ruby
      #remove_keys kubernetes, docker
      <record>
        app ${ record.dig("kubernetes", "labels", "app") }
        job ${ record.dig("kubernetes", "labels", "app") }
        namespace ${ record.dig("kubernetes", "namespace_name") }
        pod ${ record.dig("kubernetes", "pod_name") }
        container ${ record.dig("kubernetes", "container_name") }
        host ${ record.dig("kubernetes", "host") }
      </record>
    </filter>
    
    <match kubernetes.var.log.containers.**>
      @type copy
      <store>
        @type loki
        url "http://10.99.148.193:3100"
        label_keys "app,job,namespace,pod,host"
        extra_labels {"env":"dev"}
        flush_interval 10s
        flush_at_shutdown true
        buffer_chunk_limit 1m
      </store>
      <store>
        @type stdout
      </store>
    </match>
---

Para entornos los cuales no dispongan de internet, podrian compilar una imagen ya con la gema instalada

# source: 0lucho0/fluentd-loki:1.0.0
FROM fluent/fluentd-kubernetes-daemonset:v1.4-debian-forward-1

RUN fluent-gem i fluent-plugin-grafana-loki-licence-fix