개요

이전 글에서는 Helm 차트 기반 프로젝트 템플릿과 ArgoCD ApplicationSet을 활용해 내부 개발자 플랫폼(IDP) 구조를 정리했다. 이번 글에서는 홈랩 쿠버네티스 클러스터를 모니터링하기 위해 Prometheus와 Grafana로 메트릭을 수집하고 시각화하는 방법, Loki로 로그를 중앙에서 수집하고 분석하는 방법을 정리한다.

Grafana

모니터링의 필요성

홈랩 쿠버네티스 클러스터를 운영하다 보면 주기적으로 확인해야 할 정보가 많다. 노드와 파드의 상태, CPU와 메모리 같은 리소스 사용량, 애플리케이션의 정상 작동 여부, 문제 발생 시 원인 파악을 위한 로그 데이터가 대표적이다. 이런 정보를 시각적으로 모니터링하기 위해 다음과 같은 도구들을 사용한다.

Prometheus란?

Prometheus는 CNCF 산하의 오픈소스 모니터링 시스템으로, 시계열 데이터베이스에 메트릭을 수집하고 저장한다. PromQL로 데이터를 조회하고 분석할 수 있어 쿠버네티스 환경에서 가장 널리 쓰이는 모니터링 도구 중 하나다.

Grafana란?

Grafana는 오픈소스 데이터 시각화 플랫폼으로, Prometheus, Loki, Elasticsearch 같은 다양한 데이터소스와 연동해 대시보드를 구성할 수 있다. 직관적인 UI와 다양한 시각화 옵션을 제공해 모니터링 데이터를 한눈에 파악하기 좋다.

Loki란?

Loki는 Grafana Labs에서 만든 로그 집계 시스템으로, Prometheus와 비슷한 라벨 기반 인덱싱 방식을 사용한다. 전체 로그 내용을 인덱싱하지 않고 메타데이터 중심으로 다뤄서 리소스를 비교적 적게 쓰면서 로그를 수집하고 검색할 수 있다.

Kube-Prometheus-Stack 설치

Prometheus와 Grafana를 각각 따로 올리는 건 번거로워서, 내 홈랩에서는 두 도구를 한 번에 묶어 관리할 수 있는 Kube-Prometheus-Stack을 선택했다. 배포 방식은 이전 글들과 마찬가지로 GitOps로 맞췄다.

1. 디렉토리 및 파일 구조 생성

mkdir -p k8s-resource/apps/kube-prometheus-stack/templates
cd k8s-resource/apps/kube-prometheus-stack

2. Chart.yaml 생성

Chart.yaml은 아래처럼 구성했다:

apiVersion: v2
name: kube-prometheus-stack
description: kube-prometheus-stack chart for Kubernetes
type: application
version: 1.0.0
appVersion: "v0.79.2"
dependencies:
    - name: kube-prometheus-stack
      version: "68.1.0"
      repository: "https://prometheus-community.github.io/helm-charts"

이 설정은 Prometheus Community에서 제공하는 kube-prometheus-stack 차트의 68.1.0 버전을 사용하도록 정의하며, 이 차트는 Prometheus, Grafana, Alertmanager, Node Exporter, Kube State Metrics 등 모니터링에 필요한 모든 컴포넌트를 포함한다.

3. values.yaml 생성

values.yaml에는 아래 설정을 사용했다:

kube-prometheus-stack:
    alertmanager:
        enabled: false

    grafana:
        enabled: true
        adminPassword: prom-operator
        persistence:
            enabled: true
            size: 5Gi
        resources:
            requests:
                cpu: 200m
                memory: 512Mi
            limits:
                cpu: 500m
                memory: 1Gi
        ingress:
            enabled: false
        grafana.ini:
            auth:
                disable_login_form: true
            auth.anonymous:
                enabled: true
                org_role: Admin
        additionalDataSources:
            - name: Loki
              type: loki
              url: http://loki-stack.loki-stack.svc.cluster.local:3100

    prometheus:
        enabled: true
        ingress:
            enabled: false
        prometheusSpec:
            retention: 5d
            resources:
                requests:
                    cpu: 500m
                    memory: 2Gi
                limits:
                    cpu: 1
                    memory: 2Gi
            storageSpec:
                volumeClaimTemplate:
                    spec:
                        resources:
                            requests:
                                storage: 20Gi

    prometheusOperator:
        enabled: true
        resources:
            requests:
                cpu: 100m
                memory: 128Mi
            limits:
                cpu: 200m
                memory: 256Mi

    kubeStateMetrics:
        enabled: true
        resources:
            requests:
                cpu: 100m
                memory: 128Mi
            limits:
                cpu: 200m
                memory: 256Mi

    nodeExporter:
        enabled: true
        resources:
            requests:
                cpu: 100m
                memory: 128Mi
            limits:
                cpu: 200m
                memory: 256Mi

    thanosRuler:
        enabled: false

이 설정의 주요 특징은 다음과 같다:

  • Alertmanager: 홈랩 환경에서는 알림 시스템이 필수적이지 않으므로 비활성화하여 리소스를 절약한다.
  • Grafana: 익명 접근을 허용하여 로그인 없이 대시보드를 볼 수 있도록 구성하고, Loki 데이터소스를 미리 추가하여 로그 조회가 가능하도록 한다.
  • Prometheus: 데이터 보존 기간을 5일로 설정하여 디스크 사용량을 제한하고, 20Gi 스토리지를 할당한다.
  • 리소스 제한: 각 컴포넌트에 적절한 CPU와 메모리 제한을 설정하여 클러스터 리소스를 효율적으로 사용한다.

4. 인그레스 설정

접근 경로는 Traefik으로 통합했고, templates/ingressroute.yaml은 아래처럼 두었다:

apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
    name: prometheus-grafana-route
    namespace: kube-prometheus-stack
spec:
    entryPoints:
        - intweb
        - intwebsec
    routes:
        - kind: Rule
          match: Host(`prometheus.injunweb.com`)
          services:
              - name: kube-prometheus-stack-prometheus
                port: 9090
        - kind: Rule
          match: Host(`grafana.injunweb.com`)
          services:
              - name: kube-prometheus-stack-grafana
                port: 80

이 IngressRoute는 intwebintwebsec 엔트리포인트를 사용하여 내부 네트워크에서만 접근 가능하도록 구성하며, prometheus.injunweb.com은 Prometheus 서버로, grafana.injunweb.com은 Grafana로 라우팅된다.

5. 변경사항 커밋 및 배포

설정을 정리한 뒤에는 Git 저장소에 커밋해 반영했다:

git add .
git commit -m "Add kube-prometheus-stack configuration"
git push

ArgoCD가 변경사항을 감지하고 Kube-Prometheus-Stack을 자동으로 배포하며, 설치 상태는 다음 명령어로 확인할 수 있다:

kubectl get pods -n kube-prometheus-stack

정상적으로 설치되면 다음과 비슷한 결과가 표시된다:

NAME                                                       READY   STATUS    RESTARTS   AGE
kube-prometheus-stack-grafana-7dc95d688d-vwm6j             3/3     Running   0          2m
kube-prometheus-stack-kube-state-metrics-c6d6bc845-zrdbp   1/1     Running   0          2m
kube-prometheus-stack-operator-5dc88c8847-9xp6g            1/1     Running   0          2m
kube-prometheus-stack-prometheus-node-exporter-4jlnz       1/1     Running   0          2m
kube-prometheus-stack-prometheus-node-exporter-7m8nj       1/1     Running   0          2m
kube-prometheus-stack-prometheus-node-exporter-c445j       1/1     Running   0          2m
prometheus-kube-prometheus-stack-prometheus-0              2/2     Running   0          2m

Loki-Stack 설치

메트릭 쪽을 정리한 뒤에는 로그 수집용으로 Loki-Stack도 붙였다. Loki는 Prometheus와 비슷한 라벨 기반 인덱싱 방식을 써서 홈랩에서도 비교적 가볍게 다루기 좋았다.

1. 디렉토리 및 파일 구조 생성

mkdir -p k8s-resource/apps/loki-stack/templates
cd k8s-resource/apps/loki-stack

2. Chart.yaml 생성

Chart.yaml은 아래처럼 두었다:

apiVersion: v2
name: loki-stack
description: loki-stack chart for Kubernetes
type: application
version: 1.0.0
appVersion: "v2.9.3"
dependencies:
    - name: loki-stack
      version: "2.10.2"
      repository: "https://grafana.github.io/helm-charts"

3. values.yaml 생성

values.yaml에는 아래 설정을 넣었다:

loki-stack:
    loki:
        enabled: true
        persistence:
            enabled: true
            size: 20Gi
        config:
            limits_config:
                enforce_metric_name: false
                reject_old_samples: true
                reject_old_samples_max_age: 168h
            schema_config:
                configs:
                    - from: 2025-01-16
                      store: boltdb-shipper
                      object_store: filesystem
                      schema: v11
                      index:
                          prefix: index_
                          period: 24h
        resources:
            requests:
                cpu: 200m
                memory: 256Mi
            limits:
                cpu: 1000m
                memory: 1Gi

    promtail:
        enabled: true
    grafana:
        enabled: false
    prometheus:
        enabled: false
    filebeat:
        enabled: false
    fluent-bit:
        enabled: false
    logstash:
        enabled: false
    serviceMonitor:
        enabled: true

이 설정의 주요 특징은 다음과 같다:

  • Loki: 로그 데이터를 저장하기 위한 20Gi 스토리지를 할당하고, 7일(168시간) 이상 된 로그는 거부하도록 설정하여 디스크 사용량을 관리한다.
  • Promtail: 각 노드에서 컨테이너 로그를 수집하여 Loki로 전송하는 DaemonSet 에이전트를 활성화한다.
  • Grafana, Prometheus: 이미 Kube-Prometheus-Stack으로 설치했으므로 비활성화한다.
  • ServiceMonitor: Prometheus가 Loki의 메트릭을 수집할 수 있도록 ServiceMonitor를 활성화한다.

4. 변경사항 커밋 및 배포

이 설정도 정리한 뒤에는 바로 Git에 반영했다:

git add .
git commit -m "Add Loki-Stack configuration"
git push

설치가 완료되면 다음 명령어로 확인할 수 있다:

kubectl get pods -n loki-stack
NAME                            READY   STATUS    RESTARTS   AGE
loki-stack-0                    1/1     Running   0          2m
loki-stack-promtail-xxxxx       1/1     Running   0          2m
loki-stack-promtail-yyyyy       1/1     Running   0          2m

모니터링 시스템 접근

내 로컬 머신에서는 Grafana와 Prometheus에 접속하기 위해 hosts 파일도 함께 수정했다:

192.168.0.200 prometheus.injunweb.com grafana.injunweb.com

이제 웹 브라우저에서 다음 URL로 접속할 수 있다:

  • Grafana: http://grafana.injunweb.com
  • Prometheus: http://prometheus.injunweb.com

Grafana 대시보드 활용

Grafana에 들어가 보니 Kube-Prometheus-Stack이 기본으로 넣어준 대시보드가 이미 꽤 괜찮았다. 나는 좌측 메뉴의 “Dashboards"에서 미리 구성된 대시보드를 주로 살펴봤다.

특히 “General” 폴더 안에 있는 “Kubernetes / Compute Resources” 관련 대시보드들은 클러스터의 CPU, 메모리, 네트워크 사용량을 네임스페이스, 파드, 컨테이너 단위로 파악하는 데 매우 유용하며, “Node Exporter” 관련 대시보드는 각 노드의 디스크 I/O, 네트워크 트래픽, 시스템 부하 같은 상세한 하드웨어 수준의 메트릭을 확인할 수 있어 인프라 모니터링에 도움이 된다.

Loki로 로그 탐색

Grafana에서 Loki 데이터소스를 사용하여 클러스터의 모든 컨테이너 로그를 중앙에서 탐색할 수 있으며, Loki는 LogQL이라는 쿼리 언어를 사용하여 로그를 필터링하고 검색한다.

기본 로그 쿼리

Grafana의 “Explore” 메뉴에서 데이터소스로 “Loki"를 선택해 로그를 보기 시작했고, 자주 쓴 쿼리는 아래와 같았다:

특정 네임스페이스의 로그 조회:

{namespace="kube-system"}

특정 파드의 로그 조회:

{namespace="argocd", pod=~"argocd-server.*"}

에러 로그만 필터링:

{namespace="traefik"} |= "error"

JSON 로그에서 특정 문자열 조회:

{namespace="default"} |= "timeout" | json

Loki를 통해 여러 파드와 노드에 분산된 로그를 중앙에서 관리하고, 문제 발생 시 LogQL 쿼리를 통해 신속하게 원인을 파악할 수 있다.

마치며

이번 글에서는 내 홈랩 쿠버네티스 클러스터에 Kube-Prometheus-Stack과 Loki-Stack을 붙여 메트릭과 로그를 한곳에서 볼 수 있게 만든 과정을 정리했다.

기본 클러스터 설치부터 ArgoCD GitOps 환경, Longhorn 분산 스토리지, Traefik 인그레스 컨트롤러, Vault 시크릿 관리, CI/CD 파이프라인, 그리고 Prometheus와 Grafana, Loki를 활용한 모니터링 시스템까지 구성하면서 홈랩의 기본 골격을 어느 정도 갖추게 되었다. 이제 이 인프라를 바탕으로 클라우드 서비스 비용 부담 없이 프로덕션과 비슷한 쿠버네티스 환경에서 다양한 프로젝트를 테스트하고 개발할 수 있다.