스토리 홈

인터뷰

피드

뉴스

조회수 1179

EOS Smart Contract 를 위한 준비

EOS Smart Contract 를 위한 준비와 토큰 발행 그리고 C++를 활용해 토큰의 간단한 기능을 개발해 보겠습니다.환경 구성 및 지갑 생성은 SAM 님의 아래 2글을 참고해 주시기 바립니다.EOS — 설치 및 실행 (1/2)EOS — 동작구조 및 환경설정(2/2)지갑 생성하기SAM 님의 포스트를 참고 하셨다면 아마 다음과 같이 ‘default’ (별도의 이름을 지정하지 않았을 시) 지갑을 생성 하셨을 겁니다.이 지갑을 사용하여 계정을 Create 한 후 Key 를 Import 하겠습니다.Key 생성하기$ cleos create key위 명령을 실행 하시면 다음과 같은 화면을 얻을 수 있습니다.create key 명령의 결과**주의 : Private Key는 Public Key의 소유를 증명하는 중요한 개념으로 절대 타인에게 노출하면 안됩니다.AdditionalKey 생성 후 지갑에 import 하기 귀찮으시다면 생성된 지갑에서 바로 Key 를 생성하셔도 됩니다.$ cleos wallet create_key위와같이 key가 생성 됩니다. 하지만 public key 만 보이기 때문에 하단 명령 입력 후 지갑 key를 입력하면 private key를 확인할 수 있습니다.$ cleos wallet private_keys지갑에 Key import하기지갑은 Public Key — Private Key를 저장하는 저장소 입니다. 생성된 키를 지갑에 저장하기 위해 다음과 같은 명령어를 입력합니다.$ cleos wallet import-n : 옵션을 사용하면 지갑의 이름을 지정합니다. 지정하지 않는다면 기본 생성된 default 지갑으로 지정됩니다.위 명령을 입력 하면 key 가 임포트 되었다는 결과를 확인 할 수 있습니다.** 만약 지갑을 Unlock 한 상태가 아니라면 ‘private key: Error 3120003: Locked wallet’ Exception 이 나옵니다.unlock 을 위해 다음 명령을 실행한 후 wallet 생성시 저장했던 Key를 입력하여 Unlocked 상태로 만들어 줍니다.$ cleos wallet unlock password: Unlocked: default(Optional) 지갑에 저장된 Key 리스트 확인다음 명령어를 입력하여 지갑에 key 가 잘 import 됐는지 확인합니다.$ cleos wallet keys계정 생성eosio.token 이라는 이름으로 계정을 생성하도록 하겠습니다.** 지갑과 Key 그리고 계정에 관해서는 Hexlant 미디움에 게재될 예정입니다.$ cleos create account eosio eosio.token EOS63kstp8kthzJY3rAotp1LAxUDbWk4MywReG578R2ddbktrDHYKcreator : eosioaccount name : eosio.tokenowner key : 지갑에 import 된 keyAdditional본 포스팅은 local 환경에서 빌드 후 System Contract 들이 적용되지 않은 상황을 가정하였습니다. 만약 Public Network 환경에서 접속 시 eosio 와 eosio.token을 사용할 수 없습니다.또한 계정이름은 다음과 같은 규칙을 따릅니다.- 12문자- 12345abcdefghijklmnopqrstuvwxyz 만 사용 가능** 만약 ‘Error 3090003: provided keys, permissions, and delays do not satisfy declared authorizations’ 에러 발생 시 eosio 에 대한 key 를 지갑에 import 해야 합니다.eosio 에 대한 정보는 다음과 같습니다.public key: EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CVprivate key: 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3위 과정을 모두 마쳤다면, EOS 지갑과 키 그리고 계정에 대한 권한을 모두 가지고 있는 상태가 됩니다. 다음 포스팅에서는 이 계정을 사용 하여 Token 을 발행하는 방법을 알아보도록 하겠습니다.감사합니다#헥슬란트 #HEXLANT #블록체인 #개발자 #개발팀 #기술기업 #기술중심
조회수 1128

어제의 실수는 오늘의 노하우!

Overview서비스되는 프로젝트에 첫 커밋(Commit)했던 순간이 아직도 생생합니다. 직원이 10명 남짓이던 시절, 특정 데이터를 삭제할 때나 쓰던 관리자 페이지였는데요. 당시엔 MVC Pattern, Transaction 등 아무것도 몰랐기 때문에 실수를 반복했습니다. (팀장님으로부터 피드백도 많이 받았죠.) 어떤 실수였는지 궁금하시죠? 오늘은 두 번 다시 겪고 싶지 않은 실수들과 깨달은 몇 가지 이야기와 개발자가 꼭 지켜야할 것을 소개하겠습니다. 사용자를 생각하는 마음예전에는 로직을 짤 때 실패하는 케이스를 깊게 생각하지 않았습니다. 왜냐하면 “나는 기능을 만들고, 사용자는 내가 만든 기능을 쓴다.”고 생각했기 때문입니다. 요구 사항대로 동작하게 만들고, 예외 케이스는 사용자의 책임으로 돌렸습니다. 하지만 이런 태도로 개발하면 UI/UX는 발전할 수 없고, 서비스도 개선될 수 없으며, 사용자의 불만만 생긴다는 걸 곧 알게 되었죠. 작년 이맘때쯤 브랜디 앱에 진열될 상품 관리 페이지를 개발했습니다. 요건에 기재된 내용을 요약하면 아래와 같았습니다.제시된 요건등록 가능한 상품의 개수는 ‘무제한’이다.하나의 페이지에 여러 구좌를 관리하는 영역이 들어갔으면 좋겠다.상품 조회 화면에는 ‘누적 판매량’과 ‘7일 판매량’ 항목이 추가되어야 한다.우선 ‘무제한’이라는 단어에 각 관리 영역마다 max-height를 지정했는데요. 여러 관리 영역이 하나의 페이지에 들어가더라도 스크롤을 많이 하지 않아도 되게 작업했습니다. 이뿐만이 아닙니다. 중복된 상품을 등록할 수도 있기 때문에 그것에 대한 유효성도 추가했죠. 하지만 막상 프로덕션(production)에 배포되니 직원들의 피드백이 쏟아졌습니다.“상품을 등록하고 다시 관리 페이지에 진입하려니 시간이 오래 걸려요.”“상품이 중복됐다고 alert이 뜨는데 어떤 상품이 겹치는지 알 수는 없나요? 혹시… 일일이 찾아야 해요?” 2)“상품 setting 후에 등록을 했는데 다시 보니 안 되어있어요!”“아뿔싸, ’무제한’이라는 단어를 보고 max-height 값만 떠올리다니!” 드러난 이슈들을 수정하면서 반성하고 또 반성했습니다. 등록된 상품들을 가져와서 페이지에 렌더링(rendering)할 때, 상품 수가 많을수록 뷰 페이지의 로딩 속도는 느려진다는 걸 예측하지 않았습니다. 심지어 하나의 페이지에 여러 구좌를 관리할 수 있도록 개발했으니, 불러와야 할 상품은 수백, 수천 개였을 겁니다. 직원들은 하염없이 페이지만 바라보며 불만을 터트릴 수밖에 없었고요. 이후엔 페이지에 진입하자마자 상품 목록을 가져오지 않고, 특정 버튼을 눌렀을 때 ajax로 상품을 로딩하는 방식으로 개선했습니다.당시 개발했던 진열 관리 화면상품 등록이 잘 안 된다는 이슈는 로컬(local) 및 스테이징(staging) 서버에서 재현되지 않아 고개를 갸웃거렸는데요. 프로덕션(production) 정보를 보고 나서야 원인을 잡을 수 있었습니다. ajax를 이용해 POST로 전송할 수 있는 array의 최대 사이즈가 정해져 있다는 걸 알게 된 것이죠.1) 결국 JSON 형태로 바꾸어 데이터를 전송하고, 서버사이드에서 배열을 다시 변환해 로직을 수행하도록 개선했습니다. 팀장님의 질문도 기억에 남습니다. 팀장님은 단호하게 물었죠.“쿼리 돌아가는 건 확인했어?”일정이 급급하다는 이유로 쿼리를 확인하는 과정을 간과했습니다. 데이터는 당연히 0건으로 나왔지만 조건에 부합하는 데이터가 없어서인지, 잘못된 질의 때문인지는 의심하지 않았던 것이죠. 팀장님은 말했습니다.“네가 자꾸 실수하면 사용자는 우리 시스템을 신뢰할 수 없을 거야.”PRODUCT_REGIST_DATETIME BETWEEN NOW() AND NOW() - 7 나 : 7일동안 등록된 상품 데이터를 가져와주세요.데이터베이스 : …???주위를 관심 있게 둘러보는 눈지난 번에 쓴 신입개발자를 위한 코드의 정석을 보면 ‘모든 개발조직은 좋은 품질의 소프트웨어를 개발할 수 있는 개발자를 원한다’는 문장이 있습니다. 좋은 품질과 가치 있는 서비스를 만드는 건 개발자가 당연히 가져야 할 책임과 소신입니다. 서비스에 대한 이해도 어느 정도 필요하고요. 그렇지 않으면 엉뚱한 서비스가 나옵니다.재작년, 브랜디 커머스 웹 1.0 버전을 개발했을 땐 e-commerce에 대한 이해도가 거의 없었습니다. 유사한 서비스들의 레퍼런스를 진행하고 개발을 시작해야 했는데 그저 상상력에 의존한 채 UI/UX 개발을 진행했었습니다. 그때 느꼈던 걸 몇 가지 정리해보겠습니다. 유사한 서비스를 적극적으로 사용하자!사람들은 많이 쓰는 서비스의 UI/UX에 익숙합니다. 그러므로 유명하면서도 비슷한 목적을 수행하는 다른 서비스들을 사용해보세요. 그 분야에 대한 센스가 무럭무럭 커질 겁니다. 더 나아가서는 사람들이 익숙하다고 느끼는 것보다 훨씬 더 편한 UI/UX를 떠올릴 수도 있겠지요!다른 개발자의 생각도 물어보자!같은 문장을 보고도 다르게 해석하듯, 같은 서비스를 개발하는 개발자들도 저마다 솔루션은 다릅니다. 자신은 괜찮다고 생각하더라도 다른 개발자에게 꼭 물어보세요. 미처 생각하지 못했던 의견들이 나올 수 있습니다. 즉, 많은 커뮤니케이션이 더 좋은 개발을 돕는 것이죠.개발하기 쉬운 서비스 말고, 사용자가 쓰기 편한 서비스로 만들자!일정에 쫓기면 당장 개발하기 편한 방법을 선호할 수도 있습니다. 개발자의 주관적인 판단이 UI/UX를 망칠 수 있는데도 말이죠. 실수는 자신이 만회해야 합니다. 눈앞의 것을 생각하지 말고, 사용자를 생각하며 개발합시다. 사용자가 기분 좋게 서비스를 이용하는 게 훨씬 뿌듯하잖아요. Conclusion무수한 실패담 중에 기억나는 몇 가지만 추렸습니다. 과거의 코드나 실수의 이력들을 글로 써 보니 ‘전부 내 경험이 되었구나’라는 생각이 듭니다. 지금 이 글을 읽고 있는 당신은 어떤 실수를 해보셨나요? 손해 보는 경험은 없습니다. 분명 언젠가는 도움이 될 거예요. 주석1)이 때문에 상품을 등록할 때, 스크립트에서 array로 담아 전송하면 데이터가 누락되어 제대로 등록되지 않거나 에러가 발생할 수 있는 결함이 있었다.2)중복된 상품을 화면에 표시해주는 기능은 여러 상황으로 인해 개선하지 못했다. 이후에는 발생하는 문제의 사유를 사용자에게 친절히 알려주어서 원하는 결과를 얻도록 힘쓰고 있다. 참고개발자는 개발만 잘하면 된다?사용자는 결코 실수하지 않는다글김우경 대리 | R&D 개발1팀[email protected]브랜디, 오직 예쁜 옷만#브랜디 #개발문화 #개발팀 #업무환경 #인사이트 #경험공유
조회수 1564

VCNC가 Hadoop대신 Spark를 선택한 이유

요즘은 데이터 분석이 스타트업, 대기업 가릴 것 없이 유행입니다. VCNC도 비트윈 출시 때부터 지금까지 데이터 분석을 해오고 있고, 데이터 기반의 의사결정을 내리고 있습니다.데이터 분석을 하는데 처음부터 복잡한 기술이 필요한 것은 아닙니다. Flurry, Google Analytics 등의 훌륭한 무료 툴들이 있습니다. 하지만 이러한 범용 툴에서 제공하는 것 이상의 특수하고 자세한 분석을 하고 싶을 때 직접 많은 데이터를 다루는 빅데이터 분석을 하게 됩니다. VCNC에서도 비트윈의 복잡한 회원 가입 프로세스나, 채팅, 모멘츠 등 다양한 기능에 대해 심층적인 분석을 위해 직접 데이터를 분석하고 있습니다.빅데이터 분석 기술큰 데이터를 다룰 때 가장 많이 쓰는 기술은 Hadoop MapReduce와 연관 기술인 Hive입니다. 구글의 논문으로부터 영감을 받아 이를 구현한 오픈소스 프로젝트인 Hadoop은 클러스터 컴퓨팅 프레임웍으로 비싼 슈퍼컴퓨터를 사지 않아도, 컴퓨터를 여러 대 연결하면 대수에 따라서 데이터 처리 성능이 스케일되는 기술입니다. 세상에 나온지 10년이 넘었지만 아직도 잘 쓰이고 있으며 데이터가 많아지고 컴퓨터가 저렴해지면서 점점 더 많이 쓰이고 있습니다. VCNC도 작년까지는 데이터 분석을 하는데 MapReduce를 많이 사용했습니다.주스를 만드는 과정에 빗대어 MapReduce를 설명한 그림. 함수형 프로그래밍의 기본 개념인 Map, Reduce라는 프레임을 활용하여 여러 가지 문제를 병렬적으로 처리할 수 있다. MapReduce slideshare 참조MapReduce는 슈퍼컴퓨터 없이도 저렴한 서버를 여러 대 연결하여 빅데이터 분석을 가능하게 해 준 혁신적인 기술이지만 10년이 지나니 여러 가지 단점들이 보이게 되었습니다. 우선 과도하게 복잡한 코드를 짜야합니다. 아래는 간단한 Word Count 예제를 MapReduce로 구현한 것인데 매우 어렵고 복잡합니다.MapReduce로 단어 갯수를 카운트하는 간단한 예제 (Java). 많은 코드를 작성해야 한다.이의 대안으로 SQL을 MapReduce로 변환해주는 Hive 프로젝트가 있어 많은 사람이 잘 사용하고 있지만, 쿼리를 최적화하기가 어렵고 속도가 더 느려지는 경우가 많다는 어려움이 있습니다.MapReduce의 대안으로 최근 아주 뜨거운 기술이 있는데 바로 Apache Spark입니다. Spark는 Hadoop MapReduce와 비슷한 목적을 해결하기 위한 클러스터 컴퓨팅 프레임웍으로, 메모리를 활용한 아주 빠른 데이터 처리가 특징입니다. 또한, 함수형 프로그래밍이 가능한 언어인 Scala를 사용하여 코드가 매우 간단하며, interactive shell을 사용할 수 있습니다.Spark으로 단어 개수를 카운트하는 간단한 예제 (Scala). MapReduce에 비해 훨씬 간단하다.Spark과 MapReduce의 성능 비교. I/O intensive 한 작업은 성능이 극적으로 향상되며, CPU intensive 한 작업의 경우에도 효율이 더 높다. (자료: RDD 논문)Apache Spark는 미국이나 중국에서는 현재 Hadoop을 대체할만한 기술로 급부상하고 있으며, 국내에도 최신 기술에 발 빠른 사람들은 이미 사용하고 있거나, 관심을 갖고 있습니다. 성능이 좋고 사용하기 쉬울 뿐 아니라, 범용으로 사용할 수 있는 프레임웍이기에 앞으로 더 여러 분야에서 많이 사용하게 될 것입니다. 아직 Spark를 접해보지 못하신 분들은 한번 시간을 내어 살펴보시길 추천합니다.기존의 데이터 분석 시스템 아키텍처기존의 데이터 분석 시스템 아키텍처기존의 시스템은 비용을 줄이기 위해 머신들을 사무실 구석에 놓고 직접 관리했으며, AWS S3 Tokyo Region에 있는 로그를 다운받아 따로 저장한 뒤, MapReduce로 계산을 하고 dashboard를 위한 사이트를 따로 제작하여 운영하고 있었습니다.이러한 시스템은 빅데이터 분석을 할 수 있다는 것 외에는 불편한 점이 많았습니다. 자주 고장 나는 하드웨어를 수리하느라 바빴고, 충분히 많은 머신을 확보할 여유가 없었기 때문에 분석 시간도 아주 오래 걸렸습니다. 그리고 분석부터 시각화까지 과정이 복잡하였기 때문에 간단한 것이라도 구현하려면 시간과 노력이 많이 들었습니다.Spark과 Zeppelin을 만나다이때 저희의 관심을 끈 것이 바로 Apache Spark입니다. MapReduce에 비해 성능과 인터페이스가 월등히 좋은 데다가 0.x 버전과는 달리 1.0 버전에서 많은 문제가 해결되면서 안정적으로 운영할 수 있어 비트윈 데이터 분석팀에서는 Spark 도입을 결정했습니다.Apache Zeppelin은 국내에서 주도하고 있는 오픈소스 프로젝트로써, Spark를 훨씬 더 편하고 강력하게 사용할 수 있게 해주는 도구입니다. 주요한 역할은 노트북 툴, 즉 shell에서 사용할 코드를 기록하고 재실행할 수 있도록 관리해주는 역할과 코드나 쿼리의 실행 결과를 차트나 표 등으로 시각화해서 보여주는 역할입니다. VCNC에서는 Zeppelin의 초기 버전부터 관심을 가지고 살펴보다가, Apache Spark를 엔진으로 사용하도록 바뀐 이후에 활용성이 대폭 좋아졌다고 판단하여 데이터 분석에 Zeppelin을 도입하여 사용하고 있고, 개발에도 참여하고 있습니다.또한, 위에서 언급한 하드웨어 관리에 드는 노력을 줄이기 위해서 전적으로 클라우드를 사용하기로 함에 따라서1 아래와 같은 새로운 구조를 가지게 되었습니다.새로운 데이터 분석 시스템 아키텍처새로운 데이터 분석 시스템 아키텍처새로운 데이터 분석 시스템은 아키텍처라고 하기에 다소 부끄러울 정도로 간단합니다. 애초에 전체 시스템 구성을 간단하게 만드는 것에 중점을 두었기 때문입니다. 대략적인 구성과 활용법은 아래와 같습니다.모든 서버는 AWS 클라우드를 이용수 대의 Zeppelin 서버, 수 대의 Spark 서버운영Spark 서버는 메모리가 중요하므로 EC2 R3 instance 사용로그는 별도로 저장하지 않고 서비스 서버에서 S3로 업로드하는 로그를 곧바로 가져와서 분석함중간 결과 저장도 별도의 데이터베이스를 두지 않고 S3에 파일로 저장Zeppelin의 scheduler 기능을 이용하여 daily batch 작업 수행별도의 dashboard용 Zeppelin을 통해 중간 결과를 시각화하며 팀에 결과 공유이렇게 간단한 구조이긴 하지만 Apache Spark와 Apache Zeppelin을 활용한 이 시스템의 능력은 기존 시스템보다 더 강력하고, 더 다양한 일을 더 빠르게 해낼 수 있습니다.기존현재일일 배치 분석코드 작성 및 관리가 어려움Zeppelin의 Schedule 기능을 통해 수행 Interactive shell로 쉽게 데이터를 탐험 오류가 생긴 경우에 shell을 통해 손쉽게 원인 발견 및 수정 가능Ad-hoc(즉석) 분석복잡하고 많은 코드를 짜야 함분석 작업에 수 일 소요Interactive shell 환경에서 즉시 분석 수행 가능Dashboard별도의 사이트를 제작하여 운영 관리가 어렵고 오류 대응 힘듦Zeppelin report mode 사용해서 제작 코드가 바로 시각화되므로 제작 및 관리 수월성능일일 배치 분석에 약 8시간 소요메모리를 활용하여 동일 작업에 약 1시간 소요이렇게 시스템을 재구성하는 작업이 간단치는 않았습니다. 이전 시스템을 계속 부분적으로 운영하면서 점진적으로 재구성 작업을 하였는데 대부분 시스템을 옮기는데 약 1개월 정도가 걸렸습니다. 그리고 기존 시스템을 완전히 대체하는 작업은 약 6개월 후에 종료되었는데, 이는 분석 성능이 크게 중요하지 않은 부분들에 대해서는 시간을 두고 여유 있게 작업했기 때문이었습니다.Spark와 Spark SQL을 활용하여 원하는 데이터를 즉석에서 뽑아내고 공유하는 예제Zeppelin을 활용하여 인기 스티커를 조회하는 dashboard 만드는 예제결론비트윈 데이터 분석팀은 수개월에 걸쳐 데이터 분석 시스템을 전부 재구성하였습니다. 중점을 둔 부분은빠르고 효율적이며 범용성이 있는 Apache Spark, Apache Zeppelin을 활용하는 것최대한 시스템을 간단하게 구성하여 관리 포인트를 줄이는 것두 가지였고, 그 결과는 매우 성공적이었습니다.우선 데이터 분석가 입장에서도 관리해야 할 포인트가 적어져 부담이 덜하고, 이에 따라 Ad-hoc분석을 수행할 수 있는 시간도 늘어나 여러 가지 데이터 분석 결과를 필요로 하는 다른 팀들의 만족도가 높아졌습니다. 새로운 기술을 사용해 본 경험을 글로 써서 공유하고, 오픈소스 커뮤니티에 기여할 수 있는 시간과 기회도 생겼기 때문에 개발자로서 보람을 느끼고 있습니다.물론 새롭게 구성한 시스템이 장점만 있는 것은 아닙니다. 새로운 기술들로 시스템을 구성하다 보니 세세한 기능들이 아쉬울 때도 있고, 안정성도 더 좋아져야 한다고 느낍니다. 대부분 오픈소스 프로젝트이므로, 이러한 부분은 적극적으로 기여하여 개선하여 나갈 계획입니다.비트윈 팀에서는 더 좋은 개발환경, 분석환경을 위해 노력하고 있으며 이는 더 좋은 서비스를 만들기 위한 중요한 기반이 된다고 생각합니다. 저희는 항상 좋은 개발자를 모시고 있다는 광고와 함께 글을 마칩니다.연관 자료: AWS 한국 유저 그룹 - Spark + S3 + R3 을 이용한 데이터 분석 시스템 만들기↩저희는 언제나 타다 및 비트윈 서비스를 함께 만들며 기술적인 문제를 함께 풀어나갈 능력있는 개발자를 모시고 있습니다. 언제든 부담없이 [email protected]로 이메일을 주시기 바랍니다!
조회수 1727

변화하는 웹 플랫폼 따라가기

플랫폼이 어떻게 변화해가는지를 살펴보면, 직접 경험해보지 않더라도 사람들의 문제의식이 어디에 있는지, 어떤 문제들이 언제부터 풀리게 되는지를 파악할 수 있습니다.이 글에서는 이런 변화들을 살펴볼 수 있는 몇가지 유용한 링크들을 소개하려고 합니다.어떤 논의들이 어디서 오가고 있을까WICG discoursehttps://discourse.wicg.io/WICG는 웹 인큐베이터 커뮤니티 그룹이라고 해서, 웹 표준에 기여해본 사람이 아니라도 토론을 통해 아이디어를 발전시켜서 사람들이 실제로 겪는 문제를 W3C 표준까지 끌어올리는 것을 목표로 하는 커뮤니티입니다.이 곳에서는 주로 CSS, DOM API에 대한 아이디어가 올라옵니다.ES-Discusshttps://esdiscuss.org/ES-Discuss는 WICG와 비슷하게 ECMAScript 스펙에 대해서 논의하는 메일링 리스트입니다. 위 링크는 메일링 리스트에서 오간 이야기를 쉽게 조회할 수 있도록 아카이빙 해놓은 사이트입니다.논의된 아이디어는 어디서 표준으로 다듬어지고 있을까HTML: https://github.com/w3c/html 또는 https://github.com/whatwg/htmlCSS: https://github.com/w3c/csswg-draftsJS: https://github.com/tc39/ecma262HTML은 W3C의 WebPlat WG와 WHATWG에서, CSS는 W3C의 CSSWG에서, JS는 ECMA의 TC39에서 표준을 이끌고 있습니다.위 저장소들에 공개된 초안은 표준이 되기까지 여러 단계를 거치게 되는데, 여기서 다루지는 않겠습니다. (2018-01-25 수정) 이에 대한 내용은 다음의 블로그 포스트에서 자세히 설명하고 있습니다.W3C 표준화 제정 단계ECMAScript와 TC39다듬어진 표준은 어떤 브라우저에서 얼마나 구현되고 있을까다음의 링크에서 각 주제에 대해 브라우저들이 현재 어디까지 구현을 했는지 파악할 수 있습니다.Chrome: https://www.chromestatus.com/featuresFirefox: https://platform-status.mozilla.org/Edge: https://developer.microsoft.com/en-us/microsoft-edge/platform/status/Safari: https://webkit.org/status/크롬 플랫폼 사이트의 경우 각 주제들이 어떤 버전에 반영되었는지 같이 확인할 수 있어서 편리합니다.나머지 브라우저들의 버전별 구현 상태는 https://caniuse.com/에서 주제를 검색하여 참고할 수 있습니다.구현된 기능들은 언제부터 사용할 수 있었고, 언제부터 사용할 수 있게 될까크롬과 파이어폭스는 릴리즈 캘린더를 공개적으로 관리하고 있습니다. 위에서 확인한 기능들을 담고있는 안정 버전이 언제쯤 릴리즈 될 지 다음의 링크를 보고 대략적으로 예상할 수 있습니다.Chrome: https://www.chromium.org/developers/calendarFirefox: https://wiki.mozilla.org/RapidRelease/Calendar크롬과 관련된 플랫폼 따라가기특정 크롬 버전이 어떤 V8 버전을 사용하고 있는지는 https://omahaproxy.appspot.com/에서 확인할 수 있습니다.Node.jsNode.js의 릴리즈 스케쥴은 https://github.com/nodejs/Release에서 확인할 수 있습니다.어떤 Node.js 버전이 어떤 V8 버전을 사용하고 있는지는 https://nodejs.org/en/download/releases/에서 확인할 수 있습니다.Electronhttps://electronjs.org/에서 일렉트론 최신버전이 어떤 노드, 크로미움, V8 버전을 사용하고 있는지 확인할 수 있습니다.일렉트론의 크로미움 팔로업은 깃헙 일렉트론 저장소의 Projects에서 확인할 수 있습니다: https://github.com/electron/electron/projects따라가는데 도움이 되는 블로그브라우저 벤더들이 직접 운영하는 블로그를 구독하면 웹 플랫폼의 소식을 가장 빠르게 접할 수 있습니다.ChromeChromium Blog: https://blog.chromium.org/V8 Blog: https://v8project.blogspot.kr/FirefoxMozilla Hacks: https://hacks.mozilla.org/SafariWebKit Blog: https://webkit.org/blog/EdgeMicrosoft Edge Dev Blog: https://blogs.windows.com/msedgedev/(2018-01-25 수정): @SaschaNaz님 제보로 Webkit status 사이트와 Edge 블로그 추가#스포카 #개발팀 #개발자 #인사이트 #업무일지 #후기
조회수 1817

서비스 중단 없이 Amazon EKS로 옮긴 이야기 - VCNC Engineering Blog

Amazon EKS는 AWS의 관리형 Kubernetes 서비스입니다. 2017년 11월 AWS re:Invent에서 프리뷰 버전이 출시되었고, 2018년 6월에 상용(GA) 버전이 미국 리전에만 출시되었습니다. 그래서 서울 리전을 사용해야 했던 타다 프로젝트에서는 Kubernetes 클러스터를 직접 kops로 설치하여 운영할 수 밖에 없었습니다.2019년 1월, 오랜 기다림 끝에 드디어 서울 리전에 EKS가 출시되어 기쁜 마음으로 EKS로 옮겨가게 되었습니다. 이 글에서는 직접 구축한 클러스터 대비 EKS의 특징에는 어떤 것이 있는지 살펴보고, 서비스 중단 없이 EKS로 옮기기 위한 전략을 공유하고자 합니다.EKS 서울 리전 출시를 염원하던 한국인(?)들EKS는 뭐가 다른가요?AWS에서 마스터 노드를 관리해줍니다.Kubernetes 클러스터는 마스터 노드와 워커 노드로 구성되어 있습니다. EKS는 이 중에서 마스터 노드를 직접 EC2로 띄울 필요 없이 AWS에서 관리해주는 서비스입니다. RDS를 사용할 때 직접 DB 인스턴스를 생성하지 않는 것과 비슷합니다. 별도의 설정 없이도 알아서 여러 가용 영역에 마스터 노드를 실행하여 HA(고가용성) 구성을 해주고, 비정상 마스터 노드를 자동으로 감지하고 교체합니다. 또한 자동화된 버전 업그레이드 및 패치를 지원합니다. EKS를 사용하더라도 워커 노드는 직접 EC2 인스턴스를 생성·관리해야 합니다.EKS 클러스터의 요금은 2019년 2월 현재 시간당 $0.20입니다. 타다에서는 기존에 t2.medium 3대를 마스터 노드로 사용하고 있었기 때문에 관리를 직접 하지 않는 대신 비용이 약간 증가하게 되었습니다.AWS IAM 기반 인증을 사용합니다.VCNC에서는 기존에 Kubernetes API에 접속할 때 가장 간단한 basic auth 인증 방식을 사용했습니다. 그 대신 외부 네트워크에서 접근할 수 없게 해두고 필요한 경우 Bastion 호스트를 통해 SSH 터널링하여 접속했습니다.EKS의 API 서버는 인터넷에 노출되어 있으며, 별도로 네트워크 접근 제한 설정을 할 수 없고 AWS IAM으로 사용자를 인증합니다. (물론 공개망에 노출되어 있으면 Kubernetes API 서버에 보안 취약점이 발견되는 경우 안전하지 않을 수 있는 단점이 있습니다. 앞으로 PrivateLink가 지원되면 해결될 것입니다.)IAM은 인증에만 사용되고, 특정 작업을 할 수 있는 권한은 Kubernetes 기본 RBAC로 관리됩니다. IAM 사용자나 역할을 RBAC 그룹에 매핑할 수 있습니다.EKS 인증 흐름도워커 노드 당 Pod 개수 제한이 있습니다.예를 들어 c5.large 인스턴스에는 29개의 Pod을 띄울 수 있습니다. (표 참고) 그러므로 기존 클러스터에서 노드 당 Pod이 몇 개나 되는지 미리 확인할 필요가 있습니다. 왜 이런 제약이 있을까요?Kubernetes에서는 네트워킹 플러그인으로 Pod 사이에 네트워크 통신하는 방식을 다양하게 설정할 수 있습니다. EKS는 기본적으로 amazon-vpc-cni-k8s를 사용합니다. 이 네트워킹 플러그인은 VPC 상에서 유효한 실제 IP를 Pod에 할당합니다.그러기 위해서는 하나의 EC2 인스턴스에서 여러 개의 IP를 받아와야 하고, 이를 위해 추가적인 네트워크 인터페이스(ENI)를 붙입니다. 그런데 인스턴스 타입에 따라 추가할 수 있는 ENI 수와 ENI 당 IP 수에 제한이 있습니다. 따라서 이 제한이 워커 노드 하나에 띄울 수 있는 Pod 개수 제한이 됩니다.flannel 등 오버레이 네트워크 기반의 다른 네트워크 플러그인을 사용하면 이러한 제약을 피할 수 있습니다. 하지만 EKS에서 기본 제공하는 방법을 그대로 사용하는 것이 좋고, Pod을 엄청나게 많이 띄워야 하는 상황이 아니어서 시도하지 않았습니다.EKS로 중단 없이 넘어가기개요타다의 Kubernetes 클러스터에서 돌아가는 서비스들은 모두 영속적인(persistent) 상태를 가지고 있지 않습니다. 따라서 EKS 클러스터 위에 동일한 서비스를 띄우고 외부 트래픽을 옮겨주기만 하면 특별히 데이터를 옮기지 않고도 이전이 가능했습니다. 또한 거의 대부분의 Kubernetes 리소스는 Helm 차트로 생성한 것이기 때문에 새로운 클러스터에 동일한 서비스를 띄우는 작업도 쉽게 할 수 있었습니다.이전 작업은 다음과 같은 순서로 진행했습니다.EKS 클러스터를 만들고 워커 노드를 생성모든 서비스 다시 설치트래픽을 새 클러스터로 보내기이전 클러스터 제거EKS 클러스터를 만들고 워커 노드를 생성타다의 AWS 환경은 거의 모두 Terraform으로 정의되어 관리되고 있습니다. EKS 클러스터와 워커 노드도 HashiCorp Learn의 문서를 참고해서 Terraform으로 생성했습니다. 해당 문서에 설명이 잘 되어 있어서 거의 그대로 따라할 수 있었습니다.EKS 클러스터 설정은 재사용 가능하도록 Terraform 모듈로 만들었습니다. 덕분에 테스트용 클러스터와 실서비스용 클러스터를 동일한 모듈로 변수만 바꿔서 설정할 수 있었습니다.모든 서비스 다시 설치타다의 Kubernetes 리소스는 Helm 차트로 관리되고 있어서 기존 차트를 거의 그대로 설치할 수 있었습니다. 사용자에게 직접적인 영향을 덜 주는 워커 서비스를 먼저 설치해서 제대로 동작하는 것을 확인한 뒤, 마지막으로 프론트엔드 서비스를 설치하였습니다.트래픽을 새 클러스터로 보내기타다의 모든 트래픽은 NLB로 들어온 뒤 NGINX를 거쳐 다시 적절한 Pod에 라우팅됩니다. 그러므로 타다의 모든 도메인은 NLB를 가리키고 있습니다.타다는 Route 53을 DNS 서버로 사용합니다. Route 53에는 가중치 기반 DNS 레코드를 설정할 수 있습니다. 이를 이용하여 일부 트래픽만 새 클러스터의 NLB로 보낼 수 있습니다. 처음에는 아주 적은 트래픽만 새 클러스터로 보내다가 문제 없이 작동하는 것을 확인한 다음 조금씩 트래픽을 늘려나갔습니다.DNS 가중치 설정으로 일부 트래픽만 새 클러스터의 NLB로 보낼 수 있습니다.DNS 설정에서 이전 클러스터로 가는 레코드를 완전히 제거한 뒤에도, DNS 캐시 등의 이유로 일부 클라이언트가 이전 클러스터에 접속할 수도 있습니다. 따라서, 이전 클러스터 NLB에 새 클러스터의 노드들을 붙여서 아직 DNS를 따라오지 못한 클라이언트들의 요청을 처리하였습니다.이전 클러스터 제거가장 신나면서 조심해야 하는 작업입니다. 먼저 이전 클러스터로 트래픽이 전혀 들어오지 않는 것을 확인하였습니다. 그 다음에는 Terraform에서 이전 클러스터 리소스에 대한 참조를 제거한 뒤, terraform destroy 명령으로 이전 클러스터와 관련된 리소스를 한번에 삭제할 수 있었습니다.맺음말Kubernetes는 깔끔한 추상화를 통해 컨테이너 기반 배포를 간단하게 만들어주지만, 직접 클러스터를 관리해야 하는 부담이 있었습니다. Amazon EKS는 이러한 부담을 많이 덜어주는 좋은 서비스입니다. 앞으로 EKS의 무궁한 발전을 기원합니다.VCNC에는 오랫동안 쌓아온 AWS 인프라 운영 경험이 있습니다. 타다에서는 그동안의 경험과 비교적 최근에 시작한 프로젝트의 이점을 살려 컨테이너, Infrastructure as Code 등 업계 표준의 인프라 관리 방법론을 적극 도입하려고 노력하고 있습니다. 앞으로도 이에 관해 기술 블로그에 더 자세히 공유할 계획이니 기대해주세요. 또한 저희와 함께 안정적인 서비스를 만들어나갈 좋은 분들을 기다리고 있으니 VCNC 채용에도 많은 관심 부탁드립니다.
조회수 3003

GitHub 계정으로 Kubernetes 인증하기

초기에는 kube-aws가 만들어준 관리자 인증서를 통해 Kubernetes를 관리했는데 역시나 대내외적으로 여건이 바뀌니 변화가 필요했다. 내부적으로는 개발 인력이 늘고 여러 프로젝트가 동시 진행되니 Staging 환경이 급격히 바뀌는데 계정이 하나이니 누가 무슨 작업을 했는지 확인하기 어렵고 외부적으로는 경쟁사의 보안사고 등에 영향을 받아 보안을 강화할 필요가 생겼다. 하여 보안 관련 작업을 여럿했고 그 중 하나가 바로 GitHub와 Kubernetes를 OAuth로 엮는 일이다.기본적으로는 개발자 각자가 자신의 GitHub 계정으로 인증 토큰을 받고 이를 이용해 Kubernetes API에 접근하는 것이다. 전체적인 흐름은 How I built a Kubernetes cluster so my coworkers could deploy apps faster 등을 참고하면 이해하기 그리 어렵지 않다.1. Admin time should be saved (since they are also our developers)2. New users can generate their own credentials without needing the admin3. User credential is always private for security reasons4. Developers have their own space to experiment5. Project spaces can be accessed and changed by multiple users6. In the future, we may want to enable auditing to track changes다만 저들과 달리 Webhook 토큰 인증 플러그인을 직접 짜지 않고 coreos/dex를 이용했다. Dex를 이용하면 GitHub를 비롯해 다양한 OpenID, OAuth 2.0 인증 서비스와 Kubernetes 클러스터를 엮기 쉽다. 더욱이 kube-aws에 Dex가 통합되어서 설치하기도 쉽다.설치하기구구절절 어떻게 설정하는지 설명할 생각은 없는데 회사와 프로젝트에 따라 세부적인 차이가 꽤나 클 수 있기 때문이다. 그러니 대략적인 작업 순서를 간략히 기술하고 끝내려 한다.우선 kube-aws의 cluster.yaml를 보자.# # Enable dex integration - https://github.com/coreos/dex # # Configure OpenID Connect token authenticator plugin in Kubernetes API server. # # Notice: always use "https" for the "url", otherwise the Kubernetes API server will not start correctly. # # Please set selfSignedCa to false if you plan to expose dex service using a LoadBalancer or Ingress with certificates signed by a trusted CA. # dex: # enabled: true # url: "https://dex.example.com" # clientId: "example-app" # username: "email" # groups: "groups" # selfSignedCa: true # # # Dex connectors configuration. You can add configuration for the desired connectors suported by dex or # # skip this part if you don't plan to use any of them. Here is an example of GitHub connector. # connectors: # - type: github # id: github # name: GitHub # config: # clientId: "your_client_id" # clientSecret: "your_client_secret" # redirectURI: https://dex.example.com/callback # org: your_organization # # Configure static clients and users # staticClients: # - id: 'example-app' # redirectURIs: 'https://127.0.0.1:5555/callback' # name: 'Example App' # secret: 'ZXhhbXBsZS1hcHAtc2VjcmV0' # # staticPasswords: # - email: "[email protected]" # # bcrypt hash of the string "password". You can use bcrypt-tool from CoreOS to generate the passwords. # hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" # username: "admin" # userID: "08a8684b-db88-4b73-90a9-3cd1661f5466"우선 GitHub의 Organization Settings 메뉴로 가서 OAuth Apps에 Dex를 추가한다. 이때 Authorization calllback URL은 https://dex.example.com/callback가 된다.GitHub가 준 Client ID와 Client Secret를 cluster.yaml에 적어넣는다.dex: enabled: true url: "https://dex.example.com" clientId: "example-app" username: "email" groups: "groups" selfSignedCa: false # # # Dex connectors configuration. You can add configuration for the desired connectors suported by dex or # # skip this part if you don't plan to use any of them. Here is an example of GitHub connector. connectors: - type: github id: github name: GitHub config: clientId: "GITHUB_OAUTH_APP_CLIENT_ID" clientSecret: "GITHUB_OAUTH_APP_CLIENT_SECRET" redirectURI: https://dex.example.com/callback org: DailyHotel # # Configure static clients and users staticClients: - id: 'example-app' redirectURIs: 'https://kid.example.com/callback' name: 'Example App' secret: 'ZXhhbXBsZS1hcHAtc2VjcmV0'staticPasswords: - email: "[email protected]" # # bcrypt hash of the string "password". You can use bcrypt-tool from CoreOS to generate the passwords. hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" username: "admin" userID: "08a8684b-db88-4b73-90a9-3cd1661f5466"여기서 dex.example.com은 kube-aws가 띄울 dex Deployment와 연결되는 서비스(ELB)의 도메인주소가 되어야 한다. 그런데 kube-aws는 Dex의 External service를 생성해주지 않으므로 아래와 같이 직접 서비스를 생성해야 한다. GitHub가 이쪽으로 콜백을 보내야 하므로 방화벽을 열어야 하고 회사 도메인 인증서를 붙일 것이므로 `selfSignedCa`값은 `false`로 한다.apiVersion: v1 kind: Service metadata: name: dex namespace: kube-system labels: app: dex component: identity dns: route53 annotations: domainName: dex.example.com service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:blahblah service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http service.beta.kubernetes.io/aws-load-balancer-ssl-ports: https spec: ports: # the ports that this service should serve on - name: https port: 443 targetPort: 5556 protocol: TCP selector: app: dex component: identity type: LoadBalancer loadBalancerSourceRanges: - 0.0.0.0/0staticClients / example-app는 Dex에 포함된 예제 프로그램이다. 이를 이용하면 웹 브라우저를 통해 GitHub에 인증하고 토큰을 내려받을 수 있다. DailyHotel/kid 등의 도커 이미지를 사용하면 쉽게 띄울 수 있다. kube-aws는 이 예제 프로그램을 띄우지 않기 때문에 직접 올려야 한다.apiVersion: v1 kind: Service metadata: name: kid namespace: kube-system labels: app: kid dns: route53 annotations: domainName: "kid.example.com" service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:blahblah service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http service.beta.kubernetes.io/aws-load-balancer-ssl-ports: https spec: ports: - name: https port: 443 targetPort: 5555 protocol: TCP selector: app: kid type: LoadBalancer loadBalancerSourceRanges: - 사무실IP/32 --- apiVersion: extensions/v1beta1 kind: Deployment metadata: name: kid namespace: kube-system spec: replicas: 1 template: metadata: labels: app: kid spec: containers: - name: kid image: dailyhotel/kid:latest livenessProbe: tcpSocket: port: 5555 timeoutSeconds: 120 ports: - containerPort: 5555 env: - name: CLIENT_ID value: example-app - name: CLIENT_SECRET value: ZXhhbXBsZS1hcHAtc2VjcmV0 - name: ISSUER value: https://dex.example.com - name: LISTEN value: http://0.0.0.0:5555 - name: REDIRECT_URI value: https://kid.example.com/callback이때 example-app의 REDIRECT_URI는 Dex의 REDIRECT_URI와는 다르다는 점에 주목하자. 옵션의 이름이 비슷하기 때문에 헷갈릴 수 있다. 또한 CLIENT_ID와 CLIENT_SECRET은 cluster.yaml 중 GitHub connector 설정이 아닌 staticClients 설정에서 쓴 값이라는 점도 눈여겨볼 필요가 있다.이 정도만 주의하면 dex를 설치하고 설정하는 것은 어렵지 않다. 이제 인증하는 방법을 알아보자.인증하기웹브라우저로 kid에 방문해서 토큰을 받는다. 첫 화면에서 Login 버튼을 누른 후 GitHub 로그인을 하면 토큰이 나온다.GitHub Public profile 메뉴로 가서 Public email 설정을 확인한다. 공개 이메일이 없다면 하나 추가한다. 로그인시 사용자 아이디로 쓰기 위함이다.kubeconfig 파일을 열고 kubeconfig 파일을 열고 MY_PUBLIC_GITHUB_EMAIL에는 GitHub 공개 이메일 주소를 적고 VISIT_KID_EXAMPLE_COM_AND_GET_TOKEN에는 앞서 받은 토큰을 적는다.apiVersion: v1 kind: Config clusters: - cluster: certificate-authority: credentials/ca.pem server: https://MY_KUBE_CLUSTER name: kube-aws-cluster contexts: - context: cluster: kube-aws-cluster namespace: default user: MY_PUBLIC_GITHUB_EMAIL name: kube-aws-context users: - name: MY_PUBLIC_GITHUB_EMAIL user: token: VISIT_KID_EXAMPLE_COM_AND_GET_TOKEN current-context: kube-aws-context인증 파일의 설정이 정확한지 확인하려면 kubectl --kubeconfig=./kubeconfig version을 실행해보자. 아래와 같이 Client/Server의 버전이 둘다 나오면 정상이다.$ kubectl --kubeconfig=./kubeconfig version Client Version: version.Info{Major:"1", Minor:"6", GitVersion:"v1.6.1", GitCommit:"b0b7a323cc5a4a2019b2e9520c21c7830b7f708e", GitTreeState:"clean", BuildDate:"2017-04-03T20:44:38Z", GoVersion:"go1.7.5", Compiler:"gc", Platform:"darwin/amd64"} Server Version: version.Info{Major:"1", Minor:"6", GitVersion:"v1.6.2+coreos.0", GitCommit:"79fee581ce4a35b7791fdd92e0fc97e02ef1d5c0", GitTreeState:"clean", BuildDate:"2017-04-19T23:13:34Z", GoVersion:"go1.7.5", Compiler:"gc", Platform:"linux/amd64"}참고 자료johnw188/dex-exampleKubernetes / Authenticating#데일리 #데일리호텔 #개발 #개발자 #개발팀 #기술스택 #도입후기 #일지 #경험공유 #Kubernetes #Github
조회수 2147

CSS animation으로 프로토타이핑하기

들어가며Framer, Flinto, Origami, Invision. 많은 프로토타이핑 도구가 존재합니다. 디자인에 활력을 불어넣고 개발팀과의 커뮤니케이션을 위해 필수라고 하는 프로토타이핑. 어떻게 하기는 해야겠는 데 어려운 도구나 코드르 공부하기엔 시간이 없고, 막상 열심히 공부하면 새로운 버전이 나오고, 더 좋은 도구가 나오고. 이런 경험을 많이 하셨을 겁니다. 프로토타이핑 도구로 멋지고 완결된 시나리오를 가진 결과물을 만들 수도 있습니다. 하지만 우리에게 당장 필요한 것은 지금 당장 떠오르는 아이디어를 보여줄 수 있는 아이콘의 간단한 모션, 쓱 움직이는 화면 전환 같은 것이 아닐까요? 오늘 배워서 바로 쓸 수 있는 CSS animation으로 하는 간단한 프로토타이핑 방법을 소개합니다.https://codepen.io/yunkimoon/embed/preview/BZEYgY?default-tabs=css,result&embed-version=2&height=600&host=https://codepen.io&referrer=https://blog.stibee.com/media/c7c8adfdea76b3b98829ecce41fee7d7?postId=e5bb1630afb5&slug-hash=BZEYgY<iframe data-width="800" data-height="600" width="700" height="525" data-src="/media/c7c8adfdea76b3b98829ecce41fee7d7?postId=e5bb1630afb5" data-media-id="c7c8adfdea76b3b98829ecce41fee7d7" data-thumbnail="https://i.embed.ly/1/image?url=https://s3-us-west-2.amazonaws.com/i.cdpn.io/1370087.BZEYgY.small.f06b1cb1-09d2-4285-b8b5-eb8f5b9cea7a.png&key=a19fcc184b9711e1b4764040d3dc5c07" class="progressiveMedia-iframe js-progressiveMedia-iframe" allowfullscreen="" frameborder="0" src="https://blog.stibee.com/media/c7c8adfdea76b3b98829ecce41fee7d7?postId=e5bb1630afb5" style="display: block; position: absolute; margin: auto; max-width: 100%; box-sizing: border-box; transform: translateZ(0px); top: 0px; left: 0px; width: 700px; height: 525px;">어디서, playground코딩을 공부하려면 텍스트 에디터도 설치해야 하고, 각종 패키지도 설치해야 합니다. 또한, 결과물이 담길 파일도 생성해야 하고, 여러 파일이 연결되니까 폴더 구조도 고민해야 하죠. 이런 고민을 하다 보면 시작조차 하기 싫어집니다. 그래서 브라우저에서 바로 작성하고 확인하고 공유할 수 있는 온라인 코딩 플레이 그라운드가 있습니다. 대표적으로 jsbin과 codepen이 있습니다. 그냥 해당 서비스에서 가서 각 섹션(html 또는 css)에 맞게 코드를 입력하기만 하면 됩니다. 우리는 html과 css섹션만 사용할 예정입니다. js와 같은 다른 섹션은 최소화(minimize)해주세요.codepen.io어떻게 시작할까html에 내용을 담고, css에 디자인(스타일)을 담을 겁니다. 당장 직접 작성하기는 어려우니 예제(https://codepen.io/yunkimoon/pen/BZEYgY)의 html과 css코드를 그대로 복사합니다. 코드의 주석(회색글씨)을 확인해 봅니다. 요약하면 아래와 같습니다.가장 바깥의 파란 배경 상자이미지와 그걸 담고 있는 상자파란 배경 상자에 hover(마우스 오버 이벤트)를 하면,left 포지션을 2%에서 80%로 변경여기서 중요한 건 .box상자에 설정된 transition이라는 속성입니다. transition은 딱딱한 움직임을 부드럽게 해줍니다. 여기서는 position left를 2%에서 80%로 부드럽게 바꿔주었습니다. 위치뿐만 아니라 색상(color, background), 크기(width, height)도 자연스럽게 바꿔주는 속성입니다. “all 3s”라는 값을 가지고 있는데 “모든 변경사항에 대해 3초 동안 움직여라”라는 의미입니다.꼭 알아야할 3가지css 애니메이션의 맛을 잠깐 보았습니다. transition을 통해 부드러운 움직임을 줄 수 있습니다. 하지만 더 복잡하고 멋진 움직임을 위해서는 많은 속성들을 이해하고 응용할 수 있어야 합니다. 하지만 모든 속성을 다 알아볼 수는 없으므로 가장 중요한 3가지를 알아보도록 하겠습니다. 미리 살펴본 transition과 transform, keyframe(s) 입니다.1. transition위에서 살펴본 것처럼 대상의 위치, 크기, 색상 등에 부드러운 움직임을 줍니다.2. transform대상의 위치, 크기, 방향 등을 상대적으로 변경합니다. 예제를 통해 알아보겠습니다.<iframe data-width="800" data-height="600" width="700" height="525" data-src="/media/43617ca3eab01b6f86f50b25a362c5a1?postId=e5bb1630afb5" data-media-id="43617ca3eab01b6f86f50b25a362c5a1" data-thumbnail="https://i.embed.ly/1/image?url=https://s3-us-west-2.amazonaws.com/i.cdpn.io/1370087.BZErpP.small.5ebe332d-41b1-4a16-8253-6e2df7b347d0.png&key=a19fcc184b9711e1b4764040d3dc5c07" class="progressiveMedia-iframe js-progressiveMedia-iframe" allowfullscreen="" frameborder="0" src="https://blog.stibee.com/media/43617ca3eab01b6f86f50b25a362c5a1?postId=e5bb1630afb5" style="display: block; position: absolute; margin: auto; max-width: 100%; box-sizing: border-box; transform: translateZ(0px); top: 0px; left: 0px; width: 700px; height: 525px;">2.1. rotate대상에 각도 값을 설정합니다. 즉, 주어진 값만큼 회전합니다. 첫 번째 예제와 조금 다른 부분은 :hover { }에 작성된 내용입니다. transform:rotate(360deg)에서 rotate는 회전을 뜻하고, 360deg는 각도입니다. 즉, 360도(한 바퀴)만큼 회전하라는 의미입니다. 미리 transition이 걸려있었기 때문에 부드럽게 회전하는 모습을 볼 수 있습니다.2.2. translate대상의 이동 값을 설정합니다. 주어진 값만큼 이동합니다. 값은 좌푯값으로 x축, y축 값을 나눠서 줍니다. transform: translate(100px, 100px)에서 translate는 이동을 뜻하고, 이후에 나오는 값은 순서대로 x축의 이동값, y축의 이동 값입니다. 그런데 y축 이동 값이면 위로 올라가야 할 것 같은데, 그림은 아래로 이동합니다. 그 이유는 스크린에서 좌측 위가 기준점이기 때문입니다.2.3. scale대상의 크기를 설정합니다. 즉, 주어진 값만큼 늘어나거나 줄어듭니다. 값은 가로 값, 세로 값을 차례로 줍니다. transform:scale(1.5, 2)에서 scale은 크기를 뜻하고, 1.5와 2는 각각 가로값, 세로값을 뜻합니다. 가로는 1.5배가 늘어나고 세로는 2배가 늘어납니다. 그래서 그림은 세로로 긴 비율로 보입니다.이제 우리는 css만으로 대상의 위치, 크기, 회전 애니메이션을 줄 수 있습니다 :)3. keyframes마우스 오버 액션에 대한 애니메이션을 보아왔습니다. 이렇게 사용자의 특정 반응(마우스 오버)이 없어도 자동으로 움직이도록 할 수는 없을까요? 앞의 두 예제보다 조금 복잡하지만 keyframes를 사용하면 가능합니다. keyframes는 미리 움직임을 지정해두고, 대상에 해당 애니메이션의 속성을 부여하는 방식으로 작성됩니다. 예제를 확인해 보겠습니다.<iframe data-width="800" data-height="600" width="700" height="525" data-src="/media/fc6ef62f3a79def6442479e60dcba75d?postId=e5bb1630afb5" data-media-id="fc6ef62f3a79def6442479e60dcba75d" data-thumbnail="https://i.embed.ly/1/image?url=https://s3-us-west-2.amazonaws.com/i.cdpn.io/1370087.vZMRdd.small.22bea369-dda5-4454-9f16-f5ad68f9b292.png&key=a19fcc184b9711e1b4764040d3dc5c07" class="progressiveMedia-iframe js-progressiveMedia-iframe" allowfullscreen="" frameborder="0" src="https://blog.stibee.com/media/fc6ef62f3a79def6442479e60dcba75d?postId=e5bb1630afb5" style="display: block; position: absolute; margin: auto; max-width: 100%; box-sizing: border-box; transform: translateZ(0px); top: 0px; left: 0px; width: 700px; height: 525px;">3.1. spin앞서 살펴 본 transform의 rotate를 미리 애니메이션을 만들어 놓고 대상에 animation이라는 속성을 설정했습니다.@keyframes spin 처름 spin이라는 애니메이션을 설정합니다. 그 안에는 from과 to가 있는데 각각 시작과 끝을 뜻합니다. 즉, 시작할 때는 회전이 0(rotate(0deg))이고 끝날 때는 회전이 360도(rotate(360deg))입니다.대상과 keyframes를 연결할 때는 대상에 animation: spin 8s infinite linear;와같이 애니메이션 속성을 줍니다. spin은 keyframes의 이름, 8s는 8초 동안, infinite는 무한 반복을 뜻합니다. 여기서 linear는 easing을 나타내는데, 우선은 조금 딱딱한 애니메이션이라고 해둡시다.3.2. leftAndRighttransform의 translate를 활용해서 우측으로 이동했다 돌아오는 애니메이션을 반복시키는 예제입니다. from과 to대신 조금 상세한 타임라인을 가지고 있습니다. 0%, 50%, 100%는 타임라인을 구성하는 속성들로 전체 애니메이션 시간 동안 해당하는 타이밍에 맞게 속성이 변경됩니다. 역시 infinite 속성이 있어 계속 반복되고 있습니다. 그리고 마지막에 linear대신 ease라는 속성을 넣어서 조금 부드러운 움직임 표현했습니다.3.3. hideAndShow앞서 다루지 않은 opacity(투명도)를 활용했습니다. 1이 100%이고 0은 보이지 않는 상태입니다. 1 → 0 → 1을 반복하며 보였다 안 보였다 하는 애니메이션을 보여줍니다.이제 우리는 css만으로 대상의 위치, 크기, 회전 애니메이션 반복적으로 사용할 수 있게 되었습니다. 그리고 무한 반복 애니메이션도 만들 수 있습니다.마무리 예제<iframe data-width="800" data-height="600" width="700" height="525" data-src="/media/f95d4317209e7a3488242568bbdcd5a3?postId=e5bb1630afb5" data-media-id="f95d4317209e7a3488242568bbdcd5a3" data-thumbnail="https://i.embed.ly/1/image?url=https://s3-us-west-2.amazonaws.com/i.cdpn.io/1370087.OgeMEY.small.ab075079-b3bb-443e-a11e-d707c5a6a198.png&key=a19fcc184b9711e1b4764040d3dc5c07" class="progressiveMedia-iframe js-progressiveMedia-iframe" allowfullscreen="" frameborder="0" src="https://blog.stibee.com/media/f95d4317209e7a3488242568bbdcd5a3?postId=e5bb1630afb5" style="display: block; position: absolute; margin: auto; max-width: 100%; box-sizing: border-box; transform: translateZ(0px); top: 0px; left: 0px; width: 700px; height: 525px;">앞서 살펴본 예제들을 활용한 마무리 예제를 만들어 보았습니다. 앞서 공부한 내용을 바탕으로 소스를 분석해 보시기 바랍니다. 각 버튼에는 transiton으로 부드러운 hover 전환 효과를 주었고, 녹색의 메시지는 keyframes를 주어 상하로 계속 움직이도록 했습니다. frame에 마우스가 올라가면 메시지는 프레임 바깥으로 밀려나고 사용자 메뉴가 프레임 안으로 이동하도록 했습니다. 메뉴는 하위 메뉴가 펼쳐지는 인터렉션을 가지고 있습니다.마치며전문 프로토타이핑 도구보다 결과물이 투박하고, 지금 당장 만들 수 있는 장면도 제한적입니다. 자바스크립트 같은 동적 언어가 들어가 있지 않아 표현할 수 있는 화면도 많지 않습니다. 기본적으로 제공되는 템플릿이나 자원이 없으므로 하나하나 html로 코딩하거나 공개 소스를 넣어가면서 만들어야 하는 수고로움도 존재합니다.하지만 실행만 해도 막막한 도구들을 바라보며 “언제 한 번 해보나”하는 생각을 할 시간에 간단히 익혀 한 번이라도 써먹을 수 있다면 그 자체로 의미가 있지 않을까요? 물론 탄탄한 시나리오와 설계를 가지고, 제대로 만든다면 전문 프로토타이핑 도구보다 절대 뒤쳐지지 않을 것입니다. 그리고 우리가 만든 코드들은 커뮤니케이션을 위한 전달용이 아니고 실제로 쓰일 수도 있는 코드라는 점에서도 의미가 있습니다. 간단한 프로토타이핑이라도 지금 시작해 보면 어떨까요?참고https://www.w3schools.com/css/css3_animations.aspttps://www.w3schools.com/css/css3_transitions.asphttps://www.w3schools.com/css/css3_2dtransforms.asphttp://report.stibee.com/2017 by 조은지 디자이너#슬로워크 #스티비 #CSS #퍼블리셔 #개발 #디자인 #인사이트 #꿀팁 #조언
조회수 1889

Tips for building fast portrait segmentation network with TensorFlow Lite

PrefaceDeep learning has led to a series of breakthroughs in many areas. However, successful deep learning models often require significant amounts of computational resources, memory and power. Deploying efficient deep learning models on mobile devices became the main technological challenge for many mobile tech companies.Hyperconnect developed a mobile app named Azar which has a large fan base all over the world. Recently, Machine Learning Team has been focusing on developing mobile deep learning technologies which can boost user experience in Azar app. Below, you can see a demo video of our image segmentation technology (HyperCut) running on Samsung Galaxy J7. Our benchmark target is a real-time (>= 30 fps) inference on Galaxy J7 (Exynos 7580 CPU, 1.5 GHz) using only a single core.Figure 1. Our network runs fast on mobile devices, achieving 6 ms of single inference on Pixel 1 and 28 ms on Samsung Galaxy J7. Full length video. There are several approaches to achieve fast inference speed on mobile device. 8-bit quantization is one of the popular approaches that meet our speed-accuracy requirement. We use TensorFlow Lite as our main framework for mobile inference. TensorFlow Lite supports SIMD optimized operations for 8-bit quantized weights and activations. However, TensorFlow Lite is still in pre-alpha (developer preview) stage and lacks many features. In order to achive our goal, we had to do the following:Understand details of TensorFlow and Tensorflow Lite implementation.Design our own neural network that can fully utilize optimized kernels of TensorFlow Lite. (Refer to 1, 2 and 3)Modify TOCO: TensorFlow Lite Optimizing Converter to correctly convert unsupported layers. (Refer to 4)Accelerate several quantized-layers with SIMD optimized code. (Refer to 5 and 6)We are willing to share our trials and errors in this post and we hope that readers will enjoy mobile deep learning world :)1) Why is depthwise separable layer fast in Tensorflow Lite ?Implementing low-level programs requires a bit different ideas and approaches than usual. We should be aware that especially on mobile devices using cache memory is important for fast inference.Figure 2. Memory access requires much more energy (640 pJ) than addition or multiplication.Accessing cache memory (8 pJ) is much cheaper than using the main memory (table courtesy of Ardavan Pedram) In order to get insight into how intrinsics instructions are implemented in Tensorflow Lite, we had to analyze some implementations including depthwise separable convolution with 3x3 kernelsBelow we describe some of the main optimization techniques that are used for building lightweight and faster programs.Loop UnrollingCan you spot the difference between the following two code fragments?for (int i = 0; i < 32; i++) { x[i] = 1; if (i%4 == 3) x[i] = 3; } for (int i = 0; i < 8; i++) { x[4*i ] = 1; x[4*i+1] = 1; x[4*i+2] = 1; x[4*i+3] = 3; } The former way is what we usually write, and the latter is loop-unrolled version of former one. Even though unrolling loops are discouraged from the perspective of software design and development due to severe redundancy, with low-level architecture this kind of unrolling has non-negligible benefits. In the example described above, the unrolled version avoids examining 24 conditional statements in for loop, along with neglecting 32 conditional statements of if.Furthermore, with careful implementation, these advantages can be magnified with the aid of SIMD architecture. Nowadays some compilers have options which automatically unroll some repetitive statements, yet they are unable to deal with complex loops.Separate implementation for each caseConvolution layer can take several parameters. For example, in depthwise separable layer, we can have many combinations with different parameters (depth_multiplier x stride x rate x kernel_size). Rather than writing single program available to deal with every case, in low-level architectures, writing number of case-specific implementations is preferred. The main rationale is that we need to fully utilize the special properties for each case. For convolution operation, naive implementation with several for loops can deal with arbitrary kernel size and strides, however this kind of implementation might be slow. Instead, one can concentrate on small set of actually used cases (e.g. 1x1 convolution with stride 1, 3x3 convolution with stride 2 and others) and fully consider the structure of every subproblem.For example, in TensorFlow Lite there is a kernel-optimized implementation of depthwise convolution, targeted at 3x3 kernel size:template <int kFixedOutputY, int kFixedOutputX, int kFixedStrideWidth, int kFixedStrideHeight> struct ConvKernel3x3FilterDepth8 {}; Tensorflow Lite further specifies following 16 cases with different strides, width and height of outputs for its internal implementation:template <> struct ConvKernel3x3FilterDepth8<8, 8, 1, 1> { ... } template <> struct ConvKernel3x3FilterDepth8<4, 4, 1, 1> { ... } template <> struct ConvKernel3x3FilterDepth8<4, 2, 1, 1> { ... } template <> struct ConvKernel3x3FilterDepth8<4, 1, 1, 1> { ... } template <> struct ConvKernel3x3FilterDepth8<2, 2, 1, 1> { ... } template <> struct ConvKernel3x3FilterDepth8<2, 4, 1, 1> { ... } template <> struct ConvKernel3x3FilterDepth8<1, 4, 1, 1> { ... } template <> struct ConvKernel3x3FilterDepth8<2, 1, 1, 1> { ... } template <> struct ConvKernel3x3FilterDepth8<4, 2, 2, 2> { ... } template <> struct ConvKernel3x3FilterDepth8<4, 4, 2, 2> { ... } template <> struct ConvKernel3x3FilterDepth8<4, 1, 2, 2> { ... } template <> struct ConvKernel3x3FilterDepth8<2, 2, 2, 2> { ... } template <> struct ConvKernel3x3FilterDepth8<2, 4, 2, 2> { ... } template <> struct ConvKernel3x3FilterDepth8<2, 1, 2, 2> { ... } template <> struct ConvKernel3x3FilterDepth8<1, 2, 2, 2> { ... } template <> struct ConvKernel3x3FilterDepth8<1, 4, 2, 2> { ... } Smart Memory Access PatternSince low-level programs are executed many times in repetitive fashion, minimizing duplicated memory access for both input and output is necessary. If we use SIMD architecture, we can load nearby elements together at once (Data Parallelism) and in order to reduce duplicated read memory access, we can traverse the input array by means of a snake-path.Figure 3. Memory access pattern for 8x8 output and unit stride, implemented in Tensorflow Lite's depthwise 3x3 convolution. The next example which is targeted to be used in much smaller 4x1 output block also demonstrates how to reuse preloaded variables efficiently. Note that the stored location does not change for variables which are loaded in previous stage (in the following figure, bold variables are reused):Figure 4. Memory access pattern for 4x1 output and stride 2, implemented in Tensorflow Lite's depthwise 3x3 convolution. 2) Be aware of using atrous depthwise convolutionAtrous (dilated) convolution is known to be useful to achieve better result for image segmentation[1]. We also decided to use atrous depthwise convolution in our network. One day, we tried to set stride for atrous depthwise convolution to make it accelerate computation, however we failed, because the layer usage in TensorFlow (≤ 1.8) is constrained.In Tensorflow documentation of tf.nn.depthwise_conv2d (slim.depthwise_conv2d also wraps this function), you can find this explanation of rate parameter.rate: 1-D of size 2. The dilation rate in which we sample input values across the height and width dimensions in atrous convolution. If it is greater than 1, then all values of strides must be 1.Even though TensorFlow doesn’t support strided atrous function, it doesn’t raise any error if you set rate > 1 and stride > 1. <!-- The output of layer doesn't seem to be wrong. -->>>> import tensorflow as tf >>> tf.enable_eager_execution() >>> input_tensor = tf.constant(list(range(64)), shape=[1, 8, 8, 1], dtype=tf.float32) >>> filter_tensor = tf.constant(list(range(1, 10)), shape=[3, 3, 1, 1], dtype=tf.float32) >>> print(tf.nn.depthwise_conv2d(input_tensor, filter_tensor, strides=[1, 2, 2, 1], padding="VALID", rate=[2, 2])) tf.Tensor( [[[[ 302.] [ 330.] [ 548.] [ 587.]] [[ 526.] [ 554.] [ 860.] [ 899.]] [[1284.] [1317.] [1920.] [1965.]] [[1548.] [1581.] [2280.] [2325.]]]], shape=(1, 4, 4, 1), dtype=float32) >>> 0 * 5 + 2 * 6 + 16 * 8 + 9 * 18 # The value in (0, 0) is correct 302 >>> 0 * 4 + 2 * 5 + 4 * 6 + 16 * 7 + 18 * 8 + 20 * 9 # But, the value in (0, 1) is wrong! 470 Let’s find the reason why this difference happened. If we just put tf.space_to_batch and tf.batch_to_space before and after convolution layer, then we can use convolution operation for atrous convolution (profit!). On the other hand, it isn’t straightforward how to handle stride and dilation together. In TensorFlow, we need to be aware of this problem in depthwise convolution.3) Architecture design principles for efficient segmentation networkUsually segmentation takes more time than classification since it has to upsample high spatial resolution map. Therefore, it is important to reduce inference time as much as possible to make the application run in real-time.It is important to focus on spatial resolution when designing real-time application. One of the easiest ways is to reduce the size of input images without losing accuracy. Assuming that the network is fully convolutional, you can accelerate the model about four times faster if the size of input is halved. In literature[2], it is known that small size of input images doesn’t hurt accuracy a lot.Another simple strategy to adopt is early downsampling when stacking convolution layers. Even with the same number of convolution layers, you can reduce the time with strided convolution or pooling within early layers.There is also a tip for selecting the size of input image when you use Tensorflow Lite quantized model. The optimized implementations of convolution run best when the width and height of image is multiple of 8. Tensorflow Lite first loads multiples of 8, then multiples of 4, 2 and 1 respectively. Therefore, it is the best to keep the size of every input of layer as a multiple of 8.Substituting multiple operations into single operation can improve speed a bit. For example, convolution followed by max pooling can be usually replaced by strided convolution. Transpose convolution can also be replaced by resizing followed by convolution. In general, these operations are substitutable because they perform the same role in the network. There are no big empirical differences among these operations. <!-- substitutable -->Tips described above help to accelerate inference speed but they can also hurt accuracy. Therefore, we adopted some state-of-the-art blocks rather than using naive convolution blocks.Figure 5.  Atrous spatial pyramid pooling (figure courtesy of Liang-Chieh Chen) Atrous spatial pyramid pooling[1] is a block which mimics the pyramid pooling operation with atrous convolution. DeepLab uses this block in the last layer.We also substitute most of the convolution layers with efficient depthwise separable convolution layers. They are basic building blocks for MobileNetV1[3] and MobileNetV2[4] which are well optimized in Tensorflow Lite.4) Folding batchnorm into atrous depthwise convolutionWhen quantizing convolution operation followed by batchnorm, batchnorm layer must be folded into the convolution layers to reduce computation cost. After folding, the batchnorm is reduced to folded weights and folded biases and the batchnorm-folded convolution will be computed in one convolution layer in TensorFlow Lite[5]. Batchnorm gets automatically folded using tf.contrib.quantize if the batchnorm layer comes right after the convolution layer. However, folding batchnorm into atrous depthwise convolution is not easy.In TensorFlow’s slim.separable_convolution2d, atrous depthwise convolution is implemented by adding SpaceToBatchND and BatchToSpaceND operations to normal depthwise convolution as mentioned previously. If you add a batchnorm to this operation by including argument normalizer_fn=slim.batch_norm, batchnorm does not get attached directly to the convolution layer. Instead, the graph will look like the diagram below: SpaceToBatchND → DepthwiseConv2dNative → BatchToSpaceND → BatchNorm The first thing we tried was to modify TensorFlow quantization to fold batchnorm bypassing BatchToSpaceND without changing the order of operations. With this approach, the folded bias term remained after BatchToSpaceND, away from the convolution layer. Then, it became separate BroadcastAdd operation in TensorFlow Lite model rather than fused into convolution. Surprisingly, it turned out that BroadcastAdd was much slower than the corresponding convolution operation in our experiment:Timing log from the experiment on Galaxy S8 ... [DepthwiseConv] elapsed time: 34us [BroadcastAdd] elapsed time: 107us ... To remove BroadcastAdd layer, we decided to change the network itself instead of fixing TensorFlow quantization. Within slim.separable_convolution2d layer, we swapped positions of BatchNorm and BatchToSpaceND. SpaceToBatchND → DepthwiseConv2dNative → BatchNorm → BatchToSpaceND Even though batchnorm relocation could lead to different outputs values compared to the original, we did not notice any degradation in segmentation quality.5) SIMD-optimized implementation for quantized resize bilinear layerAt the time of accelerating TensorFlow Lite framework, conv2d_transpose layer was not supported. However, we could use ResizeBilinear layer for upsampling as well. The only problem of this layer is that there is no quantized implementation, therefore we implemented our own SIMD quantized version of 2x2 upsampling ResizeBilinear layer.Figure 6. 2x2 bilinear upsampling without corner alignnment. To upsample image, original image pixels (red circles) are interlayed with new interpolated image pixels (grey circles). In order to simplify implementation we do not compute pixel values for the bottommost and rightmost pixels, denoted as green circles.for (int b = 0; b < batches; b++) { for (int y0 = 0, y = 0; y <= output_height - 2; y += 2, y0++) { for (int x0 = 0, x = 0; x <= output_width - 2; x += 2, x0++) { int32 x1 = std::min(x0 + 1, input_width - 1); int32 y1 = std::min(y0 + 1, input_height - 1); ResizeBilinearKernel2x2(x0, x1, y0, y1, x, y, depth, b, input_data, input_dims, output_data, output_dims); } } } Every new pixel value is computed for each batch separately. Our core function ResizeBilinearKernel2x2 computes 4 pixel values across multiple channels at once.Figure 7. Example of 2x2 bilinear upsampling of top left corner of image. (a) Original pixel values are simply reused and (b) – (d) used to interpolate new pixel values. Red circles represent original pixel values. Blue circles are new interpolated pixel values computed from pixel values denoted as circles with black circumference. NEON (Advanced SIMD) intrinsics enable us to process multiple data at once with a single instruction, in other words processing multiple data at once. Since we deal with uint8 input values we can store our data in one of the following formats uint8x16_t, uint8x8_t and uint8_t, that can hold 16, 8 and 1 uint8 values respectively. This representation allows to interpolate pixel values across multiple channels at once. Network architecture is highly rewarded when channels of feature maps are multiples of 16 or 8:// Handle 16 input channels at once int step = 16; for (int ic16 = ic; ic16 <= depth - step; ic16 += step) { ... ic += step; } // Handle 8 input channels at a once step = 8; for (int ic8 = ic; ic8 <= depth - step; ic8 += step) { ... ic += step; } // Handle one input channel at once for (int ic1 = ic; ic1 < depth; ic1++) { ... } SIMD implementation of quantized bilinear upsampling is straightforward. Top left pixel value is reused (Fig. 7a). Bottom left (Fig. 7b) and top right (Fig. 7c) pixel values are mean of two adjacent original pixel values. Finally, botom right pixel (Fig. 7d) is mean of 4 diagonally adjacent original pixel values.The only issue that we have to take care of is 8-bit integer overflow. Without a solid knowledge of NEON intrinsics we could go down the rabbit hole of taking care of overflowing by ourself. Fortunately, the range of NEON intrinsics is broad and we can utilize those intrinsics that fit our needs. The snippet of code below (using vrhaddq_u8) shows an interpolation (Fig. 7d) of 16 pixel values at once for bottom right pixel value:// Bottom right output_ptr += output_x_offset; uint8x16_t left_interpolation = vrhaddq_u8(x0y0, x0y1); uint8x16_t right_interpolation = vrhaddq_u8(x1y0, x1y1); uint8x16_t bottom_right_interpolation = vrhaddq_u8(left_interpolation, right_interpolation); vst1q_u8(output_ptr, bottom_right_interpolation); 6) Pitfalls in softmax layer and demo codeThe first impression of inference in TensorFlow Lite was very slow. It took 85 ms in Galaxy J7 at that time. We tested the first prototype of TensorFlow Lite demo app by just changing the output size from 1,001 to 51,200 (= 160x160x2)After profiling, we found out that there were two unbelievable bottlenecks in implementation. Out of 85 ms of inference time, tensors[idx].copyTo(outputs.get(idx)); line in Tensor.java took up to 11 ms (13 %) and softmax layer 23 ms (27 %). If we would be able to accelerate those operations, we could reduce almost 40 % of the total inference time!First, we looked at the demo code and identified tensors[idx].copyTo(outputs.get(idx)); as a source of problem. It seemed that the slowdown was caused by copyTo operation, but to our surprise it came from int[] dstShape = NativeInterpreterWrapper.shapeOf(dst); because it checks every element (in our case, 51,200) of array to fill the shape. After fixing the output size, we gained 13 % speedup in inference time.<T> T copyTo(T dst) { ... // This is just example, of course, hardcoding output shape here is a bad practice // In our actual app, we build our own JNI interface with just using c++ code // int[] dstShape = NativeInterpreterWrapper.shapeOf(dst); int[] dstShape = {1, width*height*channel}; ... } The softmax layer was our next problem. TensorFlow Lite’s optimized softmax implementation assumes that depth (= channel) is bigger than outer_size (= height x width). In classification task, the usual output looks like [1, 1(height), 1(width), 1001(depth)], but in our segmentation task, depth is 2 and outer_size is multiple of height and width (outer_size » depth). Implementation of softmax layer in Tensorflow Lite is optimized for classification task and therefore loops over depth instead of outer_size. This leads to unacceptably slow inference time of softmax layer when used in segmentation network.We can solve this problem in many different ways. First, we can just use sigmoid layer instead of softmax in 2-class portrait segmentation. TensorFlow Lite has very well optimized sigmoid layer.Secondly, we could write SIMD optimized code and loop over depth instead of outer_size. You can see similar implementation at Tencent’s ncnn softmax layer, however, this approach has still its shortcomings. Unlike ncnn, TensorFlow Lite uses NHWC as a default tensor format:Figure 8. NHWC vs NCHW In other words, for NHWC, near elements of tensor hold channel-wise information and not spatial-wise. It is not simple to write optimized code for any channel size, unless you include transpose operation before and after softmax layer. In our case, we tried to implement softmax layer assumming 2-channel output.Thirdly, we can implement softmax layer using pre-calculated lookup table. Because we use 8-bit quantization and 2-class output (foreground and background) there are only 65,536 (= 256x256) different combinations of quantized input values that can be stored in lookup table:for (int fg = 0; fg < 256; fg++) { for (int bg = 0; bg < 256; bg++) { // Dequantize float fg_real = input->params.scale * (fg - input->params.zero_point); float bg_real = input->params.scale * (bg - input->params.zero_point); // Pre-calculating Softmax Values ... // Quantize precalculated_softmax[x][y] = static_cast<uint8_t>(clamped); } } ConclusionIn this post, we described the main challenges we had to solve in order to run portrait segmentation network on mobile devices. Our main focus was to keep high segmentation accuracy while being able to support even old devices, such as Samsung Galaxy J7. We wish our tips and tricks can give a better understanding of how to overcome common challenges when designing neural networks and inference engines for mobile devices.At the top of this post you can see portrait segmentation effect that is now available in Azar app. If you have any questions or want to discuss anything related to segmentation task, contact us at [email protected]. Enjoy Azar and mobile deep learning world!References[1] L. Chen, G. Papandreou, F. Schroff, H. Adam. Rethinking Atrous Convolution for Semantic Image Segmentation. June 17, 2017, https://arxiv.org/abs/1706.05587[2] C. Szegedy, V. Vanhoucke, S. Ioffe, J. Shlens, Z. Wojna. Rethinking the Inception Architecture for Computer Vision. December 11, 2015, https://arxiv.org/abs/1512.00567[3] A. Howard, M. Zhu, B. Chen, D. Kalenichenko, W. Wang, T. Weyand, M. Andreetto, H. Adam. MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications, April 17, 2017, https://arxiv.org/abs/1704.04861[4] M. Sandler, A. Howard, M. Zhu, A. Zhmoginov, L. Chen. MobileNetV2: Inverted Residuals and Linear Bottlenecks. January 18, 2018, https://arxiv.org/abs/1801.04381[5] B. Jacob, S. Kligys, B. Chen, M. Zhu, M. Tang, A. Howard, H. Adam, D. Kalenichenko. Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference. December 15, 2017, https://arxiv.org/abs/1712.05877
조회수 1121

ASIHTTPRequest를 대체하는 iOS 네트워킹 라이브러리 2가지

ASIHTTPRequest는 iOS 개발자들 사이에서 가장 많이 이용되는 네트워킹 라이브러리인데, 간결한 인터페이스와 개선된 성능으로 인기를 끌었습니다. Github의 Objective-C Most Watched Overall에서도 2위 자리를 현재도 유지하고 있는 것을 보면 이 라이브러리가 얼마나 오랜 시간 동안 iOS 개발자들에게 사랑받았는지는 쉽게 알 수 있습니다.[request release];하지만 애석하게도, 이 라이브러리는 작년 9월에 제작 종료가 선언되었습니다. 6개월 이상 된 소식이지만 하도 오랜 시간 동안 쓰여와서 소개된 곳이 많다보니 제작 종료 소식이 많이 안 퍼지고 있는 듯합니다.여러 가지 이유가 있겠지만, 제작자는 제작 종료 선언 글을 통해 “내부가 너무 복잡해졌고, 수 년에 걸쳐 누적된 몇 가지 아키텍처 선택이 프로젝트를 유지 보수하기 어렵게 만들었다.”라고 제작 종료 선언의 이유에 대해 고백하고 있습니다.부지런히 갈아탈 준비를 해두세요.제작 종료가 선언된 라이브러리인 만큼 가능하면 새로운 라이브러리로 갈아타시는 것이 좋습니다. iOS 개발환경은 1년 단위로 빠르게 성장하고 있는데, 당장 최근 iOS5 개발환경만 해도 block 문법 기반의 API 패러다임, ARC 지원들이 현행 라이브러리들의 필수 요소처럼 굳어져 가고 있습니다. 이에 맞추어 따라갈 수 있는 라이브러리들을 쓰는 것이 장기적인 개발 환경 개선에 도움이 될 것입니다.어떤 대안이 있나?ASIHTTPRequest 라이브러리 개발자는 여러 가지 대안을 소개했지만, 저는 2가지 정도로 간추려서 추천하고자 합니다. 하나는 AFNetworking이며, 하나는 MKNetworkKit입니다.AFNetworkingAFNetworking은 최근 Facebook에 인수된 Gowalla에서 NSURLConnection, NSOperation 등의 기본 Foundation framework 위에 구현된 네트워킹 라이브러리입니다.현재 ASIHTTPRequest의 대안으로 가장 빠르게 성장하고 있는 라이브러리인데, 그 이유는 유명 애플리케이션 개발사의 개발자들이 유지하고 있는 프로젝트이면서, 꽤 명쾌한 API를 제공하고 있습니다. 기본적인 block 기반의 API 구성 외로도, SDWebImage와 같은 라이브러리에서 볼 수 있는 이미지 다운로드 헬퍼도 제공하고 있어 매우 편리합니다.자세한 사용법은 AFNetworking Github 저장소에서 확인할 수 있습니다.MKNetworkKitASIHTTPRequest는 편리한 API를 제공해주는 것으로 많은 사용자에게 사랑받았지만, 기본 NSURLConnection, NSOperation 으로 낼 수 없는 높은 퍼포먼스 또한 그의 강점이었습니다. MKNetworkKit은, ASIHTTPRequest의 아키텍처와 AFNetworking의 인터페이스를 동시에 지향하고자 하는 라이브러리입니다. 그 외에도 아래와 같은 기능들을 추가로 겸비합니다.전체 앱에 대한 single queue 관리자동 queue 크기 조절캐싱과 복구 기능비슷한 request를 하나의 처리로 수행Full ARC support아주 멋진 목표를 가지고 진행되고 있는 프로젝트이며 개발 진척도 상당히 빠른 속도로 진행 중이지만, 아직 자잘한 버그가 많다는 것이 단점입니다. 네트워킹 라이브러리는 애플리케이션 단위에선 상당히 저 수준에 있는 만큼, 이 문제는 치명적일 수 있습니다. 그래서 상업용 프로젝트에 바로 이용하기보다는 실험적인 프로젝트에서 써보면서 지켜보는 것을 추천합니다.마무리하며iOS 애플리케이션 개발 환경에서 네트워킹 라이브러리의 선택은 개발 속도와 애플리케이션 퍼포먼스에서 아주 중요한 위치에 속합니다. ASIHTTPRequest는 그 중 가장 많이 쓰였지만, 개발 종료를 선언했기 때문에 대안 라이브러리를 준비하시는 것이 좋습니다.AFNetworking은 편리하게 쓸 수 있는 API를 NSURLConnection, NSOperation 위에 구현하였으며, 믿을 수 있을 만큼 성숙하여 현재 새 프로젝트에 바로 도입하기 좋습니다. MKNetworkKit은 아직 개발이 한창 더 진행되어야 하지만 API 디자인과 개선된 퍼포먼스, ARC 지원 등 보다 미래지향적인 목표를 하고 있으므로 장기적으로 지켜볼 가치가 있습니다.이 외에도 추천하는 라이브러리가 있다면 공유해봅시다.#스포카 #개발 #개발팀 #개발자 #개발팁 #꿀팁 #인사이트 #조언
조회수 1106

[번역] 개발 게임화 시스템

이 글은 Warby Parker tech team blog의 Systems Development Gamified!를 번역한 글입니다.우리는 이슈가 있었습니다: 우리의 기술팀은 "주도권"을 우려했는데, 이는 와비 파커(Warby Parker)가 개발해야하는 요청, 우선순위, 개발일을 할당하는 것과 관련이 있었습니다.(이는 유연성, 권한부여, 효율성이라는 의문을 제기하기도 했습니다). 모든 일이 그래왔던 것처럼 우리는 발전하고 반복하여 살펴보았습니다. 이는 여러 역할을 수행하는 이해관계자들의 그룹의 사람들을 "우리의 목표를 쇄신하여 엄청난 목표를 달성할 수 있을지를 고민하는 일일 세션"에 참가토록 했습니다. 우선 우리는 현재 프로세스에서 발생하는 사소한 문제들을 해결할 수 있는 문제들에 대해 토론하기 시작했습니다. 그리고 우리는 토론에 근거해 우선순위를 정하고, 일을 선택하는 것에 대한 게임화, 시장중심적인 접근방법을 만들었고 "와블스 프로세스(The Warbles Process")라고 부르기로 했습니다.주요 이해관계자들에게(애원하다시피 부탁하여) 넓은 폭의 설문과 인터뷰를 통한 피드백 이후에, 우리는 단지 소수의 사람만이 엔지니어들에게 할당된 일에 대해서 완전히 만족한다는 점을 알게되었습니다. 주요 문제는 다음과 같습니다.- 유연성 : 이전의 프로세스들은 분기 미팅에 의해 결정되는데, 이 분기 미팅에서 다음 분기에 무엇을 할건지를 선택하고 우선순위를 정하는 일이 사업적인 니즈의 특정 영역에 의해 정해집니다. 이 프로세스는 엄청난 관심을 받고 큰 이슈로 정해지는 반면, 가끔 작거나 예상치 못한 일이 관심을 받지 못하기도 하지요. 빠르게 대처해야하고, 빠르게 진화해야하는 환경에 놓인 우리 비즈니스의 특성상, 분기 단위의 시간은 너무나 깁니다. 또한 이러한 시간 박스(Time box)는 낮은 가치의 프로젝트들을 큰 프로젝트들이 완료된 후 단순히 "틈새를 메우기 위한" 일로 치부될 수 있습니다. 그러기엔 분기는 너무 짧지요.- 가치-우선순위 결정(Value-Prioritization) : 이전의 프로세스에서, 일은 일방적으로 기울어진 시각으로 일부 영역만을 집중하는 경영진(일반적으로 해당 부서의 책임자)에 의해 우선순위가 매겨집니다. 그래서 기술팀은 경영진에 의해 선택된 일이 가끔은 기업에 초점을 맞춘것이 아니라 부서에 초점을 맞춘 것으로 느끼기도 합니다.- 권한부여(Empowerment) : 기술팀은 경영진에 의해 분기 주도적이고 우선순위가 결정된 일을 할당받습니다. 팀은 할당이라는 행동자체에 대해 권한을 행사할 수 있음에도 불구하고, 궁극적으로 의사결정자가 아닙니다. 그리고 한번 주도권을 가지게 되면, 일하는 사람은 그대로 따라가기 마련입니다. 우리는 기술팀에게 권한을 부여하기를 원했고, 이 프로세스는 앞의 목표와 상충되는 것이었습니다.이런 문제를 해결하기 위해서, 우리는 팀을 다시 북돋우고 프로세스를 정비하기로 했습니다. 프로젝트 매니저, 비즈니스 애널리스트, 소프트웨어 엔지니어, 경영진을 한 방에 몰아넣고, 우리의 프로세스 향상에 초점을 맞춘 "종이비행기 린 트레이닝(Paper Airplan Lean Traning)"이라는, 일종의 종이비행기를 접는 Lean 시뮬레이션을 시작했습니다. 우선 우리는 그룹을 두 개의 작은 팀으로 나누었습니다. 각 팀은 우리의 "꿈의 프로세스(Dream process)를 상상하도록 했습니다. 이 시뮬레이션을 반정도 하니 신기한 일이 발생했습니다: 두 팀 모두 이상적인 프로세스에 대한 같은 시각을 가지게 된 것입니다.이 깨달음으로부터, 우리는 피벗(Pivot)하여 두 개의 팀을 하나로 합쳤고 하나의 아이디어에 집중하도록 하였습니다. 엄청난 양의 포스트잇과 수많은 피자 이후에, 기술팀을 위한 우선순위 결정에 대한 새로운 접근방법을 가지게 되었습니다. 이 세션은 이 아이디어에 대해 관심있게 지켜보았던 시스템 기술개발팀의 부사장에게 프레젠테이션을 하는 것으로 마무리 되었고, CEO에게 공유하기전에 아이디어를 공식화하고 디테일을 정착하였습니다.그렇게 와블스 프로세스(Warbles Process)가 탄생하였습니다.와블스 프로세스(Warbles Process)회사 누군가의 요청을 통해 모든 것은 시작됩니다. Epic이라는 폼(Form)을 통해 제출된 백로그(Backlog)는 와비파커 전원이 볼 수 있습니다. 이것의 투표 시스템을 통해 회사의 모든 매니저들은 찬성(Up-vote), 반대(Down-vote), 포기(Decline)를 할 수 있습니다. 각 에픽에 대해서 매니저들은 그들이 생각하기에 현재 회사가 가장 최우선시해야하는지를 생각하고 투표하게 됩니다. 각 찬성표는 5 와블스, 반대표는 -2 와블스를 얻습니다. 이 결과는 그들이 할당한 와블스 가치에 의해 우선순위가 순서대로 리스트에 반영됩니다.(와블스를 일련의 경제학적인 가치 형태라고 가정합니다)와블스 프로세스는 각 기술팀에게 어떤 에픽을 선택하여 진행할지 권한을 부여합니다. 기술팀은 백로그의 상단에서부터 선택하지 않아도 됩니다(혹은 백로그에 없어도 됩니다). 각 팀은 규칙이나 특정 사업영역과 전문적 기술을 조율할 수 있는 선임기술자에 의해 리드됩니다. 리더에 의한 관리하에, 팀은 그들의 특정 기술이나 경험에 기반하여 가장 효율적으로 완수할 수 있는 에픽을 선택합니다. 6개월뒤, 평균 와블스/엔지니어 가 가장 높은 팀이 우승을 차지합니다! 이긴 팀은 특별한 팀 회식을 즐깁니다.이 가치 기반의 접근은 우리의 업무 선택과 우선순위 결정 절차를 게임화하였습니다. 그리고 팀은 상하로 정렬되거나 중심적 업무 할당방식이 아닌 요청 방식으로부터 높은 우선순위의 일을 선택하여 일함으로써 인센티브가 있다는 느낌을 받습니다. 아직은 이를지 몰라도, 우리는 이 방식이 우리와 같은 빨리 진화해야하는 조직에 굉장히 잘맞는다는 것을 깨달았고, 게다가 기술팀이 일을 선택하는 권한을 부여하여, 조직 전체적으로 주목을 받고있는 가장 밀접한 일을 하고 있다고 확신하는데 도움을 주기도 합니다.우리는 와블스 프로세스를 적극적으로 평가하는 중입니다. 지금까지 와블스는 이전의 프로세스때문에 주목을 받지 못했던 여러 프로젝트들에 대해 격렬한 비판을 할 수 있는 역할을 톡톡히 수행하고 있습니다. 또한 다른 프로세스와 비교했을때, 내부 이해관계자들과 프로젝트에 참여하는 기술팀의 행복지수가 모두 엄청나게 상승하는것을 보았습니다. 게다가 조직 전체에서 깊게 관련되지 않은 사람들에게도 긍정적인 피드백을 받는 중입니다.(모두가 윈윈하는 길이네요!)#비주얼캠프 #인사이트 #경험공유 #조언 #개발자 #개발팀
조회수 2384

Tabnabbing 피싱 공격의 동작 원리와 대응책

브라우저에서 사용자의 개인 정보를 가로채는 여러가지 피싱 공격 기법이 있습니다. 이 글에서는 그 중에서도 상대적으로 단순해서 과소평가된 Tabnabbing 공격의 동작 원리와 대응책을 함께 알아보겠습니다.Tabnabbing 의 동작 원리Tabnabbing은 HTML 문서 내에서 링크(target이 _blank인 Anchor 태그)를 클릭 했을 때, 새롭게 열린 탭(또는 페이지)에서 기존의 문서의 location을 피싱 사이트로 변경해 정보를 탈취하는 공격 기술을 뜻한다. 이 공격은 메일이나 오픈 커뮤니티에서 쉽게 사용될 수 있습니다.(출처: blog.jxck.io 영어 스펠링이 이상해 보이는 것은 기분 탓입니다)공격 절차는 다음과 같습니다:사용자가 cg**m**.example.com에 접속합니다.해당 사이트에서 happy.example.com으로 갈 수 있는 외부 링크를 클릭합니다.새 탭에 happy.example.com가 열립니다.happy.example.com에는 window.opener 속성이 존재합니다.자바스크립트를 사용해 opener의 location을 피싱 목적의 cg**n**.example.com/login 으로 변경합니다.사용자는 다시 본래의 탭으로 돌아옵니다.로그인이 풀렸다고 착각하고 아이디와 비밀번호를 입력한다.cg**n**.example.com은 사용자가 입력한 계정 정보를 탈취한 후 다시 본래의 사이트로 리다이렉트합니다.예제: 네이버 메일 vs. Gmail시나리오를 하나 그려볼까요?공격자가 네이버 계정을 탈취할 목적으로 여러분에게 세일 정보를 담은 메일을 보냅니다. 그 메일에는 [자세히 보기]라는 외부 링크가 포함되어 있습니다. 물론 이 세일 정보는 가짜지만 공격자에겐 중요하지 않습니다. 메일을 읽는 사람이 유혹에 빠져 링크를 클릭하면 그만이죠.(상단의 주소를 주목하세요)하지만 Gmail은 이 공격이 통하지 않습니다. Gmail은 이러한 공격을 막기 위해 Anchor 태그에 data-saferedirecturl 속성을 부여해 안전하게 리다이렉트 합니다.rel=noopener 속성이러한 공격의 취약점을 극복하고자 noopener 속성이 추가됐습니다. rel=noopener 속성이 부여된 링크를 통해 열린 페이지는 opener의 location변경과 같은 자바스크립트 요청을 거부합니다. 정확히 말해서 Uncaught TypeError 에러를 발생시킵니다(크롬 기준).이 속성은 Window Opener Demo 페이지를 통해 테스트해볼 수 있습니다. 크롬은 버전 49, 파이어폭스 52부터 지원합니다. 파이어폭스 52가 2017년 3월에 릴리즈 된 것을 감안하면 이 속성 만으로 안심하긴 힘들겠네요. 자세한 지원 여부는 Link types를 참고하세요.따라서, 이러한 공격이 우려스러운 서비스라면 blankshield 등의 라이브러리를 사용해야 합니다:blankshield(document.querySelectorAll('a[target=_blank]')); 참고로, noopener 속성은 이 외에도 성능 상의 이점도 있습니다. _blank 속성으로 열린 탭(페이지)는 언제든지 opener를 참조할 수 있습니다. 그래서 부모 탭과 같은 스레드에서 페이지가 동작합니다. 이때 새 탭의 페이지가 리소스를 많이 사용한다면 덩달아 부모 탭도 함께 느려집니다. noopener 속성을 사용해 열린 탭은 부모를 호출할 일이 없죠. 따라서 같은 스레드일 필요가 없으며 새로운 페이지가 느리다고 부모 탭까지 느려질 일도 없습니다.성능 상의 이점에 대한 자세한 내용은 The performance benefits of rel=noopener을 참고하세요.참고자료Tabnabbing: A New Type of Phishing AttackTarget=”_blank” - the most underestimated vulnerability ever링크에 rel=noopener를 부여해 Tabnabbing을 대비(일어)The performance benefits of rel=noopener
조회수 1307

VCNC가 Hadoop대신 Spark를 선택한 이유 - VCNC Engineering Blog

요즘은 데이터 분석이 스타트업, 대기업 가릴 것 없이 유행입니다. VCNC도 비트윈 출시 때부터 지금까지 데이터 분석을 해오고 있고, 데이터 기반의 의사결정을 내리고 있습니다.데이터 분석을 하는데 처음부터 복잡한 기술이 필요한 것은 아닙니다. Flurry, Google Analytics 등의 훌륭한 무료 툴들이 있습니다. 하지만 이러한 범용 툴에서 제공하는 것 이상의 특수하고 자세한 분석을 하고 싶을 때 직접 많은 데이터를 다루는 빅데이터 분석을 하게 됩니다. VCNC에서도 비트윈의 복잡한 회원 가입 프로세스나, 채팅, 모멘츠 등 다양한 기능에 대해 심층적인 분석을 위해 직접 데이터를 분석하고 있습니다.빅데이터 분석 기술큰 데이터를 다룰 때 가장 많이 쓰는 기술은 Hadoop MapReduce와 연관 기술인 Hive입니다. 구글의 논문으로부터 영감을 받아 이를 구현한 오픈소스 프로젝트인 Hadoop은 클러스터 컴퓨팅 프레임웍으로 비싼 슈퍼컴퓨터를 사지 않아도, 컴퓨터를 여러 대 연결하면 대수에 따라서 데이터 처리 성능이 스케일되는 기술입니다. 세상에 나온지 10년이 넘었지만 아직도 잘 쓰이고 있으며 데이터가 많아지고 컴퓨터가 저렴해지면서 점점 더 많이 쓰이고 있습니다. VCNC도 작년까지는 데이터 분석을 하는데 MapReduce를 많이 사용했습니다.주스를 만드는 과정에 빗대어 MapReduce를 설명한 그림. 함수형 프로그래밍의 기본 개념인 Map, Reduce라는 프레임을 활용하여 여러 가지 문제를 병렬적으로 처리할 수 있다. MapReduce slideshare 참조MapReduce는 슈퍼컴퓨터 없이도 저렴한 서버를 여러 대 연결하여 빅데이터 분석을 가능하게 해 준 혁신적인 기술이지만 10년이 지나니 여러 가지 단점들이 보이게 되었습니다. 우선 과도하게 복잡한 코드를 짜야합니다. 아래는 간단한 Word Count 예제를 MapReduce로 구현한 것인데 매우 어렵고 복잡합니다.MapReduce로 단어 갯수를 카운트하는 간단한 예제 (Java). 많은 코드를 작성해야 한다.이의 대안으로 SQL을 MapReduce로 변환해주는 Hive 프로젝트가 있어 많은 사람이 잘 사용하고 있지만, 쿼리를 최적화하기가 어렵고 속도가 더 느려지는 경우가 많다는 어려움이 있습니다.MapReduce의 대안으로 최근 아주 뜨거운 기술이 있는데 바로 Apache Spark입니다. Spark는 Hadoop MapReduce와 비슷한 목적을 해결하기 위한 클러스터 컴퓨팅 프레임웍으로, 메모리를 활용한 아주 빠른 데이터 처리가 특징입니다. 또한, 함수형 프로그래밍이 가능한 언어인 Scala를 사용하여 코드가 매우 간단하며, interactive shell을 사용할 수 있습니다.Spark으로 단어 개수를 카운트하는 간단한 예제 (Scala). MapReduce에 비해 훨씬 간단하다.Spark과 MapReduce의 성능 비교. I/O intensive 한 작업은 성능이 극적으로 향상되며, CPU intensive 한 작업의 경우에도 효율이 더 높다. (자료: RDD 논문)Apache Spark는 미국이나 중국에서는 현재 Hadoop을 대체할만한 기술로 급부상하고 있으며, 국내에도 최신 기술에 발 빠른 사람들은 이미 사용하고 있거나, 관심을 갖고 있습니다. 성능이 좋고 사용하기 쉬울 뿐 아니라, 범용으로 사용할 수 있는 프레임웍이기에 앞으로 더 여러 분야에서 많이 사용하게 될 것입니다. 아직 Spark를 접해보지 못하신 분들은 한번 시간을 내어 살펴보시길 추천합니다.기존의 데이터 분석 시스템 아키텍처기존의 데이터 분석 시스템 아키텍처기존의 시스템은 비용을 줄이기 위해 머신들을 사무실 구석에 놓고 직접 관리했으며, AWS S3 Tokyo Region에 있는 로그를 다운받아 따로 저장한 뒤, MapReduce로 계산을 하고 dashboard를 위한 사이트를 따로 제작하여 운영하고 있었습니다.이러한 시스템은 빅데이터 분석을 할 수 있다는 것 외에는 불편한 점이 많았습니다. 자주 고장 나는 하드웨어를 수리하느라 바빴고, 충분히 많은 머신을 확보할 여유가 없었기 때문에 분석 시간도 아주 오래 걸렸습니다. 그리고 분석부터 시각화까지 과정이 복잡하였기 때문에 간단한 것이라도 구현하려면 시간과 노력이 많이 들었습니다.Spark과 Zeppelin을 만나다이때 저희의 관심을 끈 것이 바로 Apache Spark입니다. MapReduce에 비해 성능과 인터페이스가 월등히 좋은 데다가 0.x 버전과는 달리 1.0 버전에서 많은 문제가 해결되면서 안정적으로 운영할 수 있어 비트윈 데이터 분석팀에서는 Spark 도입을 결정했습니다.Apache Zeppelin은 국내에서 주도하고 있는 오픈소스 프로젝트로써, Spark를 훨씬 더 편하고 강력하게 사용할 수 있게 해주는 도구입니다. 주요한 역할은 노트북 툴, 즉 shell에서 사용할 코드를 기록하고 재실행할 수 있도록 관리해주는 역할과 코드나 쿼리의 실행 결과를 차트나 표 등으로 시각화해서 보여주는 역할입니다. VCNC에서는 Zeppelin의 초기 버전부터 관심을 가지고 살펴보다가, Apache Spark를 엔진으로 사용하도록 바뀐 이후에 활용성이 대폭 좋아졌다고 판단하여 데이터 분석에 Zeppelin을 도입하여 사용하고 있고, 개발에도 참여하고 있습니다.또한, 위에서 언급한 하드웨어 관리에 드는 노력을 줄이기 위해서 전적으로 클라우드를 사용하기로 함에 따라서1 아래와 같은 새로운 구조를 가지게 되었습니다.새로운 데이터 분석 시스템 아키텍처새로운 데이터 분석 시스템 아키텍처새로운 데이터 분석 시스템은 아키텍처라고 하기에 다소 부끄러울 정도로 간단합니다. 애초에 전체 시스템 구성을 간단하게 만드는 것에 중점을 두었기 때문입니다. 대략적인 구성과 활용법은 아래와 같습니다.모든 서버는 AWS 클라우드를 이용수 대의 Zeppelin 서버, 수 대의 Spark 서버운영Spark 서버는 메모리가 중요하므로 EC2 R3 instance 사용로그는 별도로 저장하지 않고 서비스 서버에서 S3로 업로드하는 로그를 곧바로 가져와서 분석함중간 결과 저장도 별도의 데이터베이스를 두지 않고 S3에 파일로 저장Zeppelin의 scheduler 기능을 이용하여 daily batch 작업 수행별도의 dashboard용 Zeppelin을 통해 중간 결과를 시각화하며 팀에 결과 공유이렇게 간단한 구조이긴 하지만 Apache Spark와 Apache Zeppelin을 활용한 이 시스템의 능력은 기존 시스템보다 더 강력하고, 더 다양한 일을 더 빠르게 해낼 수 있습니다.기존현재일일 배치 분석코드 작성 및 관리가 어려움Zeppelin의 Schedule 기능을 통해 수행Interactive shell로 쉽게 데이터를 탐험오류가 생긴 경우에 shell을 통해 손쉽게 원인 발견 및 수정 가능Ad-hoc(즉석) 분석복잡하고 많은 코드를 짜야 함분석 작업에 수 일 소요Interactive shell 환경에서 즉시 분석 수행 가능Dashboard별도의 사이트를 제작하여 운영관리가 어렵고 오류 대응 힘듦Zeppelin report mode 사용해서 제작코드가 바로 시각화되므로 제작 및 관리 수월성능일일 배치 분석에 약 8시간 소요메모리를 활용하여 동일 작업에 약 1시간 소요이렇게 시스템을 재구성하는 작업이 간단치는 않았습니다. 이전 시스템을 계속 부분적으로 운영하면서 점진적으로 재구성 작업을 하였는데 대부분 시스템을 옮기는데 약 1개월 정도가 걸렸습니다. 그리고 기존 시스템을 완전히 대체하는 작업은 약 6개월 후에 종료되었는데, 이는 분석 성능이 크게 중요하지 않은 부분들에 대해서는 시간을 두고 여유 있게 작업했기 때문이었습니다.Spark와 Spark SQL을 활용하여 원하는 데이터를 즉석에서 뽑아내고 공유하는 예제Zeppelin을 활용하여 인기 스티커를 조회하는 dashboard 만드는 예제결론비트윈 데이터 분석팀은 수개월에 걸쳐 데이터 분석 시스템을 전부 재구성하였습니다. 중점을 둔 부분은빠르고 효율적이며 범용성이 있는 Apache Spark, Apache Zeppelin을 활용하는 것최대한 시스템을 간단하게 구성하여 관리 포인트를 줄이는 것두 가지였고, 그 결과는 매우 성공적이었습니다.우선 데이터 분석가 입장에서도 관리해야 할 포인트가 적어져 부담이 덜하고, 이에 따라 Ad-hoc분석을 수행할 수 있는 시간도 늘어나 여러 가지 데이터 분석 결과를 필요로 하는 다른 팀들의 만족도가 높아졌습니다. 새로운 기술을 사용해 본 경험을 글로 써서 공유하고, 오픈소스 커뮤니티에 기여할 수 있는 시간과 기회도 생겼기 때문에 개발자로서 보람을 느끼고 있습니다.물론 새롭게 구성한 시스템이 장점만 있는 것은 아닙니다. 새로운 기술들로 시스템을 구성하다 보니 세세한 기능들이 아쉬울 때도 있고, 안정성도 더 좋아져야 한다고 느낍니다. 대부분 오픈소스 프로젝트이므로, 이러한 부분은 적극적으로 기여하여 개선하여 나갈 계획입니다.비트윈 팀에서는 더 좋은 개발환경, 분석환경을 위해 노력하고 있으며 이는 더 좋은 서비스를 만들기 위한 중요한 기반이 된다고 생각합니다. 저희는 항상 좋은 개발자를 모시고 있다는 광고와 함께 글을 마칩니다.연관 자료: AWS 한국 유저 그룹 - Spark + S3 + R3 을 이용한 데이터 분석 시스템 만들기↩

기업문화 엿볼 때, 더팀스

로그인

/