데이터 입수 이상징후 탐지

안녕하세요. Data Infrastructure팀(이하 DI팀)에서 최근에는 주로 Data Application 개발을 하고 있는 Data Engineer 추이삭입니다.

이번 내용은 데이터 입수 과정에서 주요한 이상징후를 탐지하고 탐지 결과를 유관된 구성원들에게 전파하기 위한 시스템 또는 활동에 대한 소개입니다.

배경

SK플래닛은 다양한 서비스와 BM을 보유하고 있고, 200여개의 클라이언트/서버 로그와 800여개의 DB스냅샷데이터를 입수하고 있습니다. 원천 데이터의 제공 방식도 매우 다양하여 여러 가지 데이터 입수기를 개발/운영하고 있습니다. 데이터의 종류도 많고 데이터 입수기의 종류도 다양하여 일정 수준으로 공통화 시키고 상태를 관찰하는 업무가 매우 중요합니다.
사내에 시스템 모니터링을 위한 도구/시스템들은 많지만, 데이터 입수기/서비스 데이터에 대한 이상징후를 탐지하는 시스템은 없어, 데이터 입수 이상징후 탐지 시스템(이하 Insomnia or 인썸니아)을 개발하게 되었습니다.

관심사

간단하게 정리하면 아래와 같습니다.

  • 데이터 입수기의 상태는 이상 없는가?
  • 입수 데이터는 스펙(e.g., 스키마)에 부합한가?
  • 입수 데이터는 관련 법 준수에 문제 없는가?
  • 입수 패턴에 변화가 있는지?
  • 입수 데이터를 분석하는 과정에서 부산된 데이터를 재활용할 수는 없는가?

사용 기술, 시스템

  • Spark, Spark Streaming
  • Scala, Python, R
  • HDFS, Phoenix, MongoDB, RabbitMQ

탐지 프로세스

이상징후 탐지 프로세스를 간략히 설명드리면 아래와 같습니다.

  • 이상징후 탐지 결과 노티(mail, slack)
  • mail에 JIRA이슈 티켓 생성링크 클릭하여 유관부서/담당자와의 소통은 이후 JIRA로 일원화

구조

전체 구조를 도식화하면 아래와 같습니다.

 

이번 글에는 전체를 소개하기 보다는 여러 기능 중 주요한 일부 기능한 소개하고, 이후 기회가 주어진다면 나머지 부분들도 소개드리겠습니다. 소개해드릴 주요한 기능은 아래와 같습니다.

  • Data Profiler
  • Stream
  • Cube 기반 데이터 입수량 이상징후 분석

Data Profiler: 하이브 스키마가 있는 모든 데이터의 이상징후 탐지

Data Profiler(이하 DP)의 시작은 입수한 로그와 하둡클러스터(이하 DIC)에 생성된 하이브 테이블 스키마와 불일치하는 케이스가 빈발하여, HDFS의 파일 상태(사이즈 등)와 하이브 스키마와 불일치 상태를 일일이 커멘드를 쳐가면서 확인하는 일이 고통스러워 이를 간단한 커멘드로 확인할 수 있지 않을까 하는 고민에서 시작했습니다.

최초의 CLI유틸이 갖는 기능은 커멘드 헬프를 보시면 감이 오시리라 생각됩니다.

$ ./dp --help
Usage: dp [OPTION]...
check stat file and alert.
 
Mandatory arguments to long options are mandatory for short options too.
      --dataname DATANAME      데이터 이름 <db>.<table>
      --hdfs HDFS_PATH         분석할 HDFS 경로 e.g., /data/tab/2015/03/03/10 or /data/tab/2015/03/03/* or /data/tab/2015/03,/data/tab/2015/04
      --daterange DATE_RANGE   기간 범위 e.g., 20160302, 2016030201~2016030220
      --partition PARTITION    파티션 e.g., part_month=201603, p_year=2016/p_reqdate=20160331/p_reqtime=08, ...
Optional
      --help                     display this help and exit
      --cols COLUMNS             Header는 <컬럼명>, Body는 body.<컬럼명>, 다수 컬럼을 쓸때는 ","로 구분.
      --unique UNIQUE            Uniqueness를 분석. e.g., in@log_time,mbr_id => log_time, mbr_id로 유일, notin@result_code => result_code 빼고 유일
      --filelist                 hdfs, daterange, partition 옵션에 걸린 파일리스트만 출력
      --field_delim              필드 구분자 e.g., tab, comma
      --executor-cores NUM       Spark Executor Core 개수 default 2
      --num-executors NUM        Spark Executor 개수 default 4
      --driver-memory MEM        Spark Driver Memory default 2g
      --executor-memory MEM      Spark Executor Memory default 3g
 
Report bugs to <isaac.choo@sk.com>

이 CLI유틸은 이상징후 탐지의 목적보다는 데이터의 형태를 이해하는데 초점을 두어, 컬럼의 타입이 string인데, 실제 입력값도 string인지를 대조하는 기능도 포함되어 있습니다.

이런 식으로 요약된 데이터를 이상 징후 탐지에도 활용할 수 있을 것이라는 생각이 들어 DI팀 데이터 입수 파이프라인에 적합한 형태로 새롭게 개발을 시작했습니다.
(참고로, DI팀은 Bottom-up 방식으로 많은 프로젝트들이 진행되고 있습니다. (smile))

새로운 DP는 아래 순서대로 업무가 진행됩니다.

  • 신규 데이터 입수 인지
  • 분석 파라미터 수정
  • 데이터 요약 및 이상징후 탐지
  • 이상징후 탐지 결과 전파
  • 이상징후 탐지 결과 요약 리포팅

신규 데이터 입수 인지

DI팀 내부 프로젝트인 Sentinel(로그 정의/검증을 넘어 DB스키마 등을 관리하는 프로젝트) API를 주기적으로 연동하여 신규 입수 데이터를 찾아 분석 파라미터 템플릿을 생성합니다. 이 템플릿은 내부에서는 dataprops라 부르고 있고, 신규로 추가될 경우 mail, slack으로 어떤 데이터들인지 공유됩니다.

분석 파라미터 수정

템플릿으로 만들어진 분석 파라미터는 일반적으로 큰 수정 없이 사용하지만, 필요에 따라 오탐이 많거나 조정이 필요할 때 수정합니다. 파라미터는 MongoDB에 저장되고, Insomnia Web(이하 API 또는 Insomnia API)를 통해 이 파라미터를 Insomnia의 다양한 분석기들이 이용하고 있습니다.

템플릿의 예시는 아래와 같습니다.

{
  "_id": "<MONGODB_OBJECT_ID>",
  "dataDef": {
    "cutOffSize": 200,
    "db": "<DB_NAME>",
    "emptyAsNull": true,
    "pk": [
      "member_id"
    ],
    "table": "<TABLE_NAME>"
  },
  "patterns": {
    "columns": {
      "created_at": {
        "DP_COL_CARD_ANOMALOUS_MODEL": {
          "enabled": false
        },
        "DP_COL_CARD_ANOMALY_MODEL": {
          "enabled": false
        },
        "DP_COL_CARD_CHANGE_MODEL": {
          "enabled": false
        },
        "DP_COL_CARD_THRESHOLD": {
          "enabled": false,
          "expr": "cnt == 0"
        },
        "DP_COL_EMPTY_THRESHOLD": {
          "enabled": false,
          "expr": "cnt > 0 | ratio > 0.0"
        },
        "DP_COL_NULLEMPTY_THRESHOLD": {
          "enabled": false,
          "expr": "cnt > 0 | ratio > 0.0"
        },
        "DP_COL_NULL_THRESHOLD": {
          "enabled": false,
          "expr": "cnt > 0 | ratio > 0.0"
        },
        "DP_COL_PRIVACYPATTERN_MATCH": {
          "enabled": true,
          "exclude_types": []
        },
        "DP_COL_SEN_LENGTH_NOT_MATCH": {
          "enabled": false
        },
        "DP_COL_SEN_NOTNULL_NOT_MATCH": {
          "enabled": false
        },
        "DP_COL_VALUELENGTH_THRESHOLD": {
          "enabled": false,
          "expr": "value_length == 0"
        },
        "DP_COL_VALUEPATTERN_NOT_MATCH": {
          "enabled": false,
          "expr": ".*"
        }
      },
    },
    "enabled": true,
    "records": {
      "DP_ALL_DUPRATIO_THRESHOLD": {
        "enabled": true,
        "expr": "ratio > 0.0"
      },
      "DP_ALL_FILE_CHANGED": {
        "enabled": false
      },
      "DP_ALL_FILE_NOT_CHANGED": {
        "enabled": true
      },
      "DP_ALL_ROWCNT_ANOMALOUS_MODEL": {
        "enabled": true
      },
      "DP_ALL_ROWCNT_ANOMALY_MODEL": {
        "enabled": true
      },
      "DP_ALL_ROWCNT_CHANGE_MODEL": {
        "enabled": true
      },
      "DP_ALL_ROWCNT_THRESHOLD": {
        "enabled": true,
        "expr": "cnt == 0"
      },
      "DP_ALL_SEN_PK_DUP": {
        "enabled": true
      }
    }
  },
  "status": "active"
}

템플릿의 주요 특징은 아래와 같습니다.

  • DB.테이블 단위로 분석 파라미터를 관리
  • 테이블 단위(records)와 컬럼 단위(columns)의 분석 패턴을 정의
  • 패턴의 활성화(enabled)를 최소는 컬럼/패턴단위로 결정 가능

일부 주요 패턴들에 대한 설명을 드리면, 테이블 단위 분석에서는 입수 데이터가 0건이거나 특정 건수 이하인 경우를 탐지하거나 PK에 나열된 복합키기준의 데이터 중복률의 임계치를 정의 합니다.
비율인 경우 아래 형식으로 기술할 수 있습니다.

"expr": "ratio > 0.0"

만약, ratio와 cnt 둘 중 하나를 만족하는 표현식을 쓰고 싶은 경우 아래와 같이 기술하면 됩니다.

"expr": "ratio > 0.0 | cnt > 0"

컬럼 단위 주요 패턴은, Null 체크나 개인정보매칭 패턴 등이 있습니다.
개인정보매칭 패턴은 IP, 사용자ID, 주민등록번호, 여권번호, 전화번호, 신용카드번호, 이메일, 이름, 주소 등을 판별합니다. 만약 개인정보가 담겨있는 컬럼이 탐지된 경우 해당 컬럼에 대해서 암호화를 하거나, 절삭 등의 후속 조치가 이루어 집니다. 분석 사용자들이나, 데이터 프로덕트를 개발하는 개발자들에게 가급적이면 이러한 이슈가 있는지 기계적으로 탐지하여 전파하고 미연의 사태를 막는 일련의 활동은 매우 중요하다고 생각합니다.

데이터 요약 및 이상징후 탐지

DP의 주요 기능이며 Spark을 이용하여 분석하고 있습니다. 입수 데이터의 크기가 모두 동일하지 않기 때문에 Executor나 CPU Core 할당 시 데이터 사이즈를 확인하는 과정을 거쳐, 분석 파라미터에 기술된 내용을 참고하여 데이터 요약 및 분석을 진행합니다.

DP의 실행은 Insomnia API를 통해 실행되며, Celery Broker를 이용하여 동시 실행 작업의 개수를 통제하고 Chain을 통해 요약 후 분석작업을 수행하고, 기 요약한 과거 데이터를 포함한 분석이 아닌 경우 요약 즉시 분석을 수행합니다. Spark 2.x.x 부터 Spark DataFrame, SQL의 기능/성능이 개선되어 적극적으로 활용하고 있습니다.

데이터 처리하는 과정에서 고민했던 부분을 간단히 요약하면,

  • 일부 분석은 데이터 샘플링 적용
  • DI팀 내부 컴퍼넌트들과의 연계
  • 요약 데이터의 재활용: 요약 데이터는 여러 리포팅 툴에 노출하기 위해 적정 수준의 latency가 보장되어야 하고, 대용량데이터를 SQL로 질의할 수 있는 Apache Phoenix로 결정

데이터 처리에 대한 구체적인 내용은 추후 기회가 주어진다면 설명드리겠습니다.

분석 결과 탐지된 내용이 있으면 mail, slack으로 각 데이터별(정확하게는 테이블 단위) 정해진 수신자들에게 알리게 됩니다. 수신자 관리는 Insomnia API에서 담당합니다.

이상징후 탐지 결과 전파

mail, slack으로 수신 받은 탐지 결과를 확인 후 지속적으로 동 패턴으로 알림이 오거나, 심각하다고 판단되는 경우 JIRA티켓 생성 링크를 통해 티켓을 발생하게 됩니다. 티켓의 Description에는 테이블의 기초 통계 정보와 탐지 결과 및 샘플 데이터 등이 입력됩니다.

  • 검출 시각, 분석 기준시각
  • 파티션, 컬럼 개수, 파일 경로, 파일 개수, 파일 크기, 파일 최종 변경시각, 레코드 카운트, 레코드 유일 카운트, 중복률 등..

이상징후 탐지 결과 요약 리포팅

입수 데이터의 파일사이즈/레코드 카운트 추이, 각 패턴/기간 별 탐지된 테이블 리스트 등을 주기적으로 리포팅 받아 현재 구현된 패턴으로 탐지하지 못한 숨겨진 패턴을 찾는데 활용하고 있습니다.

Stream: 데이터 수집기의 상태 로그를 분석, 이상징후 탐지

DI팀에서 사용하는 데이터 입수기는 아래 표와 같이 각 입수기와 데이터의 성격에 따라 다양하게 운용되고 있습니다. 모든 입수기들이 상태 로그(=Heartbeat 로그) 분석이 필요한 것은 아니고, 중요하고 필요한 입수기에 한하여서 남기고 있습니다.

다양한 데이터 입수기를 운용하다 보니, 전체의 상태가 잘 보이지 않고, 이슈 파악에 어려움이 있어 각 입수기들은 주기적으로 Heartbeat 로그를 생성하여 Kafka 브로커에 전송하고, Insomnia는 이 데이터를 실시간으로 분석하여 가능한 빠르게 이슈를 파악하여 유관 조직/담당자에게 전파합니다.

실시간 분석은 Spark Streaming을 이용하고 있고, 1개의 Job에서 다수의 Kafka Topic을 Consuming하고 있습니다. 다만, DI팀은 Live, Dev 데이터를 별개의 Kafka 브로커로 관리하고 있어 Steaming Job은 총 2개가 실행되고 있습니다.

초기 디자인 시 Exactly-once를 만족시키기 위해 Offset관리를 했지만, 분석 특성 상 유실/중복에 대한 이슈보다는 당장 지금의 데이터 흐름과 빠른 전파가 더 중요한 요인으로 판단되어 기능은 비활성화 시킨 상태로 운영 중입니다. 시스템의 구조는 DIC에서 구축한 레코픽 데이터 처리 시스템 과 유사합니다. (Kafka Direct Stream, Apache Phoenix, …)

대부분의 분석은 규칙기반으로 작동하고 있고, 스트림에 특정 필터를 걸어 탐지하고 있습니다. 일부 패턴은 최대 최근 30분의 Time Window 내에 발생한 이벤트를 Split+Join하여 Context를 재구성하여 분석하고 있습니다.
예를 들어, Log Agent는 원천 로그 파일의 변경을 인지하고 파일의 변경 분을 전송합니다. 파일변경과 파일전송을 각기 다른 쓰레드로 처리하고 있고, 로그 전송 시점 불일치가 간혹 발생하여 파일 변경과 전송의 역전 상태가 발생하는 이슈가 있어 각 이벤트의 Time Window Size를 다르게 관리/처리하는 등의 데이터 특성을 고려한 처리들을 포함하고 있습니다.

탐지된 결과의 전파는 DP와 동일하게 mail, slack으로 알린 뒤 필요 시 JIRA티켓을 발생되는 순으로 진행됩니다.

 

Cube 기반 데이터 입수량 이상징후 분석

Cube란?

실시간으로 입수되는 로그를 적정한 Time Tick(현재는 10분) 단위로 요약하여 이상징후 탐지나 리포팅 시스템에 활용하기 위한 데이터 요약기입니다. 현재는 Insomnia Stale 패턴 분석 및 Anomaly 패턴 분석에 활용되고 있고, 리포팅 영역으로는 Sentinel Report에서 로그 입수량 추이를 살펴보는 용도로 활용되고 있습니다.

DP의 분석 파라미터와 같이 JSON형식으로 정의됩니다.

Cube의 주요 기능은 데이터를 요약하는 파라미터를 정의하는 부분이고, 아래와 같은 형식으로 기술하면 됩니다.

단위 시간당 로그 건수

  • dimensions: none
  • filter: none
  • operator
    • function: count
    • input fields: none
{
  "dimensions": [],
  "filter": "",
  "operators": [
    {
      "name": "count",
      "type": "count",
      "inputFields": []
    }
  ]
},

단위 시간당 MDN 유일 카운트

  • dimension: none
  • filter: none
  • operator
    • type: count distinct
    • input fields: mdn
{
  "dimensions": [],
  "filter": "",
  "operators": [
    {
      "name": "mdnUniqueCount",
      "type": "uniquecount",
      "inputFields": ["mdn"]
    }
  ]
},

body 필드의 exception_type 키를 그루핑키로 로그 건수

  • dimension: body필드의 exception_type 키
  • filter: none
  • operator
    • type: count
    • input fields: none
{
  "dimensions": ["@BODYFILED@.exception_type"],
  "filter": "",
  "operators": [
    {
      "name": "count",
      "type": "count",
      "inputFields": []
    }
  ]
},

헤더의 두개 필드를 그루핑 키로 특정 컬럼의 특정 값을 필터링한 뒤 바디컬럼의 log_count 합계

  • dimension: rake_lib, rake_lib_version
  • filter: action = ‘flush’ and status = ‘DONE’
  • operator
    • type: sum
    • input fields: body 컬럼의 log_count 키
{
  "dimensions": [
    "rake_lib",
    "rake_lib_version"
  ],
  "filter": "$action$ = 'flush' and $status$ = 'DONE'",
  "operators": [
    {
      "name": "sumLogCount",
      "type": "sum",
      "inputFields": [
        "@BODYFILED@.log_count"
      ]
    }
  ]
},

기술된 파라미터 기반으로 Spark Job이 실행되어 데이터 요약 후 Apache Phoenix에 저장합니다. Cube의 스케쥴링 및 동시 작업개수 통제는 DP와 마찬가지로 Insomnia API에서 담당합니다.
그리고, 매 분석 주기마다 약 200여개의 로그데이터를 요약하고 있습니다.

데이터 입수량 이상징후 분석

DI팀이 입수하는 데이터의 주류는 User Behavior 데이터로, 사용자의 라이프 사이클이나 각 서비스의 이벤트/프로모션 등에 의해 데이터 입수량이 매우 급진적으로 변하는 케이스들이 많아, 사실 분석에 어려움이 많습니다.

탐지되었던 주요 케이스를 정리하면 아래와 같습니다. 모든 케이스가 심각한 이슈가 있었던 것은 아니고, 서비스가 변화하면서 패턴이 바뀌는 경우들이 많아 해석에 주의를 기울여야 합니다.
서비스를 개발하는 조직들에서 개발로 바쁘기 때문에, DI팀과 같은 데이터 인프라조직이나 분석 조직에게 미쳐 변경 이슈를 공유하지 못하는 경우가 종종 있는데, 이런 분석을 통해 역으로 DI팀이 변화를 인지하고 전파하는 케이스들도 종종 있어, 의미 있는 신호들을 잡아내고 있구나 하는 생각을 했습니다.

신 고점

  • 일반적으로 가장 많이 발견되는 유형
  • 새로운 고점 등장 후 1~2시간 뒤 얼럿이 오지 않음

    급작스런 고점 등장

    고점 소멸 후 더 이상 얼럿없음

신 저점

  • 집계 오류이나, 입수건이 매우 적을 때 발생

    갑작스런 저점 발견

    데이터 집계 오류로 저점 지속 발견

신규 입수

  • 고점이 지속적으로 바뀌는 케이스

    3일 연속 고점을 갱신

     

    이후 이틀 뒤 새로운 고점 등장

    고점이 꺾인 이후 더 이상 탐지되지 않음

입수량 변동

  • 입수량 감소 유형

    기존 기간 대비 이른 시점에 꺾이기 시작

    2시간 뒤 봉우리의 두께가 이전 기간들에 비해 얇아진 것을 확인

    지속적으로 하락

    하락을 찍고 다시 반등

    일시적인 서비스 이슈로 인한 것으로 생각되어, 분석 기간을 변경한 결과 입수량이 낮아진 것으로 추측

    이후 2일간 이상 건으로 탐지됨(이후 트렌드를 보면 봉우리가 낮아진 상태로 안정화되어 탐지되지 않음)

과 민감

  • 시간당 로그 입수량이 균일한 경우 작은 변동에도 민감하게 이상 건으로 탐지되는 케이스

    y축 스케일 범위가 좁아 변폭이 커 보이지만 변동폭은 매우 작음(차트에 속지 말길!)

    약간의 상승에도 민감하게 반응

기타 유형

  • 매월 특정 시점에 튀는 경우
  • 영업일 유형: Galleon과 같이 업무 특성상 특정 요일. 특정 월일에 사용량이 급증하는 유형

이 분석에 대해서는 기회가 된다면, 다른 글을 통해서 만나 뵙겠습니다.

향 후 과제

아직 발전하고 있는 프로젝트이고, SK플래닛의 데이터 입수 파이프라인이 국내를 넘어 글로벌로 확장하는 과정을 겪고 있어 풀어낼 과제들이 많이 남아 있습니다.

  • DP에서 HiveContext 의존성 최대한 배제
  • DP 요약 데이터의 추이 기반 이상징후 탐지
  • DP 요약 데이터의 재활용처 확보(e.g., MeDic 등)
  • 데이터 입수기와 밀접한 결합

맺음말

개인적으로 DI팀의 데이터 입수 관제에 대한 글을 많은 분들과 공유하게 되어 뜻 깊습니다.
이런 분석과 활동이 저 혼자만의 힘으로 가능한 것은 아닙니다. 본 프로젝트는 팀 내 다양한 컴퍼넌트들과의 연계가 중요했고, 각 기능별로 도움을 준 부분을 나열해보면

  • 데이터 입수기의 운영과 확산에 많은 어려움을 겪으면서도 고품질의 상태 로그를 남겨주신 박지은, 강병수 매니저님
  • 매우 다양한 BM/서비스의 데이터 입수 요청을 통일된 형식으로 처리/정의할 수 있는 Sentinel프로젝트를 담당하는 이재근 매니저님
  • 문제없이 분석이 수행될 수 있게 안정적인 인프라를 제공해주시는 김진수, 노재호 매니저님

그리고, 본 과제를 같이 고민하시는 김홍진 매니저님과 여러 가지로 도움을 주신 조준호 Data Platform Dev본부 본부장님, 여종구 Data Infrastructure팀 팀장님께 감사의 말씀을 전합니다.

추이삭 Data Infrastructure팀

Data Infrastructure팀 Data Engineer

공유하기

  • Sanghee Park

    회사에 가져가서 팀과 같이 열심히 벤치마킹하겠습니다!! 시간내서 글 올려주셔서 너무너무 감사해요