LINE 메시징 서버가 새해 트래픽을 대비하는 과정

이 글은 많은 분들의 노력과 작업을 대표해서 작성한 것임을 밝힙니다. 신년 대응 과정에 함께 참여해주신 모든 분들의 노고에 감사드립니다.

 

들어가며

LINE의 트래픽은 메신저 특유의 독특한 패턴을 갖고 있습니다. 새해 0시가 되는 순간, 사용자들이 메신저로 새해 인사를 주고받으면서 평소 대비 메시지 발송 건수가 대폭 증가하는데요. 이때 서비스 국가별 시차와 문화에 따라서 다양한 트래픽 증가 패턴이 나타납니다. LINE에선 이런 순간적인 트래픽 증가를 문제없이 처리하기 위해 매년 다양한 준비를 하고 있는데요. 이를 ‘신년 대응’이라고 부르고 있습니다. 이번 글에서는 2020년 신년 대응에서 저희가 노력했던 일들과 그 결과에 대해서 소개하려고 합니다.

 

LINE 메시징 서버가 새해 트래픽을 대비하는 과정

각 국가의 새해 0시가 되면 많은 사용자들이 LINE으로 새해 인사 메시지를 보냅니다. 이에 따라 평소와 비교해 순간적으로 트래픽이 아주 큰 폭으로 증가하는데요. 국가에 따라 일반 메시지를 많이 전송하는 국가도 있고, 이미지나 동영상 메시지를 많이 전송하는 국가도 있습니다. LINE에서는 이런 독특한 트래픽 증가를 잘 처리하기 위해서, 신년이 되기 전에 길게는 6개월 전부터 신년을 맞이하기 위한 다양한 준비를 하고 있습니다. LINE 앱은 다양한 컴포넌트로 구성되어 있는데요. 신년 대응 준비 과정에서 각 컴포넌트를 담당하고 있는 팀들이 긴밀하게 협력하게 됩니다.

각국의 0시에 맞춰 트래픽이 순간적으로 크게 증가하는 모습
올해 신년에 관측된 시간별 미디어 메시지 발송 트렌드, 상대적으로 미디어 메시지를 더 많이 사용하는 국가가 있어 전체 트래픽 패턴과는 다른 모습을 보임 

LINE에선 서비스를 출시한 이래로 매년 새해에 관측된 트래픽 패턴과 특이사항들을 연도별로 정리해놓고 있습니다. 이 정보를 바탕으로 올해 맞이할 신년 트래픽을 예측하고 개선이 필요한 부분을 확인합니다. 또한 예측만큼 중요한 것이 현재 서버의 가용량을 정확히 판단하는 것인데요. 애플리케이션 서버와 데이터베이스의 현재 가용량을 판단하고 개선하는 작업에 대해서 차례대로 소개하겠습니다.

 

신년 대응 프로세스

 

정확하게 계측하기

LINE 메시징 서버는 매년 새로운 기능이 추가되고 다양한 개선 작업이 지속적으로 진행되고 있어서, 데일리 릴리스(daily release)를 진행하고 있습니다. 코드가 끊임없이 변화하고 있는 건데요. 그래서 매년 정확하게 계측하고 준비하여 다음에 발생할 신년 트래픽도 문제없이 처리할 수 있도록 대비해야 합니다. 현재의 서버가 신년 트래픽을 문제없이 처리할 수 있는지 확인하려면 예측한 트래픽을 정확히 시뮬레이션하는 것이 중요합니다. 그런데 실제 사용자의 읽기/쓰기 트래픽 패턴을 그대로 시뮬레이션하는 것은 매우 어려운 일입니다. 시뮬레이션 과정에서 한 가지 요인이라도 실제 트래픽과 달라지면 정확하게 계측되지 않는 문제가 발생하고, 이 때문에 시뮬레이션 환경에서는 발생하지 않았던 문제가 실제 상황에서 발생하는 일이 종종 벌어집니다. 이런 이유로 저희는 실제 트래픽을 이용한 벤치마크 테스트(Benchmark test, 이하 BMT)를 진행하고 있습니다. BMT의 시작은 서버의 상태를 한눈에 정확히 판단할 수 있는 여러 대시보드를 준비하는 것입니다.

 
 

여러 지표를 한눈에 볼 수 있는 다양한 대시보드

대시보드를 준비하고 나면 본격적으로 테스트를 진행하는데요. 테스트는 대시보드를 주의 깊게 모니터링하면서 특정 호스트로 유입되는 트래픽을 서서히 늘려가는 방식으로 진행합니다. 대시보드에서 이상 징후가 포착되면 바로 해당 호스트로 라우팅하는 트래픽을 줄입니다. 만에 하나 순간적으로 가용량 이상의 트래픽이 해당 호스트로 유입되어 처리에 실패하더라도, 다양한 방법을 이용해서 재시도하여 메시지가 유실되지 않게 설계했기 때문에 서비스에 영향을 주지 않는 범위에서 정확도 높게 계측할 수 있습니다. 

 

기존의 트래픽 패턴을 바탕으로 신년 대응 계획 수립하기

애플리케이션 서버는 각 서버에 트래픽이 분배되는 구조이기 때문에 위에 기술한 바와 같이 특정 서버로 유입되는 트래픽을 늘리는 방법으로 BMT를 진행할 수 있습니다. 하지만, 데이터베이스는 다수의 서버가 공유 자원을 사용하는 구조이기 때문에 이런 방법을 사용하기 어렵습니다. 그렇기 때문에 과거에 수집된 데이터를 바탕으로 신년 대응 계획을 수립하게 됩니다.

LINE은 서비스 출시 이래 진행했던 모든 신년 대응을 기록하여 연도별로 보관하고 있습니다. 해당 연도의 신년 트래픽 패턴은 어땠는지, 어떤 준비를 했었는지 잘 기록하고 있어서 새로운 신년 맞이 준비에 큰 도움이 됩니다. 또한 신년 대응에 참여하는 모든 팀 및 컴포넌트의 내용을 연도별로 하나의 문서에 기록하고 있어서 다른 팀이나 컴포넌트의 데이터도 쉽게 볼 수 있으며, 꼭 트래픽 패턴뿐 아니라 특이 사항이나 개선이 필요한 사항도 함께 관측하여 일목요연하게 정리하고 있습니다.

LINE 메시징 서버는 Redis와 HBase를 데이터베이스로 사용하고 있는데요. Redis 신년 대응 계획을 예로 들자면, Redis의 경우 우선 클러스터 별로 과거 데이터에 기반하여 트래픽 패턴에 따라 분류합니다. 신년 트래픽이 유입되었을 때 받는 부하가 크게 늘어나는 클러스터가 있고 그렇지 않은 클러스터가 있기 때문입니다.

트래픽 패턴을 살펴볼 때는 IOPS(Input/Output Operations Per Second)와 UsedMemory 수치를 참고합니다. 

 

IOPS

IOPS는 Redis로 유입된 초당 요청 횟수를 의미하는데요. 신년과 같이 사용자의 요청이 급증할 때 함께 증가하는 수치입니다. GET 요청이 많은 서비스의 경우, 현재 Redis 클러스터 가용량이 GET 요청을 처리하기에 충분한지를 확인합니다.

충분하지 않다면 부하를 더 잘게 분산시키기 위해서 클러스터 확장 작업을 진행할 수도 있고, 혹은 애플리케이션 수준에서 Redis 요청을 최적화하여 부하 자체를 줄일 수 있습니다. 어떤 방식으로 대응할지는 내부 논의를 거쳐 양쪽의 장단점과 효율성을 판단해보고 결정합니다.

올해 신년, Redis 클러스터에 유입된 초당 I/O 요청의 변화 추이

 

UsedMemory

UsedMemory는 Redis에 저장된 데이터의 크기입니다. 클러스터 데이터의 특성상 신년에 PUT 요청이 많다면, UsedMemory 수치를 주의 깊게 살펴봅니다. 신년에 요청량이 급증하면서 Redis 클러스터에서 현재 사용 가능한 최대 메모리 양을 초과해 버리면 데이터 특성에 따라서 서비스에 중대한 영향을 끼치게 되는 경우도 있습니다. 따라서 과거 데이터를 바탕으로 필요한 메모리 크기를 산정한 뒤 증설이 필요하면 증설 작업을 진행합니다.

올해 신년, Redis 클러스터의 메모리 사용률 변화 추이

 

개선하기

 

메시징 서버

앞서 언급한 대로 LINE 메시징 서버는 데일리 릴리스되며 끊임없이 개선되고 변화하고 있습니다. 새로운 기능을 추가하고, 다양한 개선 작업을 진행하다 보면 예상치 못한 사이드 이펙트가 종종 발생합니다. 평소 수준의 트래픽에서는 이런 사이드 이펙트가 발생해도 별 문제가 안 되지만, 순간적으로 몇 배의 트래픽이 유입되는 순간에는 큰 문제로 불거지는 경우가 있어서 매년 주의 깊게 계측하고 모니터링하고 있습니다. 

2020년 신년 대응을 준비하며 중점적으로 개선했던 부분은 푸시(push) 알림 발송 부분입니다. LINE에서는 메시징 서버에 LINE에서 공개한 고성능 비동기 서버/클라이언트 라이브러리인 Armeria를 점진적으로 도입하고 있었는데요. 2018년에는 푸시 발송 컴포넌트와 HTTP 통신 부분을 Armeria로 전환했습니다. 당시에도 앞서 말씀드렸던 BMT를 진행했고, 문제가 없다는 것을 검증한 뒤 2019년 신년을 맞이했는데요. 예상치 못했던 문제가 발생했었습니다. BMT를 통해 검증했던 트래픽 수준에서 크게 벗어나지 않는 수준의 트래픽이 유입됐지만, 0시를 기점으로 푸시 컴포넌트의 응답이 급격하게 느려졌고, 메시징 서버의 푸시 관련 큐가 모두 차버려서 유실되는 푸시가 발생했습니다. 그래서 큐에 적체된 일부 푸시를 버리는 방법으로 최대한 빠르게 복구한 뒤, 바로 원인 분석에 들어갔습니다. 

관련 지표와 로그를 분석해보니 0시를 기점으로 푸시 컴포넌트에 급격하게 많은 커넥션(connection)이 한꺼번에 생성, 해당 컴포넌트의 힙(Heap) 메모리 사용률이 급격하게 늘어나면서 Full GC(Garbage Collection)를 유발해 성능이 급격하게 떨어졌던 것을 확인했습니다. 

최대한 실제와 동일한 트래픽으로 시뮬레이션하면서 BMT를 수행하려고 했지만, 실제 상황과 다른 변인이 있었던 것입니다. 기존 BMT는 목표했던 TPS(transactions per second)까지 주의 깊게 모니터링하면서 단계적으로 트래픽을 올리는 방식이었는데요. 그러다 보니 순간적으로 몇 배의 트래픽이 유입됐을 때에 대한 테스트는 이루어지지 못했던 것입니다. 

해당 푸시 발송을 위해서 사용하고 있는 Armeria 클라이언트는 비동기 HTTP 클라이언트이기 때문에 제한된 숫자의 커넥션만으로도 높은 TPS를 낼 수 있도록 설정할 수 있었습니다. 다만, 관련 설정의 부재로 한꺼번에 많은 요청이 들어왔을 때 필요 이상의 커넥션을 생성해 사용하는 상태였습니다. 조사한 내용을 바탕으로 장애 회고 회의를 했고, 아래와 같은 개선 과제를 진행하기로 결정했습니다.

  • 성능 개선 과제
    • 한정된 수의 커넥션만 생성하여 이를 재활용할 수 있도록 개선하고, 목표한 TPS를 달성할 수 있는지 확인
  • 안전장치 마련 과제
    • 알림 담당 큐를 역할별로 분리하여 특정 컴포넌트에 발생한 장애의 영향 범위를 최소화
    • Circuit breaker를 도입해서 특정 컴포넌트가 요청을 받지 못하는 상황이 되면, 자동으로 요청을 차단하여 장애를 격리하고, 해당 컴포넌트가 빠르게 복구될 수 있도록 조치

위 개선 과제들을 완료한 후 보완된 BMT 테스트를 진행했습니다. 우선 기존과 같이 점진적으로 목표한 TPS까지 늘려보는 테스트를 먼저 수행한 후 문제가 없는 것을 확인하고, 그다음엔 TPS를 평상시 트래픽 수준으로 내렸다가 목표한 수준까지 한 번에 올려보는 테스트도 진행했습니다.

점진적으로 TPS를 올려보면서 확인, 이후 평상시 TPS 수준에서 한 번에 목표한 TPS까지 올리며 다시 확인

한 번에 목표한 TPS까지 올렸을 때도 한정된 커넥션만을 이용하여 안정적으로 푸시 알림을 발송하는 것을 확인한 후 개선 과제를 마무리했습니다.

LINE은 예상치 못한 문제가 발생했을 때 책임을 추궁하기보다는 투명하게 공유하고 재발 방지 대책을 수립하여 같은 문제가 다시 발생하지 않도록 만드는 것에 집중합니다. 이런 문화 덕분에 모든 개발자가 혁신을 두려워하지 않고, 끊임없이 더 좋고 단단한 시스템을 만들기 위한 고민에만 집중할 수 있는 것 같습니다. 이런 LINE의 문화는 LINE의 장애 보고와 후속 절차 문화 글에서 더욱 자세히 확인할 수 있습니다.

그 외에도 LINE 메시징 서버에서 사용하고 있던 Apache HTTP 클라이언트의 버전을 업그레이드하니 오히려 성능이 더 떨어지는 문제도 발견되었는데요. HTTPCLIENT-1809 이슈에서 보고된 바와 같이, Apache HTTP 클라이언트는 특정 처리량(throughput)을 넘어서면 성능이 떨어지면서 서버의 가용량을 다 채우지 않은 상황에서도 더 이상 처리량을 늘리지 못했습니다. 이 문제는, 마침 LINE 메시징 서버에 순차적으로 Armeria를 도입하고 있는 단계였기 때문에 Apache HTTP 클라이언트를 Armeria로 교체하는 방법으로 해결할 수 있었습니다.

 

Redis 클러스터

앞서 소개한 대로 Redis 클러스터는 현재 가용량과, 신년 대응을 위해 필요한 가용량을 비교하며 준비합니다. LINE 메시징 서버는 ‘Client Side Sharded’ 형태(참고)로 Redis를 사용하고 있고, 각 노드에 부하가 골고루 분산되도록 구성되어 있습니다. 현재 클러스터의 가용량이 예측되는 필요 가용량보다 부족한 경우 크게 아래와 같은 3가지 방법으로 대응합니다.

  • 해당 Redis 클러스터가 여러 가지 역할을 담당하고 있는 경우 클러스터를 분리하여 별도의 클러스터를 구성하기
  • 애플리케이션 수준에서 코드를 개선하여 클러스터를 효율적으로 사용할 수 있는 방법이 있는지 살펴보기
  • 해당 Redis 클러스터에 노드를 추가하고 각 노드가 담당하는 트래픽 줄이기

위 방법에 맞춰 올해 신년 대응에서 진행했던 클러스터 개선 사례 3가지를 간략하게 소개하겠습니다.

첫 번째는 여러 가지 성격의 데이터를 처리하는 Redis 클러스터를 분리한 작업입니다. LINE의 트래픽은 지속적으로 증가하고 있기 때문에 올해 신년에 순간적으로 몰릴 트래픽을 안정적으로 처리하기 위해서는 특정 클러스터에 조치할 필요가 있었습니다. 그래서 해당 클러스터에서 성격상 분리할 수 있는 데이터들을 별도의 클러스터를 만들어 옮기는 작업을 진행했습니다. 아래 그래프에서 해당 클러스터의 분리 작업 전후 IOPS를 확인할 수 있습니다.

분리하기 전 IOPS
분리한 후 원래 클러스터의 IOPS
분리한 후 새로운 클러스터의 IOPS

클러스터 분리 작업을 통해 지나치게 많은 트래픽이 유입되고 있던 클러스터에서 처리하는 요청 수가 1/4로 줄어들었고, 나머지 3/4의 요청은 새로 생성된 클러스터에서 처리하게 되었습니다. 클러스터 분리 작업은 무중단으로 진행해야 했기 때문에, 4~5일에 걸쳐서 주의 깊게 모니터링하며 작업을 진행했습니다.

두 번째는 비정상적인 호출을 시도하는 악성 사용자들의 요청을 판단하는 기능을 서비스에 넣어 클러스터의 IOPS를 안정시킨 사례입니다. 행동 패턴을 기반으로 악성 사용자들의 요청을 자동으로 차단하는 시스템이 이미 도입되어 대부분의 악성 요청이 차단되고 있었지만, 악성 사용자들이 끊임없이 패턴을 진화시키며 순간적으로 큰 Redis IOPS를 유발하는 클러스터가 있었습니다. 

작년 신년, 0시의 트래픽 패턴이 제대로 보이지 않을 정도로 순간순간 IOPS가 치솟는 모습

올해는 악성 사용자들을 더 빠르고 정확하게 차단하는 기능을 새로 도입해서 아래와 같이 해당 Redis 클러스터의 트래픽을 안정화시킬 수 있었습니다.

올해 신년, IOPS가 요청량을 따라 증가하는 패턴을 보이며 클러스터가 안정화된 모습

세 번째는 특정 Redis 클러스터의 Eviction Policy를 변경한 사례입니다. UsedMemory가 지속적으로 증가하는 클러스터가 있었는데요. 애플리케이션 수준에서는 TTL(Time To Live)을 지정해도 큰 상관이 없는 데이터였기 때문에 데이터에 TTL을 지정하고, Eviction Policy를 ‘volatile-ttl’로 변경 조치했습니다. 그리고 과거의 트래픽 패턴에 기반하여 예측한 신년 예상 트래픽에 따라 해당 TTL 기간 동안의 데이터는 잘 유지할 수 있는 수준으로 클러스터를 확장하는 것으로 대응을 마무리했습니다.

 

신년 대응 회고하기

신년 대응의 마지막 순서는 회고입니다. 메시징 서버뿐 아니라 모든 LINE의 서버 컴포넌트에 대한 트래픽 패턴과 특이사항을 문서에 기록하는데요. 신년 대응이 끝난 후 모두 모여서 이 문서를 기반으로 회고를 진행합니다. LINE 출시 후 지금까지 한 해도 빠짐없이 각 연도별로 기록해놓은 이 소중한 신년 대응 문서들은 회고를 준비하는 과정에서 각 컴포넌트 담당 팀이 만들어 내는데요. 이 자료들은 내년 신년 대응에 아주 중요한 근거 자료로 사용하기 때문에 작은 부분까지 빠짐없이 기록하기 위해 노력하고 있습니다. 트래픽 패턴은 정확한 수치와 그래프를 첨부하여 기록하고, 해당 시점의 모니터링 대시보드의 데이터를 스냅숏(snapshot) 형태로 보관하여 향후 신년 대응에서 빠짐없이 확인할 수 있도록 남기고 있습니다.

문서를 만들고 나면, 각 컴포넌트의 담당자들이 모여서 회고를 진행합니다. 컴포넌트에 따라 개발팀이 다른 국가에 위치한 경우도 있는데요. 그런 경우엔 모든 오피스의 개발자들이 참여할 수 있도록 화상 회의 장비를 활용합니다. 회고 과정에서 가장 중요시하는 것은 올해 신년 대응 과정에서 나타난 각 컴포넌트의 개선점을 찾는 것입니다. 신년 대응에서 관측된 데이터에서 조금이라도 이상한 점이 발견되면, 해당 이슈를 투명하게 공유한 뒤 개선 계획도 공유합니다.

 

마치며

올해 신년을 대비해 모두가 열심히 준비한 덕에 각국의 신년 트래픽을 문제없이 처리할 수 있었는데요. 문제는 없었지만 회고 자료를 준비하는 과정에서 조금 특이한 사항을 발견하게 되었습니다. 메시징 서버에서 다른 컴포넌트를 호출하며 통신하는 과정에서 레이턴시(latency)가 예상보다 늘어나는 사례를 발견했는데요. 해당 컴포넌트의 로그를 살펴보니 실제 요청을 처리한 시간보다 새로운 커넥션이 생성되는 데 걸리는 시간이 훨씬 더 길다는 것을 발견했습니다. 순간적으로 평소 대비 수 배의 트래픽을 처리하면 이와 같이 평소엔 발견하지 못했던 사례들이 발견됩니다. 문제를 발견한 후 해당 컴포넌트와 통신할 때 HTTP/2를 사용해서 커넥션을 효율적으로 사용하는 방향으로 개선 과제를 만들었고, 현재 진행 중입니다. 참고로, LINE에서 광범위하게 사용하고 있는 Armeria에서는 HTTP/2를 기본적으로 지원하고 있어서 손쉽게 HTTP/2로 전환할 수 있습니다.

LINE에서는 내년의 신년 대응을 준비하면서도 이 글에서 소개해드린 바와 같은 준비 작업을 진행할 예정입니다. LINE에서는 작은 특이사항이라도 투명하게 공유하고, 개선 및 회고 과정을 통해 더 좋은 시스템을 만들기 위해 매일 부단히 노력하고 있습니다. 지속적으로 개선되며 더욱 단단하게 만들어지고 있는 LINE의 서버 컴포넌트 개발에 관심 있다면 많은 지원 부탁드립니다.  🙂

 

채용 공고

Related Post