자바 튜닝과 성능 모니터링(APM)을 위한 체크 몇 가지
실전 환경에서 소프트웨어 공학은 무용지물인 경우가 많다. 왜냐하면, 급박한 IT 비즈니스 속도에 맞추어서 빠르게 개발되고, MSA구조에 맞추어서 클라우드 위에 서비스가 올라가면서, 매우 작고 기민한 형태로 개발이 이루어지기 때문에, 기존의 장기간 고민하면서 큰 서비스를 디자인하는 방식의 접근법은 사용할 수 없다.
기존 IT서비스들이 8~9개월에 1개의 큰 서비스를 론칭하는 형태에서는 기존의 소프트웨어 공학적인 접근법이 매우 유용한 것은 분명 사실이다. 요구사항을 잘 수집하고, 또 잘 정제하고, 이슈 트리를 만들고, 소프트웨어 아키텍처를 구성하면서 팀의 역량을 키워가면서 개발 성숙도와 팀의 성숙도를 향상하면 분명, 소프트웨어 개발의 품질은 향상된다.
하지만, 실제 주변 환경을 돌아보면, 2~3주 내에 하나의 서비스를 론칭해야 하고, 신규 기능이 계속 추가되는 환경에서 기존 방법론들은 대부분 추상적인 '가르침'만을 이야기할 뿐이다.
현시점에서 필요한 것은 '추상적인 공학'이 아니라, 지금 당장 작업하고 있는 팀의 구조와 개발 목표, 고객의 피드백과 서비스들이 빠르게 개발되고 관리되는 관점이 필요한 것이다.
물론, Node나 Python, PHP( 라라벨이나 코드 이그나이터 등)를 이용하여 빠르게 서비스를 DB서비스에 맞추어서 개발을 할 수 있다. 다만, 이렇게 만들어진 서비스가 갑자기 증가되면서 실제 서비스되면서 요구사항이 폭주하고 서비스가 복잡하게 변화하면서 문제는 발생한다.
서비스가 복잡해지면, 문제도 같이 늘어난다. 장애도 같이 증가하게 된다. 매우 당연하게 고객들은 세션이 끊어지는 현상을 경험하게 되고, 사용이 불편해지고, 그런 상황에서 경쟁자가 출현하거나, 새로운 데이터 마케팅 방법을 적용하려고 할 때에 매우 곤란한 상황을 만나게 된다.
리팩터링 할 여유도 없고, 새로운 버전과 새로운 기능에 몰두해야 한다. 이런 경우에 개발 총괄을 어떤 생각을 해야 하는 것일까? 현재까지의 정답은 기존에 만들어진 서비스로 최대한 버틸 때까지 버티면서 새로운 서비스를 만들어서 이를 대체하는 것이 최선이라고 이야기할 수 있다.
차선책은 그 부분을 어떻게든 레이어 구분을 해서 부분 부분 리팩터링을 하면서 서비스를 고도화하는 방법으로 진행하게 된다. 분명한 것은 이런 과정들이 매우 고통스러운 튜닝 작업의 연속이 되게 된다는 점이다.
아이러니하지만, 경험이 풍부한 자바 개발자가 있다면, 이런 상황들을 나름 서핑하듯이 넘어갈 수 있다. 하지만 그렇지 않다면, 이 작업들은 매우 고통스럽고, 작업 진행이 효과적으로 진행되지 않는다면, 서비스가 중단되거나 고객들이 만족하지 못하고, 신뢰가 떨어지면서 고객은 이탈할 것이고, 서비스는 문을 닫는 상황으로 진행이 된다.
대부분 스타트업들이나 IT기업들은 초기의 1차 작업은 빠르게 개발하는 환경에서 기민한 개발자가 가장 효과적으로 사용하는 툴을 사용하여 서비스를 빠르게 비즈니스 환경에 맞도록 론칭하고, 이를 기반으로 본격적인 서비스를 다시 구상하는 것이 대부분의 패턴이라고 할 수 있다.
보통, 투자를 많이 받거나, 어느 정도 고객이 확보되는 순간에 경험이 풍부한 CTO레벨을 영입해서 이를 해결하려 한다. 매우 당연하게 경험이 풍부한 CTO는 기술 스택의 부족함과 리팩터링 이슈에 대해서 질문을 개시하고, 이를 갈아 없기 위한 논의를 시작하는 것은 매우 당연하다.
하지만, 비즈니스의 특성상 중단될 수 없고, 순차적으로 넘어가는 방법에 대해서 내부 협의가 잘되지 않는 경우에는 영입된 CTO가 이탈하거나, 개발팀이 제대로 꾸려지지 않아서, 서비스가 안착되기 어려운 경우도 빈번하게 발생된다.
분명한 것은 경험이 풍부한 CTO라면, 백엔드 서비스들에 대해서 기민하게 컨트롤하기를 원하고, 100만, 1000만 사용자들을 컨트롤할 수 있는 자바 백엔드 서비스를 매우 당연하게 구상할 것이다. 더 글로벌 서비스라면 데이터 스토리지와 관련된 레이어까지 다시 구상할 것이다.
그리고, 자바 튜닝과 관련된 인프라와 리팩터링, 코드리뷰와 개발 문화를 위해서 고민하고 이를 반영하려고 할 것이다.
이번에는 그중에 자바 튜닝과 관련된 내용만 일부 나열하자.
자바 튜닝과 관련해서는 한두 페이지로 설명을 전부 할 수 없다. 다만, 여기서는 향후 구체적으로 설명하기 위한 '다이내믹 리팩터링'에 대한 사전적인 설명을 하는 정도로 기본 정리만 하겠다. 관련 내용들은 별도로 기술할 생각은 있으나, 요즘 관련된 문서들이 인터넷에 너무 많아서, 간단하게만 정리하겠다.
자바와 소프트웨어 공학적인 측면은 매우 일맥상통하는 부분이 많다. 정적인 분석이나 동적인 환경에서의 분석 및 프로파일링을 통해서 실제 CPU와 메모리, 네트워크 등의 리소스에 대한 세밀한 제어까지 가능하기 때문에 대규모적인 서비스에서 성능을 컨트롤하기 원한다면, 분명. 자바로 코딩되는 것이 매우 유용하다.
주변에서는 Node.js나 PHP로 1차 구현된 이후에, 본격적인 서비스를 위해서 JAVA로 재 코딩되는 경우를 많이 본다. 특히, '성능'이 이슈가 되거나 기민하게 가동되며, 서버 리소스 비용을 최소화하기 위해서 클라우드와 결합된 환경에서 극단적으로 튜닝하는 경우에는 자바로 코딩하는 것이 분명 효과적이다. (다만, 그만큼 복잡하고, 그만큼 경험이 풍부한 개발자가 없다면 이 선택은 어려울 수 있다. )
하지만, 리팩터링과 성능 프로파일링을 기반으로 엔터프라이즈 환경에서 유연하게 확장과 세밀한 컨드롤등이 가능한 것이 자바인 것은 굳이 더 설명이 필요 없다고 생각한다. 특히나, 리팩터링이 원활하고 원하는 프레임웍들을 손쉽게 선택할 수 있는 환경임에는 분명하다. ( 그만큼 아는 사람이 잘 쓴다. )
자바로 구현된 서비스를 중심으로 리팩터링의 이슈를 해결하기 위해서 중요한 관점들을 몇 가지 정리한다면 다음의 관점들에 대해서 관심을 가져야 한다고 정리해보자.
첫 번째. garbage collection에 대한 이해
분명한 것은 메모리에 생성되는 변수와 객체에 대한 할당과 해제에 대해서 가장 민감하게 생각되어야 한다. 자바 코딩의 기본 중의 하나인 System.gc()를 명시적으로 호출하는 경우는 논외로 하자. 아주 특이한 경우가 아니라면 직접 호출하지 않을 테니.
다만, gc와 관련된 모니터링을 위한 툴과 방법은 다음과 같다.
* jvmstat : 무료, jdk 1.4.2 이상,
* visualgc : 무료
* jps : jvm 프로세스 뷰
* jstat : jvm 통계수치 뷰
* jstatd : 원격으로 jstat을 사용하기 위한 데몬
두 번째. 프로파일링과 APM의 활용
자바로 구현된 상태에서 소스 레벨 분석용을 위하여 정적 분석을 위해서 DevPartner for Java, Jprobe, TPTP(Eclipse 기반)등을 사용할 수 있으며, 웹서비스가 실제 구현되고 동작되는 상황에서 동적 분석을 위해서 APM(Application Performance Management)을 기반으로 프로파일링 하는 방법이 있다.
정적 분석의 경우에는 대부분 String의 상태, Collection과 Map에 대한 자료구조에 대한 상황들을 관찰하고 분석한다. 특히, 대용량 데이터를 사용할 경우에는 적절한 프로파일링을 통해서 선택하는 것이 좋다.
특히, 자바의 경우에는 Collection의 다양한 클래스들이 존재한다. 사실상, 이 자료구조의 선택을 어떻게 하느냐에 따라서 전체적인 서비스의 성능과 장애의 근본 원인이 되는 경우가 대부분이다. 특히, 자료구조의 경우 동기화가 되고 안되고의 차이도 크니 이 부분을 기민하게 상요해야 한다.
세 번째. 그 이외의 것들
작게는 Loop의 성능에 대해서도 이야기를 하나. 사실상 그다지 차이가 없다. if나 switch나 큰 문제가 되지 않는다. 이외에도 static이나 syncronized 등을 참고하겠지만, 대부분의 '성능장애'는 이런 부분에서 발생하지 않는다.
큰 문제는 보통 IO병목이거나 Logging과 관련된 이슈, DB 연동을 위한 connection관리 등에서 발생한다. Connection, Statement, ResultSet 등의 관리가 핵심이다.
생각보다, XML 관련 이슈는 대규모 서비스이거나 사용자가 급증했을 때에 전체 서비스의 성능장애를 발생시키는 주된 요인이 된다. SAX의 경우 추적이 어렵고, DOM의 경우에는 메모리 사용량이 많아서 대부분의 APM들에서 추적이 불가능한 경우도 많다. ( 유일하게, 와탭 APM만 해당 이슈의 추적이 가능하다. )
그 밖에도 웹서버 관련 이슈도 많이 존재하고, DB connection Pool, session timeout 등을 주목하게 된다. JVM의 메모리 설정을 잘못해서 생기는 문제도 은근 많이 발생한다.
이러한 리팩터링의 관점들을 APM들은 매우 손쉽게 프로파일링하고 필요한 포인트들에 대해서 명쾌하게 접근할 수 있도록 매우 큰 도움을 준다. APM의 장점은 크게 다음의 두 가지로 정리할 수 있다.
하나는 Slow Query를 매우 손쉽게 찾아준다는 점이고. 다른 하나는 웹서비스 트랜잭션에 대한 프로파일링을 통해서 성능상의 이슈나 잘못된 설정등을 빠르게 찾게 해준다. 이처럼, 대부분의 WebService의 문제의 70% 정도는 Slow Query의 문제이고, 25%정도의 WAS내부의 성능문제들을 찾게 해준다.
다만, 소스코드 상에서 특정 클래스나 특정 펑션의 성능적인 이슈가 발생되어 비정상적인 성능장애가 발생한 경우에는 추적이 매우 어렵다. 기존의 APM들은 다이나믹 프로파일링이라는 기법으로 개발자의 추측과 소스코드상의 분석등을 통해서 문제가 예측되는 상황들을 하나씩 검토해 보는 방법을 사용한다.
또는, 인젝션하는 방법으로 서비스에 직접 걸어서 추출하는 방법도 있기는 하나, 이는 실 서비스에서 인젝션된 에이전트의 부하와 구분할 방법이 명확하지 않기 때문에 실서비스 보다는 개발환경에서 사용하는 경우가 많다. 대부분의 APM들은 이런 5%의 특이상황들에 대해서는 튜닝을 포기한다. 그리고, 향후 고도화작업이거나 솔루션 대체를 할 경우에 이를 해결하려고 한다.
대부분의 상용APM들이나 Scouter와 같은 오픈소스 형태의 APM으로도 대부분의 95%의 문제는 해결이 가능하다. ( 다만, Scouter의 경우에는 1~5대 정도의 서버에 적합하지, 100대이상의 대규모 서비스에 적용한다는 것은 사실상 실패를 할수 밖에 없다. )
문제가 발생한 소스 코드의 위치까지 찾는 기능을 제공하는 APM은 기존 APM제품중에는 없다.
여기서 와탭을 잠시 자랑하겠다. ~.~ ( 저.. 와탭의 CBO랍니다. ~.~ )~~~
이런 문제를 해결하는 APM이 있다. 와탭의 APM( http://www.whatap.io )은 이렇게 숨어있는 특정 소스라인의 문제들을 추적하여 리팩토링이 가능한 쉬운 방법을 제공한다. 와탭의 특허인 Active Stack이라는 기법( 10-2016-0078864 )으로 마치, MRI촬영을 하듯이 실제 동작중인 웹서비스에 설치하여 ( 재기동 없이 ) 스냅샷을 찍어서, 프로파일링하는 기법으로 문제가 되는 부분들을 손쉽게 찾아서 리팩토링을 빠르게 처리한다.
( 실제 사례 : 2년동안 못 찾은 모바일 뱅킹의 장애원인을 진단하다. )
앞에서 설명한 복잡한 정적인 코드 리뷰나 분서기법들을 사용하지 않고서도, 현재 동작중인 웹서비스의 리팩토링을 실시간으로 처리한다. 마치, Dynamic Refactoring이라고 불러야 적당할 듯하다.