Elasticsearch X-Pack Alerting 체험기

데일리호텔 / 조회수 : 4359


Logstash로 로그를 수집한 후 Elasticsearch와 Kibana로 분석하는 방법을 다룬 글은 많다. 그런데 이상하더라 이 말이지. 로그를 분석하고 경향을 파악하는 정도라면야 괜찮은데 심각한 오류 로그를 발견했을 때 Slack이나 이메일 등으로 알람 받을 수단이 마땅치 않더라. 사람이 키바나 대시보드를 5분마다 확인할 수도 없는 노릇이다. (이건 새로운 차원의 고문?)

이런 생각을 먼저 한 사람이 있기 마련이라 Yelp의 elastalert라던가 Elasticsearch의 X-Pack을 활용하면 이런 문제를 해소할 수 있다. 오늘은 그 중에서 후자를 살펴볼 예정이다.

경고! X-Pack은 Elasticsearch가 유료 서비스 시장을 열려고 야심차게 미는 모양인데 “자기네가 직접 만들었으니 쿨하겠지?”라고 쉽게 생각하면 하루 안에 절벽 아래로 떨어지는 끔찍한 기분을 맞이할 수도 있다.

X-Pack은 가격이 상당한데 Alert 등을 설정하려면 전적으로 RESTful API에 의존해야 한다. 적어도 아직까지는! 이 사실을 깨닫자마자 당황할 수 있는데 침착하자. 이것은 시작일 뿐이다. 여러분이 검색엔진의 초보라면 그 다음 난관은 검색 쿼리를 작성하는 것이다. “나는 그냥 OutOfMemoryError 로그를 발견하면 알람을 보내줬으면 좋겠어"라고 쉽게 생각했겠지만 그 간단한 결과를 얻으려면 험난한 여정을 거쳐야 한다.

"search" : {
"request" : {
"indices" : [
"",
],
"body" : {
"query" : {
"bool" : {
"must" : {
"multi_match": {
"query": "OutOfMemoryError",
"fields": ["message", "log"]
}
},
"filter" : {
"range": {
"@timestamp": {
"from": "{{ctx.trigger.scheduled_time}}||-5m",
"to": "{{ctx.trigger.triggered_time}}"
}
}
}
}
}
}
}
}

음… 좋다. 일단 이렇게 작성한 쿼리가 제대로 된 것인지 테스트하려면 어떻게 해야 하는가? 검색 API로 대충 테스트해볼 수는 있다.

GET logstash-2017.02.2*/_search
{
"query" : {
"bool" : {
"must" : {
"multi_match": {
"query": "OutOfMemoryError",
"fields": ["message", "log"]
}
}
}
}
}

어찌어찌 잘 나온다. 그래서 잘 돌 줄 알았지? 그럴 줄 알고 있다가 이런 메시지를 만난다.

Trying to query 1157 shards, which is over the limit of 1000. This limit exists because querying many shards at the same time can make the job of the coordinating node very CPU and/or memory intensive. It is usually a better idea to have a smaller number of larger shards. Update [action.search.shard_count.limit] to a greater value if you really want to query that many shards at the same time.

음… logstash 인덱스를 매시간마다 분할했더니 샤드가 꽤 많아진 모양이다. 그래서 최근 두 개의 인덱스로 검색 대상을 제한하려고 한다. Date math support in index names라는 문서에 인덱스 이름을 동적으로 바꾸는 법이 나와 있긴 하다. 그런데 막상 내가 짠 게 어떤 값이 나오는지 확인하는 방법은 제대로 안 나온다. 예를 들어 가 logstash-2017.02.22t01로 해석되는지 어떻게 아는가? 많은 삽질 끝에 방법을 찾았다.

  1. 를 URL 인코딩한다.
  2. 그렇게 얻은 값 을 가지고 인덱스 조회 API를 호출한다. GET /3Clogstash-{now-1h/d}t{now-1h{HH}}>

그러면 다음과 같이 결과가 나와서 인덱스 이름이 어떻게 해석됐는지 확인할 수 있다.

{
"logstash-2017.02.23t01": {
"aliases": {},
"mappings": {
/* 중략 */
}
}

여기까지는 전적으로 검색 쿼리 작성 경험이 부족해서 발생한 삽질이다. 하지만 애플리케이션 로그 분석을 패턴화하지 않고 이렇게 검색 쿼리를 복잡하게 짜야 한다니 아직 갈 길이 멀다는 생각이 든다. DataDog 또는 NewRelic 같은 상용 서비스를 참고해서 개선하면 좋겠다.

이제 결과를 알람으로 보내면 된다. 이래저래 고생하다 대충 아래와 같은 형태로 완성했다.


PUT _xpack/watcher/watch/outofmemoryerror 
{
  "trigger" : {
    "schedule" : { "cron" : "0 0/4 * * * ?" }
  },
  "input" : {
    "search" : {
      "request" : {
        "indices" : [
          "",
          ""
        ],
        "body" : {
          "query" : {
            "bool" : {
              "must" : {
                "multi_match": {
                   "query": "OutOfMemoryError",
                   "fields": ["message", "log"]
                }
              },
              "filter" : {
                "range": {
                  "@timestamp": {
                    "from": "{{ctx.trigger.scheduled_time}}||-5m",
                    "to": "{{ctx.trigger.triggered_time}}"
                  }
                }
              }
            }
          },
            "sort" : [
                { "@timestamp" : {"order" : "desc"}},
                "_score"
            ]
        }
      }
    }
  },
  "condition" : {
    "compare" : { "ctx.payload.hits.total" : { "gt" : 0 }}
  },
  "actions" : {

    "notify-slack" : {
      "throttle_period" : "5m",
      "slack" : {
        "message" : {
          "to" : [ "#ops", "@dev" ],
          "text" : "로그 모니터링 알람",
          "attachments" : [
            {
              "title" : "OutOfMemoryError",
              "text" : "지난 5분 동안 해당 오류가 {{ctx.payload.hits.total}}회 발생했습니다. 가장 최근의 오류는 다음과 같습니다.",
              "color" : "warning"
            },
            {
              "fields": [
                {
                    "title": "환경",
                    "value": "Prod",
                    "short": true
                },
                              {
                                  "title": "발생시각",
                                  "value": "{{ctx.payload.hits.hits.0._source.@timestamp}}",
                                  "short": true
                              },
                              {
                                  "title": "메시지",
                                  "value": "{{ctx.payload.hits.hits.0._source.message}}",
                                  "short": false
                              },
                              {
                                  "title": "확인명령어",
                                  "value": "`GET /{{ctx.payload.hits.hits.0._index}}/{{ctx.payload.hits.hits.0._type}}/{{ctx.payload.hits.hits.0._id}}`",
                                  "short": false
                              }
                          ],
              "color" : "warning"
            }
          ]
        }
      }
    }
  }
}


4분마다 검색 쿼리를 실행해서 최근 5분 간의 레코드를 감시하기 때문에 동일한 오류에 대해 2회 연속으로 알람을 받을 가능성이 있다. X-Pack은 이를 우회할 방법을 제공하지 않는 것 같다. 그래서 쿼리가 발견한 레코드의 인덱스 ID를 Slack 메시지 중 확인명령어 필드에 넣었다. 알람이 두 번 왔지만 인덱스 아이디가 동일하다면 오류가 한번 발생한 것으로 간주하면 된다.

참고 문서

위의 Alert를 작성하며 도움을 받은 문서는 다음과 같다.

기타

Elasticsearch Cloud는 기본적으로 이메일 발송을 지원하기 때문에elasticsearch.yml 설정에 xpack.notification.email를 추가하지 않아도 된다. 아니, 추가하면 잘못된 설정이라며 거부한다. Illegal이라고만 하지 이유를 자세히 알려주지 않기 때문에 삽질하기 쉽니다. Invalid addresses라고 오류 로그가 찍히면 이것은 설정 문제가 아니다. 이메일 설정 메뉴로 가서 Watcher Whitelist에 수신 이메일 주소를 등록하면 문제가 해결된다.


테스트용 로그 메시지를 Fluentd로 보내고 싶다면 fluent-cat 명령을 이용한다.

echo '{"message":"Dummy OutOfMemoryError"}' | fluent-cat kubernetes.log


Originally published at Andromeda Rabbit.

#데일리 #데일리호텔 #개발 #개발자 #개발팀 #인사이트


관련 스택

기업문화 엿볼 때, 더팀스

로그인

/