기존에 CloudWatch로 로그를 볼 수 있었던 거랑 달리, 이번에는 인터넷 연결이 불가능한 컴퓨터라 온프레미스 환경에서 서버를 실행해야 했다. 서버 실행은 간단했지만, 만약 에러가 나면 어디서 에러가 났는지, 왜 났는지 찾는 게 너무 막막할 것 같았다.
예를 들어, 2일 3일 후에 문제를 확인하려고 하면 로그 자체를 찾기가 힘들어서 에러의 원인을 파악하기가 어려울거 같았고 이런 상황을 대비해서 Grafana Loki를 사용하기로 했다.
loki의 구성
- distributor
- 로그 수집 및 초기 처리 클라이언트(예: Promtail)로부터 로그 데이터를 수집한다. Distributor는 이 데이터를 받으면 이를 검증하고 초기 처리를 수행한다. 데이터가 올바른 형식인지 확인하고, 불필요한 데이터는 제거한다.
- 데이터 분할 및 분산 Distributor는 받은 로그 데이터를 해시를 이용해 분할하여 "Ingester"로 분산시킨다. Ingester는 실제 로그 데이터를 임시적으로 저장하고, 데이터가 최종적으로 저장소로 이동하기 전까지 데이터를 관리한다. 이를 위해 Distributor는 로그 스트림에 기반한 고유한 해시 키를 계산하여, 적절한 Ingester 노드로 데이터를 라우팅하고, 이러한 해싱 메커니즘은 특정 로그 스트림이 항상 동일한 Ingester에 분산되도록 보장된다.
- 일관성과 가용성 보장 Distributor는 클러스터 내에서 로그 데이터의 중복성과 일관성을 보장하기 위해 쿼럼 개념을 사용한다. 기본적으로, 로그 데이터는 여러 Ingester 노드에 복제된다. Distributor는 이러한 복제를 통해 데이터 유실 가능성을 최소화하며, 로그 데이터의 가용성을 높이고, 특정 수의 Ingester가 데이터를 수신했다고 확인되면 Distributor는 성공적으로 수신된 것으로 처리한다.
- 오류 처리 및 신뢰성 Distributor는 장애 복구 및 오류 처리 메커니즘을 갖추고 있어, 특정 Ingester 노드가 응답하지 않거나 장애가 발생했을 때에도 데이터를 안전하게 다른 노드로 전송할 수 있다.
- Ingester
- 로그 데이터 임시 저장 및 배치 처리 Ingester는 Distributor로부터 받은 로그 데이터를 메모리에 임시 저장한다. 로그는 일정 시간이 지나거나 지정된 크기에 도달하면 배치로 묶어서 영구 저장소(예: AWS S3, Google Cloud Storage 등)로 전송한다.
- 데이터 복제 및 관리 Ingester는 받은 로그 데이터를 복제하여 장애 발생 시에도 안전하게 복구할 수 있도록 한다. 복제된 데이터는 여러 Ingester 노드에 저장되기 때문에 특정 노드에 문제가 생겨도 다른 노드에서 데이터를 제공할 수 있다.
- 쿼리 처리 및 일관성 사용자가 특정 로그를 조회할 때, Ingester는 메모리에 저장된 데이터를 참조해 빠르게 결과를 반환하고, 데이터가 영구 저장소로 이동하기 전에도 쿼리 요청에 응답할 수 있다.
- Querier
- 사용자 요청 처리 및 쿼리 최적화 Querier는 사용자가 보낸 로그 쿼리 요청을 처리한다. 요청을 받으면, Ingester와 영구 저장소에서 필요한 데이터를 가져와 사용자에게 반환한다. Querier는 대량의 로그 데이터를 효과적으로 처리하기 위해 병렬 처리와 쿼리 최적화 기법을 사용하여 빠르고 효율적인 응답을 제공해준다.
- 데이터 결합 및 집계 Querier는 Ingester와 영구 저장소에서 동시에 로그 데이터를 가져와 결합하고, 이를 통해 사용자에게 일관된 로그 데이터를 제공한다. 이 과정에서 필터링, 집계, 정렬 등 다양한 데이터 처리 작업을 수행해 사용자에게 필요한 형식으로 데이터를 반환한다.
- 캐싱을 통한 성능 향상 Querier는 자주 요청되는 쿼리 결과를 캐싱하여 동일한 요청이 들어올 때 캐시된 데이터를 반환함으로써 응답 시간을 단축하고 시스템 부하를 줄인다. 이를 통해 대규모 로그 데이터를 빠르게 조회할 수 있는 성능을 보장한다.
- 스토리지(Storage)
- 로그 데이터의 영구 저장 Loki의 스토리지 계층은 로그 데이터를 영구적으로 저장한다. 주로 객체 스토리지(예: AWS S3, GCS, MinIO 등)를 사용하며, 대규모 로그 데이터를 저장할 수 있다. 저장된 데이터는 압축되어 공간을 효율적으로 사용한다.
- 지속 가능한 데이터 보존 로그 데이터는 설정에 따라 일정 기간 동안 보조된다.. Loki는 데이터 보존 정책을 통해 오래된 로그 데이터를 자동으로 삭제하거나, 필요에 따라 아카이빙 처리할 수 있다. 이를 통해 사용자는 원하는 데이터 보존 기간을 설정하고 스토리지 용량을 효율적으로 관리할 수 있다.
- 데이터 읽기 최적화 Querier가 사용자 요청을 처리할 때, 스토리지에 저장된 데이터를 효율적으로 읽어오기 위해 인덱싱과 같은 최적화 기법을 사용한다. 이러한 최적화는 대량의 로그 데이터에서도 빠른 검색 성능을 보장한다.
인덱싱된 부분: 타임스탬프와 라벨은 인덱싱되어 검색 성능을 향상시킨다.
인덱싱되지 않은 부분: 로그 내용(Log Line)은 인덱싱되지 않으며, 이를 통해 인덱싱에 따른 오버헤드를 줄이고 시스템의 리소스 사용을 효율적으로 관리한다. 필요시 전체 텍스트 검색이 수행되지만, 기본적으로는 라벨을 통해 로그 데이터를 필터링하고 찾는 것이 주된 접근 방식이다.
loki는 수평적 확장이 용이하지만 우리 서비스에는 서비스별로 따로 서버를 둘 필요가 없어서 싱글 서버로 운영하기로 했다.
먼저 우리는 파일 관리가 힘들거 같아 promtail을 사용하지않고 콘솔에 나오는 로그데이터를 바로 loki4j 를 이용해서 loki로 전송했다.
<appender name="LOKI" class="com.github.loki4j.logback.Loki4jAppender">
<http>
<url><http://localhost:3100/loki/api/v1/push></url>
</http>
<format>
<message>
<pattern>%date{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern>
</message>
<sortByTime>true</sortByTime>
</format>
<metricsEnabled>true</metricsEnabled>
</appender>
아래 설정을 통해 loki 세팅을 했고
auth_enabled: false # 인증 비활성화 (기본 설정)
server:
http_listen_port: 3100 # Loki가 HTTP 요청을 수신하는 포트 설정
ingester:
wal:
enabled: true
dir: /wal
lifecycler:
ring:
kvstore:
store: inmemory # 인메모리 키-값 저장소 사용
replication_factor: 1 # 데이터 복제 계수 설정 (1로 설정하면 복제하지 않음)
chunk_idle_period: 5m # 청크가 비활성 상태로 유지되는 최대 시간
chunk_retain_period: 30s # 청크를 보관하는 최소 시간
max_transfer_retries: 0 # 청크 전송 재시도 횟수 설정
# 인덱스를 저장하는 방법 지정
schema_config:
configs:
- from: 2022-10-24 # 스키마 설정의 유효 시작 날짜
store: boltdb-shipper # 인덱스를 저장하는 방식 설정 (boltdb-shipper 사용)
object_store: filesystem # 청크 데이터를 파일 시스템에 저장
schema: v11 # 스키마 버전 설정
index:
prefix: index_ # 인덱스 파일의 접두사 설정
period: 24h # 인덱스가 회전하는 주기 설정 (하루마다 새로운 인덱스 생성)
# 청크를 저장하는 방법 지정
storage_config:
boltdb_shipper:
active_index_directory: /loki/index # 활성 인덱스 파일이 저장될 디렉토리 경로
cache_location: /loki/boltdb-cache # 인덱스 캐시 위치 설정
shared_store: filesystem # 공유 저장소로 파일 시스템 사용
filesystem:
directory: /loki/chunks # 청크 데이터를 저장할 디렉토리 경로
limits_config:
enforce_metric_name: false # 메트릭 이름 강제 여부 설정 (false로 설정하여 모든 로그 수집 허용)
reject_old_samples: true # 오래된 샘플의 수집 거부 여부 설정
reject_old_samples_max_age: 168h # 허용되는 샘플의 최대 나이 (7일)
retention_period: 720h # 로그 데이터의 보존 기간 설정 (30일)
chunk_store_config:
max_look_back_period: 720h # 청크 조회 시 최대 되돌아갈 수 있는 기간 설정 (30일)
compactor:
working_directory: /loki/compactor # Compactor의 작업 디렉토리
shared_store: filesystem
retention_enabled: true # 데이터 보존 기간 설정 활성화
retention_delete_delay: 1h # 보존 기간 지난 데이터 삭제 지연 시간 (선택 사항)
compaction_interval: 5m # 데이터 압축 주기
grafana와 loki를 docker에 띄워서 로그를 확인할 수 있게 되었다.
이번에 loki를 공부하면서 Grafana와 loki를 통해 로그를 보는 과정은 간단했지만 loki가 어떤식으로 구성되어있고
왜 분산처리가 잘되는지를 알 수있는 시간이였다.
'트러블슈팅 > 회사' 카테고리의 다른 글
modbus 데이터 가져오기 (1) | 2024.12.28 |
---|---|
Modbus protocol (2) | 2024.12.28 |
로그 수집 및 에러 알림 만들기 (0) | 2024.10.18 |
modbus 데이터 흐름 구조 (0) | 2024.10.15 |
instancio (0) | 2024.10.15 |