EKS 환경에서 MSA로 구축된 서비스 로그를 Fluent Bit을 사용하여 OpenSearch로 적재하고 있다. 이 과정에서 발생하는 시간 불일치 문제를 해결하고, 로그 내의 실제 시간을 OpenSearch의 기본 타임스탬프로 사용하는 방법을 정리한다.
문제 상황: @timestamp와 실제 로그 시간의 불일치
일반적으로 수집되는 서비스 로그의 예시는 다음과 같다.
2025-09-17 10:16:14.223 32487 --- [nio-8080-exec-3] INFO o.s.web.servlet.DispatcherServlet : POST "/api/users/login", parameters={}
2025-09-17 10:16:14.225 32487 --- [nio-8080-exec-3] DEBUG c.e.s.security.JwtAuthenticationFilter : Attempting authentication for user 'alice'
기본 설정으로 Fluent Bit을 사용하면, 로그가 Fluent Bit에 의해 처리되는 시점의 시간이 OpenSearch의 @timestamp 필드에 저장된다. 하지만 로그가 생성된 실제 시간과 수집되는 시간에는 차이가 발생할 수 있다. 정확한 로그 분석을 위해서는 로그 라인 시작 부분에 기록된 실제 시간을 OpenSearch의 기본 시간으로 해야할 경우가 생길 수 있다.
기본적인 fluent-bit.conf 파일 구조는 다음과 같다.
[SERVICE]
flush 1
daemon Off
log_level debug
parsers_file parsers.conf
# ... (기타 설정)
[INPUT]
Name tail
Tag servicelog
Path /log/service_*.log
multiline.parser multiline
# ... (기타 설정)
[FILTER]
Name parser
Match servicelog
Key_Name log
Parser service_log_parser
Reserve_Data On
[OUTPUT]
Name opensearch
Match log-service
Host opensearch-test
Port 9200
Logstash_Format On
Logstash_Prefix service_app
# ... (기타 설정)
방법 1: 별도 timestamp 필드 생성 후 OpenSearch에서 매핑
첫 번째 방법은 정규표현식을 사용해 로그에서 시간 부분만 별도의 timestamp 필드로 추출하는 것이다.
parsers.conf 파일은 다음과 같이 설정한다.
[PARSER]
Name service_log_parser
Format regex
Regex ^(?<timestamp>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3})\s+(?<log>[\s\S]*)$
이렇게 설정하면 OpenSearch에는 Fluent Bit의 수집 시간인 @timestamp와 로그에서 파싱한 timestamp 필드가 함께 저장된다.
{
"@timestamp": "2025-09-17T01:16:15.500Z",
"timestamp": "2025-09-17 10:16:14.223",
"log": " 32487 --- [nio-8080-exec-3] INFO o.s.web.servlet.DispatcherServlet : POST \"/api/users/login\", parameters={}"
}
이 방법의 단점은 새로 생성된 timestamp 필드의 타입이 **text(string)**라는 점이다. 시간 기반으로 정렬하거나 분석하려면 이 필드를 date 타입으로 변경해야 한다. 이를 위해 OpenSearch에 인덱스 템플릿을 생성하여 명시적으로 매핑을 지정해야 한다.
OpenSearch Dashboards의 Dev Tools에서 아래와 같이 인덱스 템플릿을 생성한다.
PUT _index_template/service_template
{
"index_patterns": [
"service_app*"
],
"template": {
"mappings": {
"properties": {
"@timestamp": {
"type": "date"
},
"timestamp": {
"type": "date",
"format": "strict_date_optional_time||yyyy-MM-dd HH:mm:ss.SSS||epoch_millis"
}
}
}
},
"priority": 300
}
주의: 인덱스 템플릿은 생성된 이후에 새로 들어오는 인덱스에만 적용된다. 기존에 생성된 인덱스들의 timestamp 필드 타입을 변경하려면 reindex 작업이 필요하다.
방법 2: @timestamp를 로그 시간으로 대체하기 (권장)
두 번째 방법은 별도의 필드를 만들지 않고, 파싱한 로그 시간을 OpenSearch의 기본 @timestamp 필드에 덮어쓰는 것이다.
parsers.conf 파일에 Time_Key, Time_Format, Time_Offset 설정을 추가한다.
[PARSER]
Name service_log_parser
Format regex
Regex ^(?<timestamp>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3})\s+(?<log>[\s\S]*)$
Time_Key timestamp
Time_Format %Y-%m-%d %H:%M:%S.%L
Time_Offset +0900
- Time_Key:
Regex에서timestamp라는 이름으로 추출된 필드를 시간 필드로 사용하도록 지정한다. 이 값이@timestamp를 대체하게 된다. - Time_Format:
Time_Key로 지정된 필드의 시간 형식을 Fluent Bit에 알려준다. - Time_Offset: Fluent Bit은 기본적으로 UTC를 사용하므로, 한국 시간(KST)에 맞게
+0900오프셋을 설정한다.
이 방법을 사용하면 별도의 timestamp 필드가 생기지 않으며 @timestamp가 로그에 기록된 실제 시간으로 저장된다. 따라서 인덱스 템플릿을 추가로 생성할 필요가 없다.
파싱 후 원본 로그 전체 내용 유지하기
위와 같이 파싱을 적용하면 log 필드에는 시간 부분을 제외한 나머지 내용만 남게 된다. 경우에 따라 원본 로그 전체를 log 필드에 저장하고 싶을 수 있다.
이때는 fluent-bit.conf의 [FILTER] 섹션에 Preserve_Key On 옵션을 추가한다.
[FILTER]
Name parser
Match servicelog
Key_Name log
Parser service_log_parser
Reserve_Data On
Preserve_Key On
- Reserve_Data: 파싱 대상인
log필드 외에 다른 필드가 있을 경우 그대로 유지한다. - Preserve_Key: 파싱의 대상이 된
Key_Name(log필드)의 원본 내용을 파싱 후에도 그대로 유지한다.
이 설정을 추가하면 @timestamp는 로그 시간으로 대체되고, log 필드에는 시간 정보가 포함된 원본 로그 전체가 저장된다.
{
"@timestamp": "2025-09-17T10:16:14.223+09:00",
"log": "2025-09-17 10:16:14.223 32487 --- [nio-8080-exec-3] INFO o.s.web.servlet.DispatcherServlet : POST \"/api/users/login\", parameters={}"
}'Data > OpenSearch' 카테고리의 다른 글
| OpenSearch shard 개수 제한 문제 해결 (1) | 2025.07.09 |
|---|