| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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
- JPA
- 성능 최적화
- Java
- NIO
- DevOps
- mysql
- CloudNative
- GitOps
- 백엔드
- 성능최적화
- prometheus
- 데이터베이스
- 백엔드개발
- kafka
- selector
- monitoring
- Kubernetes
- redis
- webflux
- helm
- SpringBoot
- Kotlin
- netty
- spring boot
- grafana
- jvm
- RDBMS
- Today
- Total
유성
[클라우드 네이티브] 2편: Helm-Chart, 고가용성 로깅 아키텍처(ELK) 구현(실전) 본문
[클라우드 네이티브] 1편: 온프레미스에서 클라우드 네이티브로: 로깅 아키텍처(ELK) 설계
과거 온프레미스 환경에서 고가용성을 갖춘 ELK 스택을 구축하고 유지하는 것은 꽤나 어려운 작업이였다.단순히 서버를 띄우는 것을 넘어, 인프라와 애플리케이션 사이의 수많은 '수동 연결 고
youseong.tistory.com
이전 글에서는 온프레미스 환경을 클라우드 네이티브로 전환하는 이론적 배경을 살펴보았다면, 이번 실전 편에서는 Argo CD와 Helm을 통해 실제 ELK 스택을 구축하는 과정을 공유한다.
모든 인프라는 코드로 관리되며, 복잡한 설치 과정은 Helm 차트로 추상화했다.
먼저, 실습에 사용할 인프라 구축 코드를 로컬 환경으로 가져오자.
# 실습 코드 클론 및 특정 커밋으로 체크아웃
git clone https://github.com/youseonghyeon/k8s-argocd.git k8s-argocd
cd k8s-argocd
git reset --hard 78118f4c7b0f280c8dfb2c7d57060c0a173b5ea1
1. Helm 차트 구조 설계
단일 서비스가 아닌 다수의 인프라 구성 요소를 한 번에 관리하기 위해 디렉토리별로 관리를 진행했다.
이는 메인 차트 하나가 여러 서브 차트를 갖는 구조가 된다.
완성된 차트 구조도
my-elk/
├── Chart.yaml # 차트의 메타데이터 및 의존성 정의
├── values.yaml # 전체 서비스의 설정값 중앙 관리
└── templates/ # 실제 쿠버네티스 리소스 템플릿
├── _helpers.tpl # 이름, 레이블 등 공통 템플릿 정의
├── kafka/ # [Strimzi] Kafka 클러스터 및 토픽 관련
├── logstash/ # Logstash 배포 및 Pipeline 설정
├── elasticsearch/ # [ECK] Elasticsearch 클러스터 정의
├── kibana/ # Kibana 시각화 도구 설정
├── filebeat/ # DaemonSet 및 RBAC 설정
└── (hpa, ingress 등) # 공통 인프라 설정
2. Kafka 구축 (Strimzi와 KRaft 모드)
로그 파이프라인의 완충 지대 역할을 하는 Kafka는 쿠버네티스 네이티브 오퍼레이터인 Strimzi를 사용해 구축한다.
Kafka Cluster (kafka/kafka-cluster.yaml)
과거의 Kafka는 상태 관리를 위해 Zookeeper가 필수였으나, 최근에는 이를 제거한 KRaft를 사용할 수 있다.
metadata:
...
annotations:
strimzi.io/node-pools: "enabled"
strimzi.io/kraft: "enabled" # KRaft 모드 활성화
spec:
kafka:
...
config:
offsets.topic.replication.factor: 1
default.replication.factor: 1
- KRaft 모드: 별도의 Zookeeper Pod를 띄울 필요가 없어 아키텍처가 단순해지고 리소스 소모가 줄어든다.
- Replcation Factor: 현재는 사양이 좋지 않아, 복제본을 1로 설정했지만 운영 환경에서는 최소 3이상을 권장한다.
Kafka NodePool 설정 (kafka/kafka-nodepool.yaml)
spec:
replicas: 1 # 클러스터를 구성하는 노드 수
roles: # 해당 노드군이 수행할 역할
- controller # 두뇌 역할 (메타데이터 관리)
- broker # 근육 역할 (실제 데이터 저장 및 처리)
storage:
type: ephemeral # 테스트 환경을 위해 임시 저장소 사용 (운영 시 persistent-claim 권장)
- 역할 분리: 대규모 환경에서는 '의사결정만 내리는 Controller'와 '데이터만 담는 Broker' 풀을 나누어 리소스를 최적화할 수 있다.
- NodePool의 이점: 특정 역할을 수행하는 노드들만 골라서 사양을 높이거나(Scale-up), 개수만 늘리는(Scale-out) 관리가 매우 유연해진다.

3. Logstash 필터링 (Idempotency)
Kafka를 통과한 로그들은 Logstash라는 필터를 거치며 분석하기 쉬운 데이터로 변환된다.
유연한 데이터 파싱과 시간 동기화
로그는 기본적으로 JSON 형식을 권장하지만, 실제 환경에서는 예외적인 텍스트 스트링이 섞일 수 있다.
filter {
# 1. 방어적 JSON 파싱: 메시지가 '{'로 시작할 때만 JSON 분석 시도
if [message] =~ /^\{.*/ {
json {
source => "message"
target => "app_log"
}
}
# 2. 타임스탬프 동기화: 앱에서 발생한 실제 시간(app_log.timestamp)을 데이터의 기준 시간으로 설정
date {
match => [ "[app_log][timestamp]", "ISO8601" ]
target => "@timestamp"
}
}
- 방어적 파싱: 잘못된 형식의 로그로 인해 파이프라인이 멈추는 것을 방지한다.
- 시간 변환: 네트워크 지연 등으로 인해 Logstash에 늦게 도착하더라도, 실제 로그 발생 시간을 기준으로 인덱싱하여 시계열 분석의 정확도를 높인다.
멱등성의 핵심 Fingerprinting
이번 설계에서 중요한 부분으로 중복 방지이다. 분산 시스템에서는 네트워크 재시도 등으로 인해 동일한 로그가 두 번 전송될 가능성이 늘 존재한다.
fingerprint {
source => ["@timestamp", "[kubernetes][pod][name]", "message"]
target => "[@metadata][fingerprint]"
method => "SHA1"
key => "my_secret_key"
}
- 작동 원리: 시간 + 파드 이름 + 로그 메시지를 조합해 고유한 해시값을 만든다.
- 결과: Elasticsearch 출력단에서 이 해시값을 document_id로 사용하면, 동일한 로그가 다시 들어와도 새로운 데이터를 쌓는 대신 기존 데이터를 '덮어쓰기' 한다. 결과적으로 지저분한 중복 데이터 없이 단 하나의 진실(Single Source of Truth)만 남게 된다.
4. 저장소와 가시성, ECK를 이용한 ES & Kibana 구축
Elasticsearch와 Kibana는 ECK(Elastic Cloud on Kubernetes) 오퍼레이터를 통해 배포된다.
직접 Deployment를 짜는 대신 오퍼레이터를 사용한 이유는 운영의 자동화 때문이다.
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
name: {{ .Release.Name }}-es
spec:
version: {{ .Values.elkVersion | default "8.12.0" }}
nodeSets:
- name: default
count: {{ .Values.elasticsearch.nodeCount | default 1 }}
config:
node.store.allow_mmap: false
volumeClaimTemplates:
- metadata:
name: elasticsearch-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: {{ .Values.elasticsearch.storage | default "10Gi" }}
- 자동화된 스토리지: volumeClaimTemplates를 정의해두면, 노드가 늘어날 때마다 오퍼레이터가 자동으로 적절한 PVC를 할당하고 연결해준다.
- 유연한 스케일링: nodeCount 숫자만 바꾸면 클러스터 구성과 샤드 재배치 등 복잡한 작업을 오퍼레이터가 백그라운드에서 처리한다.
Kibana
kind: Kibana
metadata:
name: {{ .Release.Name }}-kb
spec:
elasticsearchRef:
name: {{ .Release.Name }}-es # 위에서 생성한 ES 클러스터를 자동으로 찾아 연결
http:
service:
spec:
type: NodePort # 외부 접속을 위해 포트를 개방
- 간편한 연동: elasticsearchRef 하나로 ES 클러스터와 Kibana 사이의 복잡한 인증 및 엔드포인트 설정을 생략할 수 있다.
- 가시성 확보: 이제 사용자는 브라우저를 통해 NodePort로 접속하여, 아까 Logstash가 예쁘게 가공해놓은 멱등성 있는 로그들을 실시간으로 분석할 수 있다.

5. 마치며
지금까지 Argo CD와 Helm를 이용해 로그 파이프라인을 구축해보았다.
사실 이 방대한 YAML 파일과 설정값들을 모두 외워서 쓰는 것은 불가능하다.
하지만 각 도구의 특성과 사용법에 맞춰 사용하는 방식을 배우는 법을 터득한다면 쉽게 구성할 수 있다.
- 표준화된 도구는 Helm의 values.yaml으로 제어한다.
- 복잡한 인프라는 오퍼레이터에게 관리를 맡기며,
- 우리의 비즈니스 로직은 직접 Deployment로 설계한다.
중요한 것은 어떤 코드를 치는지가 아닌 어떤 레이어에 어떤 도구를 배치할 것인지 이다.
이어지는 3편에서는 메트릭 수집 서비스인 Prometheus와 Grafana를 설계하는 방법을 알아본다.
1편
[클라우드 네이티브] 1편: 온프레미스에서 클라우드 네이티브로: 로깅 아키텍처(ELK) 설계
과거 온프레미스 환경에서 고가용성을 갖춘 ELK 스택을 구축하고 유지하는 것은 꽤나 어려운 작업이였다.단순히 서버를 띄우는 것을 넘어, 인프라와 애플리케이션 사이의 수많은 '수동 연결 고
youseong.tistory.com
'DevOps' 카테고리의 다른 글
| [클라우드 네이티브] 4편: Prometheus 메트릭 모니터링 아키텍처 구현(실전) (0) | 2026.02.08 |
|---|---|
| [클라우드 네이티브] 3편: 고가용성 모니터링(메트릭) 아키텍처 설계 전략 (0) | 2026.02.07 |
| [클라우드 네이티브] 1편: 고가용성 로깅 아키텍처(ELK) 설계 전략 (0) | 2026.02.06 |
| Kubernetes 기반 PostgreSQL 고가용성(HA) 아키텍처 구축 및 카오스 검증 (0) | 2026.02.01 |
| GitHub Actions와 ArgoCD로 완성하는 선언적 쿠버네티스 배포 시스템 (0) | 2026.01.29 |