2015년 4월 28일 화요일

Spark ubunto에서 설치

Spark는 버클리 학생들이 아이디어를 제안해서 만들어진 프로젝트이다. 빠르게 버전업 되는 핫한 프레임워크이다. 단지 워낙 빠르게 버전업되고 만들어진지 얼마 안 돼다보니 안정화가 들 돼어 있다. 즉 버그가 많다. 그렇지만 큰 데이터를 분석할 때 그런 듯 싶고 우리가 쓰는 환경은 한 번에 최대 50Mbyte 정도의 데이터만 처리하면 돼기 때문에 In-memory에서 메모리가 깨지는 문제는 아직까진 발견할 수 없다. Gbyte단위로 분석하면 깨지지 않을까 추측을 해본다.

설치라고 해서 할 건 별로 없다. 실행 이미지가 들어 있기 때문이다.

아래주소로 방문해서
http://spark.apache.org/downloads.html

원하는 패키지를 다운 받는다.

$ wget http://apache.mirror.cdnetworks.com/spark/spark-1.3.1/spark-1.3.1-bin-hadoop2.6.tgz

$ tar xvzf spark-1.3.1-bin-hadoop2.6.tgz

$ cd conf/
$ cp spark-env.sh.template spark-env.sh
$ vi spark-env.sh

# 원하는 설정을 넣어주면 된다. 우리는 다음과 같이 넣었다.
export SPARK_WORKER_OPTS="-Dspark.worker.cleanup.enabled=true -Dspark.worker.cleanup.appDataTtl=3600"
export SPARK_MASTER_IP=192.168.0.1

export SPARK_WORKER_MEMORY=8g
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH

$ cp spark-defaults.conf.template spark-defaults.conf
$ vi spark-defaults.conf

# 여기에도 원하는 설정을 넣어주면 된다.
park.master    spark://192.168.0.1:7077
spark.default.parallelism   4

$ cp slaves.template slaves
$ vi slaves
# 노예들을 다 적어 주면 된다. 일할 서버 아이피들..
192.168.0.1
#192.168.0.2
#192.168.0.3

$ cd ..
$ ./sbin/start-all.sh

만약 다음과 같은 에러가 났다면..
Exception in thread "main" java.net.UnknownHostException: test: test: unknown error
        at java.net.InetAddress.getLocalHost(InetAddress.java:1484)
        at org.apache.spark.util.Utils$.findLocalIpAddress(Utils.scala:771)
        at org.apache.spark.util.Utils$.localIpAddress$lzycompute(Utils.scala:763)
        at org.apache.spark.util.Utils$.localIpAddress(Utils.scala:763)
        at org.apache.spark.util.Utils$.localIpAddressHostname$lzycompute(Utils.scala:764)
        at org.apache.spark.util.Utils$.localIpAddressHostname(Utils.scala:764)
        at org.apache.spark.util.Utils$$anonfun$localHostName$1.apply(Utils.scala:816)
        at org.apache.spark.util.Utils$$anonfun$localHostName$1.apply(Utils.scala:816)
        at scala.Option.getOrElse(Option.scala:120)
        at org.apache.spark.util.Utils$.localHostName(Utils.scala:816)
        at org.apache.spark.deploy.worker.WorkerArguments.<init>(WorkerArguments.scala:29)
        at org.apache.spark.deploy.worker.Worker$.main(Worker.scala:528)
        at org.apache.spark.deploy.worker.Worker.main(Worker.scala)
Caused by: java.net.UnknownHostException: anal01: unknown error
        at java.net.Inet4AddressImpl.lookupAllHostAddr(Native Method)
        at java.net.InetAddress$2.lookupAllHostAddr(InetAddress.java:907)
        at java.net.InetAddress.getAddressesFromNameService(InetAddress.java:1302)
        at java.net.InetAddress.getLocalHost(InetAddress.java:1479)

        ... 12 more

아래를 추가해주고
$ sudo vi /etc/hosts
127.0.1.1       test

다시 실행
$ ./sbin/start-all.sh


2014년 10월 6일 월요일

Install Graphite and statsd on Ubuntu 12.04 LTS

https://gist.github.com/bhang/2703599

여기에 있는 스크립트 대로 실행하고

sudo python manage.py syncdb

이거 실행할 때 에러가 나면..

/tmp/graphite_reqs.txt 파일에
django-tagging

이렇게된 부분을

django-tagging==0.3.1

이렇게 수정하고.

아래 부분을 다시 한 번 실행시키면 된다.
sudo pip install -r /tmp/graphite_reqs.txt

2014년 9월 26일 금요일

requirejs + angularjs 에서 requirejs 걷어내기와 grunt 적용하기

requirejs 걷어내는 이유


  • requirejs에서 제공하는 r.js를 사용하여 minify, gzip, versioning을 하면 오래 걸린다. 800초 정도. (1 core CPU, 2GB Memory).
  • concatenation이 안 된다. 이 부분은 우리 소스 구현 방식 때문인지 play!framework에서 제공된 r.js가 안 해주는지 아님 몰라서 못 하는 건지 모르지만 현재 안 되고 있다.
  • angularjs 개념도 어려운데 requirejs까지 넣었더니 이해를 못 하는 부분이 너무 많다. 이해하는데 시간도 부족하고 angualrjs만으로도 충분히 프로젝트를 진행시킬 수 있다는 판단이 들며 특히나 requirejs가 해주는 부분이 미약하다. 외부 library의 의존성 정도 유지 보수 하는 정도. 이것 또한 angularjs가 충분히 할 수 있다고 판단되어진다.

작업내용

index.scalar.html에 해당 js 파일을 모두 include 시킨다. 





여기서 /assets/admin/js/admin.js를 삭제하고 /assets/admin/js/app.js에 chucar.admin 모듈을 다음과 같이 선언해주고 그 모듈에 모든 서비스 관련 directives, services, controllers 등을 추가한다.












Grunt를 적용하는 이유

  • requirejs를 걷어 냄으로써 대신 minify, concatenation, gzip를 해주는 스크립트이다.

작업내용

node.js 설치

grunt 설치
$ npm install -g grunt-cli
$ npm install grunt --save-dev
$ npm init
--> 이 때 name을 js나 node로 넣지 말랜다. It's assumed that it's js, since you're writing a package.json file, and you can specify the engine using the "engines" field.
Gruntfile.js 파일 생성

grunt-contrib-uglify, grunt-contrib-concat 은 없기 때문에 다음과 같이 설치
$ npm install grunt-contrib-uglify --save-dev
$ npm install grunt-contrib-concat --save-dev

2014년 8월 28일 목요일

츄카 시스템 구조 소개 - 하편

3. 하지만 이미지 파일 서버와 이미지 전송은 AWS에서

츄카(https://chucar.co.kr) 서비스 오픈전에 마이메이크업스튜디오(http://mymakeupstudio.co.kr, 이하 마메스)라는 서비스를 개발했었습니다. 이 서비스도 NCloud기반으로 인프라를 구성했고 NCloud의 파일 스토리지를 사용했구요.  마메스가 두달정도 실운영되고 사용자가 증가되는 상황에서 큰 서비스 장애가 두번 발생했었습니다. 원인은 NCloud 파일 스트리지 장애였구요.

물론 100% 완전 무결한 시스템은 없다고 생각합니다. 장애는 발생가능하고 그걸 빠르게 알고 복구할수 있고 프로세스적으로 장애상황이 발생하지 않도록 하는 것이 중요하다고 생각합니다만. NCloud의 경우는 문제가 많았다고 생각하고 있습니다.

첫 장애때 상황을 보면 그날 NCloud 공지로 콘솔 시스템 업그레이드를 한다고 했었습니다. 오후 6시로인가에 잠시 콘솔을 사용할수 없다고 공지가 되었고 실서비스는 문제가 없다고 했습니다.
하지만 그렇지 않았습니다. 파일 스토리지로 파일 업로드가 되지 않는 상황이 발생하였고 두시간여 동안 장애가 지속된 것으로 기억합니다. 당시 파악된 원인은 파일스토리지 API 서버에 문제가 있던 것이었고 콘솔 업그레이드를 하면서 파일스토리지 API 서버도 함께 변경이 된것이 문제였습니다.
어이없는 일이었죠. 1) 콘솔 업그레이드만을 공지하고 다른 시스템도 함께 건드린것. 2) 실서비스되고 있는 시스템을 오후시간에 건드린것. 도저히 네이버의 운영이라고 생각할수 없는 일이었습니다. 차라리 공지가 제대로 되었고 오후가 아닌 새벽시간에 작업이 이루어졌으면 저희도 대응을 했을텐데.
이일로 강력하게 항의를 했고 요금할인을 받는 것으로 마무리 했습니다. 담당자분에게 이런 프로세스적인 장애가 발생하지 않도록 노력하겠다는 다짐을 받았구요. 사용자에게 나쁜 서비스 경험을 준것은 어찌할수 없었지만요.

바로 다음주에 장애가 또 발생했습니다. 오후 9시 이후에 업로드된 파일이 일정시간후 삭제되는 그런 엄청난 장애였습니다. ㄷㄷㄷ. 시스템 변경 공지도 없었고 장애가 난지 일주일밖에 되지 않은 상황에서... 이번에는 NCloud의 API 서버에 로직이 잘못되어서 파일을 삭제하게 되었다고 하더군요. 제대로 테스트되지 않은 로직을 배포한 것도 문제지만 시스템 변경 공지도 없었다는 건 더 문제 였습니다. 정말 어이없지만 원인보고만 받고 바로 다음날 AWS S3로 바로 마이그레이션 했습니다.

저도 개발자이고 서비스를 만드는 사람으로써 장애는 일어날수 밖에 없다고 생각합니다. 대신 장애를 줄이는 노력을 해야하고 그것을 위해서 다양한 방법을 동원해야죠. 그런데 앞의 두 사건은 프로세스 그리고 그걸 운영하는 사람의 문제였다고 생각되어서 쉽게 해결될수는 없다고 봤습니다. 프로세스는 문화로 오랜시간 만들어진 것이기 때문에 쉽게 바꿀수 없고 바꾸려면 시간이 걸리는 것이라고 생각했기 때문입니다.

참고.
- 가상 서버는 여전히 NCloud를 잘 사용하고 있습니다.
- 얼마전에 NCloud를 파일 스토리지가 장애라면서 전화 연락이 왔었습니다.(두번의 장애로 먼저 연락을 주더군요.) S3로 바꾸길 잘했다고 속으로 생각했습니다.

4. 앱/웹 모두 단일 API 서버를 통해 서비스

요즘 서비스를 개발하면 앱은 기본으로 개발하고 웹서비스는 추가로 개발하곤 합니다. 그런데 츄카(https://chucar.co.kr)는 앱/웹을 모두 개발해야한다고 전략적으로 판단했고 두가지 클라이언트를 지원해야 했습니다. 그럼 앱용 API 서버, 웹서비스용 서버 이렇게 두가지가 개발되는 것이 일반적입니다. 이때 비지니스 로직은 API 서버와 웹서비스 서버 양쪽에 두거나 레이어를 하나더 만들어서 API, 웹서비스 - 비지니스 로직 - DB 이렇게 구성해야 할 것입니다.
비지니스 로직을 두벌 만드는 건 서비스 유지보수나 일관성 측면에서 문제가 있고 레이어가 추가되는건 효율 측면에서 문제가 있습니다. 그래서 API 서버를 하나로 두고 웹/앱 모두에서 사용가능하도록 하고 싶었고 그러기 위해서 웹에서도 API 서버로 서비스 요청을 처리하도록 해야했습니다.

이때 사용할 수 있는 기술은 여러가지가 있을수 있습니다. 플래시, 실버라이트류의 기술 또는 ajax 를 이용한 기술. 당연히 플래시류의 기술은 검토 대상이 아니었고 ajax를 이용한 클라이언트를 생각했습니다. 그동안의 경험상 일부분이 아닌 전체를 웹페이지를 ajax를 통해 구성하는건 헬게이트를 제손으로 직접 여는 것이므로 javascript MVC framework 도입을 검토했고 다음과 같은 그래프로 쉽게 결론 내렸습니다.


http://www.google.com/trends/explore#cat=0-5&q=angularjs,%20backbone.js,%20ember.js,%20knockoutjs&date=today%2012-m&cmpt=q

그래프 상에서 파란선이 angularjs 입니다. 이 그래프만 봐도 다른것을 선택할 이유는 없어보입니다.
초반 세팅과정이 쉽지는 않았습니다. 처음 접해보는 것인데다가 requirejs에 엮어서 쓰려고 했더니 애로가 많았습니다. 다행히 https://github.com/mariussoutier/play-angular-require-seed 시드 프로젝트 도움을 받아서 세팅하고 하나씩 배우면서 개발을 진행했습니다. 이전에 jquery 만을 사용하던 방식에 비하면 이건 정말 신세계 였습니다. 안드로이드나 윈도우 애플리케이션을 개발하는 느낌으로 웹 프론트앤드를 개발할수 있다니. 특히나 인상적인건 angularjs는 html상에서 directive나 attribute로 표현되기 때문에 퍼블리셔와 협업이 이전보다 수월했고 데이터 바인딩 방식으로 인해서 코드량도 드라마틱하게 줄고 결국 생산성이 높아졌습니다.
개발하면서 문제가 없는건 아니었는데 새로운 프레임워크를 배워야 해서 발생하는 점은 제외하더라도 그동안 어려웠던 걸 쉽게 해준 대신에 쉽게 되었던걸 어렵게 만들어버렸다는 점입니다.
angularjs 모듈화가 되지 않은 외부 라이브러리/API 를 바로 사용하기가 힘들다는 것이었는데 이부분은 다른 글에서 좀더 자세하게 말씀드리겠습니다.

어째거나 angularjs를 도입해서 단일 API 서버로 웹/앱을 모두 지원하도록 했습니다.

5. 이중화는 서버가 놀고 있어도 해야 하는 것

서비스 시작시 트래픽은 특별한 서비스가 아니고서야 저사양의 서버 한대로 충분히 커버가 가능한 양일 것입니다. 츄카도 다르지 않아서 CPU가 올라가는 모습은 거의 볼수 없는데요.
혹시 모를 상황에 대비하기 위해 API 서버/DB 서버 모두 이중화했습니다. 
API 서버는  NCloud의 로드밸런서를 이용해서 이중화했습니다. 클라우드 서비스를 사용하는 장점으로 값비싼 L4장비를 저렴하게 이용할수 있었고 서버 이미지를 떠서 저장할 수 있기 때문에 유사시 동일한 서버를 바로 생성해서 서비스에 투입할수 있습니다.(아직 그래본적은 없다는...) 

DB 서버는 mysql replication을 이용해서 master-slave 구조로 이중화 했습니다. slave는 read only 로 설정해서 통계용 데이터 추출등에도 사용해서 실서비스에 영향을 주지 않고 내부적인 데이터 사용이 가능하도록 했습니다. 그리고 나중에 서비스가 커져서 DB 부하가 문제가 되는 경우 select query는 slave에서 읽도록 하는 등의 방법을 고려해 볼수도 있습니다.


by 치킨연구소 테리야끼



2014년 8월 23일 토요일

츄카 시스템 구조 소개 - 상편

츄카 시스템은 다음 그림과 같은 구조로 설계되었습다. Client-Server-DB로 이어지는 전형적인 3-Tier구조로 아키텍처의 설명보다는 각 tier에서 왜 특정 솔루션을 선택했는지 이유를 중심으로 소개하려고 합니다.


츄카 시스템 구조도

1. Play Framework 와 Java

어떤 언어를 사용할지 또 그 언어 기반으로 어떤 프레임워크를 사용할지 가장 많이 고민했던 부분입니다. 우리에게 맞는 것이 어떤것인가를 생각했을때 스타트업이라는 것과 개발리소스가 부족할 것이라는 것에서 다음 두가지 조건이 가장 중요하다고 봤습니다.

- 생산성
- 유지보수성

두가지 조건이 통하는 면도 있고 어떤 부분은 상충되는 부분도 있다고 생각되는데 이 두가지를 적절히 조화할 수 있는 것이 무었일까 생각했을때 결론이 Java와 Play Framework 였습니다.

먼저 생산성 측면에서 보면 Java 개발에 필수라고 할수 있는 Eclipse나 Intellj 같은 IDE의 파워가 강력하고 Java로 되어 있는 수많은 오픈소스와 개발 리소스들도 생산성에 큰 도움을 줄수 있다고 봤습니다. 하지만 python, php 와 같은 언어와 같은 인터프리터가 아니기 때문에 개발 결과물을 보기 위해서 compile 과정을 거치고 또 서버를 재시작하는 과정이 문제이기는 합니다.
Play Framework은 이런 문제를 해결하기 위해 triggered execution 이란 기능을 제공합니다. 이름에서 대충 예상되듯이 소스가 변경되면 compile을 수행하고 서버 재시작없이 결과물을 확인 가능합니다. 이런 기능이 있기 때문에 인터프리터가 가질수 있는 장점을 Java 개발에서도 경험할 수 있습니다.
또한 Play Framework 은 경량프레임워크이기 때문에 간단한 설정으로 시작이 가능하고 비지니스 로직 개발만 집중해서 개발만 할수 있도록 되어 있습니다.

두번째 유지보수성 측면에서는
일단 유지보수성이 훨씬 중요한 요소라고 생각되는데 개발보다 유지보수 기간이 더 긴것이 당연하고 또 스타트업의 특성상 오픈이후 실 사용자들의 반응을 보고 개편을 하는 것이 더 중요하기 때문입니다. 이런 측면에서 볼때 python, php 같은 언어들은 문법적 오류를 코드가 실행되어야 알수 있는데 이점은 그동안의 경험상 굉장히 유지보수를 힘들게 하는 지점이었습니다.
물론 테스트 케이스를 촘촘히 잘만들면 된다고 하지만 테스트 코드의 코드 커버리지를 100%로 만드는 것은 현실적으로 불가능한 일이기도 하고(사실 50%도 쉽지 않죠) DB 작업이 대부분인 웹서비스의 테스트 코드는 테스트를 위한 데이터 준비도 만만치 않은 작업입니다.
그래서 Java같은 static typing  언어가 유지보수성에서는 좋다고 판단했습니다. 코스 수정시에도 변수명이나 함수명을 수정하면 이것을 사용하는 부분에서 바로 compile error가 발생하고 이걸 쉽게 파악할수 있기때문에 이런 static typing의 특성은 시스템이 커지고 복잡해 질수록 유지보수성에 도움을 준다고 생각합니다.

참고
playframework - https://www.playframework.com/
sbt - http://www.scala-sbt.org/
triggered execution - 이 기능은 정확히는 play framework 의 빌드 툴인 sbt 의 기능입니다. 하루종일 개발을 하다보면 가끔 비정상적으로 빌드가 되는 경우가 있어서 가끔은 클린빌드를 해줘야 하기는 합니다. --;

2. Ncloud를 서버 인프라로

저희 개발팀은 모두 N사 출신입니다. 아주 큰회사죠. 스타트업을 시작하고 나서 다시 한번 느꼈습니다. "내가 맡아서 개발한 부분은 정말 작은 것이구나. 내가 개발하기 위해 정말 많은 사람들이 일하고 있구나."

그중에서 인프라 구축은 큰 리소스가 들어가는 일입니다. 인력도 그렇고 돈도 그렇고. 서버를 구매해서 사용한다면 초반에는 트래픽이 적다고 낮은 스펙의 서버를 구매해서 쓸수는 없습니다. 왜냐하면 트래픽이 증가할때를 생각해야되고 그때 서버를 교체한다면 남는 낮은 사양의 서버의 처리도 애매하고 등등.

그래서 클라우드 서비스를 이용하기로 했습니다. 처음에는 AWS를 생각했습니다. 국내 서비스인 S사나 K사는 소문이 별로 좋지 않아서 간지나게 AWS를 쓰기로 했죠.
free tier 수준에서는 1년동안 무료로 사용이 가능해서 개발기간 동안은 free tier 수준으로 세팅해서 사용했습니다. 이제 실서버를 세팅하려는데 비용이 얼마나 나올지 전혀 예상이 안되더군요. 사용량을 넣으면 비용을 계산해주기는 하는데 항목이 너무 세세하고 복잡해서 어떻게 넣어야 할지 모르겠더라구요. 이미 서비스가 되고 있는거면 예상을 해서 넣겠는데 그런것도 아니고 지금 생각해보면 분명 월 100만원에 훨씬 못미치는 금액에서 +- 였을텐데도 예측이 안되니까. 그게 좀 힘들었습니다.

그러다가 네이버 ncloud 소식을 듣게 되었습니다. 네이버도 클라우드 서비스를 하고 있더군요. 알려지지 않아서 그렇지 내부 서비스도 여기서 서비스를 하고 많은 업체가 들어가 있다고 하더군요.
당장 담당자와 연락해서 설명을 들었습니다. 아주 심플했습니다. 서버를 몇대 쓰면 얼마가 나오겠다가 바로 계산되니까. 좋았어요. 게다가 메뉴얼도 한글이고--;;
가격 정책도 심플하지만 서비스도 심플하고 콘솔도 심플했습니다. "뭐 우리는 다른 거 필요없음. 그냥 서버만 있으면 됨" 이라고 생각하고 계약하고 바로 세팅 진행했습니다.
있어야 할건 다 있고 없을건 없는 그런 클라우드.

하지만 얼마 지나지 않아서 엄청난 일이 발생하는데...

참고
ncloud - https://eco.ncloud.com/noauth/product/main

오늘은 여기까지 쓰겠습니다. 집에 가야 해서... 다음편에 Ncloud에서 일어난 엄청난 일과 남은 주제에 대해서 말씀드리도록 할께요.

by 치킨연구소 테리야끼

사용자 웹 페이지 로딩 속도 개선

1. 현상
메인 페이지에서 헤더와 풋터가 뜨고 바디가 뜨는데 그 간격이 커서 느리게 뜨는 듯 함. (간극이 한 1.2 ~ 1.7로 복불복..)

2. 목표
최소한의 소스 변경으로 간극을 줄여보자~

3. 분석
> 방법 : 웹 브라우저 개발자도구를 이용해서 분석 (크롬에 있는)
> css파일이 뜨고, 외부 스크립트(다음 맵, 구글 Analytics, 리타겟팅)와, 내부스크립트(jquery, requirejs, angularjs, 우리가 정의한 스크립트들)이 모두 한번에 다 로딩된 뒤 바디가 로딩됨.

4. 문제점
> 스크립트들이 너무 많아~ 99개(service 42개, 기본 library 16개, 내외부 library 41개)
> 스크립트들과 css파일들은 용량이 하나하나 작지만 각 파일에 공백이 차지하는 크기가 무시 못 하겠네~
> 압축을 하면 더 줄 겠네?
> 메인 페이지에서 쓰는 스크립트들 보다 더 많은 스크립트가 로딩되네~
> 이미지도 우리 웹서버에서 요청하네~
> 우리 스크립트들이 로딩되는 중간에 외부 스크립트들이 먼저 로딩되서 실행되네~

5. 이번 사용자 메인 페이지 로딩 속도 개선의 방향
> 공백을 줄이는 minify를 하자~
> 압축을 하자~
> 스크립트를 줄여보자~

6. 방법
> minify : requirejs에서 제공하는 r.js를 이용하여 줄임 (play!framework에서 지원하고 있음)
> 모든 자바스크립트들을 gzip으로 압축 (play!framework에서 지원함)
> 메인 페이지 로딩시 필요한 스크립트 개수를 줄임
>> 공통 library 중에 합칠 수 있는 건 합치기 (내외부 library 41개 -> 18개)
>> lazy loading 기법을 써서 페이지에 필요한 자바스크립트를 로딩하도록 수정

7. 결과
기존 대비 간극이 (1.05 ~ 1.2로 바뀌었음)
기대효과는 드라마틱하게 1초 이내로 떨어질 것으로 기대했으나…

8. 향후 과제
> 아직도 lazy loading을 적용해하는 것들이 있지만 소스 변경 범위가 커서 차츰차츰 적용하는 것으로
> 외부 스크립트들을 메인 서비스 가 뜬 뒤에 로딩하고 동작하도록~
> 이미지들을 파일 서버로~

9. Why Faster Websites Make More Money

Both Amazon and Walmart realized the missed opportunity here and discovered that just a 100 millisecond improvement to their page loading times translated into a 1% increase in sales.

2014년 8월 22일 금요일

Ubuntu 12.04 에서 최신 nginx 설치하기.

$ sudo service nginx stop
$ sudo add-apt-repository ppa:nginx/stable
$ sudo apt-get update

$ sudo apt-get upgrade (하면 커널이 바뀐다.)
or
$ sudo apt-get install nginx

$ sudo service nginx start