Loki 3.x 설치하고 S3 기반 TSDB 설정하기

U
uniglot
  1. Monitoring
  2. Kubernetes
  3. Loki
  4. Log
지금까지 배부른 회사에서 데이터독만 쓰면서 신경 끄며 살아와서 그런가, 모니터링에 대한 지식이 너무 빈약하다는 것을 많이 느끼는 요즘이네요... 😢
쿠버네티스에서 로그를 수집 및 저장하기 위해 로키(Loki)를 배포하기 위한 노력의 흔적을 공유합니다!
로키가 출시된지 꽤 시간이 지났지만 여전히 로키 설치에 관해 자세히 설명하는 글이 많지 않았고, loki-stack 헬름 차트가 더 이상 활발하게 개발되고 있지 않아 설정에 어려움을 많이 겪었답니다.
그러므로 이번 글에서는 loki , promtail 차트를 사용해서 로키를 배포하고, 최소한의 동작을 하도록 설정해 보도록 할게요!

1. 큰 그림

우선 프로메테우스(Prometheus)의 경우 kube-prometheus-stack 헬름 차트(차트 리포지토리 링크)로 설치했다고 가정하겠습니다.
그라파나의 경우 프로메테우스를 예전에 설치해서 이미 사용하고 있었고, 이번에 설치해야 할 차트는 아래의 두 가지였어요.
loki : 로그 소스로부터 로그를 push받아 로그를 처리, 저장하여 그라파나 등을 통해 시각화하기 위한 로그 시스템이에요. (차트 리포지토리 링크)
promtail : 각 쿠버네티스 노드로부터 컨테이너 로그를 가져와 로키로 push하기 위한 에이전트예요. (차트 리포지토리 링크)
다시 말하면, 프롬테일로 로그를 긁어모아서 로키로 쏴 주는 구조예요.

2. 로키 차트 설정

2.1. 로키의 배포 모드

로키의 배포 모드(공식 문서 링크)는 세 가지가 있어요. 이 중, 로키 차트에서 지정한 기본값은 단순 확장가능 모드(v3.3.2 밸류 참조)입니다.
우리 클러스터에는 단순 확장가능 모드로 로키를 설치하기로 결정했어요.

모놀리식 모드 (monolithic mode)

로키의 모든 컴포넌트가 한 바이너리로 되어 있어요.
레플리카 확장이 가능하지만, 복제하지 않아도 되는 컴포넌트까지 모두 복제하게 돼요.
단순 개발 환경, PoC 환경이나 하루 최대 20GB 정도의 로그를 처리할 때 적합해요.

단순 확장가능 모드 (simple scalable mode)

로키의 컴포넌트를 의미 단위로 묶어 두었어요.
쓰기 타겟(write target): distributor, ingester
읽기 타겟(read target): querier, query frontend
백엔드 타겟(backend target): compactor, query scheduler, index gateway, ruler
모놀리식과 마이크로서비스 모드 사이의 유연함과 복잡성을 가져요.
하루 최대 1TB 정도의 로그를 처리하기에 적합해요.

마이크로서비스 모드 (microservices mode)

모든 컴포넌트가 따로 배포돼요.
가장 유연하고 성능적으로 최적화하기 좋지만, 매우 복잡해요.
하루 1TB 이상의 로그를 처리하기에 적합해요.

2.2. 로키 스토리지 설정 - 오브젝트 스키마

로키 차트를 통해 설치할 때 기본적으로 파일시스템을 사용하게 됩니다. 이를 위해 MinIO의 사용이 기본적으로 활성화되어 있어요. (v3.3.2 밸류 참조 - 모놀리식, 단순 확장가능, 마이크로서비스)
미니오는 S3 API를 그대로 따르는 오브젝트 기반 스토리지이며, 차트 기본값을 사용한다면 미니오를 사용해 파일시스템에 로그를 저장하게 되어 있어요. 파일시스템을 사용하더라도 S3의 오브젝트 스키마를 그대로 사용하므로 스키마 설정은 아래와 같이 s3 를 지정하게 됩니다. (공식 문서 참조)
⚠️
키값의 형태에 주의하세요!
카멜케이스를 채택한 schemaConfig와 다르게, 다른 밸류는 언더스코어 케이스를 채택한 경우가 많아요.
예시: storage_config
loki:
  schemaConfig:
    configs:
      - from: "2025-01-22"
        store: tsdb
        object_store: s3
        schema: v13
        index:
          prefix: loki_index_
          period: 24h
from : 여기에 지정한 날짜부터 생산된 로그를 저장해요.
store : 로키에서 사용할 스토리지 방식을 지정해요. 로키 2.8 버전 이후부터는 TSDB가 권장됩니다.
index.period : 스토리지에 저장될 인덱스의 보관 기간이에요. 24시간 이외의 값을 사용할 수 없습니다. (공식 문서 참조)
하지만 우리는 오브젝트 스토어를 위해 S3 버킷을 사용할 생각이므로, 미니오를 명시적으로 비활성화해 줄게요.
⚠️
미니오를 비활성화한 상태에서 파일시스템을 사용하기를 원한다면 object_storefilesystem 으로 지정해야 해요!
loki:
  schemaConfig:
  # omit

minio:
  enabled: false

2.3. 로키 스토리지 설정 - S3 버킷 설정

그 다음 S3 버킷을 로키와 연결해야 합니다. 이를 위해서는 loki.storage_config 값과 loki.storage 값을 설정해 주어야 해요. 둘 중 하나라도 빠지면 정상적으로 동작하지 않아요!
우선 로키에서 저장하는 데이터 중 chunk와 ruler에 사용할 버킷을 반드시 생성해야 해요. (v3.3.2 밸류 참조) 이를 위해 버킷을 서울 리전에 아래와 같이 생성했다고 가정하겠습니다.
testtest-loki-chunk
testtest-loki-ruler
그 다음 아래와 같이 차트 밸류를 설정해 줍니다.
loki:
  schemaConfig:
  # omit
  storage_config:
    tsdb_shipper:
      active_index_directory: "/var/loki/index"
      cache_location: "/var/loki/index_cache"
      cache_ttl: 24h
    aws:
      region: ap-northeast-2
      bucketnames: testtest-loki-chunk
  storage:
    type: s3
    bucketNames:
      chunk: testtest-loki-chunk
      ruler: testtest-loki-ruler
    s3:
      region: ap-northeast-2
storage_config.tsdb_shipper : TSDB shipper가 S3로부터 값을 읽고 쓰기 전에 값을 저장하거나 캐싱해 두는 곳을 지정합니다. (공식 문서 참조)
여기서 /var/loki 부분은 기본값입니다. commonConfig.path_prefix 로 변경할 수 있습니다. (v3.3.2 밸류 참조)
active_index_directory 의 경우 쓰기 타겟에 생성돼요.
cache_location 의 경우 백엔드 타겟에 생성돼요.
storage_config.aws : 스토리지 설정 첫 번째 부분
이 부분에는 chunk 버킷만 지정합니다. 설명이 잘 된 공식 문서를 찾을 수 없어서 링크한 공식 문서에서 유추하였어요...😭 (공식 문서 참조)
storage : 스토리지 설정 두 번째 부분
여기서는 chunk와 ruler 모두 지정해 줍니다.

2.4. S3 접근을 위한 EKS IRSA 설정

이제 로키의 연관 파드가 S3로부터 읽고 쓸 수 있도록 권한을 설정해 줘야 해요. storage_configstorage 에서 액세스 키를 설정하는 방식도 사용할 수 있지만, AWS 관련 키를 평문으로 코드베이스에 기록하는 것은 너무나도 좋지 않은 습관이에요. 이런 방식을 피하기 위해서 S3 접근이 가능하도록 IAM 롤을 만들어 EKS OIDC 프로바이더에 연결하는 방식을 사용하겠습니다.
⚠️
EKS의 OIDC 프로바이더는 이미 가지고 있다고 가정할게요!

2.4.1. 퍼미션 생성

아래와 같은 퍼미션을 생성합시다.
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket",
                "s3:PutObject",
                "s3:GetObject",
                "s3:DeleteObject"
            ],
            "Resource": [
                "arn:aws:s3:::testtest-loki-chunk",
                "arn:aws:s3:::testtest-loki-chunk/*",
                "arn:aws:s3:::testtest-loki-ruler",
                "arn:aws:s3:::testtest-loki-ruler/*",
            ]
        }
    ]
}

2.4.2. 롤 생성

testtest-loki라는 이름의 롤을 생성한 뒤 위에서 생성한 퍼미션을 붙여 봅시다.
그 다음, EKS에서 사용할 수 있도록 신뢰 관계를 구축해 줍니다.
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::<계정 아이디 숫자>:oidc-provider/oidc.eks.ap-northeast-2.amazonaws.com/id/<OIDC 아이디>"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "oidc.eks.ap-northeast-2.amazonaws.com/id/<OIDC 아이디>:sub": "system:serviceaccount:<로키 네임스페이스>:<로키 서비스 어카운트 이름>",
                    "oidc.eks.ap-northeast-2.amazonaws.com/id/<OIDC 아이디>:aud": "sts.amazonaws.com"
                }
            }
        }
    ]
}
서비스 어카운트의 이름은 testtest-loki-sa 라고 가정할게요.

2.4.3. 헬름 밸류 설정

IRSA에 사용될 서비스 어카운트를 생성하고, testtest-loki 롤을 연결시켜 볼게요.
loki:
# omit

serviceAccount:
  create: true
  name: testtest-loki-sa
  annotations:
    "eks.amazonaws.com/role-arn": "arn:aws:iam::<계정 아이디 숫자>:role/testtest-loki"

2.5. 마지막: 레플리카 수 조정

각 타깃의 레플리카 수는 기본적으로 세 개씩이에요. (v3.3.2 밸류 참조) 이를 두 개씩으로 바꾸기만 해 봅시다!
backend:
  replicas: 2
read:
  replicas: 2
write:
  replicas: 2
이것으로 로키 헬름 밸류 설정은 마무리가 되었어요. 이제 프롬테일을 준비해 봅시다.

3. 프롬테일 차트 설정

다행히도 프롬테일 차트의 설정은 기본값에서 거의 바뀌지 않아요. 다만 프롬테일의 클라이언트로 로키를 가리키도록 설정해 주어야 해요.
config:
  clients:
    - url: "http://loki-gateway/loki/api/v1/push"
      tenant_id: 1
여기서는 로키와 프롬테일의 네임스페이스가 동일하다고 가정하고, 곧바로 loki-gateway 서비스를 가리키도록 하였어요.
tenant_id: 1 의 경우 "no org id" 에러로 401이 발생하는 것 때문에 링크된 이슈를 참조하여 추가해 준 것입니다. 이 부분은 workaround로 보여서, 나중에 원인을 명확하게 찾아서 보완할 생각이에요.
그리고 Fargate 노드에는 프롬테일 데몬셋이 띄워지면 안 되기 때문에 아래와 같은 affinity 설정을 해 줍니다.
affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
        - matchExpressions:
          - key: "eks.amazonaws.com/compute-type"
            operator: NotIn
            values:
              - fargate

4. 완성된 헬름 밸류

여기까지 정리된 헬름 밸류 설정입니다.

4.1. 로키

loki:
  schemaConfig:
    configs:
      - from: "2025-01-22"
        store: tsdb
        object_store: s3
        schema: v13
        index:
          period: 24h
          prefix: loki_index_
  limits_config:
    allow_structured_metadata: true
    volume_enabled: true
  storage_config:
    tsdb_shipper:
      active_index_directory: "/var/loki/index"
      cache_location: "/var/loki/index_cache"
      cache_ttl: 24h
    aws:
      region: ap-northeast-2
      bucketnames: testtest-loki-chunk
  storage:
    type: s3
    bucketNames:
      chunk: testtest-loki-chunk
      ruler: testtest-loki-ruler
    s3:
      region: ap-northeast-2

serviceAccount:
  create: true
  name: testtest-loki-sa
  annotations:
    "eks.amazonaws.com/role-arn": "arn:aws:iam::<계정 아이디 숫자>:role/testtest-loki"

backend:
  replicas: 2
read:
  replicas: 2
write:
  replicas: 2

minio:
  enabled: false

4.2. 프롬테일

config:
  clients:
    - url: "http://loki-gateway/loki/api/v1/push"
      tenant_id: 1
affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
        - matchExpressions:
          - key: "eks.amazonaws.com/compute-type"
            operator: NotIn
            values:
              - fargate

4.3. 배포 결과

ArgoCD를 통하여 배포를 완료했습니다!
S3에도 열심히 청크를 보내고 있는 것을 확인했습니다.

5. 그라파나와 연결

이 부분은 kube-prometheus-stack 의 밸류 수정이 필요하지만, 되는 것을 보기 위해서 우선 손으로 작업했어요.
현 상태에서는 인증을 두고 있지 않기 때문에 로키 게이트웨이의 서비스를 그대로 바라보게 하면 됩니다. 다만 위에 언급했던 tenant_id: 1 과 같은 이슈로, X-Scope-OrgID: 1 헤더를 설정해 주어야 해요. 이 부분은 차후 고도화하면서 개선하고, 이와 함께 헬름 밸류로 설정하도록 할 생각이에요.

6. 결과

그라파나로 로그를 볼 수 있어요!
여기까지 로키를 배포하기 위한 과정을 가능한 자세하게 적어 보았는데요. 궁금한 점이 있으시면 언제든 질문해 주세요. 저도 설정을 좀 더 최적화해 나갈 예정입니다!
uniglot
Subscribe to 'uniglot'
Subscribe to my site to be the first to receive notifications and emails about the latest updates, including new posts.
Join Slashpage and subscribe to 'uniglot'!
Subscribe
👍
1