| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
- docker
- 백엔드
- 트랜잭션
- GitOps
- redis
- mysql
- selector
- prometheus
- NIO
- RDBMS
- grafana
- 데이터베이스
- Kubernetes
- Java
- DevOps
- 성능 최적화
- SpringBoot
- 백엔드개발
- monitoring
- jvm
- 동시성제어
- webflux
- CloudNative
- Kotlin
- 성능최적화
- kafka
- spring boot
- JPA
- netty
- helm
- Today
- Total
유성
[클라우드 네이티브] 1편: 고가용성 로깅 아키텍처(ELK) 설계 전략 본문
과거 온프레미스 환경에서 고가용성을 갖춘 ELK 스택을 구축하고 유지하는 것은 꽤나 큰 복잡성을 지니고 있었다.
단순히 서버를 띄우는 것을 넘어, 인프라와 애플리케이션 사이의 수많은 '수동 연결 고리'를 관리해야 했기 때문이다.
이 글에서는 과거의 ELK 구성 방식과 클라우드 네이티브 환경의 ELK 구성을 비교해 본다.
1. 과거 온프레미스 환경: 운영의 발목을 잡았던 로깅의 복잡성
과거 온프레미스 환경에서는 고가용성 ELK를 구성할 때 꽤 큰 복잡성을 가지고 있었다.

이러한 구성에서 애플리케이션을 확장한다고 가정해 보자.
애플리케이션을 확장한다는 것은 새로운 물리 서버나, VM을 할당받는 것을 의미한다.
이때마다 다음과 같은 작업이 반복되었다.
- Filebeat 수동 배포: 새 서버가 뜰 때마다 Filebeat 설치, 서비스 등록(systemd 등), 실행 권한 설정을 매번 수행해야 한다.
- 설정의 파편화: 서버마다 로그 경로가 미세하게 다르거나, 특정 서버의 Filebeat 설정만 누락되는 등 구성관리 문제가 빈번했다.
- 배포 스크립트의 복잡성: 쉘 스크립트로 이 과정을 관리했는데, 앱이 늘어날수록 이 스크립트 자체가 관리 포인트가 되었다.
로그 디렉터리 및 권한 관리의 복잡성
이뿐만이 아니다. 앱과 Filebeat의 물리적인 설정을 맞추는 게 고역이었다.
- 경로 의존성: 앱 설정 파일(logback.xml)에 정의된 로그 경로와 Filebeat의 inputs 경로가 1글자라도 다르면 로그 수집이 불가능하다.
- 권한 문제: 앱 프로세스가 생성한 로그 파일에 Filebeat 프로세스가 접근할 수 있는 권한이 없으면 로그가 유실된다.
- 디스크 풀 장애: 앱 서버의 디스크가 꽉 차면 로그 수집도 멈추고 앱도 죽는다. logrotate 설정도 서버마다 관리해야 한다.
고가용성 구성의 역설
Redis Cluster나 Elasticsearch는 한번 잘 구성해 두면 안정적이지만, 이를 지탱하기 위한 앞단의 복잡성이 높았다.
- Logstash 로드 밸런싱: 여러 대의 Logstash로 부하를 분산하기 위해 L4를 추가하거나 모든 Filebeat에 IP를 기입하는 막일이 필요했다.
- Redis 가용성: Redis 노드 장애 시 Filebeat의 재연결 로직이 완벽히 작동하는지 검증하기가 매우 까다로웠다.
고가용성이라고는 하지만 Scale Out을 해야 한다고 하면, 상당히 부담스러운 작업이었다.
2. 클라우드 네이티브 환경에서의 로깅 혁신
과거의 복잡했던 수동 관리 방식에서 벗어나, 쿠버네티스(k8s) 기반의 클라우드 네이티브 환경은 '자동화'와 '추상화'를 통해 로깅 시스템의 패러다임을 바꾸었다.
노드 쪽 구성을 먼저 보자.
(Kafka와 이후 로깅 서비스는 Node 안에 위치해 있지만, 이해를 위해 밖으로 뺐다.)

핵심 변화: 앱과 수집기의 강한 결합 해제
과거에는 앱 하나당 Filebeat 하나를 붙여야 했지만, 이제는 애플리케이션과 로깅 에이전트가 서로의 존재를 몰라도 되는 구조로 발전했다.
데몬셋(DaemonSet) 인프라 관리의 자동화
- 노드당 하나의 Filebeat: 앱이 몇 개가 되던, 데몬셋은 각 노드(서버)에 딱 하나의 Filebeat만 띄운다.
- 스케일 아웃의 자유: 서버(Node)가 늘어나면 k8s가 알아서 Filebeat를 복제해서 배치한다. 운영자는 더 이상 Filebeat가 깔렸는지 걱정할 필요가 없다.
/var/log/containers 로그 링크 모음
쿠버네티스는 모든 Pod의 로그를 노드의 특정 경로에 링크(Link)하여 저장한다.

- 통합 뷰 제공: 각기 다른 Pod들이 내보내는 로그가 /var/log/containers라는 하나의 '심볼릭 링크'에 표준화된 이름으로 모인다.
- 단일 감시 포인트: Filebeat는 이제 여러 경로를 헤맬 필요 없이, 이 표준화된 경로 한 곳만 바라보면 노드 내의 모든 로그를 낚아챌 수 있다.
3. 로깅 파이프라인의 고가용성과 데이터 무결성
노드에서 수집된 로그가 중앙 저장소에 안전하게 도달하기 위해서는 Kafka - Logstash - Elasticsearch로 이어지는 구간에서 장애 대응과 데이터 중복 방지가 동시에 이루어져야 한다.

클러스터 구성과 오퍼레이터의 역할
이 파이프라인의 모든 서비스는 가용성 극대화를 위해 최소 3개 이상의 노드로 클러스터링했다.
- 의사결정 보장: 모든 서비스는 최소 3개 이상의 노드로 구성되어, 특정 노드 다운 시에도 과반수 원칙에 따라 리더를 선출하고 중단 없이 작동한다.
- 운영 자동화: K8s 오퍼레이터가 상태를 실시간 감지하여 장애 시 파드를 자동 재생성하거나 설정을 복구한다.
Kafka와 Logstash 분산 처리와 장애 복구
Kafka에서 Logstash로 로그가 전달되는 과정에서는 '일감의 배분'과 '중단 없는 수집'이 핵심이 된다.
- 컨슈머 그룹: 여러 대의 Logstash를 동일한 group_id로 묶어 운영한다. 이를 통해 Kafka의 파티션들이 각 Logstash에 중복 없이 할당되어 대량의 로그를 병렬로 빠르게 처리한다.
- 장애 시 리밸런싱: Logstash 인스턴스에 순단이 발생하면, Kafka는 이를 감지하고 해당 인스턴스가 담당하던 파티션을 다른 정상적인 Logstash에게 넘겨 수집의 연속성을 지킨다. (Commit 시점에 따른 중복이 발생할 수 있다)
- 백프레셔: 만약 Kafka 없이 filebeat가 ES로 데이터를 쏟아부으면 장애가 발생할 수 있다. Kafka가 버퍼 역할을 하여 소비자(Logstash)가 처리 속도에 맞게 흐름을 조절한다.
Elasticsearch 멱등성을 통한 최종 방어
Kafka의 최소 한번 전달 특성상, Logstash가 커밋을 하기 전 재시작되면 동일한 로그가 중복 전송될 수 있다.
이를 해결하기 위해 Kafka의 '정확히 한번 전달'을 사용하지 않고(리소스 사용량 증가) 저장소 수준의 멱등성을 확보하는 방식으로 처리한다.
- Fingerprinting 생성: Logstash 필터 단계에서 로그의 원본 데이터(시간, Pod, 메시지 등)를 조합해 고유한 해시값을 생성한다.
- 문서 ID 강제 지정: 생성된 해시값을 Elasticsearch의 고유 문서 ID(_id)로 지정한다.
- 중복 제거 원리: 동일한 로그가 다시 들어와도 ES는 기존에 존재하는 ID임을 확인하고 쓰기 대신 수정(upsert)을 수행한다. 결과적으로 사용자는 중복 없는 정확한 로그 데이터만을 확인하게 된다.
설계 시 고민해야하는 트레이드오프
Fingerprint 기반 멱등성은 완벽하지 않다. 동일한 타임스탬프, Pod, 메시지를 가진 로그가 아주 짧은 시간 안에 두 번 발생하면 해시값이 충돌하여 두 번째 로그가 유실될 수 있다.
이를 최소화하기 위해 해시 조합에 sequence 값을 추가하는 방안을 검토했으나, 모든 로그 수집기가 sequence를 보장하지 않아 최종적으로 타임스탬프를 나노초 단위로 포함하는 방식으로 충돌 가능성을 낮췄다.
로그 생성 시점 NodeId를 seed로 한 TSID를 부여하는 방안도 검토했다. 이 방식은 해시 충돌 가능성을 원천 차단하고 Logstash의 연산 부하도 줄일 수 있다.
다만 애플리케이션 레벨의 코드 변경이 필요하다는 점에서, 앱과 로깅 시스템을 완전히 분리한다는 이 아키텍처의 설계 원칙과 트레이드오프가 존재한다.
또한 Kafka 파티션 수와 Logstash 인스턴스 수의 균형도 중요한 포인트였다.
파티션이 인스턴스보다 많으면 일부 인스턴스에 과부하가 생기고, 적으면 병렬 처리 효율이 떨어진다. 현재는 파티션 수를 Logstash 인스턴스 수의 배수로 유지하는 방법이 있고, 파티션은 한번 늘리면 줄일 수 없기에, 서비스가 안정기에 진입하기 전이라면 1배수를 유지하는 것이 효율적이다.
4. 구성 결과 및 기대 효과
클라우드 네이티브 기반의 로그 집중화 아키텍처를 통해, 우리는 인프라의 복잡성을 숨기고 애플리케이션 개발 본연의 가치에 집중할 수 있는 환경을 구축했다.
개발 생산성 극대화
가장 큰 변화는 애플리케이션 로깅 시스템의 완전한 분리이다.
- 로깅 설정 없음: 새로운 서비스를 배포할 때 Filebeat 설치, 경로 설정, 권한 획득 등 부수적인 작업을 전혀 고려할 필요가 없다.
- 표준 출력 활용: 개발자는 파일 시스템의 복잡한 경로 대신, 단순히 콘솔에 로그를 출력하기만 하면 된다. 이후의 모든 과정은 데몬셋이 자동으로 처리한다.
로그의 규격화와 가독성 향상
단순 텍스트 로그는 분석이 어렵다. 아래와 같이 Logback JSON Encoder를 사용하여 로그 구조를 정리하면, 별도의 복잡한 파싱 로직이 없어도 Logstash와 Elasticsearch에서 데이터를 정밀하게 필터링할 수 있다.
[Spring Boot 로그 표준화 설정 예시]
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<timestamp><timeZone>UTC</timeZone></timestamp>
<version/>
<logLevel/> <loggerName/>
<threadName/>
<message/> <stackTrace/> <context/>
<arguments/>
</providers>
</encoder>
</appender>
- 이점: 모든 로그가 JSON 형태로 수집되므로, Level: ERROR, Thread: main과 같은 필드 단위의 고속 검색이 가능해진다.

강력한 가시성 (Kibana 시각화)
중앙으로 집계된 모든 로그는 Kibana를 통해 한 곳에서 관리된다.
- 실시간 모니터링: 수천 개의 Pod에서 발생하는 로그를 단일 타임라인에서 실시간으로 관제할 수 있다.
- 통합 검색: 특정 trace_id나 사용자 ID를 검색하여 분산된 마이크로서비스 간의 전체 호출 흐름을 한눈에 파악할 수 있다.
- 장애 대응 가속화: 에러 발생 시 로그스태시가 생성한 멱등성 해시값과 표준화된 필드를 바탕으로 중복 없는 정확한 통계를 산출할 수 있다.
5. 마치며
지금까지 온프레미스 환경의 수동적인 로깅 방식이 가졌던 한계를 짚어보고, 이를 클라우드 네이티브 환경에서 어떻게 혁신적으로 해결했는지 살펴보았다.
가장 핵심적인 변화는 로그 관리의 주체가 '사람'에서 '인프라'로 옮겨갔다는 점이다.
과거에는 엔지니어가 로그를 수집하기 위해 서버 구석구석을 찾아다니며 연결 고리를 만들었다면, 이제는 인프라가 스스로 로그를 감지하고 중앙으로 실어 나르는 구조가 되었다.
이어지는 2편에서는 이 복잡해 보이는 Infra 환경(Kafka, Logstash, Elasticsearch)을 Helm Chart와 ArgoCD를 이용해 한 번에, 그리고 코드로 관리(GitOps)하며 구축하는 실전 방법을 알아보자.
2편 : [클라우드 네이티브] 2편: 클라우드 네이티브로 로깅 아키텍처(ELK) 구현(실전)
[클라우드 네이티브] 2편: 클라우드 네이티브로 로깅 아키텍처(ELK) 구현(실전)
[클라우드 네이티브] 1편: 온프레미스에서 클라우드 네이티브로: 로깅 아키텍처(ELK) 설계과거 온프레미스 환경에서 고가용성을 갖춘 ELK 스택을 구축하고 유지하는 것은 꽤나 어려운 작업이였다.
youseong.tistory.com
'DevOps' 카테고리의 다른 글
| [클라우드 네이티브] 3편: 고가용성 모니터링(메트릭) 아키텍처 설계 전략 (0) | 2026.02.07 |
|---|---|
| [클라우드 네이티브] 2편: Helm-Chart, 고가용성 로깅 아키텍처(ELK) 구현(실전) (0) | 2026.02.06 |
| Kubernetes 기반 PostgreSQL 고가용성(HA) 아키텍처 구축 및 카오스 검증 (0) | 2026.02.01 |
| GitHub Actions와 ArgoCD로 완성하는 선언적 쿠버네티스 배포 시스템 (0) | 2026.01.29 |
| 정답 없는 CI/CD, 우리 환경에 맞는 최적의 파이프라인 설계 (0) | 2026.01.23 |