안녕하세요? (주)칠로엔에서 서버관리를 맡고 있는 매드(MAD)입니다.
이번에는 (주)칠로엔에서 서비스 중인 B2B 모델 LinkMusic(https://www.linkmusic.io/product)에 대하여
약 두 달 간 진행된 ELK 스택을 활용한 분산 로깅 시스템 구축 - 기술적 도전과 해결 방안에 대하여 포스팅 합니다.
사내 내규 상 실제 구현 스크립트들은 공개할 수 없는 점 양해바랍니다.
[서론]
현대 소프트웨어 아키텍처에서 로깅 시스템은 단순한 디버깅 도구를 넘어 시스템 상태 모니터링, 성능 분석, 트러블슈팅의 핵심 요소로 자리 잡았습니다.
특히 마이크로서비스 아키텍처와 분산 시스템이 보편화 됨에 따라, 중앙화된 로깅 시스템의 중요성이 더욱 부각되고 있습니다.
이번 글에서는 LinkMusic 프로젝트에서 ELK 스택(Elasticsearch, Logstash, Kibana)을 활용해 구축한 분산 로깅 시스템의 기술적 측면과 도전 과제들을 심층적으로 살펴보겠습니다.
본문은 개발 일지에 보다 가깝습니다.
본문에 앞서 Elasticsearch에 대한 역 색인에 대하여 이해하는 것이 중요하다고 판단,
이해한 바를 바탕으로 풀어 씁니다.
[Elasticsearch의 역 색인(Inverted Index) 메커니즘: 수학적 이해]
1. 역 색인 토크 나이저의 수학적 기초
1.1 집합론적 정의
역 색인은 형식적으로 다음과 같은 함수로 정의할 수 있습니다:
여기서:
- : 모든 고유 용어(term)의 집합
- : 문서(document) 집합
- : 문서 집합의 멱집합(power set)
각 용어 에 대해, 역 색인 는 해당 용어를 포함하는 모든 문서의 집합을 반환합니다:
1.2 행렬 이론적 표현
역 색인은 용어-문서 행렬(term-document matrix)의 전치(transpose)로도 볼 수 있습니다. 만약 문서-용어 행렬을 라 하면:
여기서 각 원소 는:
는 용어 가 문서 에서 갖는 가중치입니다(예: 용어 빈도).
역 색인은 이 행렬의 전치 로 표현됩니다:
이 행렬에서 각 행 는 용어 가 등장 하는 모든 문서와 그 가중치를 나타냅니다.
1.3 그래프 이론적 해석
역 색인은 이분 그래프(bipartite graph) 로도 표현할 수 있습니다:
- 정점 집합: 용어 집합 와 문서 집합 의 합집합
- 간선 집합
각 간선 에는 가중치 가 부여될 수 있습니다.
2. 인덱싱 알고리즘과 자료구조
2.1 역 색인 구조의 수학적 표현
역 색인은 다음과 같은 구성요소로 이루어집니다:
- 사전(Dictionary): 매핑 함수
- 포스팅 리스트(Posting List): 각 용어 에 대해 형태의 튜플 리스트
- : 문서
- : 문서 에서 용어 의 빈도
- : 용어 가 문서 에 등장 하는 위치 목록
형식적으로, 역색인 는 다음과 같이 정의됩니다:
2.2 (🙏🏻 매우 중요) 인덱싱 알고리즘의 시간 복잡도 분석
2.2.1 단일 문서 인덱싱 복잡도
문서 의 인덱싱 시간 복잡도:
- 토큰화: ← 절댓값 주의😉
- 사전 조회 및 업데이트: , 여기서 는 문서 의 고유 용어 수, 는 전체 고유 용어 수
2.2.2 일괄 처리 인덱싱 복잡도
SPIMI(Single-Pass In-Memory Indexing) 알고리즘의 복잡도:
- 시간 복잡도: , 여기서 은 전체 토큰 수
- 공간 복잡도:
2.2.3 병합 기반 인덱싱
메모리 내 인덱스를 디스크로 플러시하고 병합하는 과정의 복잡도:
여기서:
- : 총 포스팅 수
- : 병합할 인덱스의 수
- : 블록 병합 요소(merge factor)
2.3 트라이 및 B-트리 기반 사전 구현
2.3.1 트라이(Trie) 기반 사전
트라이 노드 에 대한 재귀적 정의:
- : 자식 노드 맵
- v: 용어 종료 여부
- : 연관된 포스팅 리스트
검색 시간 복잡도: , 여기서 는 용어의 길이입니다.
2.3.2 B-트리 기반 사전
B-Tree에서 용어 를 찾는 시간 복잡도:
여기서 는 B-트리의 분기 요소(branching factor)입니다.
[본문]

1. 분산 시스템에서의 로깅 문제
1.1 CAP 이론과 로깅 시스템
분산 시스템을 설계할 때 CAP 이론(일관성, 가용성, 분할 내성)을 고려하는 것은 필수적 입니다. 로깅 시스템도 예외가 아닙니다:
- 일관성(Consistency): 모든 노드가 동일한 시점에 동일한 로그 데이터를 볼 수 있는가?
- 가용성(Availability): 시스템 일부가 실패해도 로그 수집이 계속 되는가?
- 분할 내성(Partition Tolerance): 네트워크 분할 상황에서도 시스템이 작동하는가?
ELK 스택은 기본적으로 AP(가용성, 분할 내성)에 중점을 둔 시스템으로, 일관성 보다는 로그 데이터의 유실 방지와 시스템 가용성을 우선시 합니다.
1.2 시간 동기화 문제
분산 환경에서 발생하는 가장 큰 기술적 도전 중 하나는 서로 다른 서버에서 생성된 로그의 타임스탬프 동기화 문제입니다.
이는 단순한 NTP 서버 설정 문제가 아닌, 분산 시스템의 본질적 특성인 '시간 동기화의 한계'와 관련이 있습니다.
시간 동기화 문제 해결 방안:
1. 모든 서버에서 NTP(Network Time Protocol) 설정
2. 로그 생성 시 타임스탬프와 함께 서버 ID 기록
3. 벡터 클럭(Vector Clock) 알고리즘 활용하여 이벤트 순서 추적
2. ELK 스택의 아키텍처와 자료구조
2.1 Elasticsearch의 인덱싱 구조
Elasticsearch는 역 색인(Inverted Index) 기반의 검색 엔진입니다.
역 색인은 특정 단어가 어떤 문서에 등장 하는지를 저장하는 자료구조로, 검색 쿼리 실행 시 O(1)에 가까운 시간 복잡도로 결과를 찾을 수 있습니다.
역색인(Inverted Index)의 간략한 예
{
"error": [doc1, doc3, doc7],
"warning": [doc2, doc5],
"info": [doc1, doc4, doc6]
}
그러나 역색인 구조는 쓰기 작업에서 상대적으로 비용이 높습니다.
각 용어가 역색인에 추가될 때마다 인덱스를 재구성해야 하기 때문입니다. (B-Tree 기반 자료구조로 구현된 데이터 베이스의 인덱스도 비슷한 문제를 가지고 있죠)
이러한 특성을 이해하고 적절한 샤딩(Sharding) 전략을 수립하는 것이 중요합니다.
2.2 Elasticsearch의 샤딩과 레플리케이션
Elasticsearch는 대용량 데이터를 분산 저장하기 위해 샤딩(Sharding)과 레플리케이션(Replication) 메커니즘을 사용합니다:
- 샤딩: 인덱스를 여러 조각(샤드)으로 분할하여 수평적 확장 가능
- 레플리케이션: 각 샤드의 복제본을 생성하여 가용성과 내결함성 향상
이러한 분산 저장 방식은 이진 탐색 트리(BST)와 해시 테이블의 개념이 결합된 형태로 볼 수 있으며, 일관된 해싱(Consistent Hashing) 알고리즘을 사용해 데이터 분산을 관리합니다.
Elasticsearch 인덱스 설정 예시
{
"settings":
{
"number_of_shards": 5,
"number_of_replicas": 1
}
}
3. 로그 수집 파이프라인과 네트워크 고려사항
3.1 Logstash의 처리 파이프라인과 병렬성
Logstash는 입력(Input) → 필터(Filter) → 출력(Output)의 파이프라인 구조로 로그를 처리합니다.
이 과정에서 Producer-Consumer 패턴이 적용됩니다:
- 입력 플러그인이 로그를 수집하여 큐에 저장 (Producer)
- 워커 스레드가 큐에서 로그를 꺼내 필터 처리 후 출력 (Consumer)
Logstash 처리 모델:입력 → [내부 큐(병목 지점)] → 워커 스레드 풀(필터링) → 출력
이 구조에서 내부 큐는 잠재적 병목 지점이 될 수 있으며, 메모리 압박 상황에서는 Backpressure 문제가 발생할 수 있습니다.
이를 해결하기 위해 Persistent Queue 설정이 중요합니다:
logstash.yml 설정
queue.type: persistedqueue.max_bytes: 4gb
3.2 네트워크 고려사항과 TCP/IP 최적화
로그 시스템은 네트워크 의존도가 높은 시스템으로, TCP 연결 관리와 네트워크 파티션에 대한 이해가 필수적입니다:
- 연결 유지(Keep-Alive): TCP 연결 재사용으로 핸드셰이크 오버헤드 감소
- 타임아웃 설정: 네트워크 불안정성 대비를 위한 적절한 타임아웃 구성
- TCP 버퍼 크기: 높은 처리량을 위한 버퍼 최적화
this.logstashTransport = new winston.transports.Http ( { host: logstash_host, port: logstash_port, path: '/', ssl: use_ssl, agent: new https.Agent ( { rejectUnauthorized: false, keepAlive: true, keepAliveMsecs: 10000, maxSockets: 100 } ), timeout: 5000 } );
4. 운영체제 수준의 최적화
4.1 파일 디스크립터 한계
Elasticsearch와 Logstash는 많은 수의 파일과 소켓 연결을 사용하므로, 운영체제의 파일 디스크립터 한계를 적절히 설정해야 합니다:
/etc/security/limits.confelasticsearch soft nofile 65536elasticsearch hard nofile 65536
이는 운영체제의 자원 할당 메커니즘에 직접적으로 관련되며, 이해하지 못하면 "Too many open files" 에러로 이어질 수 있습니다.
4.2 메모리 관리와 가상 메모리
JVM 기반 애플리케이션인 Elasticsearch와 Logstash는 메모리 관리가 중요합니다:
- 힙 메모리 설정: 적절한 JVM 힙 크기 설정
- 가상 메모리 설정:
vm.max_map_count
증가
- 스왑 비활성화: 성능 저하 방지
가상 메모리 설정
:sysctl -w vm.max_map_count=262144
ES_JAVA_OPTS="-Xms1g -Xmx1g"
5. 데이터 라이프사이클 관리
5.1 시계열 데이터 관리 전략
로그 데이터는 시계열 데이터의 특성을 가지며, 시간이 지날수록 가치가 감소합니다. 효율적인 데이터 관리를 위해 인덱스 라이프사이클 관리(ILM)를 구현해야 합니다:
ILM 정책 예시
{ "policy": { "phases": { "hot": { "actions": { "rollover": { "max_size": "50GB", "max_age": "1d" } } }, "warm": { "min_age": "2d", "actions": { "shrink": { "number_of_shards": 1 }, "forcemerge": { "max_num_segments": 1 } } }, "cold": { "min_age": "30d", "actions": { "freeze": {} } }, "delete": { "min_age": "90d", "actions": { "delete": {} } } } } }
이 정책은 B-Tree Index의 재구성과 유사한 방식으로 시간이 지남에 따라 데이터를 최적화하고 제거합니다.
5.2 쿼리 최적화와 인덱스 설계
로그 데이터에 대한 효율적인 쿼리를 위해서는 인덱스 설계가 중요합니다:
- 인덱스 템플릿: 필드 매핑과 설정을 사전 정의
- 키워드 필드 활용: 정확한 매칭을 위한
keyword
타입 사용
- 쿼리 캐싱: 자주 사용되는 쿼리의 결과 캐싱
인덱스 템플릿 예시
{ "index_patterns": ["logs-*"], "mappings": { "properties": { "@timestamp": { "type": "date" }, "service": { "type": "keyword" }, "message": { "type": "text" }, "level": { "type": "keyword" } } } }
6. 보안 고려사항
6.1 HTTPS와 인증서 관리
ELK 스택의 보안을 위해 HTTPS와 SSL/TLS 인증서를 적절히 관리해야 합니다:
- 자체 서명 인증서: 개발 환경에서는 편리하나 보안 경고 발생
- CA 서명 인증서: 프로덕션 환경에서 권장
- 인증서 갱신: 인증서 만료 시 자동 갱신 메커니즘 구현
Elasticsearch 보안 설정
xpack.security.http.ssl.enabled: true xpack.security.http.ssl.key: certs/es01/es01.key xpack.security.http.ssl.certificate: certs/es01/es01.crt xpack.security.http.ssl.certificate_authorities: certs/ca/ca.crt
6.2 네트워크 격리와 방화벽 설정
로깅 인프라는 보안 측면에서 민감한 정보를 포함할 수 있으므로, 네트워크 보안이 중요합니다:
- 네트워크 분리: 로그 시스템을 별도 VLAN이나 서브넷으로 격리
- 방화벽 규칙: 필요한 포트만 개방 (Elasticsearch: 9200, Kibana: 5601)
- 접근 제어: IP 기반 접근 제한 구현
7. 어려운 점과 주의사항
7.1 기술적 도전과 해결 방안
1) 대량 로그 유입 시 성능 저하
- 문제: 갑작스러운 트래픽 증가로 인한 로그 폭증 시 시스템 부하 증가
- 해결: 버퍼링 레이어 추가 (Redis/Kafka), Logstash 수평 확장, 백프레셔 구현
2) 샤드 할당 불균형
- 문제: 데이터 불균형으로 인한 "핫 샤드" 발생
- 해결: 샤딩 전략 재검토, 라우팅 키 활용, 강제 재할당 고려
3) 메모리 압박과 GC(Garbage Collection) 이슈
- 문제: 힙 메모리 부족으로 인한 GC 지연 및 시스템 응답 지연
- 해결: JVM 힙 크기 최적화, GC 튜닝, 데이터 압축 고려
GC 로깅 활성화
ES_JAVA_OPTS="-Xms1g -Xmx1g -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/var/log/elasticsearch/gc.log"
7.2 주의해야 할 운영 사항
1) 클러스터 상태 모니터링
- 노드 추가/제거 시 샤드 재배치 과정에서 시스템 부하 증가
/_cluster/health
API를 통한 정기적인 상태 확인 필요
2) 인덱스 관리 자동화
- 수동 인덱스 관리는 운영 실수로 이어질 수 있음
- ILM(Index Lifecycle Management) 정책 구현으로 자동화
3) 백업 전략
- 스냅샷 API를 활용한 정기적인 백업 설정
- 백업 데이터 검증 및 복원 테스트 정기 실행
4) 보안 취약점 관리
- 정기적인 보안 패치 및 버전 업데이트
- 기본 계정 암호 변경 및 최소 권한 원칙 적용
결론
ELK 스택을 활용한 분산 로깅 시스템 구축은 단순한 설정 이상의 깊은 기술적 이해를 요구합니다.
컴퓨터 공학의 핵심 개념인 자료구조, 알고리즘, 운영체제, 네트워크, 데이터베이스 시스템에 대한 이해를 바탕으로, 효율적이고 안정적인 로깅 시스템을 구축할 수 있습니다.
특히 분산 시스템의 본질적인 문제인 CAP 이론의 제약과 분산 데이터 관리의 복잡성을 이해하는 것이 중요합니다.
또한 시스템 규모에 따른 확장성과 성능 최적화, 그리고 보안 측면의 고려사항을 균형 있게 적용해야 합니다.
로깅 시스템은 단순한 로그 저장소가 아닌, 시스템의 건강 상태를 모니터링하고 문제를 조기에 발견할 수 있는 핵심 인프라입니다.
다양한 통계를 기반으로 향후 서비스 업데이트 방향 및 특정 리소스에 대한 고도화를 진행할 수 있습니다.
이러한 관점에서 ELK 스택의 기술적 깊이를 이해하고 적용하는 것은 현대 소프트웨어 시스템의 안정성과 신뢰성을 높이는 데 필수적인 요소입니다.