스토리 홈

인터뷰

피드

뉴스

조회수 2231

비트윈의 멀티티어 아키텍처를 위한 프레젠터 이야기 - VCNC Engineering Blog

블로그 첫 글에서 비트윈의 시스템 아키텍처에 대해 다룬 적이 있습니다. 시스템 구성의 미래에 대한 계획으로 멀티티어 아키텍처에 대해 언급했었는데, 이는 프로토콜을 단순화시키고 배포 자동화를 가능하게 하기 위해서 클라이언트와 비즈니스 로직을 담당하는 서버 사이에 일종의 게이트웨이를 두는 것이었습니다. 그 외에도 여러 가지 필요성이 생겨 해당 역할을 담당하는 프레젠터라는 것을 만들게 되었고 비트윈의 채팅 시스템에 적용하게 되었습니다. 만드는 과정 중에 여러 기술적인 문제들이 있었고 이를 해결하기 위한 노력을 하였습니다. 이 글에서는 비트윈 시스템에서의 프레젠터에 대해 이야기 하고자 합니다.프레젠터프레젠터는 일종의 게이트웨이 입니다. 기존의 시스템에서는 클라이언트들이 ELB를 통해 채팅 서버에 직접 TCP 연결을 하였습니다. 하지만 비트윈 PC버전과 자체 푸시 서버를 만들면서 ELB로는 해결할 수 없는 부족한 점들이 생겼고, ELB의 부족한 점을 채워줄 수 있는 시스템이 필요하게 되었습니다. ELB를 대체하는 역할 외에도 다른 여러 필요했던 기능들을 제공하는 프레젠터를 만들기로 하였습니다.프레젠터는 ELB의 역할을 할 뿐만 아니라 여러 다른 기능들도 제공합니다.프레젠터의 기능패킷을 적절한 샤드로 중계비트윈에서는 커플 단위로 샤딩하여 같은 커플의 채팅 요청에 대해서는 같은 채팅 서버에서 처리하고 있습니다. Consistent Hash를 통해 커플을 여러 채팅 서버로 샤딩하고 ZooKeeper를 이용하여 이 정보를 여러 채팅 서버 간 공유합니다. 프레젠터 또한 ZooKeeper와 연결을 하여 어떤 채팅 서버가 어떤 커플을 담당하는지에 대한 정보를 알고 있도록 설계되어 있습니다. 따라서 프레젠터는 첫 연결 시 보내는 인증 패킷을 보고 해당 채팅 연결에서 오는 요청들을 어떤 채팅 서버로 보내야 할지 판단할 수 있습니다. 어떤 채팅 서버로 보낼지 판단하는 과정은 처음 한 번만 일어나며, 이후 패킷부터는 자동으로 해당 채팅 서버로 중계합니다.프레젠터의 이런 기능 덕분에 클라이언트는 더 이상 어떤 채팅 서버로 붙어야 하는지 알아내는 과정 없이 아무 프레젠터와 연결만 맺으면 채팅을 할 수 있게 되었습니다. 기존에는 클라이언트들이 여러 채팅 서버 중 어떤 서버에 붙어야 하는지 확인하는 작업을 한 후에 할당된 채팅 서버로 연결 맺어야 했습니다. 그래서 클라이언트가 채팅 서버와 연결을 맺기 위해 다소 복잡한 과정을 거쳐야 했지만, 이제는 클라이언트가 프레젠터의 주소로 연결 요청만 하면 DNS Round Robin 통해 아무 프레젠터와 연결하는 방식으로 프로토콜을 단순화할 수 있었습니다. 덕분에 새로운 채팅 서버를 띄울 때마다 ELB를 Warm-Up 시켜야 했던 기존 시스템의 문제가 없어졌습니다. 그래서 비트윈 개발팀의 오랜 염원이었던 채팅 서버 오토스케일의 가능성도 열렸습니다.많은 수의 연결을 안정적으로 유지PC버전과 푸시 서버를 만들면서 기존의 채팅 연결과 다르게 많은 수의 연결이 장시간 동안 유지 되는 경우를 처리할 수 있어야 했습니다. 기존에는 TCP 릴레이를 하도록 설정된 ELB가 연결들을 받아주었습니다. 한 머신당 6만 개 정도의 Outbound TCP 연결을 맺을 수 있는데, ELB도 트래픽에 따라 여러 대의 머신에서 돌아가는 일종의 프로그램이므로 이 제한에 걸린다고 생각할 수 있습니다. 따라서 많은 수의 연결을 맺어놓고 있어야 하는 경우 ELB에 문제가 생길 수 있다고 판단했습니다. (과거 ELB가 연결 개수가 많아지는 경우 스케일아웃이 안되는 버그 때문에 문제가 된 적이 있기도 했습니다) 또한 클라이어트 연결당 내부 연결도 하나씩 생겨야 하면 클라이언트가 연결을 끊거나 맺을 때마다 서버 내부 연결도 매번 끊거나 연결해야 하는 오버헤드가 발생합니다.이를 해결하기 위해 프레젠터에서는 TCP 연결을 Multiplexing하는 프로토콜을 구현하여 적은 수의 내부 연결로 많은 수의 클라이언트 연결을 처리할 수 있도록 하였습니다. 서버 내부에서는 고정된 개수의 몇 개의 연결만 맺어 놓고 이 연결들만으로 수많은 클라이언트 연결을 처리할 수 있습니다. 이처럼 TCP Multiplexing을 하는 것은 Finagle과 같은 다른 RPC 프로젝트에서도 지원하는 기능입니다.TCP Multiplexing 프로토콜을 통해 많은 수의 클라이언트 연결을 소수의 서버 내부 연결로 처리합니다.또한, 프레젠터는 많은 수의 SSL 연결을 처리해야 하므로 암복호화 로직을 실행하는데 퍼포먼스가 매우 중요하게 됩니다. 채팅 서버 한 대를 제거하거나 하는 경우 많은 연결이 한꺼번에 끊어지고 연이어 한꺼번에 연결을 시도하게 되는 경우가 있을 수 있는데, 이 때 대량의 SSL Handshaking을 하게 됩니다. 기존 서버들로 대량의 SSL Handshaking을 빠른 시간안에 처리하기 위해서는 높은 퍼포먼스가 필요합니다. Java로 작성된 프로그램만으로 이런 퍼포먼스 요구사항을 달성하기 어려우므로, 클라이언트와의 연결을 담당하는 부분은 OpenSSL, libevent를 이용한 C++로 코드로 작성하였습니다. 인증 패킷을 파싱하거나 패킷들을 릴레이 하는 등의 로직을 담당하는 부분은 Alfred라는 Netty를 이용하여 만든 인하우스 RPC 라이브러리를 이용해 작성되었습니다. 연결을 담당하는 부분은 TCP 연결을 유지하는 역할과 들어온 패킷들을 Netty로 작성된 모듈로 릴레이 하는 역할만 담당하므로 매우 간단한 형태의 프로그램입니다. 짧은 시간 안에 어럽지 않게 구현할 수 있었습니다.클라이언트의 연결을 받아주는 역할을 하는 부분은 C++, 실제 로직이 필요한 부분은 Java로 작성하였습니다.여러 네트워크 최적화 기술의 지원ELB에는 여러 네트워크 최적화 기술들을 아직 제공하지 않는 경우가 있습니다. 대표적으로 HTTP/2 혹은 SPDY, QUIC, TCP Fast Open 등이 있습니다. 특히 모바일 환경에서는 SSL Handshaking 등 부가적인 RTT로 인한 지연을 무시할 수 없으므로 이런 기술들을 이용한 초기 연결 시간 최적화는 서비스 퀄리티에 중요한 부분 중 하나입니다. ELB는 AWS에서 관리하는 서비스이므로 AWS에서 이런 기능들을 ELB에 적용하기 전에는 이용할 수 없지만, 프레젠터는 직접 운영하는 서버이므로 필요한 기능을 바로바로 적용하여 서비스 품질을 높일 수 있습니다. ELB에서 이미 제공하는 최적화 기술인 SSL Session Ticket이나 다른 몇몇 기술은 이미 적용되어 있고 아직 적용하지 않은 기술들도 필요에 따라 차차 적용할 예정입니다.프레젠터의 구현C++ 연결 유지 모듈프레젠터는 퍼포먼스를 위해 C++로 작성되었습니다. 이는 Pure Java를 이용한 암복호화는 프레젠터에서 원하는 정도의 퍼포먼스를 낼 수 없기 때문입니다. 처음에는 OpenSSL과 libevent를 이용해 작성된 코드를 JNI를 통해 Netty 인터페이스에 붙인 event4j라는 인하우스 라이브러리를 이용하려고 했으나, 코드가 복잡하고 유지보수가 어렵다는 점 때문에 포기하였습니다. 그 후에는 netty-tcnative를 이용해보고자 했으나 테스트 결과 연결당 메모리 사용량이 큰 문제가 있었고, 이를 수정하기에는 시간이 오래 걸릴 것 같아 포기하였습니다. 결국, 페이스북에서 오픈소스로 공개한 C++ 라이브러리인 folly를 활용하여 프레젠터를 작성하게 되었습니다. folly의 네트워크 API들이 OpenSSL과 libevent를 이용해 구현되어 있습니다.릴레이 로직프레젠터는 첫 인증 패킷을 파싱하여 릴레이할 채팅 서버를 판단하며, 이후의 패킷부터는 실제 패킷을 까보지 않고 단순 릴레이 하도록 설계하였습니다. 처음의 Netty 파이프라인에는 Alfred 프로토콜을 처리할 수 있는 핸들러들이 설정되어 있어 인증 패킷을 파싱 할 수 있으며 인증 패킷에 있는 정보를 바탕으로 어떤 채팅 서버로 패킷을 릴레이 할지 결정합니다. 그 이후 파이프라인에 있던 핸들러를 모두 제거 한 후, 읽은 byte 스트림을 Multiplexing Protocol 프레임으로 감싸서 그대로 릴레이 하는 매우 간단한 로직을 담당하는 핸들러 하나를 추가합니다. 덕분에 로직 부분의 구현도 매우 간단해질 수 있었으며, 채팅 서버에 API가 추가되거나 변경되어도 프레젠터는 업데이트할 필요가 없다는 운영상 이점도 있었습니다.Multiplexing Protocol프레젠터의 Multiplexing Protocol은 Thrift를 이용하여 직접 정의 하였으며, 비트윈 개발팀 내부적으로 사용 중인 RPC 라이브러리인 Alfred에 이 프로토콜을 구현하였습니다. Thrift를 통해 C++과 Java로 컴파일된 소스코드를 각각 프레젠터의 연결 처리 부분과 로직 처리 부분에서 이용하여 통신합니다. 프레젠터에서는 Multiplexing된 TCP 연결들을 Stream이라고 명명하였으며 이는 SPDY나 HTTP/2에서의 호칭 방법과 유사합니다. SPDY나 HTTP/2도 일종의 Multiplexing 기능을 제공하고 있으며, 프레젠터의 Multiplexing Protocol도 SPDY 프레임을 많이 참고하여 작성되었습니다.수 많은 클라이언트와의 TCP연결을 Stream으로 만들어 하나의 내부 TCP연결을 통해 처리합니다.Alfred에서는 Multiplexing 된 TCP 연결을 Netty의 Channel 인터페이스로 추상화하였습니다. Netty에서 TCP 연결 하나는 Channel 하나로 만들어지는데, 실제 Stream도 Channel 인터페이스로 데이터를 읽거나 쓸 수 있도록 하였습니다. 이 추상화 덕분에 비트윈 비즈니스 로직을 담당하는 코드에서는 Stream으로 Multiplexing 된 TCP 연결을 마치 기존의 TCP 연결과 똑같이 Channel을 이용해 사용할 수 있었습니다. 그래서 실제 비즈니스 로직 코드는 전혀 건드리지 않고 프레젠터를 쉽게 붙일 수 있었습니다.로드 밸런싱클라이언트는 Route53에서 제공하는 DNS Round Robin 기능을 이용하여 아무 프레젠터에 연결하여 채팅 요청을 날리게 됩니다. 하지만 무조건 동등하게 Round Robin 하게 되면 새로 켜지거나 하여 연결을 거의 맺지 않고 놀고 있는 프레젠터가 있는데도 연결을 많이 맺고 있는 기존 프레젠터에에 연결이 할당되는 문제가 생길 수 있습니다. 충분한 시간이 흐르면 결국에는 연결 개수는 동등하게 되겠지만, 처음부터 놀고 있는 프레젠터에 새로운 연결을 가중치를 주어 할당하면 로드를 분산되는 데 큰 도움이 될 것입니다. 그래서 Route53의 Weighted Routing Policy 기능을 이용하기로 하였습니다. 현재 연결 개수와 CPU 사용량 등을 종합적으로 고려하여 Weight를 결정하고 이를 주기적으로 Route53의 레코드에 업데이트합니다. 이런 방법으로 현재 로드가 많이 걸리는 서버로는 적은 수의 새로운 연결을 맺게 하고 자원이 많이 남는 프레젠터로 더 많은 새로운 연결이 맺어지도록 하고 있습니다.스케일 인/아웃AWS에서는 트래픽에 따라 서버 개수를 늘리기도 하고 줄이기도 하는 AutoScaling 이라는 기능이 있습니다. 프레젠터가 스케일 아웃될때에는 프레젠터가 스스로 Route53에 레코드를 추가하는 식으로 새로운 연결을 맺도록 할 수 있습니다. 하지만 스케일 인으로 프레젠터가 제거될 때에는 Route53에서 레코드를 삭제하더라도 함부로 프레젠터 서버를 종료시킬 수 없습니다. 종종 클라이언트의 DNS 캐싱 로직에 문제가 있어, Route53에서 레코드를 삭제되었는데도 불구하고 이를 업데이트하지 못해 기존 프레젠터로 연결을 시도하는 경우가 있을 수 있기 때문입니다. 따라서 프레젠터 클러스터가 스케일 인 될 때에는 기존의 모든 연결이 끊어지고 충분한 시간 동안 새로운 연결이 생기지 않은 경우에만 서버를 종료시켜야 합니다. AutoScaling Group의 LifeCycleHook을 이용하여 위와 같은 조건을 만족 시켰을 때에만 프레젠터 서버를 완전히 종료시키도록 하였습니다.못다 한 이야기프레젠터라는 이름이 이상하다고 생각하시는 분들이 있을 것으로 생각합니다. 멀티티어 아키텍처를 이야기할 때 프레젠테이션 티어, 어플리케이션 티어, 데이터베이스 티어로 구분하곤 하는데 이 프레젠테이션 티어에서 나온 이름입니다. 지금은 실제 프레젠터가 하는 역할과 프레젠테이션 티어가 보통 맡게 되는 역할에는 많은 차이가 있지만, 어쩌다 보니 이름은 그대로 가져가게 되었습니다.프레젠터에서 AutoScaling을 하기 위해 LifeCycleHook을 이용합니다. 이때 프레젠터를 위해 LifeCycleHook 이벤트를 처리하는 프로그램을 직접 짠 것이 아니라 비트윈 개발팀이 내부적으로 만든 Kharon이라는 프로그램을 이용하였습니다. Kharon은 인스턴스가 시작되거나 종료될 때 실행할 스크립트를 작성하고 인스턴스의 특정 위치에 놓는 것만으로 LifeCycleHook을 쉽게 이용할 수 있게 하는 프로그램입니다. Kharon 덕분에 비트윈 내 다양한 시스템에서 별다른 추가 개발 없이 LifeCycleHook을 쉽게 활용하고 있습니다. 후에 Kharon에 대해 자세히 다뤄보도록 하겠습니다.정리비트윈 개발팀에서는 오랫동안 유지되는 수많은 채팅 서버 연결들을 처리하고 클라이언트와 서버 간 프로토콜을 단순화시키는 등 여러 이점을 얻고자 ELB의 역할을 대신하는 프레젠터를 만들었습니다. 프레젠터를 만드는 과정에서 여러 기술적 문제가 있었습니다. 이를 해결하기 위해 C++로 연결 유지 모듈을 따로 작성하였고 Multiplexing Protocol을 따로 정의하였으며 그 외 여러 가지 기술적인 결정들을 하였습니다. 이런 과정에서 시행착오들이 있었지만 이를 발판 삼아 더 좋은 기술적 결정을 내리기 위해 고민하여 결국 기존 시스템에 쉽게 적용할 수 있고 쉽게 동작하는 프레젠터를 만들어 이용하고 있습니다.
조회수 2485

사운들리 코드 품질 관리 이야기

안녕하세요 "사운들리"입니다 :)오늘은 사운들리의 코드 품질 관리에 대해 이야기 해보려 합니다.몇몇 개발자에게는 지루하고 악몽같은 이야기일 수 있겠네요.제 경우에는 예전에는 이런 품질이라는 단어를 멀리했지만 결국 제가 작성한 코드에 발목을 많이 잡히다 보니, 자연스레 관심을 갖게 되었습니다.일단, 어떤 소프트웨어가 좋은 품질의 소프트웨어일까요?좋은 품질이란? 책에 나올법한 내용을 보면, 아래와 같은 항목을 토대로 소프트웨어 품질을 판단한다고 합니다.ISO/IEC 9126 : Software engineering - Product qualityFunctionality: 명시된 요구사항을 잘 충족했는지Reliability: 명시된 조건과 시간 아래에서 일정 성능을 유지 하는지Usability: 사용하기 위해 어느정도의 노력과 자원이 필요한지Efficiency: 소모 자원과 성능간의 효율Maintainability: 수정하기 위해 어느정도의 노력이 필요한지Portability: 다른 환경에서도 사용 할수 있는지출처: https://en.wikipedia.org/wiki/ISO/IEC_9126 뭔가 복잡해 보이지만, 결국 개발자라면 위의 항목은 누구나 추구하게 되는 가치라고 생각 합니다.그런데 말입니다. 이런 좋은 내용을 마음 속으로만 간직한 채 코드를 작성하면 정말 좋은 소프트웨어를 만들 수 있을까요? 저는 객관적인 방법으로 코드를 평가한다면 좋은 피드백이 될 것이라고 생각합니다. (물론 이 성적표를 남에게 보여주는 것과는 다른 문제에요 ㅎㅎ)어떻게 품질을 체크하는가 소프트웨어의 품질을 체크하는 데에 다양한 방법과 툴이 제시되고 있는데요, 저는 크게 두 가지로 분류 해보겠습니다.유저 입장의 품질: 유저의 요구사항에 맞는 소프트웨어인지 체크개발자 입장의 품질: 내가 지금 이 코드를 의도한 대로 잘 작성하고 있는지 체크 유저 입장의 품질은 언급하지 않아도 중요함을 누구나 알고 있습니다. 이 부분이 만족이 되지 않으면 제품이 아니죠! 그래서 저는 개발자 입장에서 스스로 챙길수 있는 품질을 사운들리는 어떻게 챙겨보고 있는 지 이야기 해보도록 하겠습니다. 실은 제가 개발자 입니다 ㅎㅎ사운들리 개발자의 코드는 아래와 같이 흘러갑니다.<그림1> 사운들리 코드 개발상의 품질 관리 순서도간단히 각 항목을 훑어 보겠습니다.Local Machine 각자 갖고 있는 맥북으로, 다양한 IDE를 사용해 코딩합니다. 그리고 git 을 이용해 commit 하고, github 에 push 하죠.Github push 된 수정사항은 pull request 를 통해 동료에게 알려집니다. 이후 코드리뷰를 통해 merge 하게 됩니다. 코드리뷰는 많은 사람들에 의해 그 중요성이 부각되고 있습니다. 사운들리는 같은 모듈을 만드는 개발자끼리, 그리고 다른 모듈에 영향을 주는 코드일 경우에는 해당 모듈의 개발자도 리뷰를 합니다. 코드리뷰를 통해 다른 사람이 어떤 기능을 작성했는지 보고, 오류도 찾고, 더 좋은 방법이 있으면 공유도 하고, 칭찬도 하고, 훈수도 두고 합니다. 참고로 사운들리는 git-flow 정책에 따라 git branch를 운영하고 있습니다.Jenkins  Github 에 commit 이 등록되면 Jenkins 는 자동으로 빌드를 시작 합니다. Jenkins 는 단순 빌드 성공 실패를 떠나서, 코드 품질에 대한 몇가지 report 를 발생 시킵니다. 아래에서 좀더 자세히 다뤄보겠습니다.SonarQube Jenkins 에서 빌드하면서 SonarQube 에 포함된 분석 기능을 사용하게 됩니다.그렇다면, 코드 품질의 지표는 무엇일까요?Jenkins가 발생시키는 레포트를 통해서 알 수 있는 내용은 아래와 같습니다.코딩 스타일 체크 결과: 작성된 코드가 미리 정의된 코딩 스타일에 맞게 작성되어 있는지?Unit Test 결과: 유닛 테스트 결과 (당연히 전부 pass 해야겠죠)Test code coverage 결과: 테스트 코드가 전체 코드의 몇 % 를 커버 하고 있는지 (우리의 최종목표는.. 60%.. 덜덜덜)정적 분석 결과: 코드를 실행하지는 않지만, 코드 그 자체에서 발생할 수 있는 결함을 찾아줍니다. 이 네 가지 레포트는 객관적 수치를 나타내주기 때문에 일종의 코드 품질 지표로 삼을 수 있습니다. 물론 이 지표만 잘 관리 했다고 해서 좋은 코드를 작성했다고 말할 수는 없습니다. 다만 좋은 코드를 작성하기 위한 기초 중의 기초라고 볼 수 있겠죠 :)품질 체크를 위한 툴(tool)은 개발 언어에 따라 다를 수 있습니다. 사운들리에서는 다양한 언어로 소프트웨어가 작성되어 있습니다. 따라서 언어마다 위의 결과를 얻기 위해서 서로 다른 툴을 사용하고 있습니다. AndroidJavaJavascriptC/C++코딩 스타일checkstylecheckstyle jshintcppcheckUnit testjunitjunitmochagoogletestCode coveragejacococoberturamocha-covgcov정적 분석sonarqubesonarqube sonarqubecppcheck 각 개발자는 위의 네 가지 결과를 얻기 위해서 빌드 시스템에 툴을 포함하여 개발하고 있습니다. 제가 주로 개발하고 있는 java 언어에 해당하는 툴들을 좀 더 자세히 살펴보겠습니다.checkstyle코딩 스타일을 체크 해줍니다. xml 파일로 미리 정의 되어있고요. 매번 빌드할때마다 스타일이 틀린것을 지적해 줍니다.코딩 스타일은 중요합니다. 같이 개발하는 개발자와 코딩 스타일이 같다면 마치 내가 작성한 코드처럼 쉽게 읽을 수 있죠.junitjunit 은 자바 유닛 테스트 프레임워크 입니다. 유닛 테스트 코드를 편하게 작성하게 해주고, 쉽게 테스트 결과를 볼 수 있습니다.유닛 테스트 코드를 작성하면 내가 작성한 모듈을 작은 단위로 테스트 해서, 작은 로직에서 발생하는 시시콜콜한 문제를 방지 할 수 있습니다. 테스트 코드를 작성해서 검증한 부분은 스스로도 신뢰가 갑니다.기능 수정간에 유닛 테스트에서 fail 이 나는 경우가 발생하는데, 모르는 사이에 다른 모듈에 영향을 준 것을 알게 됩니다. 다른 모듈에 모르고 영향을 주게 되면 뒷처리가 어려워지잖아요~coberturacode coverage 를 계산해 주는 툴입니다.유닛 테스트 코드가 실행되면, 작성된 코드의 각 부분을 실행하게 됩니다. cobertura 는 이때 각 코드의 어느부분이 실행되었는지 확인해서 통계를 내줍니다.주로 line coverage / branch coverage 두 지표를 보는데요, line coverage 는 해당 라인이 한번이라도 실행 되면 check 되고, branch coverage 는 각 라인에 있는 조건문을 다 따로 check 합니다. 당연히 branch coverage 를 달성하는게 어렵겠죠?sonarqube소나큐브는 다양한 plug-in 을 통해서 정적 분석을 하고, 시각화를 해주는 툴입니다.사운들리는 주로 정적 분석 용도로만 소나큐브를 사용하고 있습니다. (지원하는 plug-in 을 보면 젠킨스와 기능이 겹치는 부분이 있습니다.)정적분석으로 실제 문제가 되는 부분을 찾는 경우도 있고, minor 한 부분에 대한 지적을 하는 경우도 있습니다. 그러나 이런 minor 한 부분도 꼼꼼하게 잘 챙겨야 좋은 개발자가 된다고 믿고 있습니다.마치며 여기까지 사운들리의 코드 품질 관리에 대해 이야기 해보았습니다. 품질 관리를 해보신 분은 아시겠지만, 이런 툴을 쓰다보면 항상 행복하게 코드 품질을 관리할 수는 없습니다. 매달 세워놓은 목표를 달성하기 위해서 뼈를 깎는 노력으로 테스트 코드를 작성해야 되고, 당장 기능 수정해서 배포해야 되는데, 작성해 둔 테스트 케이스가 Fail 되어 말썽을 부릴 수도 있습니다. 그렇지만 객관적 기준으로 코드 품질을 관리하다보면 어느샌가 큰 노력없이 좋은 코드를 작성하는 개발자가 되지 않을까 생각해 봅니다. 코드 졸면서 막 짜도 style warning 0건/ 정적분석 오류없음 / 테스트 코드 기본 탑재 뭐 이런 개발자 말입니다 ㅎㅎ 다른 개발자분들은 어떻게 자신이 작성한 코드의 품질을 관리하고 있는지 궁금하네요.알고 계신 좋은 방법이 있다면 언제든지 공유 부탁드리겠습니다~!#사운들리 #개발자 #개발 #인사이트 #조언 #개발후기 #후기
조회수 2265

당신이 고민해야 할 성능 분석 요소

IT 서비스는 더욱 복잡해지고 어플리케이션과 인프라의 경계도 클라우드 환경과 함께 허물어지고 있습니다. 많은 기업들이 가상화를 넘어 컨테이너로 가고 있으며 서버리스도 더이상 낮설지 않습니다. 인프라의 변화와 함께 아키텍처의 변화도 다양하게 만들어져 가고 있습니다. 복잡성이 아무리 높아져도 우리는 서비스의 성능을 보장해야 합니다. 서비스의 성능을 보장하기 위해 우리가 체크해야 할 중요 요소들을 알아보려고 합니다. 1. 인프라스트럭처와 클라우드서비스의 성능은 코드 밖에서도 만들어집니다. 그중에서도 인프라스트럭처는 매우 중요한 요소입니다. 국내에서 인프라스트럭쳐 분야는 클라우드로 전환하는 과도기적인 상황에 있습니다. SMB 시장에서 클라우드는 익숙한 환경이지만 국내 엔터프라이즈 기업의 클라우드 도입 비율은 20%가 되지 않습니다. 특히 클라우드를 도입하려는 엔터프라이즈 기업들은 데이터 센터, 퍼블릭 클라우드, 프라이빗 클라우드를 모두 사용하는 상황으로 넘어가면서 클라우드에 대한 모니터링 체계를 구성하는데 많은 어려움을 겪고 있습니다. 특히 기존의 자원 사용량을 설계하고 운영하던 방식에서 스케일의 변화를 통해 서비스의 성능을 실시간으로 조절하는 클라우드 서비스 운영 방법은 조직의 구조 변화를 동반하기 때문에 더욱 어려운 작업이기도 합니다. 이렇듯 클라우드의 전환은 최근 웹 서비스의 성능에 많은 영향을 미치고 있으며 데이터독이나 뉴렐릭 그리고 와탭 같은 성능 분석 서비스들은 클라우드 기반의 인프라 모니터링 기능들을 강화하고 있습니다. 2. 데이터베이스어플리케이션 성능 이슈의 80% 이상이 데이터베이스 레이어에서 발생합니다. 대부분의 엔터프라이즈 기업들은 자사의 어플리케이션을 성능 분석을 위해 DBA 포지션을 마련하거나 필요에 의해 컨설팅을 받고 있지만 아쉽게도 스타트업은 DBA포지션을 마련하는 경우가 거의 없습니다. 웹 서비스의 규모가 커지기 시작하면 데이터베이스로 인한 지연 장애가 매우 심각해 지기 시작합니다. 레거시로 인한 이슈까지 추가되면 서비스의 성능은 지속적으로 낮아지게 되므로 데이터베이스는 꾸준히 관리해야 하는 요소입니다.데이터베이스의 비중이 높다보니 어플리케이션 분석 서비스 중에서도 데이터베이스만 집중적으로 분석하는 도구들이 있습니다. 국내에서는 엑셈과 티맥스에서 데이터베이스 분석 솔루션을 제공하고 있습니다.  3. 오픈 소스와 써드파티 소프트웨어최근 두가지 형태의 트렌드가 서비스 성능에 영향을 주고 있습니다. 하나는 오픈 소스이고 다른 하나는 써드 파티 소프트웨어 입니다. 안정화 된 오픈 소스를 사용하더라도 설정 이슈 또는 사용 환경 이슈로 성능에 영향을 주는 상황이 많이 발생합니다. 위젯, 광고플랫폼, 플러그인등의 써드파티 또한 웹 서비스의 성능에 영향을 주는 요소입니다. 최근 써드 파티의 사용은 점점 늘어나는 추세로 인해 장애 발생에 대한 위험도는 더욱 높아가고 있습니다. 특히 써드 파티는 시간이 흐르면서 성능에 조금씩 부하를 누적시키기도 하므로 충분히 주의를 기울여야 합니다. 이런 환경에서도 서비스의 성능을 유지하기 위한 방법으로 통계 기반의 메소드 분석 기법 모니터링의 중요한 요소가 되어 가고 있습니다. 와탭의 Java 모니터링이 메소드 분석 서비스를 제공하고 있습니다. 4. 모바일구글 이 운영하는 더블클릭(https://www.doubleclickbygoogle.com/articles/mobile-speed-matters/)에 따르면 북미에서 3G에서의 모바일 페이지 로딩까지 소요되는 시간은 평균 19초입니다. 한국은 이미 4G를 넘어가고 있기도 하고 모바일 기기의 성능도 매우 높아서 북미와 상황이 다르지만 모바일 기반의 웹 서비스 성능을 분석할 수 있는 방안의 필요성은 높아져 가고 있습니다. 이와 함께 다양한 환경을 지원하는 end-to-end 모니터링의 중요성이 점점 대두되고 있는 상황입니다.  5. 컨테이너최근 인프라스트럭처의 새로운 흐름은 컨테이너 입니다. 한국은 리눅스 기반의 서비스 구축 시스템이 잘 발달한 덕분에 클라우드 도입이 다른 나라보다 늦은 편입니다. 하지만 최근 국내에 컨테이너 기반의 인프라스트럭처 도입 기업들이 많아지고 있습니다. 우리나라는 가상화를 건너뛰고 컨테이너부터 활성화 될수도 있을 거라 생각됩니다. 컨테이너 환경은 가상화보다 더 많은 인프라를 더 유동적으로 사용하게 되므로 기존의 규모를 뛰어 넘는 관리 체계를 만들어 나가야 합니다. 데이터독과 뉴렐릭 같은 SaaS 기반의 모니터링 서비스들은 이미 컨테이너의 대한 지원을 하고 있으며 와탭 또한 단순 지원을 넘어 컨테이너 전용 서비스를 준비중에 있습니다. 6. 마이크로 서비스많은 기업들이 클라우드와 함께 Micro Service Arichtecture를 도입하고 있기 때문에 독립적인 어플리케이션을 기반으로 하는 서비스 구조는 계속 발전해 나갈 것입니다. 마이크로 서비스와 클라우드의 조합은 커져가는 서비스의 규모를 독립적인 작은 단위로 나눌 수 있어서 매력적이긴 하지만 과거와 다른 운영 조직과 프로세스를 만들어야 하는 숙제를 만들었습니다. 예를 들면 기존에는 하나의 임계치를 사용하여 서비스의 위험도를 관리했다면 이젠 독립적으로 동작하는 서비스들의 임계치를 각각 어떻게 설정하고 관리할 것인지 고민해야 합니다. 독립된 마이크로 서비스의 성능 이슈가 전체 서비스 성능 이슈로 확대되지 않더라도 작게 발생하는 이슈들을 관리하지 못한다면 지속적으로 발전해야 하는 서비스의 미래도 흔들리게 될 것입니다. 7. 서버사이드 코드정상적인 상황이라면 서버사이드 코드에서 발생되는 지연시간은 찰나에 가깝지만 장애 상황에서의 지연은 서버사이드에서 발생하는 경우가 많습니다. 특히 방어가 되어 있지 않은 코드들은 물리적 요소의 작은 변화에 대처하지 못하고 웹 서비스 전체에 영향을 미치게 됩니다. 스타트업의 경우 개발팀이 운영을 함께 맡고 있는 경우가 많기 때문에 서버사이드의 코드를 직접 분석하곤 합니다. 하지만 서비스의 성능이 느려지는 상황 자체를 파악하지 못하는 경우가 많습니다. 서버 사이드에서 평균 응답시간을 체크하는 경우 10초 평균 응답시간이 0.5초를 넘는 경우는 거의 없습니다. 하지만 0.5초의 평균 응답시간을 같는 서비스라 할지라도 하루 동안 10초이상 걸린 고객의 숫자는 규모에 따라 1,000명이 넘을 수도 있습니다. 서비스에 규모가 있다면 꼭 APM을 사용해야 합니다.8. 네트워크 지연네트워크의 지연으로 인한 고객 불만은 예상외로 많이 발생합니다. 인프라스트럭처 이슈로 볼 수도 있겠지만 서비스를 운영한다면 항상 체크하고 있어야 하는 요소입니다. 해당 이슈를 확인 하려면 웹서비스 모니터링을 사용하시면 됩니다. 웹서비스 모니터링을 통해 네트웍상태를 포함한 서비스의 응답시간을 체크해 볼수 있습니다. 와탭의 경우 내부적으로 웹서비스 모니터링을 개발하여 사용하고 있지만 아직 서비스 하고 있지는 않습니다.  9. 자원 사용률자원 사용률은 최근 새로 떠오르는 이슈입니다. 이전에는 인프라스트럭쳐가 고정값이였기 때문에 자원 사용률이 모자라는 경우 서비스 성능을 포기하고 초과되는 고객의 요청을 앞단에서 버리거나 대기시키는 기법들을 사용해왔습니다. 클라우드 환경에서는 자원 사용량의 임계치가 넘어가면 자동으로 스케일을 조정하는 환경이 마련되면서 성능을 유지하는 것이 가능합니다.  클라우드 환경에서 과부하 상태에 접근하면 자동으로 인프라의 규모가 확장되고 과부하 상태는 정상으로 돌아갑니다. 이렇게 환경이 바뀌면서 자원 사용률의 중요 이슈가 성능에서 비용으로 전환되고 있습니다. 부하에 따른 스케일링 정책을 어떻게 정하는지에 따라서 성능과 비용 모두가 영향을 받기 때문에 Auto Scale에 대한 모니터닝이 관심을 받고 있습니다.  마무리웹 서비스의 성능에 영향을 주는 요소는 정말 많습니다. 와탭랩스 IT 기업의 어플리케이션을 모니터링 하기 때문에 기업의 IT 어플리케이션 성능 문제에 대해 항상 고민하고 있습니다. 해당 내용은 매달 또는 분기별로 트렌드를 반영하여 업데이트하고 할 생각입니다. 많은 분들에게 도움이 되었으면 좋겠습니다. #와탭랩스 #개발자 #개발팀 #인사이트 #경험공유 #일지
조회수 1504

AWS 이사하는 날

오늘 8퍼센트의 AWS 인프라를 일본에서 한국으로 옮겼다. 기술적인 내용은 이사를 리드하신 세바님이 다뤄 주시기로 하셨고, 나는 그냥 오늘을 남겨두려고 한다.(세바님이 8퍼센트 서울살자에 기술적인 내용들을 다뤄주셨다.)올해 초 AWS 서울 리전이 열리면서 도쿄에서 옮겨 가야겠다 라고 생각한 것이 벌써 수개월이 지났다. 작업을 시작하면 얼마나 걸릴지 예상이 잘 되지도 않고, 인프라 전체를 예쁘게 정리해야 한다는 부담 때문에 쉽게 손이 가지 않았다. 하지만 새로 조인하신 세바님이 AWS 이사를 가지 못해 여러 가지 제약이 생기는 답답한 상황을 참지 못하시고 총대를 메셨다. 아마 나보다 좀 더 답답하셨나 보다. :)17시에 다 함께 모여 현재 작업 진행상황과 오늘 이전 계획을 검토한 후 바로 퇴근을 했다. 긴 밤이 되리라 생각해서 조금이라도 잠을 자려고 노력은 했지만 두 아이가 있는 집에서 쉬는 것은 역시 쉽지 않더라. 지하철을 타고 23시 30분에 이모작 근무를 위해 다시 회사에 들어섰다. 이미 몇몇 분이 모여서 오늘 작업에 대한 이야기를 나누고 있었다. 아침에 만나는 것보다 왠지 반갑다. 모두 모여서 파이팅을 외치고 기념사진을 하나 찍고 작업을 시작했다.(웃으면서 시작한 작업을 웃으면서 마칠 수 있을 것인가?)일단 서버 작업 공지를 띄우고 작업을 시작한다. 지난 회사에서는 모든 서비스가 24시간 운영되었어야 했기 때문에 서버 점검 시간을 따로 갖지 못해다. 그래서 큰 서버 업데이트 작업을 할 때마다 시간에 쫓기고, 장애 발생을 실시간으로 해결해 가며 작업을 했었다. 하지만 이번에는 시간을 확보해두고 작업을 하는 것이라 그래도 마음에 좀 여유가 있었다.이전 작업을 하기 위해 각 파트를 담당하는 시니어 개발자들만 있어도 충분한데, 서버 이전을 하는 것이 흔치 않은 경험이기 때문에 주니어들도 가능하면 참여를 요청했다.(꼬꼬마들이 세바님 뒤에 쪼르르 모여서 설명을 듣고 있다)코드를 이해하기에 가장 좋은 방법은 코드를 함께 짜면서 설명을 하는 것이고, 인프라를 이해하기에 가장 좋은 방법은 인프라를 설치하는 과정을 함께 하는 것이다. 하나하나 작업을 해가면서 이런저런 이야기들을 나누었다. 이전을 하는 서버의 역할, 더 나은 아키텍처,  AWS의 역사,  AWS의 여러 가지 서비스의 세부적인 옵션에 대해서도 이야기를 나누었다.  세바님이 꼼꼼하게 준비를 해주신 덕분에 1시 30분이 되니 기본적인 이전 작업이 끝났다. 야식을 먹고 맥주를 한잔 마시고 각각의 기능들에 대한 본격적인 테스트를 시작했다.(야식은 12시 전에는 치킨을 시켜야 하고 12시 후에는 족발을 시켜야 한다)드디어 세바님을 제외한 다른 잉여 인력들이 할 일이 생겼다. 체크리스트에 있는 항목들을 하나씩 테스트 하기 시작한다. 꼼꼼하게 준비를 했지만 역시나 예상하지 못했던 문제들이 드러난다. 다행히 이전 작업을 되돌려야 할 만큼 큰 문제는 아니었기에 적절히 대응을 하고 계속 테스트를 진행했다.어느덧 시간이 흘러 3시가 되었다. http://8percent.kr 의 도메인을 도쿄에 있는 서버에서 서울에 있는 서버로 변경했다. 이제 내부 시스템들을 추가적으로 점검해야 한다. 가능하면 끝까지 확인을 하고 자리를 뜨고 싶었지만 내일 오후 사무실을 지키면서 혹시 모를 장애에 대응을 해 줄 사람이 필요할 것 같아 먼저 퇴근을 했다.집에 돌아오면서 생각해 보니 오늘 내가 한 일이 거의 없었다. 기뻤다. 서버 이전 작업을 내가 해야만 하는 일로 생각하며 계속 들고 있었는데 세바님이 먼저 나서서 이 일을 진행해 주셨다. 중요한 작업 중에 자리를 뜨는데도 전혀 불안함 마음이 들지 않았다.아침에 일어나서 슬랙을 확인했다. 슬랙에 별다른 멘트가 없는 것을 보니 큰 문제는 없나 보다. 야호! 이전된 서버가 정상적으로 운영되고 있는지 확인을 위해 심사팀도 일찍부터 출근해서 테스트를 진행하고 있었다. 회사에 도착해 보니, 나를 제외하고는 모두 밤을 새워 일을 하고 계셨다. 다들 몽롱한 표정이다. 고맙다.하루에도 8시간씩 같이 일하는 동료들이지만 왠지 이렇게 같이 밤을 지새워서 작업을 하고 나면 동지애가 생긴다. 긴 밤을 고생해준 개발팀 멤버들에게 다시 한번 고마운 마음을 전한다. 앞으로도 잘 부탁드려요!(제가 따로 드릴것은 없어서 박수를!)#8퍼센트 #에잇퍼센트 #AWS #서버 #서버이전 #인프라 #개발팀 #팀워크 #조직문화
조회수 1740

HBase 설정 최적화하기

커플 필수 앱 비트윈은 여러 종류의 오픈 소스를 기반으로 이루어져 있습니다. 그 중 하나는 HBase라는 NoSQL 데이터베이스입니다. VCNC에서는 HBase를 비트윈 서비스의 메인 데이터베이스로써 사용하고 있으며, 또한 데이터 분석을 위한 DW 서버로도 사용하고 있습니다.그동안 두 개의 HBase Cluster 모두 최적화를 위해서 여러 가지 설정을 테스트했고 노하우를 공유해 보고자 합니다. 아랫은 저희가 HBase를 실제로 저희 서비스에 적용하여 운영하면서 최적화한 시스템 구성과 설정들을 정리한 것입니다. HBase를 OLTP/OLAP 목적으로 사용하고자 하는 분들에게 도움이 되었으면 좋겠습니다. 아래 구성을 최적화하기 위해서 했던 오랜 기간의 삽질기는 언젠가 따로 포스팅 하도록 하겠습니다.HBaseHBase는 Google이 2006년에 발표한 BigTable이라는 NoSQL 데이터베이스의 아키텍처를 그대로 따르고 있습니다. HBase는 뛰어난 Horizontal Scalability를 가지는 Distributed DB로써, Column-oriented store model을 가지고 있습니다. 사용량이 늘어남에 따라서 Regionserver만 추가해주면 자연스럽게 Scale-out이 되는 구조를 가지고 있습니다. 또한, Hadoop 특유의 Sequential read/write를 최대한 활용해서 Random access를 줄임으로 Disk를 효율적으로 사용한다는 점을 특징으로 합니다. 이 때문에 HBase는 보통의 RDBMS와는 다르게 Disk IO가 병목이 되기보다는 CPU나 RAM 용량이 병목이 되는 경우가 많습니다.HBase는 많은 회사가 데이터 분석을 하는 데 활용하고 있으며, NHN Line과 Facebook messenger 등의 메신저 서비스에서 Storage로 사용하고 있습니다.시스템 구성저희는 Cloudera에서 제공하는 HBase 0.92.1-cdh4.1.2 release를 사용하고 있으며, Storage layer로 Hadoop 2.0.0-cdh4.1.2를 사용하고 있습니다. 또한, Between의 데이터베이스로 사용하기 위해서 여러 대의 AWS EC2의 m2.4xlarge 인스턴스에 HDFS Datanode / HBase Regionserver를 deploy 하였습니다. 이는 m2.4xlarge의 큰 메모리(68.4GB)를 최대한 활용해서 Disk IO를 회피하고 많은 Cache hit이 나게 하기 위함입니다.또한 Highly-Available를 위해서 Quorum Journaling node를 활용한 Active-standby namenode를 구성했으며, Zookeeper Cluster와 HBase Master도 여러 대로 구성하여 Datastore layer에서 SPOF를 전부 제거하였습니다. HA cluster를 구성하는 과정도 후에 포스팅 하도록 하겠습니다.HDFS 최적화 설정dfs.datanode.handler.countHDFS에서 외부 요청을 처리하는 데 사용할 Thread의 개수를 정하기 위한 설정입니다. 기본값은 3인데 저희는 100으로 해 놓고 사용하고 있습니다.dfs.replicationHDFS 레벨에서 각각의 데이터가 몇 개의 독립된 인스턴스에 복사될 것 인가를 나타내는 값입니다. 저희는 이 값을 기본값인 3으로 해 놓고 있습니다. 이 값을 높이면 Redundancy가 높아져서 데이터 손실에 대해서 더 안전해지지만, Write 속도가 떨어지게 됩니다.dfs.datanode.max.transfer.threads하나의 Datanode에서 동시에 서비스 가능한 block 개수 제한을 나타냅니다.과거에는 dfs.datanode.max.xcievers라는 이름의 설정이었습니다.기본값은 256인데, 저희는 4096으로 바꿨습니다.ipc.server.tcpnodelay / ipc.client.tcpnodelaytcpnodelay 설정입니다. tcp no delay 설정은 TCP/IP network에서 작은 크기의 패킷들을 모아서 보냄으로써 TCP 패킷의 overhead를 절약하고자 하는 Nagle's algorithm을 끄는 것을 의미합니다. 기본으로 두 값이 모두 false로 설정되어 있어 Nagle's algorithm이 활성화되어 있습니다. Latency가 중요한 OLTP 용도로 HBase를 사용하시면 true로 바꿔서 tcpnodelay 설정을 켜는 것이 유리합니다.HBase 최적화 설정hbase.regionserver.handler.countRegionserver에서 외부로부터 오는 요청을 처리하기 위해서 사용할 Thread의 개수를 정의하기 위한 설정입니다. 기본값은 10인데 보통 너무 작은 값입니다. HBase 설정 사이트에서는 너무 큰 값이면 좋지 않다고 얘기하고 있지만, 테스트 결과 m2.4xlarge (26ECU) 에서 200개 Thread까지는 성능 하락이 없는 것으로 나타났습니다. (더 큰 값에 관해서 확인해 보지는 않았습니다.)저희는 이 값을 10에서 100으로 올린 후에 약 2배의 Throughput 향상을 얻을 수 있었습니다.hfile.block.cache.sizeHBase 의 block 들을 cache 하는데 전체 Heap 영역의 얼마를 할당한 것인지를 나타냅니다. 저희 서비스는 Read가 Write보다 훨씬 많아서 (Write가 전체의 약 3%) Cache hit ratio가 전체 성능에 큰 영향을 미칩니다.HBase 에서는 5분에 한 번 log 파일에 LruBlockCache (HBase 의 Read Cache) 가 얼마 만큼의 메모리를 사용하고 있고, Cache hit ratio가 얼마인지 표시를 해줍니다. 이 값을 참조하셔서 최적화에 사용하실 수 있습니다.저희는 이 값을 0.5로 설정해 놓고 사용하고 있습니다. (50%)hbase.regionserver.global.memstore.lowerLimit / hbase.regionserver.global.memstore.upperLimit이 두 개의 설정은 HBase에서 Write 한 값들을 메모리에 캐쉬하고 있는 memstore가 Heap 영역의 얼마만큼을 할당받을지를 나타냅니다. 이 값이 너무 작으면 메모리에 들고 있을 수 있는 Write의 양이 한정되기 때문에 디스크로 잦은 flush가 일어나게 됩니다. 반대로 너무 크면 GC에 문제가 있을 수 있으며 Read Cache로 할당할 수 있는 메모리를 낭비하는 것이기 때문에 좋지 않습니다.lowerLimit와 upperLimit의 두 가지 설정이 있는데, 두 개의 설정이 약간 다른 뜻입니다.만약 memstore 크기의 합이 lowerLimit에 도달하게 되면, Regionserver에서는 memstore들에 대해서 'soft'하게 flush 명령을 내리게 됩니다. 크기가 큰 memstore 부터 디스크에 쓰이게 되며, 이 작업이 일어나는 동안 새로운 Write가 memstore에 쓰일 수 있습니다.하지만 memstore 크기의 합이 upperLimit에 도달하게 되면, Regionserver는 memstore들에 대한 추가적인 Write를 막는 'hard'한 flush 명령을 내리게 됩니다. 즉, 해당 Regionserver이 잠시 동안 Write 요청을 거부하게 되는 것입니다. 보통 lowerLimit에 도달하면 memstore의 크기가 줄어들기 때문에 upperLimit까지 도달하는 경우는 잘 없지만, write-heavy 환경에서 Regionserver가 OOM으로 죽는 경우를 방지하기 위해서 hard limit가 존재하는 것으로 보입니다.hfile.block.cache.size와 hbase.regionserver.global.memstore.upperLimit의 합이 0.8 (80%)를 넘을 수 없게 되어 있습니다. 이는 아마 read cache 와 memstore의 크기의 합이 전체 Heap 영역 중 대부분을 차지해 버리면 HBase의 다른 구성 요소들이 충분한 메모리를 할당받을 수 없기 때문인 듯합니다.저희는 이 두 개의 설정 값을 각각 0.2, 0.3으로 해 놓았습니다. (20%, 30%)ipc.client.tcpnodelay / ipc.server.tcpnodelay / hbase.ipc.client.tcpnodelayHDFS의 tcpnodelay 와 비슷한 설정입니다. 기본값은 전부 false입니다.이 설정을 true로 하기 전에는 Get/Put 99%, 99.9% Latency가 40ms 와 80ms 근처에 모이는 현상을 발견할 수 있었습니다. 전체 요청의 매우 작은 부분이었지만, 평균 Get Latency가 1~2ms 내외이기 때문에 99%, 99.9% tail이 평균 Latency에 큰 영향을 미쳤습니다.이 설정을 전부 true로 바꾼 후에 평균 Latency가 절반으로 하락했습니다.Heap memory / GC 설정저희는 m2.4xlarge가 제공하는 메모리 (68.4GB)의 상당 부분을 HBase의 Read/Write cache에 할당하였습니다. 이는 보통 사용하는 Java Heap 공간보다 훨씬 큰 크기이며 심각한 Stop-the-world GC 문제를 일으킬 수 있기 때문에, 저희는 이 문제를 피하고자 여러 가지 설정을 실험하였습니다.STW GC time을 줄이기 위해서 Concurrent-Mark-and-sweep GC를 사용했습니다.HBase 0.92에서부터 기본값으로 설정된 Memstore-Local Allocation Buffer (MSLAB) 을 사용했습니다.hbase.hregion.memstore.mslab.enabled = true #(default)hbase-env.sh 파일을 다음과 같이 설정했습니다.HBASE_HEAPSIZE = 61440 #(60GB)HBASE_OPTS = "-XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps"GC log를 Python script로 Parsing해서 STW GC 시간을 관찰하고 있습니다. 지금까지 0.2초 이상의 STW GC는 한 번도 발생하지 않았습니다.그 밖에 도움이 될 만한 설정들hbase.hregion.majorcompactionHBase는 하나의 Region에 대해서 여러 개의 StoreFile을 가질 수 있습니다. 그리고 주기적으로 성능 향상을 위해서 이 파일들을 모아서 하나의 더 큰 파일로 합치는 과정을 진행하게 됩니다. 그리고 이 과정은 많은 CPU usage와 Disk IO를 동반합니다. 그리고 이때 반응 속도가 다소 떨어지게 됩니다. 따라서 반응 속도가 중요한 경우에는, 이 Major compaction을 off-peak 시간대를 정해서 manual 하게 진행하시는 것이 좋습니다.저희는 사용자의 수가 상대적으로 적은 새벽 시간대에 crontab 이 실행시키는 script가 돌면서 전체 Region에 대해서 하나하나 Major Compaction이 진행되도록 하였습니다.기본값은 86,400,000 (ms)로 되어 있는데, 이 값을 0으로 바꾸시면 주기적인 Major Compaction이 돌지 않게 할 수 있습니다.hbase.hregion.max.filesizeHBase는 하나의 Region이 크기가 특정 값 이상이 되면 자동으로 2개의 Region으로 split을 시킵니다. Region의 개수가 많지 않을 때는 큰 문제가 없지만, 계속해서 데이터가 쌓이게 되면 필요 이상으로 Region 수가 많아지는 문제를 나을 수 있습니다. Region 수가 너무 많아지면 지나친 Disk IO가 생기는 문제를 비롯한 여러 가지 안 좋은 점이 있을 수 있기 때문에, split 역시 manual 하게 하는 것이 좋습니다. 그렇다고 Table의 Region 수가 너무 적으면 Write 속도가 떨어지거나 Hot Region 문제가 생길 수 있기 때문에 좋지 않습니다.HBase 0.92.1 에서는 기본값이 1073741824(1GB)로 되어 있는데, 저희는 이 값을 10737418240(10GB)로 늘인 후에 manual 하게 split을 하여 Region의 개수를 조정하고 있습니다.hbase.hregion.memstore.block.multipliermemstore의 전체 크기가 multiplier * flush size보다 크면 추가적인 Write를 막고 flush가 끝날때까지 해당 memstore는 block 됩니다.기본값은 2인데, 저희는 8로 늘려놓고 사용하고 있습니다.dfs.datanode.balance.bandwidthPerSec부수적인 설정이지만, HDFS의 Datanode간의 load balancing이 일어나는 속도를 제한하는 설정입니다. 기본값은 1MB/sec로 되어 있지만, 계속해서 Datanode를 추가하거나 제거하는 경우에는 기본값으로는 너무 느릴 때가 있습니다. 저희는 10MB/sec 정도로 늘려서 사용하고 있습니다.dfs.namenode.heartbeat.recheck-intervalHDFS namenode에만 해당되는 설정입니다.Datanode가 응답이 없는 경우에 얼마 후에 Hadoop cluster로부터 제거할 것인지를 나타내는 값입니다.실제로 응답이 없는 Datanode가 떨어져 나가기까지는 10번의 heartbeat가 연속해서 실패하고 2번의 recheck역시 실패해야 합니다. Heartbeat interval이 기본값인 3초라고 하면, 30초 + 2 * recheck-interval 후에 문제가 있는 Datanode가 제거되는 것입니다.기본값이 5분으로 되어 있는데, fail-over가 늦어지기 때문에 사용하기에는 너무 큰 값입니다. 저희는 문제가 있는 Datanode가 1분 후에 떨어져 나갈 수 있도록 이 값을 15,000 (ms) 으로 잡았습니다.Read short-circuitRegionServer가 로컬 Datanode로부터 block을 읽어올 때 Datanode를 통하지 않고 Disk로부터 바로 읽어올 수 있게 하는 설정입니다.데이터의 양이 많아서 Cache hit이 낮아 데이터 대부분을 디스크에서 읽어와야 할 때 효율적입니다. Cache hit에 실패하는 Read의 Throughput이 대략 2배로 좋아지는 것을 확인할 수 있습니다. OLAP용 HBase에는 매우 중요한 설정이 될 수 있습니다.하지만 HBase 0.92.1-cdh4.0.1까지는 일부 Region이 checksum에 실패하면서 Major compaction이 되지 않는 버그가 있었습니다. 현재 이 문제가 해결되었는지 확실하지 않기 때문에 확인되기 전에는 쓰는 것을 추천하지는 않습니다.설정하는 방법은 다음과 같습니다. dfs.client.read.shortcircuit = true #(hdfs-site.xml) dfs.block.local-path-access.user = hbase #(hdfs-site.xml) dfs.datanode.data.dir.perm = 775 #(hdfs-site.xml) dfs.client.read.shortcircuit = true #(hbase-site.xml)Bloom filterBloom filter의 작동방식에 대해 시각적으로 잘 표현된 데모 페이지HBase는 Log-structured-merge tree를 사용하는데, 하나의 Region에 대해서 여러 개의 파일에 서로 다른 version의 값들이 저장되어 있을 수 있습니다. Bloom filter는 이때 모든 파일을 디스크에서 읽어들이지 않고 원하는 값이 저장된 파일만 읽어들일 수 있게 함으로써 Read 속도를 빠르게 만들 수 있습니다.Table 단위로 Bloom filter를 설정해줄 수 있습니다.ROW와 ROWCOL의 두 가지 옵션이 있는데, 전자는 Row key로만 filter를 만드는 것이고, 후자는 Row+Column key로 filter를 만드는 것입니다. Table Schema에 따라 더 적합한 설정이 다를 수 있습니다.저희는 데이터 대부분이 메모리에 Cache 되고 하나의 Region에 대해서 여러 개의 StoreFile이 생기기 전에 compaction을 통해서 하나의 큰 파일로 합치는 작업을 진행하기 때문에, 해당 설정을 사용하지 않고 있습니다.결론지금까지 저희가 비트윈을 운영하면서 얻은 경험을 토대로 HBase 최적화 설정법을 정리하였습니다. 하지만 위의 구성은 어디까지나 비트윈 서비스에 최적화되어 있는 설정이며, HBase의 사용 목적에 따라서 달라질 수 있음을 말씀드리고 싶습니다. 그래서 단순히 설정값을 나열하기보다는 해당 설정이 어떤 기능을 하는 것인지 저희가 아는 한도 내에서 설명드리려고 하였습니다. 위의 글에서 궁금한 점이나 잘못된 부분이 있으면 언제든지 답글로 달아주시길 바랍니다. 감사합니다.저희는 언제나 타다 및 비트윈 서비스를 함께 만들며 기술적인 문제를 함께 풀어나갈 능력있는 개발자를 모시고 있습니다. 언제든 부담없이 [email protected]로 이메일을 주시기 바랍니다!

기업문화 엿볼 때, 더팀스

로그인

/