엔지니어링

Logstash에는 Redis면 충분합니다

Logstash 배포에서는 이벤트 급증으로 인해 Elasticsearch나 기타 다운스트림 구성 요소가 정체되는 것을 제어하기 위해 메시지 큐를 사용합니다. Redis는 이러한 메시지 큐로 사용되는 기술 중 하나입니다. 빠른 속도와 사용 편의성, 적은 리소스 사용량으로 인해 가장 많이 사용되는 기법 중 하나이기도 합니다.

출시된 지 5년이 지난 오래 전 Logstash 버전 1.0.4부터 이 기능이 적용된 이유는 바로 이 때문일 것입니다!

하지만 Logstash 파이프라인 개선을 위한 최근 변경 사항과 Redis 플러그인 자체의 개선으로 인해 현재 Logstash Redis 입력 플러그인의 최대 가용량을 사용하지 못하고 있을 수 있습니다.

Logstash는 시스템에 있는 코어 수를 탐지하고 그에 따른 파이프라인 워커의 수를 일치시키는 설정을 합니다. 하지만 파이프라인 워커는 필터 및 출력 플러그인에만 중요하고,  입력 플러그인에는 영향을 미치지 않습니다. Redis 입력 플러그인에서 '스레드'를 설정하지 않았다면 성능의 일부만 활용하고 있는 것입니다.

테스트 설정에는 안정적인 최신 버전의 Logstash(2.3.4)이 포함되었고 Redis 3.0.7이 같은 시스템에서 실행되었으며 시스템은 2015 MacBook Pro였습니다. 사용된 Logstash 구성은 다음과 같습니다.

input {  
  redis {
    host => "127.0.0.1"
    data_type => "list"
    key => "redis_test"
    # batch_count => 1
    # threads => 1
  }
}
output { stdout { codec => dots } }

다음 조건으로 이를 실행합니다: bin/logstash -f some_config_file.config | pv -Wart > dev/null

이 테스트 설정은 최대 처리량에 대한 테스트용입니다. 실제 성능 수치는 어떤 다른 플러그인을 사용하는지에 따라 크게 달라집니다.

스레드 설정에 대한 몇 가지 옵션을 반복하고 다른 설정에는 기본값을 사용하여 다음과 같은 결과를 얻을 수 있었습니다:

  • 스레드 => 1 : 37.4K/sec
  • 스레드 => 2 : 57K/sec
  • 스레드 => 4 : 67K/sec
  • 스레드 => 8 : 87.3K/sec

이번에는 batch_count => 250으로 하고 -b 250 을 Logstash 명령줄에 추가하여 같은 테스트를 다시 실행함으로써 증가된 성능을 완전히 활용합니다:

  • 스레드 => 1 : 35.2K/sec
  • 스레드 => 2 : 48.9K/sec
  • 스레드 => 4 : 58.7K/sec
  • 스레드 => 8 : 72.5K/sec

보이는 것처럼 기본값 batch_count 125를 250으로 증가시키면 성능이 저하됩니다.

과거에는 Redis로 작업 시 batch_count 를 큰 값으로 설정하는 것이 일반적이었습니다. 하지만 이는 더 이상 모범 사례가 아니며, 실제로 성능을 저하시키게 됩니다. 이는 Logstash 파이프라인 아키텍처가 변경된 것이 주 원인입니다. batch_count 를 250으로 증가시키더라도 스레드 => 8일 때 성능이 89.5K/sec에서 72.5K/sec로 떨어집니다. 최적의 batch_count 는 pipeline-batch-size 기본값인 125이며 여기에는 이유가 있습니다!

심층 분석

Redis 입력에 대한 최근 변경에는 배치 모드를 기본적으로 켜고 Redis 서버에서 Lua 스크립트를 사용하여 배치 검색 명령을 실행하는 것이 포함됩니다. 또한 Lua 스크립트는 트랜잭션 역할을 하므로 배치를 취하여 한 단계에서 큐를 단축시킬 수 있습니다. 이를 통해 다른 Redis 연결이 강제로 대기하도록 만듭니다. 업스트림 Logstash 구성의 Logstash Redis 출력 플러그인이 그러한 연결 중 하나입니다.

또한 업스트림 Logstash가 Redis에 이벤트를 추가하는 방법이 다운스트림 Logstash 인스턴스의 성능에 영향을 미칠 수 있다는 점을 알아야 합니다. 예를 들어 테스트 결과를 준비하면서 redis_cli 에서 크기가 LLEN redis_test인 목록을 쿼리하면 측정에 영향을 너무 자주 미치는 것으로 나타났습니다. 업스트림 Logstash 인스턴스에서 이러한 요소를 파악하려고 해보십시오:

  • 이벤트 생성 비율
  • 이벤트 크기(최소, 최대 및 평균)
  • redis 출력에 사용된 batch_events 용량

더 큰 크기의 batch_events (예: 500)를 업스트림 Redis 출력에 사용하는 것을 고려할 수도 있습니다. 이렇게 하면 Redis 호출 전에 이벤트를 버퍼링하게 되어 다운스트림 Redis에 개입하여 Redis에서 가져올 수 있는 입력 스레드 시간을 주게 됩니다. 그러나 이벤트 생성 속도가 이보다 빠르면 유용하지 않을 수도 있습니다. 500개 배치일 때 50K/sec라면 배치 버퍼가 10밀리초만에 채워집니다.

배치 크기가 커질수록 그에 비례해서 Lua 스크립트의 성능이 저하되는 것을 볼 수 있었습니다. 즉, Redis 입력의 배치 크기가 큰 경우 Redis 양쪽의 Logstash 인스턴스 모두의 성능에 영향을 주게 됩니다.

일반적으로 Redis 입력 구성과 파이프라인 배치 크기, 워커 스레드를 조정하는 목적은 입력이 필터/출력 단계와 보조를 맞출 수 있도록 하기 위한 것입니다. 사용자는 기본값의 경우에 필터/출력 단계의 평균 처리량이 얼마인지 알고 있어야 합니다. 예를 들어 필터/출력 단계의 처리량이 35K/sec일 때 1~2개의 워커가 초당 충분한 수의 이벤트를 가져온다면 redis 입력 워커 수를 8개로 바꿀 필요가 별로 없습니다.

차이를 만드는 또 한가지는 JSON 인코딩(1), 디코딩(2) 및 인코딩(3)으로, 업스트림 Redis 출력에서 1, 다운스트림 Redis 입력에서 2, Elasticsearch 출력에서 3, 이렇게 3차례 발생합니다. 문자열 값이 큰 이벤트는 디코딩하는 데 시간이 걸리므로 운영 데이터에 가까운 크기와 크기 분포를 나타내는 데이터 세트를 테스트에 이용해야 합니다.

여러 개의 Logstash 인스턴스와 하나의 Redis 서버를 사용하여 처리량 확장

대부분의 사용자는 모두 동일한 Redis 서버 목록을 가리키는 여러 개의 Logstash 인스턴스가 실행된 서로 다른 하드웨어에서 처리량을 개선하려고 합니다.

이렇게 해도 효과는 있지만 같은 조정 팁을 각 인스턴스에 적용하는 것이 좋습니다. 서로 다른 Redis 서버나 다른 목록에 연결된 경우 외에는 하나의 구성에 여러 개의 Redis 입력을 정의하는 것은 큰 의미가 없습니다. 하나의 Redis 입력의 스레드만 조정하면 됩니다. 3개의 Logstash 인스턴스가 같은 Redis 서버를 가리키면 Redis 클라이언트 연결 시 일정 수준의 경쟁이 발생하는 것이 분명하며, 이는 하나의 인스턴스를 조정한 후 함께 실행 시 3개 모두를 다시 조정해야 할 가능성이 있음을 나타냅니다.

결론

머지 않은 미래에 당사는 Jedis Java 라이브러리를 중심으로 한 JRuby 래퍼로 Redis 입력을 향상시킬 예정입니다. 그러면  Redis Cluster 모드 지원이 추가로 제공됩니다!

한편 Redis 입력 성능을 향상시키려면 사용하는 스레드 수를 늘려 보십시오. 그리고 기본값이 아닌 batch_count를 사용하거나 여러 개의 Logstash 인스턴스가 하나의 Redis 서버를 가리킬 경우, 측정을 하고 그에 맞도록 조정을 해야 합니다.

Redis와 Logstash에 대해 궁금한 점이 있으시면 저희 포럼에 에 자유롭게 올려주십시오. IRC(#logstash)와 GitHub을 통해 연락을 주셔도 됩니다.

즐거운 Logstashing 되시길 바랍니다!