스토리 홈

인터뷰

피드

뉴스

조회수 2096

음성 기반 인터페이스의 등장

필자가 재직 중인 일정 데이터 스타트업 히든트랙(린더)은 현재 SKT NUGU, Google Assistant에서 '아이돌 캘린더'라는 이름의 일정 검색/구독 서비스를 운영 중이며, 삼성 빅스비와 협업을 통해 내년 상반기 전시/공연 일정 검색/구독 서비스 상용화를 앞두고 있다.https://blog.naver.com/nuguai/221387861674세계적으로도 아직 음성 관련 서비스 사례가 많지 않은 상황에서 VUI 기반 서비스 개발에 도움이 될만한 자료를 국내에서 찾기는 더더욱 쉽지 않았고, 향후 음성 기반 서비스를 준비하는 다른 이들이 우리가 겪었던 시행착오를 줄일 수 있기를 바라는 마음으로 간단하게 5부작 형태의 글로 우리가 고민해온 과정을 준비해보았다.음성 서비스 시장의 확대해외 리서치 업체 닐슨에 따르면 2018년 2분기 기준 미국 가구 중 4분의 1에 해당하는 24%가 최소 1대 이상의 AI 스피커를 소유하고 있으며 미국 성인의 20%가 하루 1회 이상 음성 검색 서비스를 활용하고 있다. 국내 리서치 전문 기관인 컨슈머 인사이트에 따르면 국내 AI 스피커 사용 경험률은 11%에 달하며 올해 안으로 세계 5위 수준의 스피커 시장 점유율(3%)을 확보할 것으로 예상된다.아마존 에코는 시각 장애인들이 콘텐츠에 접근하는 속도를 최대 10배까지 빠르게 만들어주었으며 SKT 내비게이션 서비스 T-Map은 NUGU의 음성 인터페이스를 통해 터치 인터랙션을 26%까지 감소시켜 사고 위험을 줄였다.음성 서비스 시장이 확대되고 있다는 것과, 그 변화가 사람들의 삶에 많은 영향을 끼치고 있다는 것은 누구도 부정할 수 없는 자명한 사실이다.하지만 여전히 아쉬운 일상 속 음성 서비스 만족도그렇다면 과연 우리의 일상 속 음성 서비스 경험의 만족도는 어떨까?지난 4월 진행된 컨슈머인사이트의 조사에 따르면 국내 주요 음성 서비스에 대한 사용자 만족률은 49%로, 절반에 채 못 미치고 있는 상황이다."국내 음성 서비스 만족도 - 49%"주요 불만족 이유로는 ‘음성 명령이 잘되지 않는다’(50%), ‘자연스러운 대화가 곤란하다’(41%), ‘소음을 음성 명령으로 오인한다’(36%) 등이 꼽혔으며, 아직도 대다수의 사용자들에게 AI 스피커는 기업들의 서툰 시도로 인식되고 있다.국내 음성 기반 서비스 만족도는 타 스피커 상용화 국가들과 대비해서도 현저히 낮은 편인데, 유독 국내의 사용자들이 만족스러운 음성 서비스 경험을 누리지 못하고 있는 이유가 대체 무엇인지, 이번 글을 통해 잠시 논해보고자 한다.1. 과열된 AI 마케팅국내 'AI 스피커' 시장은 타 국가 대비 매우 치열한 점유율 경쟁이 벌어지고 있는 곳이다. 미국의 경우만 하더라도 구글 어시스턴트, 아마존 알렉사, 애플 시리의 삼파전이 벌어지고 있는 상황에서 국내는 KT 기가지니, SKT NUGU, 네이버 클로바, 카카오 i, 삼성 빅스비 등 5개가 넘는 다양한 플레이어들이 이 작은 시장을 차지하기 위해 혈투를 벌이고 있다.AI, 즉 인공지능은 사전적으로 '인간의 지능으로 할 수 있는 사고, 학습, 자기 개발 등을 컴퓨터가 할 수 있도록 하는 방법'을 뜻하는데, 현존하는 대다수의 속칭 'AI' 서비스들이 해당 수준에 다다르기에는 아직 많은 시간이 필요하다는것은 누구도 부정할 수는 없을듯 하다. 경쟁이 과열되다 보면 제품을 판매하기 위해 다소 공격적인 선택을 하는 경우가 있고, 현재 국내에서 이루어지고 있는 AI라는 용어의 지나친 남발이 바로 그 대표적인 예시라고 할 수 있다.멀리 갈 것 없이 각 나라에서 스피커를 부르는 호칭을 보면 잘 알 수 있는데, 우리가 흔히 'AI 스피커'라 부르는 구글 홈, 아마존 에코 등 대다수의 스피커는 미국 내에서 '스마트 스피커'라는 단어로 통용된다.(구글에 AI Speaker를 검색해보면 Smart Speaker로 자동 대체되는 것을 확인할 수 있다)구글 내 AI 스피커 검색 결과(첫 두 검색은 광고)즉, 아직은 '스마트'하다고 부를 수밖에 없는 수준의 기능에 대한 과장 된 'AI 마케팅'으로 인해 국내 사용자들은 시장 생성 초기부터 고도화된 인공지능을 기대하게 되고, 이는 결국 자연스레 낮은 사용자 만족도로 이어질 수밖에 없는 것이다.향후 AI가 음성 기반 서비스의 핵심 기술이 될것은 분명하지만 당장의 지나친 기대감은 되려 국내 음성 기반 서비스의 *캐즘 기간을 장기화시킬 수 있을것으로 우려된다.*캐즘: 첨단기술 제품이 선보이는 초기 시장에서 주류시장으로 넘어가는 과도기에 일시적으로 수요가 정체되거나 후퇴하는 단절 현상2. 조금 더 시간이 필요한 기술력앞서 언급한 컨슈머 인사이트의 조사에 따르면 사용자의 불만족 이유 중 TOP 3 모두가 '낮은 인식률' 바탕으로 하고 있는 것을 재차 확인할 수 있다.1. 음성 명령이 잘되지 않는다(50%)2. 자연스러운 대화가 곤란하다(41%)3. 소음을 음성 명령으로 오인한다(36%)  컨슈머인사트 AI 스피커 만족도 통계음성 서비스 경험은 사용자의 명확한 의사가 전달되지 않는다면 애초에 시작될 수 없다. 자연스러운 대화를 진행하기 위해서는 결국 사람의 언어, 즉 자연어를 분석하여 의도를 파악할 수 있어야 하며 이를 실현하기 위해서는 아래에 소개 된 ASR(음성 인식)과 NLU(자연어 처리)가 높은 수준으로 구현되어야 한다.T map X NUGU 디자인 사례로 알아보는 음성인터페이스 디자인 1강 - https://youtu.be/Dz-rxGV-dOAASR과 NLU 성능이 뒷받침되지 않는 음성 서비스는 아무리 고도화 된 서비스 로직이 준비된들 '대화'가 진행될 수 없으며 부족한 성능은 결국 국내 대다수 스피커들이 "죄송합니다. 무슨 말인지 이해 못했어요"를 출력하며 사용자 불만족도를 상승시키는 주요 요인으로 볼 수 있다.인식 정확도를 상승시키기 위해서는 결과적으로 더 많은 양의 학습 데이터가 필요하며 대다수의 업체가 아직 관련 기술력이 많이 부족한 상황에서도 공격적으로 스피커를 출시하는 이유 또한 결국 초기 점유율 높여 이 학습 데이터를 지속적으로 쌓기 위해서다.국내에서는 아직 높은 수준으로 두 단계를 구축한 메이저 업체가 없는 상황에서, 국내 기업들은 경쟁력을 확보하기 위해 관련 기술력을 가진 국내외 다양한 기업에 지속적으로 투자를 늘려나가고 있는 상황이다.http://www.zdnet.co.kr/view/?no=201702231628363. 더 많은 고민이 필요한 음성 사용자 경험(VUX) 디자인이번 협업 프로젝트를 진행하며 VUX를 공부하는 과정에서 우리의 사례를 포함한 몇 가지 재미있는 질문들을 발견할 수 있었다.질문1. 음악 앱이 재생되는 상황에서 사용자가 "앞으로 10초"라고 말했다면, 빨리 감기를 하는 게 맞을까 되감기를 하는 게 맞을까? - 네이버 클로바 사례질문2. 자정이 살짝 넘은 새벽 1시, 사용자가 "내일 일정 알려줘"라고 말했다면, 향후 23시간 동안의 일정을 알려주는 게 맞을까 23시간이 지난 그 다음날 일정을 알려주는 게 맞을까? - 히든트랙 린더(빅스비, SKT 파트너 스타트업) 사례질문3. '오늘'이라는 이름의 기업이 존재하는 상황에서 "오늘 기업 정보 알려줘"라고 말했다면, 오늘의 주요 기업 정보를 제공하는게 맞을까 주식회사 '오늘'의 정보를 제공하는게 좋을까? - 딥서치(빅스비 파트너 스타트업) 사례앞서 언급했던 1,2번의 사용자 만족도 문제가 이미 어쩔 수 없는 국내 시장의 지나친 경쟁과 더 시간이 필요한 기술력에 대한 아쉬움을 토로하는 내용이었다면, 3번의 VUI상의 새로운 경험에 대한 고민들이 이번 글을 쓰게 된 계기이자 목적이라고 볼 수 있다. 아직도 각 질문에 대한 뚜렷한 정답이 없는 상황에서 위와 같은 고민들을 함께 논의하며 최대한으로 정답에 가까운 선택을 내릴 수 있었으면 한다.클로바의 "앞으로 10초", 린더의 "내일 일정 알려줘", 딥서치의 "오늘 기업 정보 알려줘"에 대한 해답과 같이 '최선'이라고 부를 수 있는 가이드가 아직 존재하지 않는 현 VUX 시장은 더욱더 깊은 고민과 통찰이 필요한 시점이다. 단순히 해외 사례를 그대로 인용하여 국내 서비스에 적용하는 것이 아닌 정서와 문화, 그리고 각 콘텐츠에 대한 높은 이해도를 바탕으로 적절히 녹여낼 수 있어야 한다.올해 초 처음으로 챗봇을 디자인해보며 겪었던 애로사항들을 적은 부족한 글이 새로운 디자인을 시도하는 이들에게 조금이나마 도움이 되었다는 피드백을 받을 수 있었고,http://magazine.ditoday.com/ui-ux/일정-구독-서비스-린더의-탄생/이에 용기를 얻어 이번에는 다소 길지만 조금 더 많은 내용을 담고 있는 글을 준비하게 되었다.SKT NUGU, 삼성 빅스비와의 협업 과정에서 '음성 기반 인터페이스(VUI)'는 챗봇과는 확연히 다른 또 다른 형태의 디자인이라는 것을 알 수 있었고, 단순히 대화형 인터페이스(CI: Chatting Interface)를 음성의 형태로 재가공하는 것이 아닌, 서비스 기반부터 리디자인이 필요하다는것을 깨달았다.이미 구글, 아마존, 애플 등 메이저 업체들이 수년간의 경험과 데이터를 기반으로 다양한 VUX 가이드라인을 제시하고 있으며, 최근에는 SKT NUGU, 네이버 클로바 등 국내 업체들도 조금씩 VUX 서비스 제작에 대한 구체적인 로드맵을 제공하고 있는 상황이다.https://developers.nugu.co.kr/docs/voice-service-design-guideline/앞으로 약 다섯 달간 연재 진행 예정인 향후 4편의 내용들은 위 가이드 문서들에서 언급하는 다양한 해외와 국내 사례들을 바탕으로 주제를 선정하였으며, 각 편의 내용들은 VUI 서비스 제작 경험이 있는 다양한 국내 회사들의 고민 과정을 조금씩 담고 있다.1편: 음성 기반 인터페이스의 등장2편: 음성 기반 인터페이스와 TPO3편: 음성 기반 인터페이스와 페르소나4편: 음성 기반 인터페이스 vs GUI5편: 국내 음성 기반 인터페이스 현황음성 인터페이스는 정말 유용할까?음성 인터페이스는 먼 미래의 것이 아니다. 우리는 이미 수 년 전부터 다양한 종류의 음성 인터페이스를 접해왔으며, 그중 대표적인 예시가 바로 누구나 한 번쯤은 경험해보았을 ARS, 자동응답 시스템이다.각종 정보를 음성으로 저장 한 후, 사용자가 전화를 이용하여 시스템에 접속하면 음성으로 필요한 정보를 검색할 수 있도록 사용법을 알려주고, 필요한 정보를 찾으면 이를 음성으로 들려 주는 바로 그 시스템이 현 음성 인터페이스 경험의 모태라 할 수 있다.예약을 진행하는 과정에서 어떤 제품군을 수리 맡기고 싶은지, 냉장고인지, 컴퓨터인지, 노트북인지, 핸드폰인지 '말로 검색하고 말로 예약 확인을 받는' 바로 그 과정이 바로 수년 전부터 존재해온 음성 인터페이스이다. 우리가 말로, 음성으로 수리하고 싶은 제품을 말하고 응답을 받아온 이유는 간단하다.더 편했기 때문이다.다만 그렇다고 해서 음성 인터페이스가 모든 분야를 혁신시킬 변화의 축이 되기는 힘들다.음성 입출력의 한계는 매우 명확하며, 시각적 입출력이 반드시 필요한 산업과 분야(음식, 지도 등)는 꾸준히 기존과 같은 시각 기반의 인터페이스를 필요로 할 것이다.모든 분야에 적용될 수는 없는 음성 인터페이스이지만 한가지 확실한 것은 이제 시작이라는 것이다.다소 장황하고 부족한 이 글이 조금이나마 앞으로의 험난한 여정을 도울 기초적인 가이드가 될 수 있었으면 하는 마음으로 연재를 시작해본다.저도 아직 많이 낯선 분야인만큼 의아하시거나 틀린부분이 있다면 댓글로 많은 지적 및 피드백 부탁드립니다. 감사합니다 :)#히든트랙 #음성기반기술 #스타트업인사이트 #UX디자인 #음성기반디자인
조회수 788

[Buzzvil People] Alan Kim, Senior Software Engineer

 Buzzvil People에서는 다양한 배경과 성격 그리고 생각을 지닌 버즈빌리언들을 한 분 한 분 소개하는 시간을 갖습니다. 어떻게 버즈빌에 최고의 동료들이 모여 최고의 팀을 만들어가고 있는 지 궁금하시다면, 색색깔 다양한 버즈빌리언들 한분 한분의 이야기가 궁금하시다면, Buzzvil People을 주목해주세요.1. 간단한 자기 소개 부탁드립니다. 안녕하세요. 김진언입니다. 영어 이름은 Alan Kim 이고, 현재 버즈빌에서 클라이언트 팀의 리더를 맡고 있습니다. 저는 2002년에 병역 특례로 IT업계에서 근무를 시작했고, 첫 직장에서는 연구소 장비들에 들어가는 소프트웨어 개발을 하다가 그 뒤로 모바일 게임 포팅, 게임 서버 개발 등 지금까지 14년 동안 여러 회사에서 여러가지 업무를 해 왔습니다. 소프트웨어를 만드는 능력이 뛰어난 분들은 워낙 많기 때문에  A급 개발자라고 소개할 수는 없겠지만(^^) 컴파일러 관련 시스템 개발이나 게임, 네비게이션 시스템 등 꼭 APP개발에 국한되지 않은 다양한 경험을 해 왔기 때문에 그런 장점을 살릴 수 있는 부분에서는 나름대로 역할을 하고 있다고 생각합니다.  저는 예전부터 개발자로서 여러가지 경험을 하는 것을 중시해 왔는데요. 그러다보니 임베디드 시스템부터 게임, 게임 서버, PC 툴이나 스마트폰 관련 개발까지 다양한 분야의 경험을 쌓아올 수 있었습니다. 그런데 요즘에는 다양한 경험을 하는 것보다 어떤 개발을 하든지 간에 지켜나가야 하는 기준을 세우는 것이 중요하다는 생각을 많이 하고 있습니다. 그러다 보니 코드 자체 보다는 코드를 작성하는 방식에 대한 고민들을 많이 하게 되는 데요. 최근에는 ‘클린 코드’에 대해 많이 관심을 가지고 있습니다. 요즘에는 과거에 비해 Computing resource나 컴파일러 최적화 등이 눈에 띄게 발전해왔습니다. 따라서 무조건 효율좋은 코드를 짜기보다는 조금 효율은 부족하더라도 Readability가 잘 갖추어진 코드를 짜는게 중요하다는 생각을 하게 되었고 클린코드를 위해 고민해야 하는 모듈화 방법이나 디자인 패턴에 대해서 여러가지로 고민 중입니다. 실제로 저희 팀을 운영할때도 굉장히 강조하고 있는 부분이기도 합니다. 개발 이외의 성격적인 부분으로는 평소에 말수가 적어서 종종 과묵한 사람이라 오해받고는 하는데요. 표현력이 서툴러 말수가 적을 뿐, 다른 분들을 싫어하는 게 아니니 오해하지 않으셨으면 좋겠습니다. ^^  2. 어떻게 버즈빌에 오시게 되셨나요? 버즈빌과의 인연을 설명하려면 먼저 Jay와 슬라이드 조이라는 미국 회사를 말씀 드려야 하는데요. Jay는 인포뱅크라는 회사에서 처음 인연이 시작된 동료인데, Jay가 스타트업을 창업한 후 1년쯤 지난 시점에 저에게 연락이 와서, 같이 일해보자는 제안을 했고 그렇게 슬라이드 조이에 조인했습니다.  그 당시에 저는 인프라웨어라는 회사에서 게임 서버를 개발하고 있었는데요. Jay가 권유했던 그 시기가 마침  진행했던 프로젝트가 완료된 시점이었고, 개발자로서 이런 저런 고민을 하던 시기였습니다. 내가 개발하고 싶은 것, 배워보고 싶은 것, 도전해보고 싶은 것과 같은 부분을 현 회사에서 충족할 수 있느냐에 대한 갈등이 심했던 때 였죠. 원래 저는 직장을 선택함에 있어서 제가 정말 하고 싶은 일인지와 리스크가 크더라도 그 결과를 구성원이 함께 공유할 수 있는 구조를 가지고 있는지를 많이 고민했었는데요. 결혼을 하게 되면서 자연스레 조금 더 안정적인 직장을 찾고 싶어서 들어가게 된 곳이 인프라웨어라는 회사였습니다. 하지만 그곳에서 1년간 문제 없이 일하다보니 안정적인 점은 좋았지만 제 의견이 반영되기 힘들다는 점과 개발과정에서 소통이 중시되지 않는다는 점이 저와는 맞지 않는 부분이라고 생각하고 있었습니다. 그렇기 때문에 좋은 타이밍에 연락이 왔다 생각하여 큰 고민 없이 자연스럽게 합류하게 된 것 같습니다. 물론 미래가 불확실 할 수 있는 스타트업으로 가는 것에 대해서 가족들도 염려했지만 당시 슬라이드 조이를 이루고 있던 멤버들이 정말 훌륭하고 의견들도 잘 맞았기 때문에 슬라이드 조이로의 합류를 결정하게 되었던 것 같습니다. 그 후, 여러가지 우여곡절이 있었지만, 슬라이드 조이도 상당한 성장을 이루었고 마침 좋은 기회로 버즈빌과 합병이 되면서 지금은 버즈빌에서 클라이언트 개발팀의 리더로 근무하게 되었습니다. 3. 버즈빌에서 어떤 업무를 담당하고 계신가요? 현재 클라이언트 팀의 리더를 맡고 있지만, 슬라이드조이에서는 백엔드를 전담했었고 버즈빌에서 근무를 시작할 시점에는 광고 서버쪽을 다뤘기 때문에 아직도 약간의 서버쪽 업무를 병행해 가면서 일하고 있습니다. 차츰 앱 서버를 포함한 백엔드쪽 업무를 줄이고, 클라이언트 팀 리더로서의 업무에 주력하기 위해 일부 업무를 서버 팀 개발자에게 옮기고 있습니다. 버즈빌 클라이언트 팀의 업무가 일반적인 클라이언트 개발과 다른 점이 있다면 단순히 하나의 앱을 개발하는 것이 아니라 stable 한 SDK를 개발해야 한다는 점입니다. 여러 버즈스크린 파트너들의 다양한 개발환경에서 동작되어야 하고, 어떤 상황에서든 항상 동작되어야 하기 때문에 시스템 전반에 걸친 깊은 이해가  이 필요한 일입니다. 때문에 단순히 소프트웨어 개발자 보다는 깊이 연구하고 세밀하게 설계하는 능력을 가진 사람이 필요하고 현재 클라이언트 팀의 경우 그런 사람들로 구성이 되어있다고 생각합니다.   클라이언트 팀의 리더가 되면서 아무래도 개발 자체보다는 팀원들이 개발을 잘 할 수 있도록 하는 환경들에대해 더 많은 고민을 하고 관련 업무들을 하고 있습니다. CI(Continuous Integration)나 개발 프로세스 등 흔히 말하는 DevOps에 관련된 일들을 하고 있습니다. 뿐만 아니라 구현 방식에 대한 최종적인 방향을 결정하는 의사결정이나 개발 과제가 내려오는 과정과 개발이 완료된 과제들에 대해 외부의 팀과 커뮤니케이션이 필요한 사항들을 맡아서 관리하고 있습니다. 4. 스타트업에서 혹은 광고업계에서 일하는 느낌이 어떠세요? 스타트업은 한 사람, 한 사람의 목소리가 회사의 성장에 직접적으로 영향을 줄 수 있다는 점이 큰 매력이라고 생각합니다. 저의 경우에는 게임 서버 개발(수천명 규모), 자동차 관련 TF팀(수백명 규모), 스타트업 두 곳, 또 다른 게임회사(60명 규모) 등 많은 케이스의 회사에서 근무를 해 왔기 때문에 큰 차이를 직접적으로 느낄 수 있었는데요. 저에게 스타트업이란 “회사의 성장에 직접적으로 기여할 수 있는 곳” 입니다. 대부분의 회사는 규모가 커짐에 따라 어쩔 수 없이 여러가지 체계가 도입되고, 문화적으로도 경직화 되는것 같아요. 사원 개인이 회사의 의사 결정에 참여 하기는 정말 어렵고, 위로부터 내려온 결정 사항에 자신의 동의 여부와 관계 없이 일이 하는 경우가 대부분입니다. 개인의 개성은 최소화하고 회사라는 체계의 한 부품으로만 움직이게 되고요. 동기부여 없이 그에 따른 무의미한 업무가 반복되면 회사 생활이 정말 재미 없게 되는 거 같습니다. 예를 들어, 특정 Feature를 개발 함에 있어서도 왜 이 Feature에 대한 개발이 필요한지, 어떤 맥락에서 이 Feature의 구현이 중요한지에 대한 커뮤니케이션 없이 그저 과제만 주어지는 경우는 해당 업무에 대한 흥미를 떨어뜨린다고 생각합니다. 하지만 스타트업의 특성상 다른 팀과의 의사소통이 활발하게 진행될 수 있는 환경이다 보니 같은 일을 하더라도 좀 더 큰 맥락에서 파악하고 내가 하는 일에 대한 의미와 기대효과를 고민해보면서 일 할 수 있다는 것이 장점이라는 생각이 드네요. 5. 이것만큼은 버즈빌이 참 좋다! 어떤 게 있으실까요?  아마 많은 분들도 공감하시겠지만, 회사 일이란 게 혼자 하는 게 아니니만큼 같이 일하는 직원이 어떤 사람이냐에 따라서 자신의 업무에도 영향을 받게 되죠. 일에 대한 열정도 없고, 월급 생각하며 대충 일하는 직원과 같이 일하게 되면 다른 팀원들도 영향을 받게 됩니다. 안 좋은 케이스의 일을 몇 번 겪고 나니 일 할 때는 같이 일하는 사람이 어떤 사람인지에 대해 촉각을 많이 세우게 되었죠. 그런데 제가 지금 버즈빌에 근무한 지 1년 반이 넘은 것 같은데, 처음 입사할 때나 지금이나 여전히 느끼는 점은 버즈빌에는 ‘능력자들’이 정말 많다는 점입니다. 보통 회사에서는 10명 중 1명 있을까 말까 할 정도로 특출난 인재들이 한 곳에 모여있다는 그 부분이 정말 인상적이었어요. 스펙도 스펙이지만 보통 사회 초년생이라 일컫는 어린 나이 임에도 일에 대한 진지한 태도와 전문성, 빠른 일 처리 속도 등 대부분의 직원들이 ‘능력자’라 부르기에 손색이 없을 정도입니다. 그런 직원들과 매일 생활하니 저 또한 분발해야겠다는 생각과 함께 많은 의욕을 얻고 있어요.   또한 개발자로서 느끼는 버즈빌은 제가 일했었던 다른 회사들과 극명하게 차이가 있는 곳이라는 생각이 듭니다. 회사가 평등한 문화를 가지고 있다고 PR을 하는 경우가 많은데 버즈빌 만큼 실제로도 수평적인 문화를 가진 회사는 처음입니다. 개발에 관련된 여러가지 의사결정을 함에 있어서도 서로의 의견을 존중하고 워낙 뛰어난 사람들이 많다보니 서로에게 많은 것들을 배울 수 있는게 좋습니다. 저도 팀장이지만 팀원들에게 여러가지를 물어볼 때도 많고 팀원들 통해 새롭게 배우는 것들도 많구요. 이렇게 뛰어난 사람들도 많이 있고 이런 사람들 간에 서로에게 시너지를 줄 수 있는 좋은 문화를 가지고 있다는 점은 분명 다른 회사에서 찾아보기 힘든 장점이라고 생각합니다. 저는 합병을 통해 입사했기 때문에, 버즈빌이라는 회사에 기대를 많이 하고 합류하게 되었는데요. 그 점에 있어서는 200% 이상 충족되었다고 생각합니다. 구성원 한 사람 한 사람을 믿고 자신의 업무에 집중할 수 있다는 것은 저에게 있어 매우 중요한 의미이기 때문에, 현재 회사 생활이 무척 만족스럽네요. 6. 개인적인 목표나 꿈이 있으신가요? 있다면, 버즈빌에서의 경험이 어떻게 도움이 된다고 생각하시나요? 먼 미래에 대한 목표는 ‘돈 걱정 없는 편안한 노후’고요.(^^)  사실 이런 목표를 늘 생각하면서 살고 있진 않습니다. 미래보다는 현재가 중요하고 단기적인 목표에 최선을 다하는 것에 보람을 느끼는 편입니다. 당장 생각나는 목표는…혼자만의 여행일까요? 예전부터 같은 일상에 지치거나 슬럼프가 올때면 한번씩 여행을 가서 충분히 쉬고 생각도 정리하는 시간을 가졌었는데요. 버즈빌에는 다양한 국적의 직원들이 많다보니 그들의 성장환경과 문화 이야기를 들으면 저도 당장 떠나고 싶은 기분이 들 때가 있어요.  가정이 있다보니 혼자 여행을 가는게 쉽지만은 않고 지난 3월에 회사 워크샵으로 발리를 다녀 온 이후로 더욱 눈치가 보이기는 하지만, 몽골에 배낭여행을 가는게 너무 하고 싶네요. 그리고… 생각만 하고 실천을 잘 못하고 있는 부분이 있는데, ‘영어권 나라로의 이민’을 위해 기반을 마련하는 것 입니다. 영어권 나라들의 경우 대부분이 한국에 비해서 삶의 방식이 자유롭고 틀에 박힌 부분이 적다는 생각이 들더라구요. 제가 느끼기엔 아직까지 우리나라가 여유가 없고 바쁜 느낌이 강하게 들어서 좀더 여유로운 환경에서 생활해 보고 싶다는 꿈이 있습니다. 무엇보다 영어가 중요할텐데, 버즈빌에서는 많은 의사 소통을 영어로 하고 있다보니 생각해 보면 엄청 좋은 기회이지만, 아직까지 영어 사용을 적극적으로 하지 않고 있어서 다시 한번 마음을 다 잡고 시작해 봐야겠습니다.
조회수 4876

웹서버 로그 수집과 모니터링 설정

우리는 고객이 무엇에 관심 있어 하고 무엇에 관심 없어하는지, 어떤 것을 보았을 때 클릭해 들어가고 어떤 것을 보았을 때 사이트에서 이탈하는지 궁금해 합니다. 이러한 정보를 얻기 위해 봐야 할 것은 역시 웹서버의 접속 로그입니다.처음에는 매일 생성되는 로그 파일을 일일이 파싱해서 원하는 정보를 DB에 쌓는 방법을 이용했지만, 이러한 방식은 한계가 있었습니다. 저장할 수 있는 데이터의 양에 심각한 제한이 있었고, 따라서 처음에 얻고자 했던 데이터 이상의 것을 새로 추출할 수도 없었습니다.그래서 지금은 웹서버 로그를 하둡(Hadoop) 클러스터에 쌓고 있습니다. Google Analytics 같은 외부 분석툴을 사용하기도 하지만, 아무래도 데이터를 우리 손에 직접 들고 있는 것이 더 유연한 분석을 제공할 수 있지요. 클러스터에서 로그를 분석하려면 가장 먼저 로그 수집 시스템을 만들어야 합니다.이번 포스팅에서는 이 로그 수집 시스템이 어떻게 만들어져 있는지, 그리고 그보다 더 중요한 시스템의 모니터링을 어떻게 하고 있는지 설명하려고 합니다.Flume 에이전트 설정하기Apache FlumeApache Flume은 로그와 같은 데이터의 흐름(streaming)을 제어할 수 있게 해주는 도구입니다. 단순하면서도 확장성 높은 구조로 되어 있기 때문에 많은 시스템에서 채택하는 도구가 되었고, 리디북스에서도 Flume 을 사용하게 되었습니다.Flume 의 기본 구조는 단순합니다.기본적인 에이전트 구성 (이미지 출처: Apache Flume 홈페이지)에이전트(agent)는 Source, Channel, Sink 로 이루어진 자바 프로세스이다.소스(source)는 외부에서 이벤트를 입력받아 채널(channel)로 전달하고, 채널은 이벤트를 저장하고 있다가 싱크(sink)로 전달한다. 싱크는 이벤트를 외부로 출력한다.한 에이전트의 Sink와 다른 에이전트의 Source가 같은 타입이면, 에이전트 간에 이벤트를 전달할 수 있다.굉장히 간단하지만 강력한 모델입니다. Flume 은 Avro, Thrift, Exec, HDFS, Kafka 등 다양한 라이브러리를 적용한 소스와 싱크를 미리 제공하고 있기 때문에, 사용자는 자기 입맛에 맞게 이를 조합해서 시스템을 구성할 수 있습니다.예를 들면 아래와 같습니다.좀 더 복잡한 에이전트 구성 (이미지 출처: Apache Flume 홈페이지)초기 에이전트 구성: Avro를 통해 클러스터에 로그 전송저희가 맨 처음 설정한 Flume 에이전트의 구성은 다음과 같습니다.초기 에이전트 구성각 웹서버ExecSource: exec 명령으로 실행된 프로세스의 표준 출력을 이벤트로 입력받음. (tail -F <로그파일>)MemoryChannel: 메모리상의 큐(queue)로 구현된 채널AvroSink: 클러스터에 상의 에이전트가 실행하는 Avro RPC 서버로 이벤트를 전송하둡 클러스터AvroSource: 웹서버의 에이전트가 Avro RPC 로 보내는 이벤트를 수신MemoryChannelHDFSSink: HDFS 상의 지정된 경로의 파일에 이벤트 내용을 출력각 웹서버에는 에이전트가 하나씩 실행되어서, 로그 파일에 새로 추가되는 로그를 클러스터에 전송합니다. 클러스터 상의 에이전트는 단 한 개 존재하는데, 웹서버로부터 전송받은 로그를 HDFS(Hadoop File System) 에 파일로 출력하는 역할을 합니다. 웹서버 에이전트와 클러스터 에이전트 간의 통신은 Avro RPC 로 하게 하였습니다. Flume 에서 기본적으로 AvroSource 와 AvroSink 를 구현하여 제공해 주는 것을 이용했습니다.사실은 클러스터 상의 에이전트가 Avro 서비스를 통해 데이터를 모아 주지 않고, 웹서버 상의 에이전트가 HDFSSink 를 이용해서 직접 클러스터에 파일을 쓰게 하더라도 대부분의 경우는 상관없습니다. 하지만 리디북스의 경우는 그렇게 할 수 없었는데, 왜냐하면 웹서버와 하둡 클러스터가 서로 다른 네트워크 상에 있기 때문입니다.리디북스의 웹서버는 국내 IDC에 존재하지만 하둡 클러스터는 Miscrosoft Azure 클라우드 내의 가상머신으로 실행되고 있습니다. 따라서 하둡의 네임노드(namenode)가 인식하는 각 노드의 사설 IP 주소를 웹서버들이 쉽게 접근할 수 없습니다. 이를 우회하는 다양한 방법을 시도해 보았지만 최종적으로는 Avro 서비스를 중간에 두어 해결하였습니다.모니터링 알람 설정하기JSON 리포팅 사용다음은 에이전트 프로세스를 모니터링하는 문제가 있었습니다. 예기치 않은 에러로 에이전트가 종료되어서 로그가 수집되지 않고 있는데 며칠 동안 모르고 있어서는 안되겠지요.Flume 에서는 모니터링 인터페이스도 여러가지를 제공하고 있는데, 그 중 가장 이용하기 간편한 것은 HTTP 를 통한 JSON reporting 이었습니다. 에이전트 자체가 HTTP 서비스로 작동해서, 특정 포트로 요청을 보내면 에이전트의 상태를 JSON 으로 정리하여 응답을 주게 되어 있습니다. 에이전트 실행시에 옵션 몇 개만 추가하면 바로 설정할 수 있기 때문에 매우 간단합니다.Health 페이지를 이용한 모니터링그런데 이 리포팅이 제대로 나오지 않으면 어떻게 알림을 받을 수 있을까요? 각 서버마다 JSON 리포팅을 요청해서 응답이 제대로 오지 않으면 이메일을 보내는 스크립트를 만들어서 cron 으로 5분마다 실행하는 방법도 있습니다. 하지만 이 스크립트가 제대로 동작하지 않거나, 이게 실행되는 서버가 다운되면?결국 스스로를 믿지 못하고 택한 방법은 외부 서비스 Pingdom을 이용하는 것이었습니다. 단, 외부 서비스가 각각의 웹서버에 직접 접근하여 리포팅을 요청하는 방식은 보안상 문제가 될 수 있어서 아래와 같이 보완하였습니다.웹 서비스 상에 health 페이지 구현. 이 페이지는 각 웹서버의 에이전트의 JSON reporting 포트로 요청을 보내서, 결과를 종합해서 다시 JSON 으로 보여줌.모든 에이전트가 정상적으로 리포트를 보내면 {“status”: “OKAY”} 를, 아니면 {“status”: “ERROR”} 를 보여줌.이 health 페이지의 내용을 모니터링하도록 Pingdom 설정. {“status”: “OKAY”} 가 응답에 없으면 알람 메일이 오도록 함.{ "status": "OKAY", "metrics": { "192.168.0.101": { "SOURCE.log_src": { ... }, "SINK.avro_sink": { "BatchCompleteCount": 562110, "ConnectionFailedCount": 294, "EventDrainAttemptCount": 56246850, "ConnectionCreatedCount": 31, "Type": "SINK", "BatchEmptyCount": 16, "ConnectionClosedCount": 30, "EventDrainSuccessCount": 56243927, "StopTime": 0, "StartTime": 1459135471379, "BatchUnderflowCount": 610 }, "CHANNEL.mem_channel": { ... } }, "192.168.0.102": { ... } } }Health 페이지의 Json내용JSON 리포팅의 문제이렇게 설정해 놓고, 며칠간 로그가 HDFS 상에 잘 수집되는 것을 확인하고 만족해 했습니다. 그런데 며칠간 신경을 쓰지 않은 사이, 다시 에이전트를 확인해 보니 모든 웹서버 에이전트가 죽어 있었습니다. HDFS에 로그도 쌓이지 않았구요.확인해 보니, MemoryChannel 의 설정 문제였습니다. byteCapacity 값을 실수로 너무 작게 설정해서, 채널 큐가 메모리 부족으로 터져나간 것이죠. 해당 문제는 byteCapacity 값을 늘려서 간단하게 해결했습니다.문제는 알람이 오지 않았다는 것이었습니다. 문제를 재현해 본 결과, 채널이 터져서 에이전트 실행이 중단되어도, 에이전트 프로세스는 죽지 않고 ExecSource 에서 실행한 자식 프로세스(tail -F)만 죽어 있었습니다. 이렇게 되면 JSON 리포팅도 정상적으로 나오기 때문에, 결국 JSON 리포팅으로는 이런 유형의 에러를 잡지 못한다는 결론이 나왔습니다.클러스터에 모니터링 설정하기결국 웹서버상에서 모니터링하는것 보다는 데이터를 최종 전달받는 하둡 클러스터 상에서 모니터링하는 것이 안정적이라 판단하였습니다. 다행히도, 하둡 클러스터에서 사용할 수 있는 꽤나 좋은 모니터링 도구가 이미 있었습니다.CDH 의 알람 트리거리디북스에서는 기본 하둡 패키지가 아닌, Cloudera에서 제공하는 하둡 배포판인 Cloudera CDH를 사용하고 있습니다. CDH는 클러스터 상에서 사용되는 서비스마다 각종 테스트를 자동으로 실행하여, 테스트가 통과되지 않을 때마다 메일로 알람을 보내줍니다. 그리고 웬만한 필수 테스트는 기본적으로 설정되어 있지만, 사용자가 커스텀 서비스를 직접 제작할 수도 있습니다. CDH가 각 에이전트의 소스, 채널, 싱크마다 초당 전송한 이벤트 개수 등의 측정치(metric)을 모두 기록하고 있기 때문에, 이 값들이 일정 수준 이상/이하가 될 때마다 알람이 트리거되도록 설정할 수 있습니다.CDH의 알람 트리거 편집 화면웹서버마다 알람 설정하기그런데 이것으로 끝이 아닙니다. 클러스터 에이전트는 각 서버에서의 트래픽이 모두 모이는 곳이기 때문에, 여기에서 모니터링을 하는 것은 웹서버 상에서 모니터링하는 것보다 기준이 애매해집니다.10대의 웹서버 중에 한 대만 문제가 생겼을 경우, 클러스터 에이전트가 받는 트래픽은 0으로 줄어드는 것이 아니라 90%로 줄어듭니다. 알람을 트리거하는 역치(threshold)를 평소 트래픽의 90%로 잡아야 한다는 것이지요. 그런데 트래픽이라는 것이 원래 날짜와 시간에 따라 달라지기 때문에, 이 역치값을 고정된 값으로 정할 수가 없습니다. 트래픽이 높은 때를 기준으로 하면, 트래픽이 낮아지는 새벽 시간마다 가짜 알람(false alarm)이 오게 되겠지요. 그렇다고 트래픽이 낮은 때를 기준으로 하면, 트래픽이 높은 때 웹서버 에이전트가 죽더라도 새벽이 될 때까지 알 수 없습니다.결국 클러스터 단에서도 각 웹서버마다 트래픽을 구분해 주어야 한다는 결론이 나옵니다. 다행히 한 에이전트가 여러 개의 채널과 싱크를 가질 수 있고, 이벤트 헤더의 내용에 따라 소스가 어느 채널로 이벤트를 보낼지 결정해 주는 채널 셀렉터 (Channel Selector)라는 것이 있습니다.웹서버 에이전트의 소스에서는 각 이벤트 헤더에 자기 호스트명을 달아 준다. (Interceptor 는 각 이벤트에 원하는 헤더를 달아주는 역할을 한다. HostInterceptor 이용)클러스터 에이전트는 1개의 소스와, 웹서버 대수만큼의 채널 및 싱크가 있다.클러스터의 소스는 이벤트의 host 헤더를 보고 그에 해당하는 채널로 이벤트를 전달한다. (MultiplexingSelector 사용)각 채널은 자신에게 대응되는 싱크에 이벤트를 전달하고, 싱크는 각자의 HDFS 경로에 이벤트를 파일로 출력한다.최종 에이전트 구성: 채널 셀렉터로 트래픽 나누기최종적으로 나온 에이전트의 구성은 다음과 같습니다.최종 에이전트 구성그리고 에이전트 설정 파일은 아래와 같이 작성했습니다.... log_to_avro.sources.log_src.type = exec log_to_avro.sources.log_src.command = tail -F /path/to/log/file log_to_avro.sources.log_src.restart = true log_to_avro.sources.log_src.channels = mem_channel log_to_avro.sources.log_src.interceptors = ts_ic host_ic # 호스트 인터셉터 설정 log_to_avro.sources.log_src.interceptors.ts_ic.type = timestamp # 이벤트 헤더에 timestamp 삽입 (날짜별 구분을 위해) log_to_avro.sources.log_src.interceptors.host_ic.type = host # 이벤트 헤더에 호스트명 삽입 (호스트별 구분을 위해) log_to_avro.sources.log_src.interceptors.host_ic.useIP = true # 호스트명 대신에 IP 사용 log_to_avro.channels.mem_channel.type = memory log_to_avro.channels.mem_channel.capacity = 10000 log_to_avro.channels.mem_channel.transactionCapacity = 10000 log_to_avro.channels.mem_channel.byteCapacityBufferPercentage = 20 log_to_avro.channels.mem_channel.byteCapacity = 10485760 log_to_avro.sinks.avro_sink.type = avro log_to_avro.sinks.avro_sink.channel = mem_channel log_to_avro.sinks.avro_sink.hostname = hostname.of.cluster.agent log_to_avro.sinks.avro_sink.port = 4141 ...웹서버 에이전트 설정파일... avro_to_hdfs.sources.avro_src.type = avro avro_to_hdfs.sources.avro_src.bind = 0.0.0.0 avro_to_hdfs.sources.avro_src.port = 4141 avro_to_hdfs.sources.avro_src.channels = c_101 c_102 avro_to_hdfs.sources.avro_src.selector.type = multiplexing # Multiplexing Selector 설정 avro_to_hdfs.sources.avro_src.selector.header = host # 호스트 이름으로 채널 나누기 avro_to_hdfs.sources.avro_src.selector.mapping.192.168.0.101 = c_101 # 192.168.0.101 에서 온 이벤트는 c_101 채널로 avro_to_hdfs.sources.avro_src.selector.mapping.192.168.0.102 = c_102 # 192.168.0.102 에서 온 이벤트는 c_102 채널로 # 채널 c_101 설정 avro_to_hdfs.channels.c_101.type = memory avro_to_hdfs.channels.c_101.capacity = 10000 avro_to_hdfs.channels.c_101.transactionCapacity = 10000 avro_to_hdfs.channels.c_101.byteCapacityBufferPercentage = 20 avro_to_hdfs.channels.c_101.byteCapacity = 10485760 # 싱크 k_101 설정 avro_to_hdfs.sinks.k_101.type = hdfs avro_to_hdfs.sinks.k_101.channel = c_101 avro_to_hdfs.sinks.k_101.hdfs.fileSuffix = .log.gz avro_to_hdfs.sinks.k_101.hdfs.path = hdfs://namenode/path/to/logs/dir/%Y%m%d/%{host} # 날짜별, 호스트별로 다른 디렉토리에 avro_to_hdfs.sinks.k_101.hdfs.rollSize = 104857600 avro_to_hdfs.sinks.k_101.hdfs.rollInterval = 7200 avro_to_hdfs.sinks.k_101.hdfs.rollCount = 0 avro_to_hdfs.sinks.k_101.hdfs.fileType = CompressedStream avro_to_hdfs.sinks.k_101.hdfs.codeC = gzip # 채널 c_102 설정 avro_to_hdfs.channels.c_102.type = memory avro_to_hdfs.channels.c_102.capacity = 10000 avro_to_hdfs.channels.c_102.transactionCapacity = 10000 avro_to_hdfs.channels.c_102.byteCapacityBufferPercentage = 20 avro_to_hdfs.channels.c_102.byteCapacity = 10485760클러스터 에이전트 설정파일p.s. Flume 설정 파일은 변수 또는 외부 파일 include 등을 지원하지는 않아서, 위와 같이 반복되는 설정을 여러 번 써 주어야 합니다.호스트마다 CDH 알람 트리거 설정그리고 CDH 상에서도 웹서버 호스트의 개수만큼 알람 트리거를 만들어 줍니다. 초당 이벤트 개수가 0에 가깝게 떨어지면 알람이 오도록 해 주면 됩니다. 채널/싱크 중 어느 것을 기준으로 해도 크게 상관은 없는데, 저희는 싱크가 초당 이동완료한 이벤트 개수를 기준으로 했습니다.CDH에서의 알람 트리거 상태 화면이렇게 해 놓으면 또 한가지 좋은 점은, CDH가 알아서 차트를 그려 주기 때문에, 웹서버마다 트래픽 추이를 한눈에 볼 수 있다는 것입니다.HDFSSink의 초당 이벤트 개수 그래프맺음말지금까지 Apache Flume 과 CDH 를 사용해 로그 수집 시스템을 구성하고 모니터링을 설정한 후기를 살펴 보았습니다. 이 과정에서 느낀 점들을 한번 정리해 보겠습니다.첫째, 일견 간단해 보이는 기능이었지만 의외로 많은 시행착오를 거쳐야 했습니다. 아무리 간단해 보이더라도 각자의 상황에 맞추어 시스템을 설계하는 데에는 그에 맞는 고민을 거쳐야 합니다.둘째, 처음에는 로그가 일단 수집되게 하는 것이 가장 중요하다고 생각했는데, 실제로 겪어보니 모니터링이 훨씬 어렵고 중요한 문제라는 것을 알게 되었습니다. 어떤 기능이 일단 실행되도록 설정을 해 놓더라도, 그것이 매일 문제없이 실행됨을 보장받는 것은 또 다른 문제입니다.셋째, Health 페이지와 Pingdom을 이용한 웹서버 측의 모니터링은 JSON 리포팅의 문제 때문에 큰 쓸모가 없게 되었습니다. 하지만 꽤 유용한 테크닉이라는 생각이 들고, 어딘가에서는 비슷하게 이용할 수 있을 것 같습니다.마지막으로 CDH 쓰면 좋습니다. 많은 것들이 편해집니다.P.S. 리디북스 데이터팀에서는 이러한 로그 시스템을 함께 고민하고 만들어나갈 분들을 찾고 있습니다. 많은 관심 부탁드립니다.#리디북스 #개발 #서버 #서버개발 #모니터링 #로그 #Flume #CDH #로그수정 #인사이트
조회수 4125

서버 비용을 70%나 줄인 온디맨드 리사이징 이야기

비트윈의 서버에는 사용자들이 올리는 수많은 사진이 저장되어 있습니다. 2016년 3월 기준으로 커플들이 데이트에서 찍은 사진, 각자의 프로필 사진, 채팅을 나누며 올린 재미있는 짤방까지 약 11억 장의 사진이 저장되어 있습니다. 비트윈에서는 이러한 사용자들의 소중한 추억을 잘 보관하고, 사용자들의 요청을 빠르고 비용 효율적으로 처리하기 위해서 많은 노력을 기울이고 있습니다. 이번 포스팅에서는 비트윈 개발팀이 사용자들의 사진 처리를 보다 효율적으로 하기 위해서 어떠한 노력을 하였는지 공유하고자 합니다.기존의 아키텍쳐¶비트윈 사용자가 채팅창이나 모멘츠 탭에서 사진을 업로드 할 경우, 해당 사진은 업로더 서버라고 불리는 전 세계 각지에 퍼져 있는 사진 업로드 전용 서버 중 가장 가까운 서버를 자동으로 찾아서 업로드 됩니다. 업로더 서버는 사진을 해당 AWS Region의 S3 bucket에 적재하고, 미리 지정된 크기의 썸네일을 자동으로 생성하여 역시 S3에 저장합니다. 그리고 Tokyo Region에 있는 비트윈 메인 서버에 이 결과를 토큰 형태로 전송하여 DB에 그 정보를 저장하도록 합니다. 이러한 과정을 통해서 일반 HTTP request보다 훨씬 큰 용량을 가지고 있는 사용자의 사진이 최대한 적은 지연시간을 가지고 업로드되도록 합니다.사용자가 올린 사진은 원본이 S3에 저장됨과 동시에 미리 정해진 사이즈로 썸네일을 생성해서 저장된다.하나의 사진이 대략 5장에서 6장의 서로 다른 크기의 썸네일로 리사이징이 되는데, 이는 클라이언트의 디스플레이 크기에 따라서 최적화된 이미지를 내려주기 위함이었습니다. 예를 들어서 아주 작은 썸네일이면 충분한 채팅 프로필 표시 화면을 그리기 위해서 사용자가 올린 3백만 픽셀이나 되는 원본 사진을 받아서 클라이언트가 리사이징 하는 것은 지연 시간뿐 아니라 과도한 데이터 사용이라는 측면에서 효율적이지 않기 때문에 작게 리사이징 해놓은 사진을 내려주는 것이 더 바람직합니다.비트윈 사용자들의 넘치는 사랑(?)에 비트윈은 출시 후 5년 동안 약 11억 장, 썸네일을 모두 합치면 66억 장의 사진을 저장하게 되었습니다. 이 사진은 전부 AWS S3에 저장되어 있으며, 썸네일을 합친 총 용량은 2016년 3월 기준 무려 738TB였습니다. 이에 따라 사진을 저장하기 위한 S3 비용이 전체 인프라 운영 비용에서 상당 부분을 차지하게 되었습니다.기존 아키텍쳐의 비효율성¶비트윈 팀은 어느 날 위와 같은 기존의 사진 전송 아키텍쳐에 의문을 가지게 되었습니다. 비트윈 서비스가 다른 서비스와 가장 다른 특징 중의 하나는 커플 간의 데이터는 그 둘 사이에서만 공유된다는 점입니다. 일반적인 웹사이트 같은 경우, 하나의 게시물 혹은 이미지가 수천 수 만명의 유저에게 전달되지만 비트윈에서는 그렇지 않습니다. 즉, 개별 사진의 Fan-out이 작다는 점을 특징으로 가지고 있습니다.그리고 클라이언트에서 LRU를 기반으로 한 파일 캐쉬를 사용하고 있는데, 이를 통해서 위에서 말씀드린 채팅창 프로필 사진 같은 경우 클라이언트에서 캐쉬될 가능성이 매우 커지게 됩니다. 그리고 CDN으로 사용하고 있는 AWS의 CloudFront에서도 약 30~40%의 추가적인 Cache hit을 얻을 수 있었습니다. 즉, 이미 Fan-out이 낮은 리소스가 높은 Cache hit rate를 가지는 사용패턴을 가지고 있는 셈이 됩니다.더군다나 사용자의 디바이스 사이즈에 따라서 미리 리사이징 해놓은 썸네일 중 일부는 아예 사용하지 않는 사용패턴이 나타나기도 합니다. 아이패드와 같은 큰 디스플레이를 가진 클라이언트를 쓰는 사용자와 아이폰4를 사용하는 사용자가 필요로 하는 썸네일의 크기는 다를 수밖에 없기 때문입니다.아래의 그래프는 S3 접근 로그를 분석해서 파악한 특정 기간 내에 같은 해상도를 가지는 썸네일을 클라이언트가 한 번 이상 재요청 하는 비율을 나타내는 그래프입니다. 하루 내에 같은 해상도의 사진을 요청하는 경우는 10% 가 되지 않으며, 한 달 안에도 33% 정도에 불과한 것을 알 수 있습니다.특정 기간 내에 S3에 저장된 썸네일이 다시 요청되는 비율결국 비트윈 팀은 미리 여러 해상도의 썸네일을 준비해서 저장해 놓은 아키텍쳐보다는 사용자가 요청할 때 그 요청에 알맞게 리사이징된 썸네일을 새로 생성해서 내려주는 게 훨씬 비용 효율적이라는 결론에 도달하게 됩니다.새로운 아키텍쳐¶Skia¶하지만 이러한 온디맨드-리사이징 아키텍쳐로의 변환에 가장 큰 걸림돌이 있었습니다. 바로 사진의 리사이징에 오랜 시간이 걸린다는 점이었습니다. 비록 아키텍쳐 변화를 통해서 저희가 얻을 수 있는 비용 이득이 크더라도, 비트윈 사용자 경험에 느린 사진 리사이징이 방해가 되어서는 안 되었습니다.이때 저희가 찾은 것이 바로 Skia 라이브러리였습니다. Skia 라이브러리는 Google에 의해서 만들어진 2D 그래픽 라이브러리로써, 크롬이나 안드로이드, 모질라 파이어폭스 등에 사용되고 있었습니다. 그리고 이 라이브러리는 CPU 아키텍쳐에 따라서 인스트럭션 레벨로 매우 잘 최적화가 되어 있었습니다. 저희가 기존에 쓰고 있던 ImageMagicK에 비해서 거의 4배 속도로 이미지 리사이징을 처리할 수 있었으며, 총 CPU 사용량도 더 적었습니다. 저희는 이 라이브러리를 Python으로 wrapping한 PySkia라는 라이브러리를 내부적으로 만들어서 사진 리사이징에 사용하기로 하였습니다.WebP¶저희는 여기서 한발 더 나아가 보기로 했습니다. 단순히 리사이징만 Skia로 대체하는 것이 아니라, 원본 사진의 저장도 더 효율적으로 할 방법을 찾게 되었습니다. 그 결과 자연스럽게 떠오른 것이 비트윈 스티커 시스템에서 사용되었던 WebP 방식이었습니다. WebP 역시 구글이 만든 이미지 인코딩 방식으로써, 비슷한 화질을 가지는 JPEG에 비해서 약 26% 정도의 용량이 절약된다는 점에서 장점이 있습니다.온디멘드-리사이징¶위에서 언급한 대로 Skia 리사이징과 WebP 원본 저장을 합하여 아래와 같이 필요한 해상도의 사진을 그때그때 리사이징 하는 온디멘드-리사이징 아키텍쳐로 옮겨가게 되었습니다.사용자가 올린 사진은 원본이 WebP로 변환되어 S3에 저장된다. 클라이언트의 요청이 있을 때는 그때그때 요청한 사이즈로 리사이징한 썸네일을 생성해서 내려준다.리사이저 서버가 사용자의 요청을 받아서 원하는 해상도의 사진을 리사이징해서 내려주기까지 채 100ms가 걸리지 않는데, 이 정도면 사용자의 경험에 영향을 주지 않는다고 판단하였습니다. 리사이저 서버는 업로더 서버와 함께 세계 각지의 AWS Region에 배포되어 있으며, 이는 사용자가 요청한 사진을 최대한 빨리 받아가기 위함입니다.기존 사진 마이그레이션¶위와 같은 아키텍쳐 전환을 통해서 새롭게 업로드 되는 사진들은 원본만 WebP로 변환되어 저장한 후 요청이 들어올 때만 온디멘드 리사이징이 되지만, 그동안 비트윈 사용자들이 축적해 놓은 11억 장의 사진은 여전히 여러 사이즈의 썸네일로 미리 리사이징이 되어 있는 비효율적인 상태였습니다. 저희는 이 사진들도 마이그레이션하는 작업에 착수했습니다.11억 장이나 되는 원본 사진들을 전부 WebP로 변환하고, 나머지 50억 장의 미리 생성된 썸네일 사진을 지우는 작업은 결코 간단한 작업이 아니었습니다. 저희는 이 작업을 AWS의 Spot Instance와 SQS를 통해서 비용 효율적으로 진행할 수 있었습니다.Auto Scaling with Spot instance¶마이그레이션 작업은 크게 다섯 단계로 이루어져 있습니다.커플 단위로 작업을 쪼개서 SQS에 쌓아놓습니다.Worker가 SQS로부터 단위 작업을 받아와서, 해당 커플에 존재하는 모든 사진을 WebP로 변환하고 S3에 올립니다.S3로의 업로드가 확인되면, 그 변경 사항을 DB에 적습니다.기존 썸네일 사진들을 삭제합니다.기존 썸네일이 삭제되었다는 사실을 DB에 적습니다.작업을 하는 도중에 얼마든지 Worker가 중단되거나 같은 커플에 대한 작업이 두 번 중복되어서 이루어질 위험이 있습니다. 이를 위해서 마이그레이션 작업을 멱등적으로 구성하여서 사용자의 사진이 손실되는 등의 사고가 발생하지 않도록 하였습니다. 중간마다 DB에 접근해서 변경된 내용을 기록해야 하는 작업의 특성상, 작업의 병목 구간은 비트윈 DB였습니다. 그리고 사진 인코딩을 바꾸는 작업의 특징상 많은 CPU 자원이 소모될 것으로 생각하였습니다.DB에 부담이 가지 않는 범위내에서 많은 CPU 자원을 끌어와서 작업을 진행해야 할 필요성이 생긴 것입니다. 이 조건을 만족하게 하기 위해서 SQS를 바라보는 Worker들로 Auto-scaling group을 만들었습니다. 그리고 이 Auto-scaling group은 c3.2xlarge와 c3.4xlarge spot instance로 구성되어 있으며, DB의 CPU 사용량을 메트릭으로 하여 Scaling이 되도록 하였습니다. 작업은 주로 DB의 부하가 적은 새벽 시간에 집중적으로 이루어졌으며, 이 인코딩 작업은 대략 4일 정도가 소모되었습니다. 작업 과정에서 Tokyo Region에 있던 c4.2xlarge와 c3.4xlarge spot instance를 최대 140대를 사용했고, 총 사용 시간은 6,767시간이었습니다. 사용한 instance의 계산 능력을 ECU로 환산하면 총 303,933 ECU · hour를 작업에 사용하였습니다. 마이그레이션에 사용된 EC2 비용을 바탕으로 계산해 보면, 백만 장의 WebP 인코딩을 위해서 사용한 비용이 $1.8 밖에 되지 않았다는 것을 알 수 있습니다.작업 과정에서 AWS 서비스에 의외의 병목 구간이 있다는 것을 알게 되었는데, S3 단일 버킷에 1분당 1천만 개 이상의 object에 대한 삭제 요청이 들어오면 Throttling이 걸린다는 사실과 SQS의 in-flight message의 개수가 12만 개를 넘을 수 없다는 것입니다.결과¶위의 아키텍쳐 변화와 마이그레이션 작업 후 저희의 S3 비용은 70%가 넘게 감소했으며 전체 인프라 비용의 상당 부분이 감소하였습니다. 온디멘드 리사이징으로의 아키텍쳐 변화는 Storage 비용과 Computation 비용 사이의 교환이라고 볼 수 있는데, 아래 그래프에서 볼 수 있듯이 확연한 비용 절감을 달성할 수 있었습니다.총 마이그레이션 비용¶항목사용량비용 ($)EC2 spot instance6,767 hrs1,959.11SQS188,204,10489.59S3 Put/Get Requests2,492,466,8605,608.34총비용7,657.04마이그레이션 결과¶항목Before MigrationAfter Migration감소량 (%)S3 # of objects6.65 B1.17 B82.40S3 storage738 TB184 TB75.06비용 감소¶사진 저장과 리사이징에 관련된 비용이 68% 감소하였음못다 한 이야기¶이번 포스팅에서는 최근에 있었던 비트윈 사진 아키텍쳐의 변화에 대해서 알아보았습니다. 주로 사용자의 경험을 방해하지 않는 조건에서 비용을 아끼는 부분에 중점을 두고 저희 비트윈의 아키텍쳐 변화에 대해서 설명해 드렸습니다. 하지만 이 글에서 미처 언급하지 못한 변화나 개선 사항들에 대해서는 다루지 못했습니다. Tokyo Region에서 멀리 떨어져 있는 사용자를 위해서 전 세계 여러 Region에 사진 저장/전송 서버를 배포하는 일이나, 사진을 로딩할 때 낮은 해상도로부터 차례대로 로딩되도록 하는 Progressive JPEG의 적용, 사진을 아직 받아오지 못했을 때 Placeholder 역할을 할 수 있는 사진의 대표색을 찾아내는 방법 등이 그것입니다. 이에 관해서는 후에 자세히 다뤄보도록 하겠습니다.정리¶비트윈 개발팀에서는 많은 인프라 비용을 소모하는 기존 썸네일 저장 방식을 개선하여 70%에 가까운 비용 절감 효과를 보았습니다. 기존의 썸네일을 미리 생성해놓는 방식으로부터 클라이언트가 요청할 때 해당 크기의 썸네일을 리사이징해서 내려주는 방식으로 변경하였고, WebP와 Skia등의 새로운 기술을 적용하였습니다. 이를 통해서 사용자 경험에는 거의 영향을 주지 않은 상태로 비용 절감 효과를 볼 수 있었습니다.저희는 언제나 타다 및 비트윈 서비스를 함께 만들며 기술적인 문제를 함께 풀어나갈 능력있는 개발자를 모시고 있습니다. 언제든 부담없이 [email protected]로 이메일을 주시기 바랍니다!
조회수 1152

에잇퍼센트와 함께한 2016년

2016년 4월에 에잇퍼센트에 합류해서 2016년을 에잇퍼센트와 함께 보냈다. 1년을 다 채운건 아니지만 에잇퍼센트의 성장과 발전에 개발자로서 어떤 기여를 했는지 한 번 정리해 보려고 한다. 나에게 있어서는 물론이고 에잇퍼센트에도 의미가 있을 것이다.1. 대출 개설 내역 신용평가사 공유대출자에게 대출이 실행되면 이 내역을 신용평가사의 시스템으로 공유하는 것을 개발했다. 에잇퍼센트에서 받은 대출 내역이 공유되면 타 금융권에서 대출 개설 내역을 확인할 수 있으므로 추가 대출이 쉽게 이루어지지 않을 것이다. 우리의 고객은 대출자도 있지만 투자자도 있다는 측면에서 투자자의 소중한 투자금을 안전하게 지켜내기 위한 안전 장치 중 하나가 될 수 있다.  2. 성능 개선에잇퍼센트는 빠르게 성장하고 있는 스타트업이다. 스타트업이 비즈니스적으로 빠른 실행을 하며 달리다 보면 성능의 벽에 부딪힐 때가 있는데 마침 내가 합류하고 얼마 되지 않은 시점이었다. 주로 우리의 개발 환경인 Python Django 코드를 개선하는 방향으로 진행을 했고 추후에는 사용자 브라우저단 성능 개선도 진행했다. Python Django 코드 개선에 대한 자세한 내용은 내가 쓴 블로그 포스팅에서 확인할 수 있다.3. 서버 인프라 서울 이전에잇퍼센트는 서버 인프라로 Amazon Web Services(이하 AWS)를 사용하고 있다. 서비스를 처음 시작할 때에는 AWS 도쿄 리전이 가장 가까운 곳이어서 도쿄 리전을 사용하고 있었다. 그러다가 2016년이 되어 서울 리전이 생겼고 도쿄 리전에 비해 네트워크도 빠르고 비용도 저렴해서 사용하지 않을 이유가 없었다. 그래서 도쿄 리전에서 서울 리전으로 이전하는 작업을 진행했다. 밤샘 작업을 함께 해준 개발팀원들에게 고맙다는 얘기를 다시 한번 하고 싶다.그 날의 풍경과 작업 기록, 그리고 django-storages 서울 리전 연동에 대한 글까지 남겨두었다.4. Python Django 버전 업그레이드에잇퍼센트에 합류했을 때 Python 3.4 , Django 1.8을 사용하고 있었고 Python 3.5 Django 1.9로 버전 업그레이드를 진행했다. 버전 업을 하면서 발견된 큰 문제는 없었고 Django admin 의 UI 에 flat 디자인이 적용되어 화면이 이뻐졌다. 내가 직접 한 건 아니지만 화면이 이뻐졌다고 다들 좋아해 준 기억이 난다.참고로 현재의 최신 버전은 Python 3.6 Django 1.10이다.5. 테스트 개선개발을 빠르게 하는 것도 중요하지만 안정적으로 잘 동작하는 것도 그에 못지않게 중요하다. 안정적인 서비스 개발과 운영을 위해 테스트가 중요하다. 특히나 돈을 다루는 금융회사라면 더욱 중요한 것이 테스트이기에 에잇퍼센트 개발팀에도 테스트는 매우 중요하다. 테스트 코드를 잘 작성해야 하고 테스트 코드가 실제 코드를 얼마나 커버하는지에 대한 측정도 필요하다. 에잇퍼센트는 코드를 개발해서 push 하고 pull request를 할 때마다 travis를 통해서 테스트를 수행하고 커버리지를 측정하고 있다. 커버리지 측정을 처음 시작할 때와 비교해보면 기존 대비 10% 포인트 가량 커버리지가 올라갔다. 커버리지가 떨어지면 pull request를 승인하지 않는 것을 정책으로 가져가고 있다.테스트 수행 시 커버리지 측정과 함께 PEP8 준수 확인, migration 체크, 템플릿 검증 등을 하도록 개선했다. 또한 기존에는 테스트 수행 시 sqlite3을 DB로 사용했는데 개선 후에는 실제로 사용하고 있는 PostgreSQL을 사용하도록 했다. 성능을 위해 raw SQL을 사용하는 경우가 가끔 있는데 이때 테스트가 제대로 안 되는 문제를 개선할 수 있었다.6. 개발 환경 개선유지 보수가 용이하고 효율적으로 개발하기 위한 환경을 만들기 위해 노력했다. smarturls, django-debug-toolbar, factory boy 등의 패키지를 적용했으며 로컬 서버 외에 개인별로 배포해서 테스트할 수 있는 서버 환경을 만들어서 테스트를 용이하게 했다. django 설정 분리, 모델 분리, 상수 분리 등의 리팩토링도 진행했다.7. NH핀테크 오픈플랫폼 적용 (진행 중)NH 농협 은행에서 제공하는 API를 사용해서 금융 관련 작업을 자동화하고 효율화하려고 한다. NH에서 요구하는 정보보호 및 보안 기준에 맞춰 시스템을 정비하고 만들어 나가는 중이다. 올해 상반기 안에 API 연동이 되어 에잇퍼센트에 NH핀테크 오픈플랫폼이 알맞게 녹아들어 가기를 기대해본다.8. 서비스 개선에잇퍼센트의 서비스적인 개선도 몇 가지 진행했다.- 채권 상세 페이지 개선 : 투자한 채권에 대한 지급 현황, 지난 내역을 볼 수 있게 되었다.- 로그인 상태 유지 : 기본 30분 로그인이 유지되고 30일 유지를 선택할 수 있게 되었다.- Ada 챗봇 연동 : 금융권 챗봇 중 유일하게 학습하는 Ada가 서비스와 연동하기 위한 개발을 진행했다.1~7번까지의 작업은 주로 눈에 보이지 않거나 개발팀 내부적인 개선이었고 8번은 사용자에게 바로 보이는 서비스적인 개선이었다. 위에 언급한 것 외에 코드 리뷰를 열심히 한 것이 기억에 남는다. 코드 리뷰를 통해 유지보수가 용이하고 효율적인 코드를 만들어 나가고 싶었다. 어떤 코드가 유지보수가 용이하고 효율적인지 리뷰를 통해 토론하고 배워나가는 과정이 나뿐만 아니라 개발팀 모두에게 도움이 되었으리라 본다.개인적으로는 이음에서 Ruby on Rails로 개발을 재밌게 하다가 에잇퍼센트에서의 Python Django를 사용한 개발로의 도전과 전환이었다. 새로운 언어를 접하고 배워나가는 과정 또한 개발자에게는 즐거움이 아닐까 싶다. Ruby, Python 둘 다 엄청나게 잘 하는 건 아니지만 기회가 되면 Ruby와 Python 각각의 장단점을 비교해보는 것도 재미있을 것 같다.2016년은 내가 2006년 첫 회사에 들어간 지 10년이 된 해였다. 10년 전 신입 사원 시절에는 서비스적인 개선을 주로 하고 다른 팀원에게 도움을 주기보다 내 할 일에만 충실했었다. 10년이 지난 지금 작년 한 해를 이렇게 돌아보니 서비스에 직접적인 개선도 하고 다른 팀원들이 개발을 더 잘할 수 있도록 환경을 만들고 도움을 주는 데에도 제법 역할을 했다는 생각이 든다. 나 자신이 엄청나게 변화한 건 아니지만 10년이 헛된 시간은 아니었다는 생각이 들어서 내심 뿌듯하다.2016년을 이렇게 정리하고 보니 2017년에는 더 잘 해야겠다는 생각이 든다. 에잇퍼센트 모든 구성원들과 함께 엄청나고 멋지게 성장해 보고 싶다. 화이팅!2016년 처럼 올해도 해맑게 화이팅!#8퍼센트 #에잇퍼센트 #조직문화 #기업문화 #2016년 #돌아보기 #스타트업개발자 #개발자
조회수 2973

Python 기반의 웹서비스 개발 환경 구축 방법

안녕하세요. 스포카 크리에이터팀의 프로그래밍 인턴을 맡고 있는 __박종규, 정성재, 고아라__입니다. 저희 세 명은 각각 다른 OS 환경에서 웹서비스를 개발하였는데 이번 포스팅에서는 OS별로 개발 환경을 구축하는 과정을 설명하겠습니다.PythonPython(파이썬)은 비영리의 Python 소프트웨어 재단이 관리하는 개방형, 공동체 기반 개발 모델을 가지고 있습니다. Python은 C파이썬 구현이 사실상의 표준이며 동적 타이핑 범용 프로그래밍 언어로, 펄 및 루비와 자주 비교됩니다. Python은 순수한 프로그래밍 언어의 기능 외에도 다른 언어로 쓰인 모듈들을 연결하는 Glue language로써 자주 이용됩니다. 실제 Python은 많은 상용 응용 프로그램에서 스크립트 언어로 채용되고 있습니다. 또한, 유니코드 문자열을 지원하여 다양한 언어의 문자 처리에도 능합니다. 현대의 Python은 여전히 인터프리터 언어처럼 동작하나 사용자가 모르는 사이에 스스로 Python 소스 코드를 컴파일하여 바이트 코드를 만들어 냄으로써 다음에 수행할 때에는 빠른 속도를 보여줍니다.Windows에서 Python 2.x 설치 방법Linux와 Mac OS에서 Python은 기본적으로 설치되어 있기 때문에 터미널 창에서 Python 명령만으로 쉽게 실행할 수 있지만 Windows에서는 Python을 따로 설치해주어야 합니다.Python 공식 사이트해당 사이트에 들어가서 Quick Links (2.7.3) – Windows Installer 선택하여 python-2.7.3.msi 다운로드 후 실행하여 설치합니다. 특별히 원하는 버전이 있을 때에는 DOWNLOAD – Releases에서 버전별로 설치파일을 다운로드 할 수 있습니다. Python의 기본 설치 경로는 C:\Python27 이며 설치 완료 후, 환경변수를 등록해야 합니다. 환경변수를 등록하는 방법은 다음과 같습니다.내컴퓨터(마우스 오른쪽 버튼 클릭) – 속성(시스템 등록 정보) – 고급 – 환경 변수 클릭시스템 변수 – Path 선택 후 편집 클릭변수 값에 맨 뒤에 C:\Python27;C:\Python27\Scripts; 입력 후, 확인 버튼을 눌러 시스템에 적용변수 값은 각각 ; (세미콜론) 으로 구분Python의 패키지 설치 방법시작 메뉴 – 실행 – CMD 로 커맨드 창을 실행 시킨 후에 Python이 설치된 디렉터리로 이동합니다.cd C:\Python27\Scripts 그 다음 easy_install pip 명령으로 pip를 설치해줍니다. pip는 PyPI(Python Package Index)에 등록된 패키지들을 설치하고 관리할 수 있는 패키지 관리 시스템입니다.$ easy_install pip 다음은 pip 명령의 사용법입니다.pip install packageName 명령 : 최신버전으로 설치pip install packageName==0.x.x 명령 : 0.x.x버전으로 설치패키지 설치 경로 : C:\Python27\Lib\site-packagespip uninstall packageName 명령 : package 제거pip freeze 명령 : 현재 환경에 설치된 package 이름과 버전 목록 PostgreSQLPostgreSQL는 California 대학 Berkeley computer science 학부에서 개발한 POSTGRES, Version 4.2 를 기반으로 한 오브젝트 RDB 관리 시스템(ORDBMS)입니다. 또한, PostgreSQL은 BSD 허가권으로 배포되며 오픈소스 개발자 및 관련 회사들이 개발에 참여하고 있습니다. 따라서 누구라도 사용, 수정, 배포할 수 있으며 목적과 관계없이 무료로 이용할 수 있습니다.각 운영체제 별 PostgreSQL 설치 방법WindowsWindows에서 PostgreSQL을 설치하기 위해 해당 사이트로 접속합니다.PostgreSQL 공식 사이트해당 사이트의 Download탭을 선택하여 Downloads 페이지로 이동합니다. 그 다음 Binary packages 에서 Windows를 선택하여 Windows installers 페이지로 이동합니다. One click installer 에서 Download 선택 후 이 페이지 로 이동하여 해당하는 OS 버전으로 선택하여 다운로드 후 설치합니다.Linux (ubuntu 12.04 LTS)Linux(리눅스)에서 PostgreSQL은 여러 가지 방법으로 설치 할 수 있습니다. 패키지로 설치하는 방법은 터미널 창에서 아래와 같은 명령어를 입력하시면 됩니다.$ sudo apt-get install postgresql-X.X(버전명) 다른방법으로 설치하는 방법은 아래 주소로 들어가시면 자세히 나와 있습니다.PostgreSQL 공식 사이트추가로 PostgreSQL을 편하게 이용하고 싶은 사용자는 pgadmin3이라는 PostgreSQL의 GUI 프로그램을 설치하시면 됩니다. 설치 방법은 터미널 창에서 아래와 같은 명령어를 입력하시면 됩니다.$ sudo apt-get install pgadmin3 Max OS ( MAC OS X Mountain Lion 10.8.2)Mac OS에서 PostgreSQL은 Homebrew를 이용하여 설치하도록 하겠습니다. Homebrew는 Mac OS의 패키지 관리자 프로그램입니다. PostgreSQL은 Homebrew 이외에도 PostgreSQL 다운로드 사이트, Homebrew와 비슷한 기능을 하는 MacPorts 등을 이용해서도 다운받을 수 있습니다. 하지만 PostgreSQL 이외의 다른 프로그램을 설치하기에도 패키지 관리자가 유용하기 때문에 저는 Homebrew를 이용하도록 하겠습니다. 일단, Homebrew 패키지를 컴파일 하기 위해서 xCode가 설치되어 있어야 합니다. 앱스토어에서 xCode를 검색하여 받도록 합니다.xCode 설치를 완료했으면 메뉴에서 __xCode탭 - Preferences - Downloads - Command Line Tools__를 다운받습니다. 그럼 이제 Homebrew 패키지를 컴파일할 수 있게 되었습니다.이제 터미널을 실행시키고 다음 명령을 입력하면 Homebrew가 설치됩니다.$ ruby -e "$(curl -fsSkL raw.github.com/mxcl/homebrew/go)" Homebrew가 이미 설치되어있다면 업데이트할 사항이 없는지 확인해보고 업데이트를 시켜주세요.$ brew update이제 Homebrew 설치를 완료했으니 간단하게 PostgreSQL을 설치할 수 있습니다.$ brew install postgresql 설치된 버전을 $ psql –ver 명령으로 확인해 보니 PostgreSQL 9.1.4 버전이 설치되어 있었습니다.psycopg2psycopg2는 Python library의 한 종류로써 하는 역할은 Python에서 PostgreSQL를 활용하게 해주는 library입니다. 설치 방법은 터미널 창에서 아래와 같은 명령어를 입력하시면 됩니다.$ pip install psycopg2 * 참고 ubuntu/debian 사용하시는 분 중에서 설치가 안 되시는 분들은 psycopg2를 설치하시기전에 python-dev라는 패키지 파일을 설치하시면 psycopg2를 설치 하실 수 있습니다. 설치 방법은 터미널 창에서 아래와 같은 명령어를 입력하시면 됩니다.$ sudo apt-get install python-dev FlaskFlask는 Python용 Micro Framework이며, Jinja2 template engine과 Werkzerg WSGI toolkit에 의존합니다. Flask에는 기본적으로 많은 환경설정 값들이 존재합니다. 규칙에 따라 템플릿 파일과 CSS, JavaScript, Images 등의 파일들은 templates과 static 이라는 하위 디렉터리에 각각 보관해야 합니다. 기본적으로 Flask는 다른 라이브러리에는 존재하는 Database abstraction layer, Form validation 등의 기능을 포함하지 않지만, 기능을 추가할 수 있는 확장성을 제공합니다. 이미 수많은 라이브러리들이 Database integration, Form validation, Upload handling, Various open authentication technologies등을 제공합니다. SQLAlchemy나 다른 DB Tool을 이용하여 고급 패턴을 구현할 수 있으며, Flask를 이용해보면 다양한 기능을 확장할 수 있다는 것을 알 수 있을 것입니다.Flask 설치 방법Flask를 설치하려면 터미널 창에서 다음 명령어를 입력하면 됩니다. (windows의 경우 cmd창) $ sudo pip install flask SQLAlchemySQLAlchemy는 Oracle, DB2, MySQL, PostgreSQL, SQLite와 같은 관계형 데이터베이스에 강력하고 유연한 인터페이스를 제공하는 Python SQL Toolkit이자 Object Relation Mapper(ORM)입니다. 여기서 ORM은 객체를 관계형 DB 테이블에 매핑해주는 역할을 하는데 SQLAlchemy는 객체를 매핑하기 위해 특정 클래스를 상속받지 않아도 되기 때문에 높은 수준의 라이브러리라고 할 수 있습니다.앞선 단계까지의 설치가 완료되었다면 SQLAlchemy의 설치법 역시 pip 명령을 쓰면 되므로 간단합니다. 윈도우는 커맨드창을 실행시키고, 리눅스와 MAC OS의 경우 터미널을 실행시켜 다음 명령을 입력합니다. 저는 SQLAlchemy 0.7.2 버전을 설치했습니다.$ pip install SQLAlcheymy==0.7.2Permission denied라는 문구가 뜨면 권한이 없는 것이므로 관리자 계정으로 설치를 해주어야 합니다. 관리자 계정은 $ sudo su로 로그인하거나 명령 앞에 sudo를 붙이면 됩니다.$ sudo pip install SQLAlchemy==0.7.2 여기에서 ==0.7.2는 설치할 SQLAlchemy의 버전을 뜻하며 버전을 명시하지 않으면 최신 버전으로 깔립니다.Flask-SQLAlchemyFlask-SQLAlchemy는 SQLAlchemy를 더욱 뒷받침할 수 있는 Flask의 확장으로 SQLAlchemy 0.6 이상의 버전을 필요로합니다. Flask-SQLAlchemy 역시 pip 명령으로 설치할 수 있으며 저는 Flask-SQLAlchemy 0.15 버전을 설치했습니다.$ sudo pip install Flask-SQLAlchemy==0.15 마치며지금까지 Windows, Linux, Mac OS에서 Python, PostgreSQL을 이용한 웹서비스 개발 환경 구축 방법에 대해 알아보았습니다. 이 글을 통해서 처음 접한 사람들도 특정 OS에 구애받지 않고 쉽게 웹서비스 개발 환경을 구축하는 데에 도움이 되었으면 좋겠습니다.출처Python 위키백과pip 위키백과 Flask 홈페이지PostgreSQL plusPostgreSQL 위키백과 Essential SQLAlchemy, O REILLY, 2008Flask-SQLAlchemy 홈페이지#스포카 #개발 #개발팀 #개발자 #파이썬 #Python #개발환경 #업무환경 #꿀팁 #조언 #인사이트
조회수 7444

Kafka 모니터링

Kafka 도입 이후에 점진적으로 모니터링을 개선해나간다. Kafka와 그 제반 환경에 대해 이해한만큼 모니터링을 구성하고 모니터링 시스템에서 피드백을 받아 다시 학습하고 그렇게 배운 것을 토대로 다시 모니터링을 구성한다. 그 과정을 따라 나가며 Kafka 를 어떻게 모니터링하면 좋을지 알아보자.프로세스 모니터링아무래도 가장 기초적이면서 중요한 지표는 Kafka 프로세스가 잘 살아 있는지 확인하는 것이다. 다섯 대로 구성한 클러스터라면 상시 Kafka 프로세스가 확인되어야 한다. 만약 Kubernetes의 StatefulSet으로 Kafka 클러스터를 구성한 경우라면 Kafka 프로세스 다섯과 프로세스 모두를 엮는 서비스, 그러니까 로드밸런서 하나를 포함해 총 여섯 개의 프로세스를 확인해야 한다. DataDog(통칭 멍멍이)을 이용해 모니터링하는 경우라면 다음과 같이 설정하면 된다.Monitoring Kafka ClusterKafka는 Zookeeper를 이용하므로 ZooKeeper 역시 동일하게 모니터링하면 된다.DataDog을 이용한 메트릭 모니터링`dd-agent는 Kafka 관련 메트릭을 Broker, Consumer, Producer 세 측면에서 수집한다.Monitoring Kafka with DatadogMonitoring Kafka performance metrics위의 두 문서가 Kafka 모니터링의 상세한 측면을 기술하는데 멍멍이를 이용하지 않더라도 꼭 한번 읽어볼만하다. 두 문서가 매우 훌륭하므로 이 글에서는 Kubernetes 환경에 초점을 맞춰 주목할 점만 살펴본다.Kubernetes 환경에서 멍멍이 에이전트는 보통 PetSet으로 구성한다. 말인즉 Kubernetes Worker 한 대마다 에이전트를 한 대씩 띄워서 Worker 안에서 작동하는 모든 도커 인스턴스의 메트릭을 수집한다. 일단 에이전트를 설정하고 나면 아래와 같이 Kafka 모니터링이 정상 작동하는지 확인하면 된다.kube exec -it dd-agent-17vjg -- /opt/datadog-agent/agent/agent.py info kafka ----- - instance #kafka-kafka-0.broker-9999 [OK] collected 46 metrics - instance #kafka-kafka-1.broker-9999 [OK] collected 46 metrics - instance #kafka-kafka-2.broker-9999 [OK] collected 46 metrics - Collected 138 metrics, 0 events & 0 service checks Emitters ======== - http_emitter [OK]Broker의 경우는 설정하기가 비교적 쉽다. Kubernetes에서 Kafka 같은 Stateful cluster는 StatefulSet으로 구성하게 되는데 이때 호스트 주소가 kafka-0, kafka-1 같이 예측 가능한 이름으로 정해지기 때문에 kafka.yaml을 미리 작성해두기 쉽다.instances: - host: kafka-0.broker port: 9999 # This is the JMX port on which Kafka exposes its metrics (usually 9999) - host: kafka-1.broker port: 9999Producer와 Consumer 모니터링은 이와는 다르다. 구현하기 나름이지만 Producer 또는 Consumer가 되는 응용프로그램은 Stateless cluster일 때가 많고 그런 경우에는 Kubernetes에서 Deployment로 클러스터를 구성한다. 이때는 StatefulSet인 경우와 달리 호스트 주소가 worker-903266370-q3rcx와 같이 예측하기 힘들게 나오므로 에이전트에 미리 설정을 넣을 수가 없다. 상당히 까다로운 문제이다.Consumer 모니터링Kafka의 설계는 매우 단순하면서도 강력해서 감탄하곤 한다. 하지만 복잡한 문제를 단순하게 풀어냈다고 해서 이를 둘러싼 환경을 제대로 모니터링하는 것도 쉽다는 뜻은 아니다. 특히 Consumer groups이 제대로 제 몫을 하고 있는지 파악하기는 더 어렵다. Consumer group마다 모니터링 체계를 갖추자니 번거롭다. 게다가 그런 번거로움을 극복하더라도 Kafka에 문제가 있는 경우를 탐지하기는 여전히 어렵다. 예를 들어 Consumer에게 가야 할 메시지 중 5%가 실제로는 전달되지 않는다 하면 이를 Consumer가 알기는 어려울 것이다. 이 외에도 Consumer 측 모니터링이 엄청나게 까다로운 문제임은 Burrow: Kafka Consumer Monitoring Reinvented에서 잘 밝혔다.Burrow: Kafka Consumer Monitoring Reinvented에 등장하는 Burrow는 Kafka를 세상에 내놓은 LinkedIn 엔지니어링 팀이 개발한 Kafka 컨슈머 모니터링 도구이다. 커뮤니티에서는 대체로 현존하는 가장 뛰어난 모니터링 도구라고 인정하는 분위기이다. 그러니 다른 도구도 많지만 우선 Burrow로 모니터링을 강화하기로 한다.Burrow로 Consumer 모니터링하기Burrow는 Dockerize가 잘 되어 있기 때문에 사용하기 어렵지 않다. LinkedIn이 공식 도커 이미지까지 제공했더라면 더 좋겠으나 GitHub에 Dockerfile과 docker-compose.yml을 올려놓아서 도커를 잘 아는 사람이라면 큰 어려움 없이 바로 설정하고 설치할 수 있다. 컨테이너 환경의 관례대로 주요 설정을 환경변수로 미리 빼놨으면 더 좋았겠지만 …알람 받기Burrow는 문제가 생겼을 때 알람을 발송하는 기능이 있다. 위키에는 이메일 알람과 HTTP 알람(Webhook)을 어떻게 설정하는지 설명한다. 그런데 Burrow 소스코드를 살펴보면 문서화되지 않은 알람 기능도 있으니… 바로! Slack 알람을 제공한다. 아직 공식 문서가 없고 소스코드도 godoc 관례에 맞춰 설명해놓은 부분이 전혀 없기 때문에 소스코드를 읽거나 GitHub 이슈에서 논의된 내용을 토대로 설정해야 한다.[slacknotifier] enable=true url=https://hooks.slack.com/services/xxxx/xxxxxxxxxx group=local,critical-consumer-group group=local,other-consumer-group threshold=0 channel="#general" username=burrower interval=5 timeout=5 keepalive=30멍멍이로 메트릭을 꾸준히 수집하고 이슈가 생겼을 때 알람을 받고자 한다면 packetloop/datadog-agent-burrow를 이용하면 된다.This plugin will push the offsets for all topics (except the offsets_topic) and consumers for every kafka cluster it finds into Datadog as a metric.멍멍이 에이전트에 필요한 파일과 설정을 넣고 나면 아래와 같이 메트릭이 수집된다.kafka.topic.offsets 와 kafka.consumer.offsets 이렇게 두 개의 메트릭만 수집하지만 각 메트릭을 cluster, topic, consumer 세 개의 토픽으로 세분화하기 때문에 실제로는 꽤 다양한 지표를 멍멍이에서 확인하고 이용할 수 있다.알`람 설정하기앞서 살펴봤지만 프로세스 모니터링 등은 어렵지 않다. 클러스터에서 한대라도 빠지면 바로 알람을 받는다. 끝!하지만 그 외의 지표는 알람의 기준을 설정하기가 힘들다. 예를 들어 Burrow의 kafka.topic.offsets 값이 600이면 정상인가? 그렇다면 700은? 또는 400은? 도무지 감을 잡을 수가 없다. 이럴 때는 멍멍이가 제공하는 Outlier detection기능으로 알람을 걸면 쉽다. 이 기능은 쉽게 말해 평소와 다른 행동을 감지했을 때 알람을 보낸다. 그러므로 정상의 범위를 확실하게 모를 때 아주 유용하다.설정 자체는 DBSCAN 또는 MAD라는 알고리즘이 등장하는 것만 빼곤 여타의 모니터링과 다르지 않기 때문에 매우 쉽다.참고 문헌How to Monitor KafkaCollecting Kafka performance metricsOriginally published at Andromeda Rabbit.#데일리 #데일리호텔 #개발 #개발자 #개발팀 #인사이트 #기술스택 #스택소개 #Kafka
조회수 1984

비트윈의 스티커 시스템 구현 이야기 - VCNC Engineering Blog

 비트윈에는 커플들이 서로에게 감정을 더욱 잘 표현할 수 있도록 스티커를 전송할 수 있는 기능이 있습니다. 이를 위해 스티커 스토어에서 다양한 종류의 스티커를 제공하고 있으며 사용자들은 구매한 스티커를 메시지의 첨부파일 형태로 전송을 할 수 있습니다. 저희가 스티커 시스템을 구현하면서 맞딱드린 문제와 이를 해결한 방법, 그리고 프로젝트를 진행하면서 배운 것들에 대해 소개해 보고자 합니다.스티커 시스템 아키텍처비트윈에서 스티커 기능을 제공하기 위해 다양한 구성 요소들이 있습니다. 전체적인 구성은 다음과 같습니다.비트윈 서버: 이전에 소개드렸었던 비트윈의 서버입니다. 비트윈의 채팅, 사진, 기념일 공유 등 제품내의 핵심이 되는 기능을 위해 운영됩니다. 스티커 스토어에서 구매한 스티커는 비트윈 서버를 통해 상대방에게 전송할 수 있습니다.스티커 스토어 서버: 스티커를 구매할 수 있는 스토어를 서비스합니다. 스티커 스토어는 웹페이지로 작성되어 있고 아이폰, 안드로이드 클라이언트와 유기적으로 연동되어 구매 요청 등을 처리합니다. 처음에는 Python과 Flask를 이용하여 구현하려 하였으나 결국엔 서버 개발자들이 좀 더 익숙한 자바로 구현하기로 결정하였습니다. Jetty와 Jersey를 사용하였고, HTML을 랜더링하기 위한 템플릿 엔진으로는 Closure Template을 이용하였습니다. ORM으로는 Hibernate/JPA, 클라이언트와 웹페이지간 연동을 위해서 Cordova를 이용하였습니다. EC2에서 운영하고 있으며 데이터베이스로는 RDS에서 제공하는 MySQL을 사용합니다. 이미 존재하는 솔루션들을 잘 활용하여 최대한 빨리 개발 할 수 있도록 노력을 기울였습니다.스티커 다운로드 서버: 스티커는 비트윈에서 정의한 특수한 포맷의 파일 형태로 제공됩니다. 기본적으로 수 많은 사용자가 같은 스티커 파일을 다운로드 받습니다. 따라서 AWS에서 제공하는 CDN인 CloudFront을 이용하며, 실제 스티커 파일들은 S3에서 호스팅합니다. 그런데 스티커 파일들은 디바이스의 해상도(DPI)에 따라 최적화된 파일들을 내려줘야하는 이슈가 있었습니다. 이를 위해 CloudFront와 S3사이의 파일 전송에 GAE에서 운영중인 간단한 어플리케이션이 관여합니다. 이에 대해서는 뒷편에서 좀 더 자세히 설명하도록 하겠습니다.구현상 문제들과 해결 방법들적정 기술에 대해 고민하다스티커 스토어 서버를 처음 설계할때 Flask와 SQLAlchemy를 이용하여 구현하고자 하였습니다. 개발팀 내부적으로 웹서버를 만들때 앞으로 Python과 Flask를 이용해야겠다는 생각이 있었기 때문이며, 일반적으로 Java보다는 Python으로 짜는 것이 개발 효율이 더 좋다는 것은 잘 알려진 사실이기도 합니다. 하지만 Java에 익숙한 서버 개발자들이 Python의 일반적인 스타일에 익숙하지 않아 Python다운 코드를 짜기 어려웠고, 오히려 개발하는데 비용이 더 많이 들어갔습니다. 그래서 개발 중에 다시 웹 서버는 자바로 짜게 되었고, 여러가지 스크립트들만 Python으로 짜고 있습니다. 실제 개발에 있어서 적절한 기술의 선택은 실제 프로젝트에 참여하는 개발자들의 능력에 따라 달라져야한다는 것을 알게되었습니다.스티커 파일 용량과 변환 시간을 고려하다사용자는 스티커 스토어에서 여러개의 스티커가 하나로 묶인 스티커 묶음을 구매하게 됩니다. 구매 완료시 여러개의 스티커가 하나의 파일로 압축되어 있는 zip파일을 다운로드 받게 됩니다. zip파일내의 각 스티커 파일에는 스티커를 재생하기 위한 스티커의 이미지 프레임들과 메타데이터에 대한 정보들이 담겨 있습니다. 메타데이터는 Thrift를 이용하여 정의하였습니다.스티커 zip파일 안에는 여러개의 스티커 파일이 들어가 있으며, 스티커 파일은 다양한 정보를 포함합니다카카오톡의 스티커의 경우 애니메이션이 있는 것은 배경이 불투명하고 배경이 투명한 경우에는 애니메이션이 없습니다. 하지만 비트윈 스티커는 배경이 투명하고 고해상도의 애니메이션을 보여줄 수 있어야 했습니다. 배경이 투명한 여러 장의 고해상도 이미지를 움직이게 만드는 것은 비교적 어려운 점이 많습니다. 여러 프레임의 이미지들의 배경을 투명하게 하기 위해 PNG를 사용하면 JPEG에 비해 스티커 파일의 크기가 너무 커집니다. 파일 크기가 너무 커지면 당시 3G 환경에서 다운로드가 너무 오래 걸려 사용성이 크게 떨어지기 때문에 무작정 PNG를 사용할 수는 없었습니다. 이에 대한 해결책으로 투명 기능을 제공하면서도 파일 크기도 비교적 작은 WebP를 이용하였습니다. WebP는 구글이 공개한 이미지 포맷으로 화질 저하를 최소화 하면서도 이미지 파일 크기가 작다는 장점이 있습니다. 각 클라이언트에서 스티커를 다운 받을때는 WebP로 다운 받지만, 다운 받은 이후에는 이미지 로딩 속도를 위해 로컬에 PNG로 변환한 스티커 프레임들을 캐싱합니다.그런데 출시 된지 오래된 안드로이드나 iPhone 3Gs와 같이 CPU성능이 좋지 않은 단말에서 WebP 디코딩이 지나치게 오래 걸리는 문제가 있었습니다. 이런 단말들은 공통적으로 해상도가 낮은 디바이스였고, 이 경우에는 특별히 PNG로 스티커 파일을 만들어 내려줬습니다. 이미지의 해상도가 낮기 때문에 파일 크기가 크지 않았고, 다운로드 속도 문제가 없었기 때문입니다.좀 더 나은 주소 포맷을 위해 GAE를 활용하다기본적으로 스티커는 여러 사용자가 같은 스티커 파일을 다운받아 사용하기 때문에 CDN을 이용하여 배포하는 것이 좋습니다. CDN을 이용하면 스티커 파일이 전 세계 곳곳에 있는 엣지 서버에 캐싱되어 사용자들이 가장 최적의 경로로 파일을 다운로드 받을 수 있습니다. 그래서 AWS의 S3와 CloudFront를 사용하여 스티커 파일을 배포하려고 했습니다. 또한, 여러 해상도의 디바이스에서 최적의 스티커를 보여줘야 했습니다. 이 때문에 다양한 해상도로 만들어진 스티커 파일들을 S3에 올려야 했는데 클라이어트에서 스티커 파일을 다운로드시 주소 포맷을 어떻게 가져가야 할지가 어려웠습니다. S3에 올리는 경우 파일와 디렉터리 구조 형태로 저장되기 때문에 아래와 같은 방법으로 저장이 가능합니다.http://dl.sticker.vcnc.co.kr/[dpi_of_sticker]/[sticker_id].sticker하지만, 이렇게 주소를 가져가는 경우 클라이언트가 자신의 해상도에 맞는 적절한 스티커의 해상도를 계산하여 요청해야 합니다. 이것은 클라이언트에서 서버에서 제공하는 스티커 해상도 리스트를 알고 있어야 한다는 의미이며, 이러한 정보들은 최대한 클라이언트에 가려 놓는 것이 유지보수에 좋습니다. 클라이언트는 그냥 자신의 디스플레이 해상도를 전달하기만 하고, 서버에서 적절히 계산하여 알맞은 해상도의 스티커 파일을 내려주는 것이 가장 좋습니다. 이를 위해 스티커 다운로드 URL을 아래와 같은 형태로 디자인하고자 하였습니다.http://dl.sticker.vcnc.co.kr/[sticker_id].sticker?density=[dpi_of_device]하지만 S3와 CloudFront 조합으로만 위와 같은 URL 제공은 불가능하며 따로 다운로드 서버를 운영해야 합니다. 그렇다고 EC2에 따로 서버를 운영하는 것은 안정적인 서비스 운영을 위해 신경써야할 포인트들이 늘어나는 것이어서 부담이 너무 컸습니다. 그래서, 아래와 같이 GAE를 사용하기로 하였습니다.GAE는 구글에서 일종의 클라우드 서비스(PaaS)로 구글 인프라에서 웹 어플리케이션을 실행시켜 줍니다. GAE에 클라이언트에서 요청한 URL을 적절한 S3 URL로 변환해주는 어플리케이션을 만들어 올렸습니다. 일종의 Rewrite Engine 역할을 하는 것입니다. 서비스의 안정성은 GAE가 보장해주고, S3와 CloudFront의 안정성은 AWS에서 보장해주기 때문에 크게 신경쓰지 않아도 장애 없는 서비스 운영이 가능합니다. 또한 CloudFront에서 스티커 파일을 최대한 캐싱 하며 따라서 GAE를 통해 새로 요청을 하는 경우는 거의 없기 때문에 GAE 사용 비용은 거의 발생하지 않습니다. GAE에는 클라이언트에서 보내주는 해상도를 보고 적당한 해상도의 스티커 파일을 내려주는 아주 간단한 어플리케이션만 작성하면 되기 때문에 개발 비용도 거의 들지 않았습니다.토큰을 이용해 보안 문제를 해결하다실제 스티커를 구매한 사용자만 스티커를 사용할 수 있어야 합니다. 스티커 토큰을 이용해 실제 구매한 사용자만 스티커를 전송할 수 있도록 구현하였습니다. 사용자가 스티커 스토어에서 스티커를 구매하게 되면 각 스티커에 대한 토큰을 얻을 수 있습니다. 스티커 토큰은 다음과 같이 구성됩니다.토큰 버전, 스티커 아이디, 사용자 아이디, 유효기간, 서버의 서명서버의 서명은 앞의 네 가지 정보를 바탕으로 만들어지며 서버의 서명과 서명을 만드는 비밀키는 충분히 길어서 실제 비밀키를 알지 못하면 서명을 위조할 수 없습니다. 사용자가 자신이 가지고 있는 스티커 토큰과 그에 해당하는 스티커를 비트윈 서버로 보내게 되면, 비트윈 서버에서는 서명이 유효한지 아닌지를 검사합니다. 서명이 유효하다면 스티커를 전송이 성공하며, 만약 토큰이 유효하지 않다면 스티커의 전송을 허가하지 않습니다.못다 한 이야기비트윈 개발팀에게 스티커 기능은 개발하면서 우여곡절이 참 많았던 프로젝트 중에 하나 입니다. 여러 가지 시도를 하면서 실패도 많이 했었고 덕분에 배운 것도 참 많았습니다. 기술적으로 크게 틀리지 않다면, 빠른 개발을 위해서 가장 익숙한 것으로 개발하는 것이 가장 좋은 선택이라는 알게 되어 스티커 스토어를 Python 대신 Java로 구현하게 되었습니다. 현재 비트윈 개발팀에서 일부 웹사이트와 스크립트 작성 용도로 Python을 사용하고 있지만 Python을 잘하는 개발자가 있다면 다양한 프로젝트들를 Python으로 진행할 수 있다고 생각합니다. 팀내에 경험을 공유할 수 있는 사람이 있다면 피드백을 통해 좋은 코드를 빠른 시간안에 짤 수 있고 뛰어난 개발자는 언어와 상관없이 컴퓨터에 대한 깊이 있는 지식을 가지고 있을 것이기 때문입니다.네 그렇습니다. 결론은 Python 개발자를 모신다는 것입니다.
조회수 295

프로그래밍 수업의 모든 것.

안녕하세요 엘리스입니다. :)엘리스의 프로그래밍 수업은 누구에 의해서, 어떻게, 어떤 생각을 바탕으로 만들어질까요?미래를 이끌어나갈 컴퓨터 사이언스 기술과 그 근간이 되는 교육 사이에서 좋은 프로그래밍 수업을 만들기 위해 치열하게 고민하는 엘리스의 코스 매니저가 직접 이야기합니다! 마침 엘리스는 코스 매니저 채용 중에 있으니 관심이 있다면 눈여겨 봐주세요!코스 매니저가 관여한 프로덕트로 인하여 사용자가 성장을 하고 있다면 그것은 충분히 의미 있는 일.안녕하세요 저는,트라우마를 극복한 프로그래밍 수업 크리에이터.Q. 자기소개 부탁드려요.A. 엘리스의 프로그래밍 과목을 만드는 코스 매니저 이용희입니다.Q. 엘리스에서 일하게 된 이유는 무엇인가요?A. 원래는 프로그래밍에 대한 트라우마가 있었어요. 하지만 기술 창업에 대한 꿈이 있었기 때문에 프로그래밍은 극복해야 할 산이었죠. 엘리스는 가장 뛰어난 기술자들이 모여 창업한 스타트업이에요. 당연히 기술 창업을 가장 가까이에서 경험할 수 있는 매력적인 곳으로 느껴졌죠. 그리고 프로그래밍 교육을 제공한다는 것 역시 기회로 느껴졌어요. 저와 같이 프로그래밍을 미워하고 두려워하는 사람들에게 보다 쉽게 배울 수 있는 환경을 마련해주고 싶다는 기대로 일을 시작하게 되었습니다.Q. 두려운 대상을 향해 몸을 던지셨군요! 그런데 코스 매니저가 프로그래밍을 몰라도 되나요?A. 많이 알면 알수록 당연히 좋아요. 많이 알고 있을수록 시도할 수 있는 것도 많고 학생에게 전달해줄 수 있는 것은 더욱더 많기 때문에요. 하지만 최소한으로는 Class가 뭔지 알고 있으면 OK. 예를 들어서 코드를 보고 이 코드가 어떤 목적을 갖는지 알 수 있으면 직접 코딩을 하지는 못한다고 해도 괜찮아요.Q. 코스 매니징 외에도 라이브 수업 참여, 조교, 챌린지 사회자 등 많은 역할을 하셨는데 이유가 있나요?A. 좋은 수업을 만들기 위한 첫 번째 방법은 코스를 만드는 모든 과정에 참여하는 사람들의 역할을 직접 체험해 보는 것이라고 생각했어요. 학생으로서, 조교로서, 사회자나 라이브 어시스턴트로서. 이렇게 하니까 학생으로서 수업을 접할 때의 감상은 무엇인지, 조교로서 가르쳤을 때는 어떤 어려움이 있는지를 알 수 있었어요. 라이브 수업 어시스턴트로 참여했을 때는 방송하시는 선생님들의 애로사항을 알 수 있겠더라고요.코스 매니징의 정수.프로그래밍적 성장을 도움으로써 가치를 만들어 냅니다.Q. 코스 매니징의 A to Z는? 구체적인 업무 프로세스가 궁금해요.A. 크게 기획 - 모집 - 제작 - 분석의 네 단계로 이루어져 있어요. 1. 수업 기획 -  어떤 과목을 만들 것인가? 주차별로 무엇을 다룰 것인가? 흥미로운 콘텐츠는? 2. 선생님, 조교 모집 - 엘리스가 구상한 수업을 가장 잘 전달할 수 있는 선생님과 조교를 모집. 3. 수업 제작 및 운영 - 실습 문제, 강의 자료 등을 엘리스의 색깔로 제작하여 수업을 운영. 4. 데이터 분석 - 학생들의 피드백과 데이터를 다음 수업의 발전 및 교육자와의 관계 개선에 반영.Q. 업무 방식은? 어떤 메리트가 있나요?A. 처음부터 끝까지 모든 과정을 주도해나가는 방식이에요. 어떤 회사를 가도 프로덕트의 end to end 프로세스를 전부 경험하기는 어려운데 엘리스에서는 그 전 과정을 경험할 수 있어요. 저는 이러한 경험이 교육 업계나 특정 프로덕트에만 적용할 수 있는게 아니라 다른 업계에 간다고 하더라도 충분히 전환될 수 있는 좋은 경험이라고 생각해요.Q. 미래 산업의 근간이 될 교육을 직접 만든다는 중책을 맡고 계신다고 생각하는데요, 좋은 프로그래밍 수업을 만들기 위해 어떤 노력들을 하시나요?A. 그런 영향을 미칠 수 있다는 게 무서운 일인 것도 같아요. 어떤 사람들은 엘리스를 통해서 프로그래밍을 처음 접하는 것일 수도 있는데 그 경험이 불쾌했다면 앞으로 프로그래밍을 배울 생각이 전혀 들지 않을 수도 있는 거잖아요. 그래서 최대한 다양한 피드백을 받아서 수렴하려고 해요. 외적으로는 대학강의, 수많은 수업들을 참고해요. 여러 강의를 보다보면 좋은 예도 많지만 모든 수업이 재미있지는 않아요. 중간에 듣다 마는 경우도 있고요. 그럴 때마다 내가 왜 중단했고 어떤 요소를 바꾸면 엘리스에서는 학생들이 끝까지 들을 수 있을까 고민해서 반영하려고 하죠.Q. 언제 보람을 느끼나요?A. 내가 관여한 프로덕트가 누군가에게 임팩트를 만들어내고 나뿐만 아니라 프로덕트를 사용하는 사람들이 성장을 하고 있다면 그것은 충분히 가치 있는 일인 것 같아요. 저희 플랫폼에서는 대시보드를 통해서, 그리고 학생이 코드를 어떻게 짜고 있는지 보면서 그 결과를 가시적으로 확인할 수 있어요. 누군가 제가 만든 코스를 수강함으로써 실질적으로 성장하는 게 눈에 보일 때 가장 큰 보람을 느끼는 것 같아요.한 번은 한 선생님께서 학생으로부터 ‘선생님 덕분에 취업할 수 있었어요’라는 메시지를 받은 것을 엘리스와 공유해주셨는데 그때 정말 행복하더라고요. 이게 엘리스가 추구하는 거다,라는 생각을 했어요. 엘리스도 하나의 커뮤니티이고 싶거든요. 이 경우에는 학생-선생님-엘리스가 서로의 영향으로 좋은 결과를 만들어 낸 거죠. 이런 접점을 앞으로 더 많이 만들려고 생각하고 있어요.대시보드에 나타나는 학생들의 학습 현황 및 성취도.엘리스는 이런 팀.가치, 성장, 사람. 포기할 수 없는 세 가지가 있는 곳.Q. 함께 일하는 동료들은 어떤 사람들인가요? 총평을 하자면?A. 항상 내가 최고의 사람들과 함께하고 있다라는 확신이 있어요. 각자 자기 분야에서 최고의 실력을 가진 사람들과 함께 일한다는 것만으로도 큰 자극이 되죠. 프로그래밍이든 스타트업 생존 노하우든 항상 뭔가를 새롭게 배우고 성장하게끔 동기부여를 해주는 사람들이에요. 저는 트라우마가 있었을 정도로 프로그래밍을 두려워했지만 이들과 함께 일하며 작은 피드백을 하나 듣는 것만으로도 제 실력이 빠르게 성장한다는 것을 몸소 느낄 수 있었어요. Q. 엘리스의 분위기, 팀 문화는 어떤가요?A. 새로운 것에 도전하는 것을 환영하는 수평적이고 자유로운 팀. 인턴도 아이디어를 제시할 수 있어요. 이 다음이 더 중요한데, 아이디어에서 그치는 게 아니라 활발한 피드백이 오가요. 아이디어를 실행하기 어렵다고 판단하더라도 왜 그렇고 어떻게 발전시킬 수 있는지 이야기하죠. 실행하게 되었을 때는 아이디어를 제시한 사람에게 일에 대한 권한이 전적으로 주어지고요. 저도 처음엔 파트타임 인턴이었지만, 이런 팀문화 덕분에 계속해서 업무 범위를 확장하고 제 역량을 키울 수 있었어요.코스 매니저 채용.Generalist & Infinite LearnerQ. 현재 코스 매니저를 구인하고 있는데요. 코스 매니저에 적합한 성향이 있나요?A. 두 단어가 떠오르네요. Generalist, 그리고 Infinite Learner. 깊게 한 분야를 아는 사람보다는 얕고 넓게 아는 사람이 더 적합하다고 생각해요. 다르게 말하면 새로운 것을 시도하는 것을 좋아하고 새로운 것을 접할 때 포용력이 높은 사람이요. 두 번째로는 배움에 재미를 느끼는 사람. 엘리스는 교육 스타트업이고 코스 매니저는 직접 교육의 경험을 만드는 사람이니 스스로가 배움에서 행복을 느끼는 사람이라면 훨씬 더 재미있게 일할 수 있겠죠. 한 가지 덧붙이면, 데이터 분석을 배우고 싶은 분께 엘리스는 최고의 장소입니다.Q. 코스 매니저로서 갖추고 있으면 좋은 역량이나 자질이 있다면?A. 소통 능력과 균형 감각. 코스 매니저는 수업을 만드는 모든 단계에서 다양한 이해당사자들과 일하게 돼요. 이들과 원활하게 소통하고 의견을 공유하는 게 중요하죠. 그리고 다양한 사람들 사이에서 최고의 균형을 찾아내는 것도 중요해요. 예를 들어서 선생님의 경우 개발만 해왔고 교육이라는 것을 접해본 적이 없는 분들이 대부분이고, 학생은 프로그래밍을 처음 접하면 그 수업이 좋은 건지 아닌지 평가하기 어려워요. 때문에 코스 매니저가 이 둘 사이에 다리를 놓는 중재자의 역할을 하기 위해서는 다양한 시각에서 볼 수 있는 균형 감각이 필요하다고 생각해요.
조회수 464

컴공생의 AI 스쿨 필기 노트 ⑧의사결정 나무

미국 스탠퍼드대학의 Xuefeng Ling 교수팀이 본태성 고혈압 발병 위험을 예측하는 AI를 개발했다고 해요. 이 연구에서 활용한 AI 모델은 의사결정 트리(decision tree) 기계학습 기법을 적용했는데요. 그 결과 AI를 통하여 10명 중 9명은 1년 내 본태성 고혈압 발병 위험을 정확하게 예측할 수 있었어요. 국내외 연구자들은 이 의사결정 트리 모델을 적용하여 고령화 시대에 폭발적으로 증가한 고혈압 환자 진료 부담을 덜 수 있을 거라고 기대하고 있다고 합니다. (기사 원문: AI 훈풍 타고 '최적 고혈압 관리'로 향한다)(Cover image : Photo by Gabe Pangilinan on Unsplash)8주 차 수업에서는 이렇듯 의학 분야에도 도움을 주고 있는 딥러닝 모델의 하나인 의사결정 트리(Decision Trees)와 의사결정 트리의 문제를 해결해주는 랜덤 포레스트(Random Forests)에 대해 배웠습니다. 예시를 통해 알아볼까요?의사결정 트리(Decision Tree)의사결정 트리는 다양한 의사결정 경로와 결과를 트리 구조를 사용하여 나타내요. 의사결정 트리는 질문을 던져서 대상을 좁혀나가는 스무고개 놀이와 비슷한 개념이에요.위의 그림은 야구 선수의 연봉을 예측하는 의사결정 트리 모델이에요. 의사결정 트리를 만들기 위해서는 어떤 질문을 할 것인지 그리고 그 질문들을 어떤 순서로 할 것인지 정해야 해요. 의사결정 트리의 시작을 ‘뿌리 노드’라고 하는데요, 위의 예에서 뿌리 노드인 ‘Years < 4> 참고로, 의사 결정 트리는 회귀와 분류 모두 가능한데요. 위의 그림과 같이 숫자형 결과를 반환하면 회귀 트리(Regression Tree)라 부르고 범주형 결과(A인지 B인지)를 반환하면 분류 트리(Classification Tree)라 불러요.  이렇게 질문을 던지고 그 질문에 따라 답을 찾아가다 보면 최종적으로 야구 선수의 연봉을 예측할 수 있게 돼요. 최적의 의사결정 트리를 만들기 위한 가장 좋은 방법은 예측하려는 대상에 대해 가장 많은 정보를 담고 있는 질문을 고르는 것이에요. 이처럼 얼마만큼의 정보를 담고 있는가를 엔트로피(entropy)라고 해요. 엔트로피가 클수록 데이터 정보가 잘 분포되어 있기 때문에 좋은 지표라고 예상할 수 있어요. 이처럼 의사결정 트리는 이해하고 해석하기 쉽다는 장점이 있어요. 또한 예측할 때 사용하는 프로세스가 명백하며 숫자형/범주형 데이터를 동시에 다룰 수 있어요. 그렇지만 최적의 의사결정 트리를 찾는 것은 어려운 일인데요. 그래서 오버 피팅, 즉 과거의 학습한 데이터에 대해서는 잘 예측하지만 새로 들어온 데이터에 대해서 성능이 떨어지는 경우가 되기 쉬워요. 이러한 오버 피팅을 방지하기 위해 앙상블 기법을 적용한 랜덤 포레스트(Random Forest) 모델을 사용해요.의사결정 트리 코드아래는 의사결정 트리를 구성하는 코드예요. # classification treefrom sklearn.tree import DecisionTreeClassifierclf = DecisionTreeClassifier()clf.fit(xtrain, ytrain)yhat_train = clf.predict(xtrain)yhat_train_prob = clf.predict_proba(xtrain)yhat_test = clf.predict(xtest)yhat_test_prob = clf.predict_proba(xtest)clf.score(xtrain, ytrain)clf.score(xtest, ytest)sklearn.tree에 있는 DecisionTreeClassifier를 임포트 합니다.clf : 의사결정 트리를 의미합니다.clf.fit으로 모델을 학습시킵니다.  clf.predict : 데이터를 테스트합니다.  clf.predict_proba : 데이터 각각에 대한 확률이 주어집니다.  clf.score : 학습 데이터와 테스트 데이터의 정확도를 확인합니다.랜덤 포레스트(Random Forest)랜덤 포레스트는 많은 의사결정 트리로 이루어지는데요. 많은 의사결정 트리로 숲을 만들었을 때 의견 통합이 되지 않는 경우에는 다수결의 원칙을 따라요. 이렇게 의견을 통합하거나 여러 가지 결과를 합치는 방식을 앙상블 기법(Ensemble method)이라고 해요.그럼 랜덤 포레스트의 ‘랜덤’은 어떤 것이 무작위라는 것일까요? 여기에서 ‘랜덤’은 각각의 의사결정 트리를 만드는 데 있어 쓰이는 요소들을 무작위적으로 선정한다는 뜻이에요. 즉 랜덤 포레스트는 같은 데이터에 대해 의사결정 트리를 여러 개를 만들어서 그 결과를 종합하여 예측 성능을 높이는 기법을 말해요. 많은 의사결정 트리로 구성된 랜덤 포레스트의 학습 과정(사진 출처 : 위키백과)랜덤 포레스트 코드아래는 랜덤 포레스트를 구성하는 코드예요.from sklearn.ensemble import RandomForestRegressorrf = RandomForestRegressor(n_estimators=100, random_state=0)rf.fit(xtrain, ytrain)yhat_test = rf.predict(xtest)rf.score(xtrain, ytrain)rf.score(xtest, ytest)sklearn.ensemble에 있는 RandomForestRegressor를 임포트 합니다.  rf : 랜덤 포레스트를 의미합니다.   rf.fit으로 모델을 학습시킵니다.    rf.predict : 데이터를 테스트합니다.    rf.score : 학습 데이터와 테스트 데이터의 정확도를 확인합니다.  이론 수업을 마치며2018년 5월 22일부터 시작한 8주간의 이론 수업이 이로써 마무리가 되었어요!! 매주 3시간 동안 어려운 내용의 수업을 듣는 게 힘들기도 했지만 그만큼 얻은 게 많아서 뿌듯하기도 합니다. 이론 수업과 AI스쿨 후기는 아쉽게도 이번이 마지막이지만, 앞으로 8주간은 팀 프로젝트 과정과 커리어 코칭 과정이 기다리고 있어요! 지금까지 8주간 이론 공부를 열심히 했기 때문에 굉장히 기대가 되네요. 살짝 알려드리면 저희 조는 시각장애인과 청각장애인을 위한 상황 해설 솔루션을 주제로 프로젝트를 진행하려고 해요! 아직 추상적인 부분이 많아 조교님으로부터 피드백을 많이 받게 될 것 같지만 그동안 배운 이론을 적용시켜서 높은 퀄리티로 프로젝트를 완성시키고 싶다는 욕심입니다. :) 이론 수업의 시작과 함께 우연한 기회로  AI스쿨 후기를 쓰게 되었는데요. 수업 내용도 어렵고 글쓰기도 익숙하지 않아 쉽지 않았지만 배운 내용을 최대한 공유하고자 했습니다. 이를 통해서 배운 내용을 복습하고 부족한 부분을 알 수 있어서 무척 뜻깊은 경험이었습니다. 부족하지만 이 글을 읽고 조금이라도 도움이 되었으면 좋겠어요! AI 스쿨이 인공지능 엔지니어를 꿈꾸는 제게 큰 발걸음이 될 수 있도록 앞으로도 저는 프로젝트에 전력을 다할 것 같습니다. 8주 동안 열심히 수업 들으신 수강생 여러분 모두 좋은 결과가 있기를 바랍니다!* 이 글은 AI스쿨 - 인공지능 R&D 실무자 양성과정 8회차 수업에 대해 수강생 최유진님이 작성하신 수업 후기입니다.
조회수 868

P2P금융에서 고도의 엔지니어링이 필수적인 이유

지난 8월30일, 매일경제신문이 주최하고 과학기술정보통신부와 금융위원회, 금융감독원이 후원한 매경핀테크어워드2018에서 렌딧이 최우수상을 수상했다. 렌딧이 굳이 이런 경연대회에 참여를 한 이유는 ‘P2P금융산업에서 기술력과 고도의 엔지니어링 파워가 얼마나 중요한 지’를 널리 알리고 싶기 때문이었다.매경핀테크어워드 수상 소식을 들은 후, 엔지니어링팀 렌딧맨들과최근 렌딧은 개발자 채용에 그 어느때보다도 열심이다. 많은 개발자들과 만나 P2P금융산업의 미래와 우리 회사가 하는 일에 대해 설명하고 좋은 개발자를 영입하기 위해 노력하고 있다. 그런데 생각보다 훨씬 더 개발자들에게 P2P금융기업이 어떤 일을 하고 있고, 왜 개발자가 도전할 만한 분야인지 알려져 있지 않다는 사실을 알게 되었다. 이번 글에서는 렌딧이 하는 일을 바탕으로 P2P금융회사에서 왜 고도의 소프트웨어 엔지니어링이 필수적으로 필요하고, 개발자 여러분이 어떤 일에 도전해 볼 수 있는지에 대해 설명해 보려고 한다. 우선 대출과 투자 등 모든 서비스가 기존 금융회사와 달리 온라인 상에서 이루어진다. 특히 렌딧이 집중하고 있는 개인신용 P2P금융의 경우, 대출 심사와 집행, 투자 모집과 운용 등 서비스 전 과정을 100% 온라인, 비대면 서비스로 구축하고 있는 디지털 금융 플랫폼이다.대출 서비스에서는 머신러닝 기반의 대출자 심사평가모델 개발이 핵심적이다. 렌딧이 자체 개발한 렌딧 개인신용평가시스템(Lendit Credit Scoring System)을 예로 들어 보겠다. 신용평가사에서 제공하는 250여가지의 금융 데이터를 순식간에 분석해 모든 대출 신청자마다 개인화 된 적정금리를 산출해 내는 시스템이다. P2P금융기업인 렌딧이 개발한 심사평가모델을 기존 금융권의 심사평가모델과 비교할 때 가장 큰 차이점은, 머신러닝 기법을 사용해 각종 금융 데이터의 최근 12개월 간 트렌드를 분석한다는 점. 이를 통해 보다 정교하게 개인의 신용을 평가해 낸다. 여기에 추가적으로 신용평가사에서 제공하는 사기정보공유(Fraud Bureau)데이터, 직장 신용정보, 상환 정보 등을 종합적으로 반영하고 있다. 최근에는 대출자가 제출하는 신분증 확인 과정에 머신러닝을 적용해 자동화해 나가기 시작했다. 투자 서비스에서는 실시간으로 분산투자 포트폴리오를 추천해 주는 알고리듬이 돌고 있다. 투자자가 투자할 금액을 입력하면 눈깜짝할 사이에 현재 투자 가능한 채권을 조합해 분산투자 포트폴리오를 추천해 주는 시스템이다. 포트폴리오에 조합된 모든 채권에 투자금을 일정한 비율로 고르게 나누어 분산투자할 수 있도록 추천해 주는 것이 특징이다. 렌딧이 개발한 분산투자 시스템은 투자자 1인이 수백~수천개의 채권에 분산하는 것과 동시에, 채권 1개도 평균 1,303명, 최대 3,814명(기준 2018년 6월30일 현재)이 나누어 리스크를 분산하도록 개발되어 있다. 이렇게 분산투자를 시스템적으로 활성화 시키고 있는 덕분에, 현재까지 렌딧의 모든 투자자가 하고 있는 분산투자의 총 누적 건수는 거의 800만 건에 육박하는 수준이다. 점점 더 많은 데이터가 축적되고 있기 때문에, 이러한 데이터를 바탕으로 고객에게 제공할 수 있는 서비스 아이디어도 하루 하루 쌓여 가고 있는 중이다.P2P금융산업이 가장 발전한 시장인 미국의 경우, 최대 규모인 렌딩클럽 한 회사가 미국 개인신용대출 시장 전체의 약 1.5%이상을 차지할만큼 금융 시장을 혁신해 나가고 있다. 렌딧 역시 지난 3년간 빅데이터 분석에 기반한 정교한 신용평가를 통해, 대출 고객의 이자를 총 100억원이 넘게 절약해 드리는 성과를 만들어 냈다. 그간 기존 금융회사들이 만들어 내지 못한 중금리 대출 시장을 스타트업인 렌딧이 활짝 열어낸 것이다.렌딧에서 우리 렌딧맨들과 함께 한국의 금융을 혁신하는 금융 플랫폼을 만들어 가실 엔지니어 여러분을 기다립니다. 관심있는 분은 주저없이 [email protected] 로 연락 주세요. 많은 엔지니어 여러분과 만나뵙고 싶습니다. 
조회수 3171

Apache Spark에서 컬럼 기반 저장 포맷 Parquet(파케이) 제대로 활용하기 - VCNC Engineering Blog

VCNC에서는 데이터 분석을 위해 다양한 로그를 수집, 처리하는데 대부분은 JSON 형식의 로그 파일을 그대로 압축하여 저장해두고 Apache Spark으로 처리하고 있었습니다. 이렇게 Raw data를 바로 처리하는 방식은 ETL을 통해 데이터를 전처리하여 두는 방식과 비교하면 데이터 관리비용에서 큰 장점이 있지만, 매번 불필요하게 많은 양의 데이터를 읽어들여 처리해야 하는 아쉬움도 있었습니다.이러한 아쉬움을 해결하기 위해 여러 논의 중 데이터 저장 포맷을 Parquet로 바꿔보면 여러가지 장점이 있겠다는 의견이 나왔고, 마침 Spark에서 Parquet를 잘 지원하기 때문에 저장 포맷 변경 작업을 하게 되었습니다. 결론부터 말하자면 74%의 저장 용량 이득, 10~30배의 처리 성능 이득을 얻었고 성공적인 작업이라고 평가하지만 그 과정은 간단하지만은 않았습니다. 그 과정과 이를 통해 깨달은 점을 이 글을 통해 공유해 봅니다.Parquet(파케이)에 대해Parquet(파케이)는 나무조각을 붙여넣은 마룻바닥이라는 뜻을 가지고 있습니다. 데이터를 나무조각처럼 차곡차곡 정리해서 저장한다는 의도로 지은 이름이 아닐까 생각합니다.Parquet을 구글에서 검색하면 이와 같은 마룻바닥 사진들이 많이 나옵니다.빅데이터 처리는 보통 많은 시간과 비용이 들어가므로 압축률을 높이거나, 데이터를 효율적으로 정리해서 처리하는 데이터의 크기를 1/2 혹은 1/3로 줄일 수 있다면 이는 매우 큰 이득입니다. 데이터를 이렇게 극적으로 줄일 수 있는 아이디어 중 하나가 컬럼 기반 포맷입니다. 컬럼 기반 포맷은 같은 종류의 데이터가 모여있으므로 압축률이 더 높고, 일부 컬럼만 읽어 들일 수 있어 처리량을 줄일 수 있습니다.https://www.slideshare.net/larsgeorge/parquet-data-io-philadelphia-2013Parquet(파케이)는 하둡 생태계의 어느 프로젝트에서나 사용할 수 있는 효율적인 컬럼 기반 스토리지를 표방하고 있습니다. Twitter의 “Julien Le Dem” 와 Impala 프로젝트 Lead였던 Cloudera의 “Nong Li”가 힘을 합쳐 개발한 프로젝트로 현재는 많은 프로젝트에서 Parquet를 지원하고 컬럼 기반 포맷의 업계 표준에 가깝습니다.Parquet를 적용해보니 Apache Spark에서는, 그리고 수많은 하둡 생태계의 프로젝트들에서는 Parquet를 잘 지원합니다.val data = spark.read.parquet("PATH") data.write.parquet("PATH") Spark에서는 이런 식으로 손쉽게 parquet 파일을 읽고, 쓸 수가 있습니다. 데이터를 분석하기 전에 원본이라고 할 수 있는 gzipped text json을 읽어서 Parquet 로 저장해두고 (gzipped json은 S3에서 glacier로 이동시켜버리고), 이후에는 Parquet에서 데이터를 읽어서 처리하는 것 만으로도 저장용량과 I/O 면에서 어느 정도의 이득을 얻을 수 있었습니다. 하지만 테스트 결과 저장용량에서의 이득이 gz 23 GB 에서 Parquet 18GB 로 1/3 정도의 저장용량을 기대했던 만큼의 개선이 이루어지지는 않았습니다.Parquet Deep Dive상황을 파악하기 위해 조금 더 조사를 해보기로 하였습니다. Parquet의 포맷 스팩은 Parquet 프로젝트에서 관리되고 있고, 이의 구체적인 구현체로 parquet-mr 이나 parquet-cpp 프로젝트 등에서 스펙을 구현하고 있습니다. 그리고 특별한 경우에는 Spark에서는 Spark 내부에 구현된 VectorizedParquetRecordReader 에서 Parquet 파일을 처리하기도 합니다.파일 포맷이 바뀌거나 기능이 추가되는 경우에는 쿼리엔진에서도 이를 잘 적용해주어야 합니다. 하지만 안타깝게도 Spark은 parquet-mr 1.10 버전이 나온 시점에도 1.8 버전의 오래된 버전의 parquet-mr 코드를 사용하고 있습니다. (아마 다음 릴리즈(2.4.0)에는 1.10 버전이 적용될 것으로 보이지만)Parquet 의 메인 개발자 중에는 Impala 프로젝트의 lead도 있기 때문에, Impala에는 비교적 빠르게 변경사항이 반영되는 것에 비하면 대조적입니다. 모든 프로젝트들이 실시간적으로 유기적으로 업데이트되는 것은 힘든 일이기 때문에 어느 정도는 받아들여야겠지만, 우리가 원하는 Parquet의 장점을 취하기 위해서는 여러 가지 옵션을 조정하거나 직접 수정을 해야 합니다.VCNC 데이터팀에서는 저장 용량과 I/O 성능을 최적화하기 위하여 Parquet의Dictionary encoding (String들을 압축할 때 dictionary를 만들어서 압축하는 방식, 길고 반복되는 String이 많다면 좋은 압축률을 기대할 수 있습니다)Column pruning (필요한 컬럼만을 읽어 들이는 기법)Predicate pushdown, row group skipping (predicate, 즉 필터를 데이터를 읽어 들인 후 적용하는 것이 아니라 저장소 레벨에서 적용하는 기법)과 같은 기능들을 사용하기를 원했고, 이를 위해 여러 조사를 진행하였습니다.저장용량 줄이기102GB의 JSON 포맷 로그를 text그대로 gzip으로 압축하면 23GB가 됩니다. dictionary encoding이 잘 적용되도록 적절한 옵션 설정을 통해 Parquet로 저장하면 6GB로, 기존 압축방식보다 저장 용량을 74%나 줄일 수 있었습니다.val ndjsonDF = spark.read.schema(_schema).json("s3a://ndjson-bucket/2018/04/05") ndjsonDF. sort("userId", "objectType", "action"). coalesce(20). write. options(Map( ("compression", "gzip"), ("parquet.enable.dictionary", "true"), ("parquet.block.size", s"${32 * 1024 * 1024}"), ("parquet.page.size", s"${2 * 1024 * 1024}"), ("parquet.dictionary.page.size", s"${8 * 1024 * 1024}"), )). parquet("s3a://parquet-bucket/2018/04/05") 비트윈의 로그 데이터는 ID가 노출되지 않도록 익명화하면서 8ptza2HqTs6ZSpvmcR7Jww 와 같이 길어지기에 이러한 항목들이 dictionary encoding을 통해 효과적으로 압축되리라 기대할 수 있었고, Parquet에서는 dictionary encoding이 기본이기에 압축률 개선에 상당히 기대하고 있었습니다.하지만 parquet-mr 의 구현에서는 dictionary의 크기가 어느 정도 커지면 그 순간부터 dictionary encoding을 쓰지 않고 plain encoding으로 fallback하게 되어 있었습니다. 비트윈에서 나온 로그들은 수많은 동시접속 사용자들의 ID 갯수가 많기 때문에 dictionary의 크기가 상당히 커지는 상태였고, 결국 dictionary encoding을 사용하지 못해 압축 효율이 좋지 못한 상태였습니다.이를 해결하기 위해, parquet.block.size를 default 값인 128MB에서 32MB로 줄이고 parquet.dictionary.page.size를 default 값 1MB에서 8MB 로 늘려서 ID가 dictionary encoding으로만 잘 저장될 수 있도록 만들었습니다.처리속도 올리기저장용량이 줄어든 것으로도 네트워크 I/O가 줄어들기 때문에 처리 속도가 상당히 올라갑니다. 하지만 컬럼 기반 저장소의 장점을 온전하게 활용하기 위해서 column pruning, predicate pushdown들이 제대로 작동하는지 점검하고 싶었습니다.소스코드를 확인하고 몇 가지 테스트를 해 본 결과, Spark에서는 Parquet의 top level field에서의 column pruning은 지원하지만 nested field들에 대해서는 column pruning을 지원하지 않았습니다. 비트윈 로그에서는 nested field들을 많이 사용하고 있었기에 약간 아쉬웠으나 top level field에서의 column pruning 만으로도 어느 정도 만족스러웠고 로그의 구조도 그대로 사용할 예정입니다.Predicate pushdown도 실행시간에 크게 영향을 줄 거라 예상했습니다. 그런데 Spark 2.2.1기준으로 column pruning의 경우와 비슷하게, top level field에 대해서만 predicate pushdown이 작동하는 것을 확인할 수 있었습니다. 이는 성능에 큰 영향을 미치기에, predicate 로 자주 사용하는 column들을 top level 로 끌어올려 저장하는 작업을 하게 되었습니다. 여기에 추가로 parquet.string.min-max-statistics 옵션을 손보고 나서야 드디어 10~30배 정도의 성능 향상을 볼 수 있었습니다.매일 15분 정도 걸리던 "의심스러운 로그인 사용자" 탐지 쿼리가 30여초만에 끝나고, cs처리를 위해 한 사람의 로그만 볼 때 5분 정도 걸리던 쿼리가 30여초만에 처리되게 되었습니다.못다 한 이야기parquet.string.min-max-statistics 옵션과 row group skipping에 관해.Parquet 같은 포맷 입장에서 string 혹은 binary 필드의 순서를 판단하기는 어렵습니다. 예를 들어 글자 á 와 e 가 있을 때 어느 쪽이 더 작다고 할까요? 사전 편찬자라면 á가 더 작다고 볼 것이고, byte 표현을 보면 á는 162이고 e는 101이라 e가 더 작습니다. Parquet 같은 저장 포맷 입장에서는 binary 필드가 있다는 사실만 알고 있고, 그 필드에 무엇이 저장될지, 예를 들어 á와 e가 저장되는지, 이미지의 blob가 저장되는지는 알 수 없습니다. 그러니 순서를 어떻게 정해야 할지는 더더구나 알 수 없습니다.그래서 Parquet 내부적으로 컬럼의 min-max 값을 저장해 둘 때, 1.x 버전에서는 임의로 byte sequence를 UNSINGED 숫자로 해석해 그 컬럼의 min-max 값을 정해 저장했습니다. 이후에 이를 개선하기 위해 Ryan Blue가 PARQUET-686에서 parquet-format에 SORT_ORDER를 저장할 수 있도록 했습니다.여기에서 문제는 이전 버전과의 호환성입니다. SORT_ORDER가 없던 시절의 Parquet 파일을 읽으려 할 때, min-max 값을 사용해 row group skipping이 일어나면 query 엔진에서 올바르지 않은 결과가 나올 수 있으니, binary 필드의 min-max 값을 parquet-mr 에서 아예 반환하지 않게 되어있습니다.하지만 이는 우리가 원하는 동작이 아닙니다. 여기에 parquet.string.min-max-statistics option을 true로 설정하면, 이전처럼 binary필드의 min-max값을 리턴하게 되고 rowgroup skipping이 작동하여 쿼리 성능을 크게 올릴 수 있습니다.마치며Spark과 Parquet 모두 많은 사람이 사랑하는 훌륭한 오픈소스 프로젝트입니다. 또한 별다른 설정이나 튜닝 없이 기본 설정만으로도 잘 돌아가는 편이기 때문에 더더욱 많은 사람이 애용하는 프로젝트이기도 합니다.하지만 오픈소스는 완전하지 않습니다. 좋은 엔지니어링 팀이라면 단지 남들이 많이 쓰는 오픈소스 프로젝트들을 조합해서 사용하는 것에서 그치지 않고 핵심 원리와 내부 구조를 연구해가며 올바르게 활용해야 한다고 생각합니다. 기술의 올바른 활용을 위해 비트윈 데이터팀은 오늘도 노력하고 있습니다.

기업문화 엿볼 때, 더팀스

로그인

/