Grafana Loki는 로그 집계 도구이며 모든 기능을 갖춘 로깅 스택의 핵심이다. 

Loki는 로그 데이터를 효율적으로 보관하도록 최적화된 데이터 저장소이다. 로그 데이터의 효율적인 인덱싱은 Loki를 다른 로깅 시스템과 구별한다. 다른 로깅 시스템과 달리 Loki 인덱스는 레이블로 만들어지며 원래 로그 메시지는 인덱싱 되지 않는다. 

 

에이전트(클라이언트)는 로그를 수집하고, 로그를 스트림(Stream)으로 변환하고 HTTP API를 통해 스트림을 Loki에 푸시(Push)한다. Promtail 에이전트가 Loki 설치용으로 설계되긴 했지만 다른 많은 에이전트들도 Loki와 원활하게 통합된다.

 

Loki는 스트림(Stream)을 색인화(indexing)한다. 각 스트림은 고유한 레이블 집합과 연결된 로그 집합을 식별한다. 좋은 레이블 집합은 간결하고 효율적인 쿼리 실행을 허용하는 인덱스 생성의 핵심이다. 

 

LogQL은 Loki의 쿼리 언어이다.

 

Loki 특징

로그를 인덱싱할 때 효율적인 메모리 사용 - 라벨 셋으로 인덱싱해서 다른 로그 집계 제품보다 인덱스가 상당히 작다. 동작할 때 메모리를 덜 쓴다.

 

멀티 테넌시(Multi-Tenancy) - Loki 사용 시 여러 테넌트가 단일 Loki 인스턴스를 활용할 수 있다. 개별 테넌트의 데이터는 다른 테넌트와 완전히 격리된다. 멀티 테넌시는 에이전트에서 테넌트 ID를 할당하여 구성된다.

 

LogQL, Loki의 쿼리 언어 - Prometheus 쿼리 언어인 PromQL 사용자는 LogQL을 통한 로그에 대한 쿼리 생성에 금방 적응할 수 있다. 이 언어는 로그 집계를 훨씬 능가하는 강력한 기능인 로그 데이터에 메트릭 생성을 용이하게 한다.

 

확장성(Scalability) - Loki는 단일 바이너리로 실행할 수 있다. 모든 구성 요소는 하나의 프로세스에서 실행된다. Loki는 Loki의 각 구성요소를 마이크로 서비스로 실행할 수 있으므로 확장성을 위해 설계되었다. Configuration을 통해 마이크로 서비스를 개별적으로 확장할 수 있으므로 유연한 대규모 설치가 가능하다.

 

유연성(Flexibility) - 많은 에이전트(클라이언트)가 플러그인을 지원한다. 이를 통해 현재 observability 구조는 Observability Stack의 기존 부분을 바꿀 필요 없이 Loki를 로그 집계 도구로 추가할 수 있다.

 

그라파나 통합 - Loki는 그라파나와 원활하게 통합되어 완전한 observability stack을 제공한다. 

 

다른 로그 시스템과 비교해 본 Loki

Grafana Loki / Promtail/Grafana vs EFK

EFK (Elasticsearch, Fluentd, Kibana) 스택은 다양한 리소스의 로그를 ingest(정제), visualize(시각화), query(쿼리)하는 데에 쓰인다. Elasticsearch의 데이터는 구조가 없는 JSON 오브젝트로 디스크에 저장된다. 각 오브젝트의 key들과 각 key의 내용은 색인된다. 데이터는 JSON 오브젝트를 사용해서 쿼리를 정의하거나 Lucene 쿼리 언어를 통해 쿼리될 수 있다. 

그와 비교해서, 싱글 바이너리 모드인 그라파나 로키는 디스크에 데이터를 저장할 수 있지만 수평적으로 늘어날 수 있는 모드(horizontally-scalable) 데이터는 클라우드 스토리지 시스템에 저장된다. S3, GCS, Cassandra와 같은. 로그는 레이블 쌍만 인덱싱되는 레이블 이름 및 값 세트로 태그가 지정된 일반 텍스트 형식으로 저장된다. 이 절충안으로 인해 전체 인덱스보다 운영 비용이 저렴하고 개발자가 애플리케이션에서 적극적으로 로그를 남길 수 있다. Loki의 로그는 LogQL을 사용하여 쿼리된다. 그러나 이런 설계 절충으로 콘텐츠(로그 내 텍스트)를 기반으로 필터링하는 LogQL 쿼리는 쿼리에 정의된 레이블과 일치하는 검색 창 내의 모든 청크를 로드해야 한다. 

 

Fluentd는 일반적으로 로그를 수집하고 Elasticsearch로 전달하는 데 사용된다. Fluentd는 많은 소스에서 로그를 수집하고 처리하고 하나 이상의 대상에 전달할 수 있는 데이터 수집기이다. 이에 비해 Promtail의 사용 사례는 특히 Loki에 맞춰져 있다. 주요 작동 모드는 디스크에 저장된 로그 파일을 발견하고 이를 레이블 세트와 연관된 로그 파일로 Loki에 전달한다. Promtail은 Promtail과 동일한 노드에서 실행되는 kubernetes 파드에 대한 서비스 검색(service discovery)을 수행하거나 컨텡너 사이드카 또는 Docker 로깅 드라이버로 작동하거나 지정된 폴더에서 로그를 읽고 systemd journal에 tail을 붙일 수 있다. 

Loki가 레이블 쌍으로 로그를 나타내는 방식은 Prometheus가 메트릭을 나타내는 방식과 유사하다. Prometheus와 함께 환경에 배포할 때 Promtail의 로그는 동일한 서비스 검색 메커니즘을 사용하기 때문에 일반적으로 애플리케이션 메트릭과 동일한 레이블을 가진다. 동일한 레이블의 로그와 메트릭을 사용하면 사용자가 메트릭과 로그 간의 컨텍스트 전환을 원활하게 수행할 수 있으므로 근본 원인 분석에 도움이 된다.

 

Kibana는 Elasticsearch 데이터를 시각화하고 검색하는 데 사용되며 해당 데이터에 대한 분석을 수행하는 데 매우 강력하다. Kibana는 위치 지도, 이상 감지를 위한 머신 러닝, 데이터의 관계를 발견하기 위한 그래프와 같은 데이터 분석을 수행하는 많은 시각화 도구를 제공한다. 예기치 않은 상황이 발생하면 사용자에게 알리도록 Alert을 구성할 수 있다.

 

이에 비해 그라파나는 Prometheus 및 Loki와 같은 소스의 시계열 데이터에 맞게 특별히 조정되었다. 메트릭을 시각화하도록 대시보드를 설정할 수 있으며 (곧 로그 지원 예정) explore view로 데이터에 대한 임시 쿼리를 만들 수 있다. Kibana와 마찬가지로 Grafana는 메트릭을 기반으로 한 Alert을 지원한다.

 

그라파나 로키의 구조

Multi-tenancy

메모리와 장기 스토리지의 모든 데이터는 Grafana Loki가 다중 테넌트 모드에서 실행 중일 때, 요청의 X-Scope-OrgID HTTP 헤더에서 가져온 테넌트 ID로 분할될 수 있다. Loki가 다중 테넌트 모드에 있지 않으면 헤더가 무시되고 테넌트 ID가 인덱스와 저장된 청크에 가짜(Fake)로 설정된다.

 

Chunk format

-------------------------------------------------------------------
  |                               |                                 |
  |        MagicNumber(4b)        |           version(1b)           |
  |                               |                                 |
  -------------------------------------------------------------------
  |         block-1 bytes         |          checksum (4b)          |
  -------------------------------------------------------------------
  |         block-2 bytes         |          checksum (4b)          |
  -------------------------------------------------------------------
  |         block-n bytes         |          checksum (4b)          |
  -------------------------------------------------------------------
  |                        #blocks (uvarint)                        |
  -------------------------------------------------------------------
  | #entries(uvarint) | mint, maxt (varint) | offset, len (uvarint) |
  -------------------------------------------------------------------
  | #entries(uvarint) | mint, maxt (varint) | offset, len (uvarint) |
  -------------------------------------------------------------------
  | #entries(uvarint) | mint, maxt (varint) | offset, len (uvarint) |
  -------------------------------------------------------------------
  | #entries(uvarint) | mint, maxt (varint) | offset, len (uvarint) |
  -------------------------------------------------------------------
  |                      checksum(from #blocks)                     |
  -------------------------------------------------------------------
  |                    #blocks section byte offset                  |
  -------------------------------------------------------------------

mint와 maxt는 유닉스 나노초 타임스탬프의 최소와 최대를 각각 표시한 것이다.

 

Block Format

블록은 entries 시리즈로 구성되어 있다. entries 시리즈 각각은 개별적인 로그 라인이다.

수 bytes의 블록은 Gzip을 사용해서 압축되어 저장된다. 아래 형식은 압축되지 않았을 때이다.

 -------------------------------------------------------------------
  |    ts (varint)    |     len (uvarint)    |     log-1 bytes      |
  -------------------------------------------------------------------
  |    ts (varint)    |     len (uvarint)    |     log-2 bytes      |
  -------------------------------------------------------------------
  |    ts (varint)    |     len (uvarint)    |     log-3 bytes      |
  -------------------------------------------------------------------
  |    ts (varint)    |     len (uvarint)    |     log-n bytes      |
  -------------------------------------------------------------------

ts는 로그의 유닉스 나노초 타임스탬프이다. len은 수 bytes의 로그 entry 길이이다.

 

스토리지 (Storage)

Single Store

로키는 싱글 오브젝트 스토리지 백엔드에서 모든 데이터를 저장한다. 이 작동 모드는 일반적으로 loki 2.0에서 이용가능했고, 빠르고 비용 절감하고 단순하지만 모든 현재와 미래 개발사항에서 언급되지 않는다. 이 모드는 오브젝트 스토리지에 인덱스를 저장하기 위해 boltdb_shipper 라는 어댑터를 사용한다. (같은 방식으로 우리는 청크(chunk)를 저장한다)

 

Deprecated: Multi Store

청크 스토어는 백그라운드 유지 관리 작업 없이 대화형 쿼리 및 지속적인 쓰기를 지원하도록 설계된 Loki의 장기 데이터 저장소이다. 

청크에 대한 인덱스. 이 인덱스는 다음으로 뒷받침 됩니다.

Amazon DynamoDB

Google Bigtable

Apache Cassandra

다음과 같은 청크 데이터 자체에 대한 키-값(KV) 저장소:

Loki의 다른 핵심 구성 요소와 달리 청크 저장소는 별도의 서비스, 작업 또는 프로세스가 아니라, Loki 데이터에 액세스해야 하는 정제(Ingester) 및 쿼리(querier) 서비스에 포함된 라이브러리이다.

청크 저장소는 청크 저장소 인덱스를 지원하는 데 사용할 수 있는 NoSQL 저장소(DynamoDB, Bigtable, Cassandra)에 대한 통합 인터페이스에 의존한다. 이 인터페이스는 인덱스가 키(key)가 지정된 항목(entries) 모음이라고 가정한다. \

 

Hash Key : 모든 reads, writes를 요구한다.

Range Key (범위 키) : 쓰기(writes)에 필요하고 읽기(reads)에는 생략할 수 있으며 접두사(prefix) 또는 범위로 쿼리할 수 있음

 

이 인터페이스는 지원되는 데이터베이스에서 약간 다르게 작동한다.

 

DynamoDB는 기본적으로 범위 및 해시 키를 지원한다. 따라서 인덱스 항목은 다이나모디비 항목으로 직접 모델링되며 해시 키는 배포 키로, 범위는 다이나모디비 범위 키로 사용된다. Bigtable 및 Cassandra의 경우 인덱스 항목(entries)은 개별 열 값으로 모델링된다. 해시 키가 행 키가 되고 범위 키가 열 키가 된다. 

스키마 세트는 청크 저장소에 대한 읽기 및 쓰기에 사용되는 일치자(matcher)와 레이블 세트를 인덱스의 적절한 작업에 매핑하는 데 사용된다. Loki가 발전함에 따라 주로 쓰기 로드 균형을 개선하고 쿼리 성능을 개선하기 위해 스키마가 추가되었다.

 

Read Path

읽기 경로는 다음과 같이 동작한다.

1. querier는 데이터에 대한 HTTP/1 요청을 받는다. 

2. querier(쿼리자)는 메모리 내 데이터에 대해 쿼리를 모든 ingester에 전달한다.

3. ingester(수집기)는 읽기 요청을 수신하고 쿼리와 일치하는 데이터를 반환한다.(있는 경우)

4. querier는 백업 저장소에서 데이터를 느리게 로드하고 반환된 ingester가 없으면 이에 대해 쿼리를 실행한다.

5. querier는 수신된 모든 데이터를 반복하고 중복을 제거하여 HTTP/1 연결을 통해 최종 데이터 세트를 반환한다.

 

Write Path

쓰기 경로는 다음처럼 동작한다.

1. distributor는 스트림에 대한 데이터를 저장하라는 HTTP/1 요청을 수신한다.

2.  각 스트림은 해시 링(Hash Ring)을 사용해서 해시된다.

3. distributor는 각 스트림을 적절한 ingester 및 해당 복제본(replica)- 구성된 복제요소 기반-로 보낸다.

4. 각 ingester(수집기)는 스트림 데이터에 대해 청크를 생성하거나 기존 청크에 추가한다. 청크는 테넌트 및 레이블 세트 별로 고유하다.

5. distributor는 HTTP/1 연결을 통해 성공 코드로 응답한다.

 

정리하면서도 뭔 소리인지 몰랐는데 그림 보니까 동일한 라벨 키,값을 가지는 경우에는 로그 엔트리(로그 줄)이 하나의 청크에 저장되고, 그 청크가 꽉 차면 압축되어 저장된다. 나눠진 작은 인덱스는 청크를 찾기 위해 유지된다. 다른 라벨 키와 값은 다른 스트림(구별되게 하는 해시 키)과 청크에 저장된다. 라벨 키 값을 하나의 해시 키로 만들어서 위치 키로 놔두고, 로그 내용은 위치 키로 지정된 청크 안에 저장하는 원리.

 

Deployment 모드 https://grafana.com/docs/loki/latest/fundamentals/architecture/deployment-modes/

이어서 번역할 것..

'좌충우돌 커리어 > SRE' 카테고리의 다른 글

[loki] 구조 - 구성 요소 (작성 중)  (1) 2022.09.30

WRITTEN BY
호두만듀
생활형 블로그 심심할 땐 끼적끼적 바쁠 때도 끼적끼적 자나 깨나 끼적끼적

,