IMG-LOGO
공지사항 :

Loki + Promtail/Grafana Alloy + Grafana로 FastAPI, Nginx, Kubernetes 로그를 한곳에서 검색하고 장애 원인을 분석

lmkfox - 2026-09-11 06:49:16 2 Views 0 Comment

Kubernetes 로그 중앙화 실습

Loki + Promtail/Grafana Alloy + Grafana로 FastAPI, Nginx, Kubernetes 로그를 한곳에서 검색하고 장애 원인을 분석하기

Kubernetes 환경에서 애플리케이션이 하나뿐일 때는 kubectl logs만으로도 충분할 수 있다.

하지만 실제 운영 환경에서는 상황이 달라진다.

FastAPI Pod가 여러 개 실행되고 있고, Nginx Ingress가 요청을 처리하며, PostgreSQL과 Redis 같은 인프라도 함께 운영된다면 장애가 발생했을 때 여러 Pod의 로그를 각각 확인해야 한다.

예를 들어 FastAPI Pod가 다음과 같이 3개 실행되고 있다고 가정해보자.

backend-7d8f6c9f5-abcde
backend-7d8f6c9f5-fghij
backend-7d8f6c9f5-klmno

사용자 요청이 어느 Pod로 전달되었는지 모르는 상황에서 특정 요청의 오류를 찾으려면 각각의 로그를 확인해야 한다.

kubectl logs backend-7d8f6c9f5-abcde
kubectl logs backend-7d8f6c9f5-fghij
kubectl logs backend-7d8f6c9f5-klmno

Pod가 수십 개, 수백 개로 증가하면 이런 방식은 사실상 불가능하다.

이 문제를 해결하기 위해 사용하는 대표적인 기술이 **로그 중앙화(Log Centralization)**다.

이번 실습에서는 다음 구조를 구축한다.

                         Kubernetes Cluster
                                |
             +------------------+------------------+
             |                  |                  |
        FastAPI Pod         Nginx Pod          Other Pods
             |                  |                  |
             +------------------+------------------+
                                |
                         Container Logs
                                |
                    Node Log Collector
                                |
                    Grafana Alloy
                                |
                                v
                         +-------------+
                         |    Loki     |
                         | Log Storage |
                         +-------------+
                                |
                                v
                         +-------------+
                         |   Grafana   |
                         | Log Search  |
                         +-------------+
                                |
                                v
                       장애 원인 분석

1. Kubernetes 로그는 어디에 저장되는가?

먼저 Kubernetes 로그 구조를 이해해야 한다.

컨테이너가 stdout 또는 stderr로 로그를 출력하면 Kubernetes가 해당 로그를 관리한다.

예를 들어 FastAPI에서 다음과 같이 로그를 출력한다고 하자.

print("API request received")

또는 Python logging을 사용한다.

import logging

logger = logging.getLogger(__name__)

logger.info("API request received")
logger.error("Database connection failed")

컨테이너에서 출력된 로그는 일반적으로 Node의 컨테이너 로그 파일로 저장된다.

대표적으로 다음과 같은 형태를 생각할 수 있다.

/var/log/containers/

그리고 Kubernetes의 Pod 로그는 다음 명령으로 확인할 수 있다.

kubectl logs <pod-name>

예:

kubectl logs backend-7d8f6c9f5-abcde

실시간 로그는 다음과 같이 확인한다.

kubectl logs -f backend-7d8f6c9f5-abcde

문제는 이 로그가 기본적으로 중앙 로그 시스템이 아니라 해당 Kubernetes 환경의 Node와 컨테이너 로그 구조에 의존한다는 것이다.

따라서 운영 환경에서는 별도의 로그 수집 시스템을 구축하는 것이 좋다.


2. 로그 중앙화가 필요한 이유

운영 서버에서 장애가 발생했다고 생각해보자.

사용자가 다음과 같은 오류를 보고한다.

로그인 버튼을 눌렀는데 500 오류가 발생합니다.

운영자는 다음을 확인해야 한다.

Nginx
  ↓
Ingress
  ↓
FastAPI
  ↓
Redis
  ↓
PostgreSQL

문제가 어디에서 발생했는지 확인해야 한다.

기존 방식이라면 다음처럼 각각 확인해야 한다.

kubectl logs nginx-pod
kubectl logs backend-pod-1
kubectl logs backend-pod-2
kubectl logs backend-pod-3
kubectl logs redis-pod

로그 중앙화 환경에서는 Grafana에서 한 번에 검색할 수 있다.

namespace="tax-prod"
app="backend"
level="error"

결과:

07:21:31 Database connection failed
07:21:31 sqlalchemy.exc.OperationalError
07:21:32 POST /api/login 500

즉,

문제 발생
   ↓
로그 검색
   ↓
오류 로그 확인
   ↓
관련 Pod 확인
   ↓
원인 분석

과정을 하나의 화면에서 수행할 수 있다.


3. Loki란 무엇인가?

Loki는 Grafana 생태계에서 사용하는 로그 집계 시스템이다.

Prometheus와 비교하면 이해하기 쉽다.

시스템 목적
Prometheus Metrics 저장
Loki Logs 저장
Grafana Metrics/Logs 시각화
Alertmanager Alert 전달

구조적으로 보면 다음과 같다.

Metrics

Application
    |
    v
Prometheus
    |
    v
Grafana


Logs

Application
    |
    v
Alloy
    |
    v
Loki
    |
    v
Grafana

Loki의 중요한 특징은 로그 전체를 무겁게 인덱싱하기보다는 라벨(label)을 중심으로 로그를 찾는 구조라는 점이다.

예를 들어 다음과 같은 라벨을 사용할 수 있다.

namespace="tax-prod"
app="backend"
container="backend"
pod="backend-xxx"

그리고 실제 로그 내용은 Loki에서 조회한다.


4. Loki와 Elasticsearch의 차이

로그 중앙화라고 하면 Elasticsearch도 많이 사용한다.

대표적인 구성은 다음과 같다.

Fluent Bit
    ↓
Elasticsearch
    ↓
Kibana

또 다른 대표적인 구성은 다음과 같다.

Grafana Alloy
    ↓
Loki
    ↓
Grafana

간단하게 비교하면 다음과 같다.

항목 Loki Elasticsearch
Grafana 연동 매우 좋음 가능
구조 Label 중심 검색/인덱스 중심
운영 난이도 상대적으로 낮음 상대적으로 높음
Kubernetes 로그 적합 적합
Metrics 연계 Prometheus와 매우 자연스러움 별도 구성
리소스 사용 비교적 가벼움 상대적으로 큼

소규모 또는 중간 규모 Kubernetes 환경에서 Prometheus + Grafana를 이미 사용하고 있다면 Loki를 추가하는 방식이 매우 자연스럽다.


5. Promtail과 Grafana Alloy

Loki에 로그를 보내기 위해 로그 수집기가 필요하다.

과거에는 Promtail이 대표적인 선택이었다.

구조는 다음과 같다.

Node
 |
 +-- /var/log/containers
 |
 v
Promtail
 |
 v
Loki

하지만 현재 Grafana 생태계에서는 Grafana Alloy를 새로운 텔레메트리 수집기로 사용하는 방향이 중요하다.

따라서 새로운 환경을 구축한다면 Alloy 중심으로 설계하는 것을 권장할 수 있다.

개념적으로는 다음과 같다.

Kubernetes Node
      |
      | Container Logs
      v
Grafana Alloy
      |
      | Push
      v
     Loki
      |
      v
   Grafana

기존 환경에서 Promtail을 이미 사용하고 있다면 당장 모두 변경해야 한다는 의미는 아니다.

다만 신규 구축에서는 Alloy를 우선 검토하는 것이 좋다.


6. 전체 아키텍처

이번 실습에서는 다음과 같은 환경을 가정한다.

Kubernetes Cluster

namespace: tax-prod

                Internet
                   |
                   v
              Ingress
                   |
                   v
                Nginx
                   |
                   v
             FastAPI Service
                   |
            +------+------+
            |             |
            v             v
        FastAPI-1      FastAPI-2
            |             |
            +------+------+
                   |
             PostgreSQL
                   |
                 Redis


Logging

FastAPI/Nginx/PostgreSQL/Redis
             |
             v
      Container stdout
             |
             v
     Kubernetes Node
             |
             v
      Grafana Alloy
             |
             v
            Loki
             |
             v
          Grafana

7. Helm으로 Loki 설치

Kubernetes 환경에서는 Helm을 사용하는 것이 편리하다.

먼저 Grafana Helm Repository를 등록한다.

helm repo add grafana https://grafana.github.io/helm-charts

Repository 정보를 업데이트한다.

helm repo update

Loki용 Namespace를 생성한다.

kubectl create namespace logging

Namespace를 확인한다.

kubectl get namespace

8. Loki 설치

실제 운영 환경에서는 Loki의 저장 방식과 복제 구조를 충분히 검토해야 한다.

학습 및 테스트 환경에서는 비교적 간단한 구성으로 시작할 수 있다.

예를 들어 Helm values 파일을 만든다.

vi loki-values.yaml

예제:

deploymentMode: SingleBinary

loki:
  auth_enabled: false

singleBinary:
  replicas: 1

  persistence:
    enabled: true
    size: 20Gi

  resources:
    requests:
      cpu: 100m
      memory: 256Mi
    limits:
      cpu: 500m
      memory: 1Gi

read:
  replicas: 0

write:
  replicas: 0

backend:
  replicas: 0

설치한다.

helm upgrade --install loki grafana/loki \
  -n logging \
  -f loki-values.yaml

설치 상태를 확인한다.

kubectl get pods -n logging

예:

NAME                    READY   STATUS
loki-0                  1/1     Running

Service도 확인한다.

kubectl get svc -n logging

9. Loki 로그 저장 구조

Loki는 로그를 저장해야 하기 때문에 운영 환경에서는 Storage 설계가 중요하다.

개념적으로 다음과 같다.

Loki
 |
 +-- Index
 |
 +-- Chunks
 |
 +-- Object Storage

개발 환경에서는 로컬 Persistent Volume을 사용할 수 있지만 운영 환경에서는 Object Storage 기반 구성을 고려하는 것이 일반적이다.

예:

Loki
 |
 +---- Object Storage
        |
        +-- S3
        +-- MinIO
        +-- Cloud Storage

따라서 운영 환경에서는 다음을 고려해야 한다.

로그 보존 기간
로그 용량
스토리지 비용
복제
백업
장애 복구

10. Grafana에 Loki 연결

앞서 Prometheus + Grafana를 구축했다면 Grafana에 Loki Data Source를 추가한다.

Grafana에서:

Connections
   ↓
Data Sources
   ↓
Add data source
   ↓
Loki

Loki Service 주소를 입력한다.

예:

http://loki.logging.svc.cluster.local:3100

Kubernetes DNS 구조는 다음과 같다.

loki
 |
 +-- logging namespace
 |
 +-- port 3100

따라서 클러스터 내부에서는 다음 주소로 접근할 수 있다.

http://loki.logging.svc.cluster.local:3100

연결 테스트 후 정상적으로 연결되면 Grafana에서 Loki 로그를 조회할 수 있다.


11. Grafana Alloy 설치

이제 로그 수집기를 구성한다.

Alloy는 Kubernetes Node의 컨테이너 로그를 읽어서 Loki로 전달하는 역할을 한다.

구조는 다음과 같다.

/var/log/containers/*.log
              |
              v
        Grafana Alloy
              |
              v
             Loki

Alloy는 모든 Node에서 로그를 수집해야 하기 때문에 일반적으로 DaemonSet 형태로 실행한다.

Node 1
 └── Alloy

Node 2
 └── Alloy

Node 3
 └── Alloy

Helm을 이용하여 설치할 수 있다.

helm repo add grafana https://grafana.github.io/helm-charts
helm repo update

Namespace를 준비한다.

kubectl create namespace logging

환경에 맞는 Alloy Helm values를 구성한 뒤 설치한다.

helm upgrade --install alloy grafana/alloy \
  -n logging \
  -f alloy-values.yaml

확인한다.

kubectl get pods -n logging -o wide

정상이라면 각 Node에 Alloy Pod가 실행된다.

NAME          NODE
alloy-abc     node-01
alloy-def     node-02
alloy-ghi     node-03

12. Alloy가 해야 하는 일

Alloy가 수행하는 역할을 단계별로 보면 다음과 같다.

1. Node 로그 파일 탐색
       ↓
2. Kubernetes metadata 확인
       ↓
3. namespace 확인
       ↓
4. pod 확인
       ↓
5. container 확인
       ↓
6. label 생성
       ↓
7. Loki로 전송

예를 들어 실제 로그가 다음과 같다고 하자.

2026-09-10 07:20:31 Database connection failed

Alloy가 Kubernetes metadata를 붙인다.

namespace=tax-prod
pod=backend-7d8f6c9f5-abcde
container=backend
app=backend

그러면 Grafana에서 다음처럼 검색할 수 있다.

namespace="tax-prod"
app="backend"

13. FastAPI 로그 만들기

FastAPI는 로그를 stdout으로 출력하는 구조로 만드는 것이 중요하다.

예:

import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(message)s"
)

logger = logging.getLogger(__name__)

API에서 로그를 출력한다.

@app.get("/health")
def health():
    logger.info("health check")
    return {"status": "ok"}

오류 발생 시:

logger.error("Database connection failed")

중요한 점은 로그를 컨테이너 내부 파일에만 저장하지 않는 것이다.

좋은 Kubernetes 애플리케이션 구조는 다음과 같다.

FastAPI
   |
   +-- stdout
   |
   v
Kubernetes
   |
   v
Alloy
   |
   v
Loki

14. Nginx 로그

Nginx에서도 access log와 error log를 확인할 수 있다.

예:

10.0.0.15 - - [10/Sep/2026:07:21:31 +0900]
"GET /api/users HTTP/1.1" 200

오류가 발생하면:

"GET /api/users HTTP/1.1" 502

이런 로그는 장애 분석에서 매우 중요하다.

예를 들어:

Nginx
  |
  +-- 502 Bad Gateway

라면 다음 단계로 FastAPI Pod 상태를 확인해야 한다.


15. Grafana에서 Loki 로그 검색

Grafana에서:

Explore
   ↓
Loki

를 선택한다.

가장 기본적인 검색은 다음과 같다.

{namespace="tax-prod"}

특정 애플리케이션:

{namespace="tax-prod", app="backend"}

특정 Pod:

{pod="backend-7d8f6c9f5-abcde"}

Nginx:

{app="nginx"}

16. Error 로그 검색

로그 내용에 error가 포함된 로그를 검색할 수도 있다.

{namespace="tax-prod"} |= "error"

대소문자를 고려하여 애플리케이션 로그 구조에 맞게 검색하는 것이 좋다.

예:

{app="backend"} |= "ERROR"

Database 오류:

{app="backend"} |= "Database"

HTTP 500:

{app="backend"} |= "500"

17. Nginx 5xx 오류 찾기

Nginx 로그를 검색한다고 하자.

{app="nginx"} |= "500"

또는:

{app="nginx"} |= "502"

다음과 같이 장애 상황을 빠르게 확인할 수 있다.

Nginx
 |
 +-- 200
 +-- 200
 +-- 200
 +-- 502
 +-- 502
 +-- 502

502가 갑자기 증가했다면 Backend 문제일 가능성이 높다.


18. FastAPI 500 오류 분석

FastAPI에서 다음과 같은 로그가 발생했다고 가정한다.

ERROR Database connection failed
ERROR sqlalchemy.exc.OperationalError
ERROR POST /api/users 500

Grafana에서:

{app="backend"} |= "500"

으로 검색한다.

그다음 같은 시간대의 로그를 확인한다.

07:21:31 Database connection failed
07:21:31 OperationalError
07:21:31 POST /api/users 500

여기에서 단순히 HTTP 500이라는 결과만 보는 것이 아니라 그 앞에서 발생한 데이터베이스 오류까지 추적할 수 있다.


19. Kubernetes 로그와 애플리케이션 로그 연결

로그 중앙화의 핵심은 Kubernetes metadata다.

예를 들어 다음과 같은 정보가 있다고 하자.

namespace = tax-prod
deployment = backend
pod = backend-xxx
container = backend
node = worker-01

장애가 발생했을 때:

Grafana
  |
  +-- namespace
  +-- deployment
  +-- pod
  +-- container
  +-- node

를 기준으로 로그를 좁혀갈 수 있다.

예:

{namespace="tax-prod", container="backend"}

특정 Node:

{node="worker-01"}

특정 Pod:

{pod="backend-xxx"}

20. 로그 검색과 Prometheus Metrics를 같이 사용하기

이 부분이 Kubernetes 모니터링에서 매우 중요하다.

Prometheus는 숫자를 본다.

CPU
Memory
Request
Error Rate
Pod Count

Loki는 로그를 본다.

ERROR
Exception
Connection failed
Timeout

Grafana에서는 이 둘을 함께 사용할 수 있다.

예를 들어 Prometheus에서:

FastAPI HTTP 500 증가

가 확인되었다고 하자.

그렇다면 Loki에서 같은 시간대를 검색한다.

{app="backend"} |= "500"

결과:

Database connection timeout

다시 PostgreSQL 로그를 검색한다.

{app="postgres"} |= "connection"

결과:

too many connections

장애 원인이 연결 수 부족이라는 것을 확인할 수 있다.

즉,

Prometheus
   |
   | "500 오류 증가"
   v
Grafana
   |
   v
Loki
   |
   | "Database timeout"
   v
PostgreSQL 로그
   |
   v
장애 원인 확인

이것이 Observability의 핵심이다.


21. 장애 테스트 1 — FastAPI Pod 삭제

실제 장애 상황을 만들어보자.

먼저 Pod를 확인한다.

kubectl get pods -n tax-prod

예:

backend-abc   Running
backend-def   Running

Pod 하나를 삭제한다.

kubectl delete pod backend-abc -n tax-prod

Deployment가 새로운 Pod를 생성한다.

kubectl get pods -n tax-prod -w

이때 Loki에서 해당 Pod 로그를 확인한다.

{namespace="tax-prod", app="backend"}

기존 Pod와 새 Pod의 로그를 비교할 수 있다.


22. 장애 테스트 2 — FastAPI CrashLoopBackOff

의도적으로 애플리케이션을 종료하도록 만들어보자.

예:

raise Exception("Intentional failure")

Pod 상태가 다음과 같이 변경될 수 있다.

CrashLoopBackOff

확인:

kubectl get pods -n tax-prod

그리고:

kubectl logs <pod-name> -n tax-prod

Grafana Loki에서도 오류 로그를 확인한다.

{app="backend"} |= "Intentional failure"

이렇게 Kubernetes 상태와 실제 애플리케이션 로그를 연결할 수 있다.


23. 장애 테스트 3 — Nginx 502

Backend가 정상적으로 응답하지 않는 상황을 만들어보자.

구조:

Client
  |
  v
Nginx
  |
  X
FastAPI

Nginx에서는:

502 Bad Gateway

가 발생할 수 있다.

Grafana에서:

{app="nginx"} |= "502"

검색한다.

그리고 Backend 로그:

{app="backend"}

를 같은 시간대에 확인한다.

이렇게 하면:

Nginx 502
   ↓
Backend connection failure
   ↓
FastAPI Pod 장애

와 같은 흐름을 확인할 수 있다.


24. 장애 테스트 4 — PostgreSQL 연결 장애

PostgreSQL Pod를 삭제해본다.

kubectl delete pod <postgres-pod> -n tax-prod

PostgreSQL이 재기동되는 동안 FastAPI에서 DB 요청을 발생시킨다.

FastAPI 로그:

Database connection failed
connection refused
timeout

Grafana에서:

{app="backend"} |= "connection"

을 검색한다.

PostgreSQL 로그도 확인한다.

{app="postgres"}

이렇게 하면 하나의 장애를 여러 컴포넌트의 로그를 통해 추적할 수 있다.


25. 장애 분석의 실제 순서

운영자가 가장 많이 사용하는 방식으로 정리하면 다음과 같다.

① 사용자 장애 신고
        |
        v
② Grafana Dashboard
        |
        v
③ Prometheus Metrics 확인
        |
        v
④ Error Rate 증가 확인
        |
        v
⑤ Loki 로그 검색
        |
        v
⑥ 오류 발생 Pod 확인
        |
        v
⑦ Kubernetes 상태 확인
        |
        v
⑧ 관련 서비스 로그 확인
        |
        v
⑨ 근본 원인 분석

예를 들어:

500 증가
  ↓
FastAPI 로그 검색
  ↓
DB Timeout
  ↓
PostgreSQL 로그 확인
  ↓
Connection Limit 초과
  ↓
DB 연결 관리 설정 확인

이런 방식으로 장애를 좁혀간다.


26. 로그에 무엇을 남겨야 하는가?

로그 중앙화 시스템을 구축하는 것만으로 좋은 운영 환경이 만들어지는 것은 아니다.

애플리케이션 로그 자체가 잘 설계되어야 한다.

좋은 로그는 최소한 다음 정보를 포함하는 것이 좋다.

시간
로그 레벨
서비스
요청 정보
사용자 요청
처리 결과
오류 정보
Trace ID

예:

2026-09-10T07:21:31
ERROR
backend
POST /api/users
request_id=abc123
Database connection failed

가능하다면 JSON 로그가 더 좋다.

{
  "timestamp": "2026-09-10T07:21:31+09:00",
  "level": "ERROR",
  "service": "backend",
  "method": "POST",
  "path": "/api/users",
  "request_id": "abc123",
  "message": "Database connection failed"
}

이렇게 하면 로그 검색과 분석이 훨씬 쉬워진다.


27. 로그 레벨 설계

대표적인 로그 레벨은 다음과 같다.

Level 의미
DEBUG 상세 디버깅
INFO 정상적인 주요 동작
WARNING 주의가 필요한 상황
ERROR 오류 발생
CRITICAL 심각한 장애

운영 환경에서 모든 로그를 DEBUG로 남기면 로그 양이 지나치게 증가할 수 있다.

일반적으로:

개발
DEBUG

운영
INFO / WARNING / ERROR

와 같이 환경별로 로그 레벨을 조절하는 방법을 고려할 수 있다.


28. 로그 보존 기간

로그는 계속 쌓인다.

예를 들어 하루에 10GB의 로그가 발생한다면:

1일   = 10GB
7일   = 70GB
30일  = 300GB

따라서 반드시 보존 정책을 정해야 한다.

예:

Application Log
    ↓
7일 Hot Log
    ↓
30일 Archive
    ↓
90일 삭제

운영 환경에서는 업무와 보안 요구사항에 따라 보존 기간을 결정해야 한다.


29. 로그 비용을 줄이는 방법

로그 중앙화 시스템에서는 불필요한 로그를 줄이는 것도 중요하다.

예를 들어 다음과 같은 로그가 하루 수백만 건 발생한다고 하자.

GET /health 200

Kubernetes readiness/liveness probe 때문에 발생하는 로그라면 실제 장애 분석에는 큰 도움이 되지 않을 수 있다.

따라서 다음과 같은 방법을 고려할 수 있다.

불필요한 DEBUG 로그 제거
Health Check 로그 제외
로그 레벨 조정
Label 개수 최소화
보존 기간 설정

특히 Loki에서는 label을 지나치게 많이 생성하지 않는 것이 중요하다.


30. Loki Label 설계에서 주의할 점

예를 들어 다음처럼 사용자 ID를 Label로 사용하는 것은 좋지 않다.

user_id=123456
user_id=123457
user_id=123458
...

값의 종류가 무한히 증가하는 Label을 사용하면 문제가 발생할 수 있다.

이런 값을 흔히 High Cardinality 문제라고 한다.

따라서 Label은 비교적 제한된 값을 사용하는 것이 좋다.

좋은 예:

namespace
app
container
node
environment

주의해야 할 예:

user_id
request_id
order_id
session_id

이런 정보는 로그 본문에 넣고 필요할 때 검색하는 방식을 고려한다.


31. Prometheus + Loki + Grafana 통합

이제 지금까지 구축한 모니터링 시스템을 하나로 합쳐보자.

                 Kubernetes
                     |
       +-------------+-------------+
       |                           |
       v                           v
   Application                 Kubernetes
       |                        Metrics
       |                           |
       v                           v
     stdout                   Prometheus
       |
       v
     Alloy
       |
       v
      Loki
       |                           |
       +-------------+-------------+
                     |
                     v
                  Grafana
                     |
          +----------+----------+
          |          |          |
          v          v          v
       Metrics      Logs      Alerts

여기에 Alertmanager까지 연결한다.

Prometheus
    |
    v
Alertmanager
    |
    +-- Email
    +-- Slack
    +-- Webhook
    +-- 기타 알림

이렇게 구성하면 Kubernetes의 관측성이 크게 향상된다.


32. Grafana Dashboard 구성

운영 환경에서는 Grafana Dashboard를 다음처럼 구성할 수 있다.

+------------------------------------------------+
| Kubernetes Overview                            |
+------------------------------------------------+
| CPU             | Memory       | Pod Status    |
| 62%             | 71%          | 20/20         |
+------------------------------------------------+
| Request Rate    | Error Rate   | Latency       |
| 320 req/s       | 0.8%         | 120ms         |
+------------------------------------------------+
| Recent Errors                                  |
| Database timeout                               |
| Redis connection refused                       |
| HTTP 500                                       |
+------------------------------------------------+
| Logs                                            |
| 07:21:31 ERROR Database connection failed      |
| 07:21:32 ERROR POST /api/users 500             |
+------------------------------------------------+

운영자는 Dashboard 하나에서 다음을 확인할 수 있다.

서버 상태
Pod 상태
CPU
Memory
Request
Error
Latency
Logs

33. Metrics와 Logs를 함께 봐야 하는 이유

예를 들어 CPU가 95%라고 하자.

Metrics만 보면:

CPU = 95%

까지만 알 수 있다.

하지만 Loki에서 로그를 확인하면:

ERROR
Worker timeout
Request processing delayed

를 확인할 수 있다.

반대로 로그에 다음이 있다고 하자.

Database connection timeout

Metrics를 보면:

PostgreSQL Connection
95%

일 수 있다.

즉,

Metrics = 무슨 문제가 발생했는가?

Logs = 왜 문제가 발생했는가?

라는 관점으로 생각하면 이해하기 쉽다.

실제 운영에서는 둘을 함께 사용하는 것이 중요하다.


34. Kubernetes Observability의 3가지 축

지금까지 배운 내용을 정리하면 Kubernetes Observability는 크게 세 가지로 볼 수 있다.

             Observability
                  |
       +----------+----------+
       |          |          |
       v          v          v
    Metrics      Logs       Traces
       |          |          |
 Prometheus      Loki      Tempo
       |          |          |
       +----------+----------+
                  |
               Grafana

Metrics

CPU
Memory
Request
Error Rate
Latency
Pod Count

Logs

ERROR
Exception
Timeout
Connection refused

Traces

Request
  ↓
Nginx
  ↓
FastAPI
  ↓
Redis
  ↓
PostgreSQL

이번 실습에서는 Metrics와 Logs를 구축했다.

다음 단계에서는 Distributed Tracing을 추가할 수 있다.


35. 최종 아키텍처

이번 실습의 최종적인 Kubernetes 운영 구조를 정리하면 다음과 같다.

                         Internet
                            |
                            v
                       Kubernetes
                            |
              +-------------+-------------+
              |                           |
              v                           v
           Ingress                    Applications
              |                           |
              v                    +------+------+
            Nginx                   |             |
              |                  FastAPI        Redis
              v
           FastAPI
              |
              v
         PostgreSQL


Metrics
--------
Node / Pod / Application
              |
              v
         Prometheus
              |
              v
           Grafana


Logs
--------
Containers
     |
     v
Grafana Alloy
     |
     v
    Loki
     |
     v
  Grafana


Alerts
--------
Prometheus
     |
     v
Alertmanager
     |
     +---- Email
     +---- Slack
     +---- Webhook

이제 단순히 Kubernetes에 애플리케이션을 배포하는 단계에서 벗어나 운영 상태를 관측하고 장애를 분석할 수 있는 환경이 만들어진다.


36. 시스템 엔지니어 관점에서 반드시 확인할 항목

Kubernetes 로그 중앙화를 구축했다면 다음 항목을 확인해야 한다.

로그 수집

kubectl get pods -n logging

Alloy가 모든 Node에서 실행되는지 확인한다.

Loki

kubectl get pods -n logging
kubectl get svc -n logging

Grafana

Loki Data Source가 정상인지 확인한다.

로그 검색

{namespace="tax-prod"}

오류 검색

{namespace="tax-prod"} |= "ERROR"

Pod별 검색

{pod="backend-xxx"}

장애 테스트

kubectl delete pod <backend-pod> -n tax-prod

로그 유입 확인

Pod 재생성 이후 Grafana에서 새로운 Pod 로그가 정상적으로 나타나는지 확인한다.


37. 최종 정리

이번 실습에서는 Kubernetes 로그 중앙화의 기본 구조를 살펴봤다.

핵심 구성 요소는 다음과 같다.

Grafana Alloy
      ↓
    Loki
      ↓
   Grafana

각각의 역할은 명확하다.

Alloy
= 로그 수집

Loki
= 로그 저장 및 조회

Grafana
= 로그 시각화 및 검색

그리고 이전에 구축한 시스템과 연결하면:

Prometheus
= Metrics

Loki
= Logs

Alertmanager
= Alerts

Grafana
= 통합 Dashboard

구조가 된다.

가장 중요한 것은 단순히 로그를 저장하는 것이 아니다.

실제 운영에서 중요한 것은 다음과 같은 장애 분석 과정이다.

사용자 오류 발생
      ↓
Grafana 확인
      ↓
Error Rate 증가
      ↓
Loki 로그 검색
      ↓
FastAPI 오류 확인
      ↓
PostgreSQL 로그 확인
      ↓
DB Connection 문제 발견
      ↓
Kubernetes 상태 확인
      ↓
근본 원인 해결

이 과정을 자동화하고 체계화하면 Kubernetes 환경에서 장애가 발생했을 때 단순히 "Pod가 죽었습니다"라고 확인하는 수준을 넘어 언제, 어떤 서비스에서, 어떤 요청이, 어떤 이유로 실패했는지 추적할 수 있게 된다.

특히 시스템 엔지니어에게 중요한 것은 다음 세 가지를 연결해서 보는 능력이다.

          Metrics
             |
             v
        이상 현상 발견
             |
             v
           Logs
             |
             v
        오류 원인 확인
             |
             v
         Kubernetes
             |
             v
        실제 장애 조치

여기에 다음 단계로 Distributed Tracing을 추가하면 다음과 같은 완성된 Observability 구조를 만들 수 있다.

                 Kubernetes
                     |
        +------------+------------+
        |            |            |
        v            v            v
     Metrics        Logs        Traces
        |            |            |
   Prometheus       Loki        Tempo
        |            |            |
        +------------+------------+
                     |
                     v
                  Grafana
                     |
                     v
             장애 원인 분석


댓글