파란하늘의 지식창고
article thumbnail
반응형

공부 목적으로 설치해 보는 과정을 정리한 것이기 때문에 실제 운영과 다릅니다.


Loki 소개

https://github.com/grafana/loki
Loki에 대한 소개를 보면 다음과 같다.
"Like Prometheus, but for logs"

Prometheus가 metric을 시계열 데이터로 저장하기 위해 사용된다면 Loki는 log 데이터를 저장하기 위해 사용된다.

https://grafana.com/docs/loki/latest/fundamentals/overview/

Loki는 log data를 효율적으로 보관하기 위해 최적화된 데이터 저장소이다.
다른 logging system과 다르게 Loki index는 label에서 작성되며 원래 log message는 색인화되지 않는다.

Prometheus가 저장과 polling을 담당한 것과 달리 Loki는 저장을 담당하고 application에서 push를 해주는 역할을 해주는 agent가 필요하다.
Promtail agent는 Loki를 위해 설계되었지만 그 외에 다른 많은 agent가 Loki와 원활하게 통합된다.

Loki는 stream을 indexing 한다.
각 steam은 고유한 label set과 연결된 log 집합을 식별한다.
label의 quality set은 간결하고 효율적인 query 실행을 허용하는 index 생성의 핵심이다.

LogQL은 Loki의 query language이다.

Loki의 특징은 다음과 같다.

  • log indexing을 위한 효율적인 memory 사용
  • multi-tenancy
  • LogQL - Prometheus의 query language인 PromQL과 유사하며, log data에서 metric을 생성하는 것이 용이함.
  • Scalability - 단일 바이너리로 사용할 수도 있으나 component 별 microservice로 실행될 수도 있음
  • Flexibility - 많은 agent가 플러그인을 지원
  • Grafana 통합

https://grafana.com/docs/loki/latest/operations/storage/

Loki는 chunk와 index라는 두 가지 유형의 데이터를 저장해야 한다.
Loki는 별도의 stream으로 log를 수신하며 각 steam은 tenant ID와 label set으로 고유하게 식별된다.
stream의 log 항목이 도착하면 chunk로 압축되어 chunk 저장소에 저장된다.
index는 각 steam의 label set를 저장하고 개별 chunk에 연결한다.

index에 대해 다음 저장소가 지원된다.

  • Single Store (boltdb-shipper)
  • Amazon DynamoDB
  • Google Bigtable
  • Apache Cassandra
  • BoltDB

chunk에 대해 다음 저장소가 지원된다.

  • Amazon DynamoDB
  • Google Bigtable
  • Apache Cassandra
  • Amazon S3
  • Google Cloud Storage
  • FlieSystem
  • Baidu Object Storage

기본 설정은 storage가 filesystem이며 Amazon S3의 경우 로컬에서 개발 시 minio를 사용할 수도 있다.
https://blog.min.io/how-to-grafana-loki-minio/

설정 파일에 대한 자세한 내용은 아래 링크에서 확인할 수 있다.
https://grafana.com/docs/loki/latest/configuration/

common 아래에 설정을 하면 전체가 공통으로 사용할 설정을 하게 된다.
Loki는 component 별 microservice로 구성할 수 있기 때문에 공통 설정에 대한 항목이 있는 듯하다.

https://grafana.com/docs/loki/latest/fundamentals/architecture/components/

Loki의 component는 대략 아래와 같이 있다.

  • Distributor - client가 수신하는 stream을 처리하는 역할, steam의 유효성을 확인하고 여러 ingester로 병렬로 전송을 적절하게 제어
  • Ingester - storage에 log data를 저장하고 write path 및 read path의 memory 내 쿼리에 대한 log data를 반환
  • Query frontend - query 발송의 API endpoint를 제공하는 선택적 서비스
  • Querier - LogQL을 사용하여 query를 처리하고 ingester와 storage에서 log를 가져옴

loki 설치하기

https://grafana.com/docs/loki/latest/installation/

로컬에서 개발을 위해 다음과 같이 구성하였다.

기존에 설치했었던 minio에 bucket을 추가하고 loki storage로 지정하였다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: loki-deployment
  labels:
    app: loki
spec:
  replicas: 1
  selector:
    matchLabels:
      app: loki
  template:
    metadata:
      labels:
        app: loki
    spec:
      securityContext:
        runAsUser: 0
      containers:
      - name: loki
        image: grafana/loki:latest
        args: 
        - "-config.file=/etc/loki/loki.yaml"
        ports:
        - name: loki 
          containerPort: 3100
        volumeMounts:
        - name: loki-config
          mountPath: /etc/loki
        - name: loki-data
          mountPath: /loki
      volumes:
      - name: loki-config
        configMap:
          name: loki-configmap
          items:
          - key: loki.yaml
            path: loki.yaml
      - name: loki-data 
        persistentVolumeClaim:
          claimName: loki-data-pvc
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: loki-configmap
data:
  loki.yaml: |
    auth_enabled: false
    server:
      http_listen_port: 3100
    common:
      path_prefix: /loki
      replication_factor: 1
      storage:
        s3:
          endpoint: minio-service:9000
          s3forcepathstyle: true
          bucketnames: loki
          access_key_id: minio_access_key
          secret_access_key: minio_secret_key
          insecure: true
      ring:
        kvstore:
          store: inmemory
    schema_config:
      configs:
        - from: 2020-10-24
          store: boltdb-shipper
          object_store: s3
          schema: v11
          index:
            prefix: index_
            period: 24h
---
apiVersion: v1 
kind: Service 
metadata: 
  name: loki-service 
  labels: 
    app: loki
spec: 
  selector: 
    app: loki 
  ports: 
    - port: 3100 
      targetPort: 3100
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: loki-data-pv
  labels:
    app: loki-data-pv
spec:
  storageClassName: ""
  capacity:
    storage: 2Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: /mnt/f/dev/data/loki-data
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: loki-data-pvc
spec:
  volumeName: loki-data-pv
  storageClassName: ""
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 2Gi

/metrics 주소를 호출하여 확인할 수 있다.

# HELP cortex_consul_request_duration_seconds Time spent on consul requests.
# TYPE cortex_consul_request_duration_seconds histogram
cortex_consul_request_duration_seconds_bucket{kv_name="ingester-ring",operation="CAS loop",status_code="200",le="0.005"} 10156
cortex_consul_request_duration_seconds_bucket{kv_name="ingester-ring",operation="CAS loop",status_code="200",le="0.01"} 10158
cortex_consul_request_duration_seconds_bucket{kv_name="ingester-ring",operation="CAS loop",status_code="200",le="0.025"} 10160
cortex_consul_request_duration_seconds_bucket{kv_name="ingester-ring",operation="CAS loop",status_code="200",le="0.05"} 10161
cortex_consul_request_duration_seconds_bucket{kv_name="ingester-ring",operation="CAS loop",status_code="200",le="0.1"} 10161
cortex_consul_request_duration_seconds_bucket{kv_name="ingester-ring",operation="CAS loop",status_code="200",le="0.25"} 10161
cortex_consul_request_duration_seconds_bucket{kv_name="ingester-ring",operation="CAS loop",status_code="200",le="0.5"} 10161
cortex_consul_request_duration_seconds_bucket{kv_name="ingester-ring",operation="CAS loop",status_code="200",le="1"} 10161

// ... 이하 생략

Promtail 소개

Loki가 로그를 저장하는 역할을 하면 Promtail은 application에서 로그를 전달하는 agent의 역할을 한다.
Promtail 이외에도 Bit, Fluentd, LogStash 등을 사용할 수 있다.

kubernetes는 node 별로 /var/log/pods 아래에 모든 pod의 로그가 기록된다.
daemonset으로 설정하고 node별로 로그를 수집하도록 처리를 하면 된다.

promtail의 설치는 아래 가이드 문서를 참고하면 된다.
https://grafana.com/docs/loki/latest/clients/promtail/installation/

설치 방식은 sidecar, daemonset 방식이 있는데 daemonset  방식을 추천한다고 한다.

  • daemonset - 각 노드마다 promtail pod가 실행되어 해당 노드 장비에서 실행 중인 파드의 로그를 추적
  • sidecar - 각 파드에 container로 추가되어 실행, 해당 파드 내부에서 로그 파일을 읽어서 Loki로 전송

pod마다 agent 형태로 설정하는 것보다 daemonset을 하나 띄워 해당 node의 pod들을 찾아 로그를 수집하는 것이 훨씬 편한 것 같다.

Prometheus가 저장소와 polling 역할을 같이 담당하는 반면 Promtail은 저장소의 역할은 하지 않고 로그를 찾아 저장소로 push 하는 역할을 한다.

하지만 설정 방식이나 문법은 크게 차이가 없다.

Promtail 설치하기

아래와 같이 설정하였다.

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: promtail-daemonset
  labels:
    app: promtail
spec:
  selector:
    matchLabels:
      app: promtail
  template:
    metadata:
      labels:
        app: promtail
    spec:
      securityContext:
        runAsUser: 0
      containers:
      - name: promtail
        image: grafana/promtail
        args:
        - -config.file=/etc/promtail/promtail.yaml
        ports:
        - name: promtail
          containerPort: 3101
        volumeMounts:
        - name: promtail-config
          mountPath: /etc/promtail
        - name: promtail-run
          mountPath: /run/promtail
        - name: promtail-containers
          mountPath: /var/lib/docker/containers
          readOnly: true
        - name: promtail-pods
          mountPath: /var/log/pods
          readOnly: true
      volumes:
      - name: promtail-config
        configMap:
          name: promtail-configmap
          items:
          - key: promtail.yaml
            path: promtail.yaml
      - name: promtail-run
        hostPath:
          path: /run/promtail
      - name: promtail-containers
        hostPath:
          path: /var/lib/docker/containers
      - name: promtail-pods
        hostPath:
          path: /var/log/pods
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: promtail-configmap
data:
  promtail.yaml: |
    server:
      enable_runtime_reload: true
      log_level: info
      http_listen_port: 3101
    
    clients:
    - url: http://loki-service:3100/loki/api/v1/push
    
    positions:
      filename: /run/promtail/positions.yaml
    target_config:
      sync_period: 10s
    scrape_configs:
    # - job_name: system
      # static_configs:
      # - targets:
          # - localhost
        # labels:
          # job: varlogs
          # __path__: /var/log/pods/**/*.log
    - job_name: bluesky-spring-job
      pipeline_stages:
      - docker: {}
      kubernetes_sd_configs:
      - role: pod
        selectors:
        - role: pod
          # field: status.phase=Running
      relabel_configs:
      - source_labels: [__meta_kubernetes_pod_container_name]
        action: keep
        regex: spring
      - source_labels: [__meta_kubernetes_pod_label_app]
        target_label: application
      - source_labels: [__meta_kubernetes_pod_name]
        target_label: instance
      - source_labels: [__meta_kubernetes_pod_container_name]
        target_label: container
      - replacement: /var/log/pods/*$1/*.log
        separator: /
        source_labels:
        - __meta_kubernetes_pod_uid
        - __meta_kubernetes_pod_container_name
        target_label: __path__
---
apiVersion: v1 
kind: Service 
metadata: 
  name: promtail-service 
  labels: 
    app: promtail  
spec: 
  selector: 
    app: promtail 
  ports: 
    - port: 3101 
      targetPort: 3101

Promtail에 웹으로 접근하면 Prometheus와 거의 유사한 UI로 검색된 서비스를 확인할 수 있다.

Grafana에서 로그 확인하기

Grafana Configuration에서 Add new data source로 Loki를 추가해 주고

 

explore에서 조회하면 로그가 정상적으로 수집되는 것을 확인할 수 있다.


여기까지 간단하게 Promtail과 Loki를 설치하고 Grafana에서 확인해 보았다.

이제까지의 글을 정리해 보면 Grafana를 대시보드로 사용하여

  • log - Promtail + Loki
  • metric - Prometheus
  • trace - Spring Project(Zipkin, Micrometer, Otel exporter) + OpenTelemetry Collector + Tempo

와 같이 구성해 보았다.

Otel이 API에 대한 스펙을 공통화하려고 하고 있지만 현재로선 Promtail이나 Prometheus의 편리함을 버릴 만큼은 아닌 것 같다.
(이후엔 또 어떻게 바뀔지 모르겠지만...)

Loki와 Tempo를 연계해 좀 더 상세한 설정을 하는 부분에 대해서는 좀 더 찾아본 후 정리하려고 한다.

반응형
profile

파란하늘의 지식창고

@Bluesky_

내용이 유익했다면 광고 배너를 클릭 해주세요