엔지니어링

쓰기 스키마(Schema on write)와 읽기 스키마(Schema on read) 비교

Elastic Stack(또는 널리 알려진 대로 ELK Stack)은 인기있는 로그 저장소입니다.

많은 사용자들이 타임스탬프 구문 분석과 아마도 쉬운 필터링을 위한 약간의 간단한 태그 추가 이상의 구조 없이 로그를 저장하기 시작합니다. Filebeat는 기본값으로 바로 그 작업을 수행합니다. 로그를 추적한 다음, 추가 구조를 추출하지 않고 가능한 한 빨리 Elasticsearch로 전송합니다. Kibana Logs UI도 로그의 구조에 대해 아무런 가정도 하지 않습니다. 간단한 “@timestamp”와 “message” 스키마로 충분합니다. 이것을 로깅에 대한 최소 스키마(minimal schema) 방식이라고 합니다. 이 방식은 디스크 공간은 절약하지만 간단한 키워드 검색과 태그 기반 필터링을 넘어서는 작업에 대해서는 그리 유용하지 않습니다.

minimal-schema.jpg

최소 스키마

일단 어느 정도 로그에 익숙해지면, 일반적으로 로그에 대해 더 많은 작업을 하고 싶어집니다. 상태 코드와 연관된 로그에서 숫자를 보게 되는 경우, 숫자를 세면서 지난 한 시간 동안 5xx번대 상태 코드가 몇 개나 있었는지 알아보고 싶어질 수도 있습니다. Kibana 스크립트 필드(scripted fields)는 사용자가 검색 시 로그에 스키마를 적용할 수 있게 하여 이러한 상태 코드를 추출하고 코드에 대해 집계, 시각화 및 기타 유형의 작업을 수행할 수 있습니다. 로그에 대한 이 접근 방식을 종종 읽기 스키마(Schema on read)라고 합니다.

schema-on-read.jpg

읽기 스키마

임시 탐색에는 편리하지만, 이 접근 방식의 문제점은 계속 진행 중인 보고 및 대시보드 작업을 위해 선택하는 경우, 검색을 수행할 때마다 또는 시각화를 다시 렌더링할 때마다 필드 추출을 재실행하게 된다는 것입니다. 대신에, 일단 원하는 구조적 필드를 정하고 나면, 재색인 프로세스가 백그라운드 작업으로 실행되기 시작하여 이러한 스크립트 필드를 영구적인 Elasticsearch 인덱스의 구조적 필드로 “유지”할 수 있습니다. 그리고 Elasticsearch로의 데이터 스트리밍에 대해서는, Logstash수집 노드 파이프라인을 설정하여 dissect 또는 grok 프로세서를 사용해 이러한 필드를 사전에 추출할 수 있습니다.

이제 세 번째 접근 방식을 소개할 때가 되었습니다. 이 접근 방식은 작성 시에 로그 구문 분석을 하여 위에서 언급한 필드를 사전에 추출합니다. 이러한 구조적 로그는 분석가에게 수많은 가치를 추가로 제공해주며, 검색 후에 필드를 추출하는 방법을 알아내야 할 필요를 없애주고, 쿼리 속도를 빠르게 하고, 로그 데이터로부터 얻는 가치를 극적으로 높여줍니다. 중앙 집중식 로그 분석에 대한 이러한 쓰기 스키마(Schema on write) 접근 방식은 수많은 ELK 사용자들이 선호해온 방식입니다.

schema-on-write.jpg

쓰기 스키마

이 블로그에서는, 이러한 접근 방식 간의 차이를 평가하고 계획 관점에서 이를 어떻게 생각해야 할지에 대해 다뤄보겠습니다. 미리 로그를 구조화하는 것이 왜 내재적 가치를 갖는지, 그리고 사전 구조가 거의 없이 시작하는 경우에도 중앙 집중식 로깅 배포가 성숙하면서 왜 자연스럽게 이쪽으로 진화해 가야 할 것이라고 생각하는지를 검토해 보려고 합니다.

쓰기 스키마(Schema on write)의 장점 탐구(와 근거 없는 믿음 깨기)

중앙 집중식 로깅 클러스터에 쓰기 작업을 할 때 왜 로그를 구조화하는 것이 좋은지에 대해 먼저 알아보겠습니다.

보다 나은 쿼리 작업 환경. 유용한 정보를 위해 로그를 검색할 때, 자연스러운 시작점은 “오류” 같은 키워드에 대해 간단한 검색을 하는 것입니다. 그러한 쿼리에 대한 결과 반환은 각 로그 행을 역색인 문서로 취급하고 전체 텍스트 검색을 실행함으로써 수행할 수 있습니다. 그러나, “my_field가 N과 같은 모든 로그 행을 구하라”는 것과 같은 좀더 복잡한 질문을 하려고 하면 어떻게 될까요? my_field라는 필드가 정의되어 있지 않으면, 이 질문을 바로 할 수 없습니다(자동 완성 아님). 로그에 이 정보가 있다는 것을 알게 된다고 해도, 기대값과 비교하기 위해 필드를 추출하려면 이제 쿼리의 일부로 구문 분석 규칙을 작성해야만 합니다. Elastic Stack에서는, 로그를 미리 구조화할 때 Kibana 자동 완성 기능이 자동으로 필드와 값을 제안하여 쿼리를 구축하게 됩니다. 이것은 분석가의 생산성을 엄청나게 높여줍니다! 이제, 사용자와 사용자의 동료들은 어떤 필드인지 알아내야 할 필요도 없고, 필드를 추출하기 위해 검색 시에 복잡한 구문 분석 규칙을 작성할 필요도 없이 바로 질문을 할 수 있습니다.

한결 빠른 기록 쿼리와 집계. Elastic Stack에서 구조적 필드에 대한 쿼리는 대량의 기록 데이터에 대해 실행되더라도 밀리초만에 결과를 반환하게 됩니다. 답변을 반환하는 데 몇 분 또는 몇 시간이 소요되는 일반적인 쓰기 스키마(Schema on write) 시스템과 비교해 보세요. 이것은 미리 추출되고 색인된 구조적 필드에서 통계 집계를 필터링하고 실행하는 것이 필드를 추출하고 그 필드에서 수학적인 작업을 하기 위해 모든 로그 행에 걸쳐 정규식(Regex)를 실행하는 것보다 훨씬 더 빠르게 때문입니다. 이것은 특히 임시 쿼리에서 중요합니다. 조사를 하는 동안에는 어느 쿼리를 실행하게 될지 모르기 때문에 임시 쿼리의 결과는 미리 가속화할 수 없습니다.

메트릭으로의 로그. 위의 내용과 관련해, 구조적 로그에서 숫자 값을 추출한 결과는 숫자 시계열이나 메트릭과 놀랍게도 비슷해 보입니다. 이런 유용한 데이터 포인트에 신속하게 집계를 실행하면 운영 관점에서 엄청난 가치가 생깁니다. 구조적 필드는 사용자가 로그의 숫자 데이터 포인트를 규모에 맞게 메트릭으로서 취급할 수 있게 해줍니다.

작업 시점. 호스트 이름에 IP 주소 같은 필드를 결정해야 할 때, 나중에 쿼리 시에 하는 것이 아니라 색인 시에 해야 합니다. 나중에 결정하면 그 이전의 트랜잭션에 대해서는 더 이상 유효하지 않을 수 있기 때문입니다. 1주일 뒤면 해당 IP는 완전히 다른 호스트 이름에 연결될 수도 있습니다. 이것은 신원 관리 시스템에서의 사용자 이름 결정, CMDB에서의 자산 태그 결정 등과 같이 가장 최근의 매핑 스냅샷만 제공하는 외부 소스에 대한 조회에 적용됩니다.

실시간 이상 징후 탐색 및 알림. 집계와 마찬가지로, 실시간 이상 징후 탐색 및 알림은 구조적 필드를 이용하면 대규모에서 가장 효율적으로 작동합니다. 다른 경우라면, 클러스터에 대한 지속적인 처리 요구가 꽤 부담스러울 것입니다. 이상 징후 탐색과 알림 작업을 생성하다가 중도에 어려움을 겪는 수많은 고객들이 도움을 요청해 옵니다. 검색 시에 필드를 추출하면 필요한 알림의 양을 규모가 따라가지 못하기 때문입니다. 이것은 수집하는 로그 데이터가 대부분 반응성 사용 사례에 대해서만 적합하며 해당 프로젝트에서 비용 대비 성과가 제한적이라는 뜻입니다.

가관측성 이니셔티브의 로그. 가관측성 이니셔티브를 진행 중인 경우, 이것이 로그 수집 및 검색만을 위해서는 충분하지 않다는 것을 알고 계실 것입니다. 로그 데이터는 어느 데이터 포인트에서 오는 것인지 불문하고 운영자에게 서비스에 대한 전체적인 기록과 현재 상황을 알려줄 수 있으려면 이상적으로는 메트릭(리소스 사용 등) 및 애플리케이션 추적과 상호 연결되어 있어야 합니다. 이 상관 관계는 구조적 필드에서 가장 잘 작동합니다. 다른 경우라면, 대규모에서는 실제 상황에서 조회는 느리고, 분석은 사용이 불가능합니다.

데이터 품질. 이벤트가 사전 처리 대상일 때, 유효하지 않거나 중복되거나 누락된 데이터를 확인해서 그러한 문제를 수정할 기회를 갖습니다. 읽기 스키마(Schema on read)를 신뢰하는 경우, 결과가 정확하게 반환되고 있는지 알 수가 없습니다. 데이터의 유효성과 완전성이 미리 확인되지 않았기 때문입니다. 이것은 반환되는 데이터를 기반으로 부정확한 결과와 잘못된 결론을 도출할 수도 있습니다.

세분화된 액세스 컨트롤. 비구조적 로그 데이터에 필드 수준의 제한 등과 같은 세분화된 보안 규칙을 적용하는 것은 어려운 일입니다. 검색 시 데이터에 대한 액세스를 제한하는 필터는 도움이 될 수 있지만, 필드의 하위 집합으로 이루어진 부분적인 결과를 반환할 수 없는 등 상당한 한계가 있습니다. Elastic Stack에서는 필드 수준의 보안을 이용해 보다 낮은 수준의 권한을 가진 사용자는 필드를 일부만 보고 다른 사용자들은 전체 데이터 세트를 볼 수 있도록 할 수 있습니다. 따라서 더 규모가 큰 사용자 그룹이 다른 정보에 대한 작업을 수행하도록 허용하면서 로그에서 개인 식별 정보(PII) 데이터를 보호하는 것이 훨씬 쉽고 유연해집니다.

하드웨어 요건

쓰기 스키마(Schema on write)에 대해 흔히 알려진 근거 없는 믿음 중 하나는 쓰기 스키마란 클러스터가 로그를 구문 분석하고, 구문 분석되지 않은 형식과 구문 분석된(즉, “색인된”) 형식 양쪽 모두를 저장하려면 더 많은 리소스가 필요하게 된다는 것과 같은 의미라는 것입니다. 개별 사용 사례를 고려하여 몇 가지 차이에 대해 분석해 보겠습니다. 사실 정답은 “경우에 따라 다르다”이기 때문입니다.

한 번의 구문 분석과 지속적인 필드 추출 비교. 구조화된 형식에서 로그를 구문 분석하고 저장하는 것은 수집 측면에서 처리 역량을 사용합니다. 그러나, 필드를 추출하기 위해 복잡한 정규식(Regex) 구문을 수행하는 비구조화된 로그에 반복적인 쿼리를 실행하는 것은 지속적으로 훨씬 더 많은 RAM과 CPU 리소스를 사용합니다. 로그에 대한 흔한 사용 사례가 간간히 발생하는 검색서비스 뿐일 것으로 예상한다면, 아마도 그것을 미리 구조화하는 것은 너무 지나친 일일 것입니다. 그러나 로그를 적극적으로 쿼리하고 로그 데이터에 대한 집계를 실행할 것으로 예상한다면, 수집에 대한 일회적인 비용은 쿼리 시에 동일한 작업을 지속적으로 재실행하는 비용보다는 덜 부담스러울 것입니다.

수집 요건. 미리 추가적인 처리를 하면, 아무 것도 하지 않을 경우보다는 수집 처리량이 다소 적을 수 있습니다. 독립적으로 Elasticsearch 수집 노드나 Logstash 인스턴스 규모를 확장함으로써 추가적인 수집 인프라를 도입하여 그 작업량을 처리할 수 있습니다. 이에 대한 접근 방식을 소개하는 훌륭한 리소스와 블로그가 있습니다. Elastic 클라우드의 Elasticsearch Service를 사용 중이라면, 수집 규모 확장은 더 많은 “수집 가능” 노드를 추가하는 것 만큼이나 쉽습니다.

저장 요건. 보이는 것처럼 직관적인 것과는 거리가 멀지만, 로그의 구조를 미리 이해하기 위해 약간의 작업을 할 때 저장 요건은 사실상 더 낮을 수 있습니다. 로그는 장황하며 시끄러울 수 있습니다. 로그를 미리 분석함으로써(모든 필드를 완전히 구문 분석하지는 않더라도), 어느 로그 행과 추출된 필드를 중앙 집중식 로그 클러스터에서 검색을 위해 온라인으로 유지해야 하는지, 그리고 어느 것을 즉시 장기 보관해야 하는지 결정할 수 있습니다. 이 접근 방식은 장황하고 시끄러운 로그의 전반적인 디스크 저장 요건을 줄여줄 수 있습니다. Filebeat는 정확히 이 목적으로 경량의 dissectdrop 프로세서를 갖추고 있습니다.

규제 요건으로 인해 모든 로그 행을 유지해야 하더라도, 쓰기 스키마(Schema on write)를 이용하면 저장 비용을 최적화할 수 있는 여러 가지 방법이 있습니다. 먼저, 컨트롤이 편합니다. 로그를 전체적으로 구조화할 필요가 없습니다. 사용 사례에 필요하면, 구조화된 메타데이터의 중요한 부분 몇 개만 추가하고 나머지는 구문 분석되지 않은 로그 행으로 남겨두어도 됩니다. 다른 한편으로는, 로그를 완전히 구조화하는 경우, 중요한 데이터 전부가 구조화된 저장 공간에 유지되며, 색인된 로그가 있는 동일한 클러스터에 “소스” 필드를 유지할 필요가 없습니다. 비용이 저렴한 저장 공간에 장기 보관하면 됩니다.

또한 저장 공간이 여전히 주요 문제 중 하나라면 Elasticsearch 기본 설정을 최적화하는 방법도 많이 있습니다. 몇 가지 간단한 변경만으로도 압축률을 낮출 수 있습니다. 또한 더 장기간 보관을 위해 저장된 액세스 빈도가 낮은 데이터의 경우, hot/warm 아키텍처고정 인덱스를 사용하여 저장 공간을 최대한 활용할 수도 있습니다. 그러나, hot 데이터인 경우, 가장 필요할 때 쿼리에 대한 대답을 기다리는 시간에 비해 저장 공간 비용이 상대적으로 저렴하다는 것을 기억해 두세요.

미리 구조 정의

또 다른 근거 없는 믿음은 로그를 저장하기 전에 구조화는 것이 어렵다는 것입니다. 이 주장이 잘못되었다는 것을 조금 밝혀 보겠습니다.

구조적 로그. 많은 로그가 이미 구조화된 형식으로 생성됩니다. 흔히 사용되는 애플리케이션은 대부분 JSON으로 직접 로깅을 지원합니다. 이것은 로그를 직접 Elasticsearch로 수집하고 이를 구문 분석할 필요 없이 비구조화된 형식으로 바로 저장하기 시작할 수 있다는 뜻입니다.

미리 작성된 구문 분석 규칙. Elastic에서 공식 지원하는 미리 작성된 구문 분석 규칙은 수십 가지가 있습니다. 예를 들어, Filebeat 모듈은 사용자를 위해 알려진 벤더 로그를 구조화하며 Logstash에는 방대한 grok 패턴 라이브러리가 포함되어 있습니다. 커뮤니티에서 훨씬 더 많은 미리 작성된 구문 분석 규칙을 이용할 수 있습니다.

자동 생성된 구문 분석 규칙. 맞춤형 로그에서 필드를 추출하는 규칙을 정의할 때는 Kibana Data Visualizer 같은 도구가 도움이 됩니다. 이런 도구는 구문 분석하는 방법을 자동으로 제안해줍니다. 로그 샘플을 붙여넣기만 하면 수집 노드나 Logstash에서 사용할 수 있는 grok 패턴을 얻을 수 있습니다.

로그 형식 변경 시 생기는 일들

마지막으로 다룰 근거 없는 믿음은 쓰기 스키마(Schema on write)가 로그 형식 변경 처리를 더 어렵게 만든다는 것입니다. 한 마디로 이것은 사실이 아닙니다. 전체 텍스트 검색 이상의 작업을 하고 있다고 가정하면, 사전에든 결과에 따라서든 로그에서 정보를 추출하기 위해 어느 접근 방식을 취하는지와 상관 없이 누군가는 로그 형식 변경을 처리해야 합니다. 색인 시에 로그를 grok하는 수집 노드 파이프라인을 갖든 아니면 검색 시에 동일한 작업을 하는 Kibana 스크립트 필드를 갖든, 로그 형식이 변경되면, 필드를 추출하는 논리는 수정되어야 합니다. 계속 유지되는 Filebeat 모듈의 경우, 우리는 업스트림 로그 벤더가 언제 새 버전을 출시하는지 추적하고 테스트 후에 호환성을 업데이트합니다.

작성 시에 로그 구조 변경을 처리할 수 있는 여러 가지 접근 방식이 있습니다.

미리 구문 분석 논리 수정. 로그 형식이 변경되리라는 것을 알고 있는 경우, 병렬 처리 파이프라인을 만들어 전환 기간 동안 양쪽 로그 버전을 모두 지원할 수 있습니다. 이것은 보통 사내에서 컨트롤하는 로그 형식에 적용됩니다.

구문 분석 실패 시 최소 스키마 작성. 모든 변경사항이 사전에 미리 알려지지는 않기 때문에 때로는 통제권 밖에 있는 로그가 통지 없이 변경되기도 합니다. 맨 처음부터 로그 파이프라인에 그런 만일의 경우를 포함시킬 수 있습니다. grok 구문 분석이 실패하면, 타임스탬프와 구문 분석되지 않은 메시지의 최소 스키마를 작성하고 운영자에게 알림을 보냅니다. 현재로서는, 분석가의 워크플로 중단을 피하고, 앞으로의 파이프라인을 수정하고, 구문 분석 논리에서 중단되는 짧은 기간 동안 필드 재색인을 고려하기 위해 새로운 로그 형식으로 스크립트 필드를 작성하는 것이 가능합니다.

구문 분석 실패 시 이벤트 작성 지연. 최소 스키마를 작성하는 것이 도움이 되지 않는다면, 구문 분석 논리가 실패하고, “배달 못 한 편지 큐”(Logstash의 기본 제공 기능)에 이벤트가 보관되며, 새로운 구문 분석 파이프라인을 통해 논리를 수정하고 배달 못 한 편지 큐에서 이벤트를 재실행할 수 있는 운영자에게 알림을 보내는 경우, 대신에 로그 행 작성을 실패할 수 있습니다. 이로 인해 분석이 중단되지만, 스크립트 필드와 재색인을 처리할 필요가 없습니다.

적절한 비유

결국 상당히 긴 기록이 되었네요. 여기까지 잘 읽어오셨다면, 칭찬해 드리고 싶습니다! 어떤 개념을 내면화하는 데 도움이 되는 것 한 가지는 좋은 비유입니다. Elastic의 보안 전문가인 Neil Desai과 얘기를 나누면서, 최근에 쓰기 스키마(Schema on write)와 읽기 스키마(Schema on read)를 비교하는 가장 좋은 비유를 들었습니다. 여러분께도 도움이 되시길 바랍니다.

결론 - 선택은 사용자에게 달려 있습니다

처음에 언급한 것처럼, 쓰기 스키마(Schema on write)와 읽기 스키마(Schema on read)를 비교할 때 모든 중앙 집중식 로그 배포에 대해 반드시 만능인 정답이 있을 필요는 없습니다. 사실 우리가 보는 대부분의 배포는 그 중간 어디쯤에 있습니다. 어떤 로그는 상당한 정도로 구조화하고 나머지는 가장 기본적인 스키마(@timestamp와 message)로 남겨둡니다. 모든 것이 로그로 무엇을 하려고 하는지에 따라, 그리고 구조화된 쿼리의 속도와 효율성에 가치를 두는지 아니면 사전에 아무런 복잡한 과정 없이 가능한 한 빨리 디스크에 데이터를 작성하는 것을 선호하는지에 따라 다릅니다. Elastic Stack은 양쪽 다 지원합니다.

Elastic Stack에서 로그 작업을 시작하려면,Elasticsearch Service를 이용하시거나 로컬 컴퓨터에 다운로드받으세요. 그리고 Kibana에서 새로운 Logs 앱을 확인해 보세요. 모든 형태나 형식의 구조적 로그 및 비구조적 로그와 작업하기 위한 워크플로를 최적화해줍니다.