태그 보관물: 아임에잇

Retrofit2 + RxJava proguard 설정하기

안녕하세요, 이음소시어스에서 안드로이드 앱 개발을 맡고 있는 김범준(준) 입니다.

최근 안드로이드 HTTP Client 라이브러리를 Volley에서 Retrofit2로 옮기는 과정에서 겪었던 ProGuard 관련 문제와 해결과정을 공유하려 합니다.

문제가 발생한 과정은 다음과 같습니다.

1. 새로운 feature에 쓰이는 API 모듈을 Retrofit2를 통해 구현하였습니다.

2. QA 까지 아무 문제가 없어서 구글 플레이에 업데이트 버전을 배포하였습니다.

3. 구글 플레이에서 앱을 다운로드하니 새로 만든 API 모듈에서 에러가 발생했습니다 ㅠㅠ

 

멀쩡하던 API가 배포하니 갑자기 안되다니.. (멘붕)

92_55169f19d5d14_1833

APK 파일을 다운로드해서 테스트하지 않고 Android Studio를 통한 빌드로만 테스트한 것이 문제였습니다.

ProGuard가 APK를 난독화하는 과정에서 문제가 생겼던 것이죠!

정확히 어떤 문제였는지 설명하기에 앞서 먼저 ProGuard와 저희가 사용한 Retrofit2 설정에 대해 간략하게 설명 드리겠습니다.

 

ProGuard 란?

ProGuard는 APK 빌드 과정에서 사용하지 않는 코드를 제거함으로써 크기를 최소화하고, 남은 코드를 난독화를 통해 리버스 엔지니어링을 어렵게하는 툴입니다.

ProGuard의 경우에는 class, method, field의 이름을 줄여 난독화 하게 되는데, 예를 들어 ‘name’이라는 String 타입의 멤버 변수를 ‘a’라고 뜻을 알 수 없는 짧은 단어로 치환하는 것이죠.

이 때, 난독화가 프로그램 작동에 영향을 줄 수 있는 부분들은 ‘-keep class ~’와 같은 방법으로 난독화에서 제외해줍니다.

 

Retrofit2 + RxJava

저희가 Retrofit2를 도입하면서 선택한 구조는 RxJava의 Observable 클래스와  GsonConverter를 이용합니다.

RxJava의 Observable를 통해 어떤 클래스로 response를 받을 것인지 지정해주면, GsonConverter를 통해 response를 JSON으로 파싱하여 지정한 클래스의 각 멤버 변수에 대입해줍니다.

API 콜을 부르면 콜백에 지정한 클래스의 객체를 돌려주니 바로 콜백에서 클래스 메소드를 사용할 수 있습니다. (넘나 편한것..)

Retrofit2 + RxJava의 자세한 설정이 궁금하신 분은 이 링크를 참고해주세요~

 

문제는 Model 클래스의 난독화에 있었다..

위에서 언급한 것처럼 response가 들어오게 되면 GsonConverter가 JSON으로 파싱해주고 Model 클래스의 멤버 변수에 대입해주는데, 이 때 ProGuard가 이 클래스의 멤버 변수를 난독화하면..! 대응 되는 이름의 멤버 변수가 없으니 문제가 생기는 것이죠.

실제로 난독화 된 클래스를 보면 다음과 같습니다. dex2jar 와 JD-GUI를 통해 디컴파일 하였습니다.

멤버 변수가 a, b, c 로 바뀐 것을 확인할 수 있습니다. 그렇다면 문제를 해결하기 위해선 ProGuard가 Model 클래스를 난독화하지 않도록 만들어주면 되겠죠~

 

ProGuard Configuration

Retrofit 홈페이지는 ProGuard를 사용할 시 어떤 설정을 적용해야 하는지 친절하게 써 놓았습니다.

스크린샷 2016-08-05 오후 3.16.38

이것만 적용하고 설정이 끝났다고 생각해서 문제가 생긴 것이죠 ㅠㅠ

keep model

위의 설정은 Retrofit2이 제대로 작동하기 위한 것이고 RxJava를 추가로 사용하기 때문에 따로 설정을 해줘야 합니다.

위과 같이 사용하는 Model 클래스를 난독화에서 제외해주시면 됩니다. 결과는 다음과 같습니다.

원래 멤버 변수 이름대로 있는 것을 확인할 수 있습니다! 앱 또한 정상 작동하였습니다.

Inner 클래스를 사용하는 경우 몇몇 변수가 난독화가 되는 일이 일어나기도 한다고 합니다. 이런 경우에는 다음과 같이 Inner 클래스도 난독화에서 제외하는 코드를 추가해줍니다.

해결!

 

후기

저에겐 안드로이드 앱 배포가 이번이 처음이었는데요, 예상치 못한 오류가 터져서 고생을 했지만 그래도 해결하고 나니 많이 배운 것 같아 뿌듯하네요 ㅎ

두서 없는 글 읽어주셔서 감사합니다~ (ㅡ)(__)

 

IM8(아임에잇) 오토스케일링 적용기

안녕하세요. 이음소시어스 개발팀 서버개발자 육승찬(루)입니다.

오늘은 이음소시어스의 핫한 서비스 “IM8(아임에잇)“의 오토스케일링 적용경험을 공유하려고 합니다. 먼저 오토스케일링에 대한 이야기를 하기 앞서서, 아임에잇이 어떤 서비스인지 간단하게 설명하고 시작하겠습니다.

IM8 (아임에잇) ?

직장인을 위한 프리미엄 소개팅 서비스

im8_1

IM8은 2013년 10월에 런칭되어 “까다로운 신원인증으로 높은 매칭 신뢰도를 주자”를 목표로 열심히 발전해 왔습니다. IM8의 8은 8개의 회원 타입(스마트커리어비주얼유니크글로벌밀리언패밀리액티브)을 의미합니다.

im8_2

현재 아임에잇의 모든 서버는 AWS를 통해서 운용하고 있습니다. 그럼 본격적으로 AWS x IM8 오토스케일링 적용기를 공유해보겠습니다. GOGO~

기존 서버

처음에는 IM8 API 서버를 EC2(t2.medium) 한대로 운영해왔습니다. 이후 트래픽의 증가에 따라 ELB에 EC2(t2.medium) 서버 2대를 붙여 운영하는 구조로  발전했습니다. 하지만 점점 더 트래픽이 증가하면서 피크 시간대에 서버가 터지는 일이 발생하기 시작했습니다. 그래서 현재 서버구조의 문제점을 정리하고 문제점을 개선하기로 결정했습니다.

서버 증설 방법

1. ELB에 서버를 더 붙이자 OR 서버의 스팩을 올리자 !

하루 두 번 푸시가 갈 때 아임에잇에 가장 접속자가 많이 몰립니다. 왜냐하면, 실제 매칭이 되는 시점에 사용자가 들어와서 상대방의 프로필을 확인하는 구조이기 때문입니다. 그래서 일반시간대에는 많은 서버가 필요 없습니다. 이 방법의 장점은 적은 시간 안에 문제를 해결할 수 있다는 점입니다. 하지만 비용적인 측면에서 굉장히 안 좋다고 판단했습니다. 접속자가 많지 않아도 항상 여러 대의 서버가 띄워져 있고 비용을 계속 지불하기 때문입니다.

2. AutoScaling (v)

접속자가 별로 없는 시간에는 최소한의 서버를 유지하고 피크 시간대에 여러 서버를 유지하는 방법입니다. 하루 두 번 피크가 있는 아임에잇에게 아주 좋은 방법이라고 생각합니다. (대부분 서비스에 좋지 않을까하는 생각이.. 😀) 평상시에는 최소한의 서버를 유지하기 때문에 비용적인 측면에서도 굉장히 유리했습니다. 그래서 이번 기회에 아임에잇에 AutoScaling을 적용해보기로 했습니다.

AutoScaling 준비하기

  • 결제서버 분리
  • 통일된 인스턴스 만들기
  • Scaling되는 서버를 항상 최신으로 유지하기
  • Scaling Rule 정하기
  • 서버를 띄우는 시간이 있기 때문에 푸시가 나가기전에 미리 띄우기 (Pre-warm)

1. 결제서버 분리

아임에잇은 모바일과 웹에서 PG서비스를 붙여서 결제할 수 있도록 제공하고 있습니다. 특정 PG는 요청 가능한 서버의 IP를 제한하고 있는 경우가 있어서 결제서버를 분리해야 했습니다. 기존 서버에서 사용하던 IP를 결제서버에 붙이고 내부에서만 통신 가능한 결제서버를 만들었습니다.

  • 기존 서버구조
기존 서버 구조

기존 서버 구조

  • 결제서버를 분리한 구조
새로운 서버 구조

새로운 서버 구조

2. 통일된 인스턴스 만들기

AWS에서 제공하고 있는 오토스케일링 기능은 미리 정의한 규칙에 따라서 인스턴스가 띄워지고 내려가는 형태로 동작합니다. 인스턴스는 AutoScalingGroup에 등록해놓은 AMI를 이용하여 띄우게 됩니다. 원래 사용하고 있던 서버 2대는 완전히 같은 인스턴스가 아니였습니다. 하나를 나중에 띄웠기 때문에 시스템 라이브러리 같은 경우 조금씩 버전이 달랐습니다. 그래서 이번 기회에 통일된 인스턴스를 만들고자 했고, 구조는 아래와 같습니다.

server

  • 시스템 라이브러리 설치 (ex. git)
  • API Rails App 설정
  • Puma 설정
  • Nginx 설정
  • Papertrail 설정 (로그 수집)

3. 서버를 항상 최신으로 유지하기

오토스케일링 되고있는 서버들은 항상 최신의 코드를 유지해야 하고 같은 응답을 줄 수 있어야 합니다. 아임에잇은 Git으로 소스코드를 관리하고 배포에는 Capistrano를 사용합니다. 지금까지는 서버 2대만을 최신으로 유지하면 됐기 때문에 아래의 순서로 배포를 해왔습니다.

  • Git fetching
  • whenever에 등록된 스케줄링 crontab에 등록
  • Puma Reload

오토스케일링을 적용하기 시작하면 서버가 딱 몇 대라고 가정할 수 없습니다. 그래서 모든 서버를 항상 최신으로 유지하기 위한 아래 두 가지 정리해봤습니다.

  • EC2 Instance가 시작되는 시점에 최신으로 만들기

EC2 설정 중에 “User Data“라는 부분이 있습니다. 인스턴스가 띄워지는 시점에 명령어를 실행시킬 수 있도록 할 수 있습니다. 여기서 소스코드를 최신상태로 유지하고 puma를 띄우는 등의 명령을 할 수 있습니다. 이렇게 하면 스케일링 돼서 실행되는 서버도 최신코드를 유지할 수 있게 됩니다. 쉽고 빠르게 모든 서버의 최신상태로 유지할 수 있지만 매 서버가 띄워질 때마다 Ruby 라이브러리 설치(bundle install)하기 때문에 서버가 띄워지고도 최신상태로 유지하는데 시간이 더 걸린다는 단점이 있습니다.

  • 최신의 AMI를 사용하기

이 방법은 미리 최신의 AMI를 항상 유지하고 서버가 띄워지는 부분에 대해서는 신경을 쓰지 않아도 됩니다. 코드를 한 번만 최신으로 유지하면 되고 라이브러리 설치 시간도 매우 줄일 수 있다는 장점이 있습니다. Ruby 라이브러리 중에 ELBAS라는 라이브러리가 있습니다. Capistrano와 연동되어 배포시 실행되고 있는 인스턴스를 최신으로 만들어주고, AMI까지 만들어주는 아주 좋은 녀석입니다… 이 라이브러리를 사용하면 빠르고 쉽게 항상 최신의 AMI를 유지할 수 있습니다.

4. Scaling Rule 정하기

처음에 AutoScalingGroup만 만들면 아무리 기다려도 서버는 스케일링 되지 않습니다… 그래서 어떤 규칙에 의해서 스케일링 할 건지 정해줘야 합니다. 먼저 AutoScalingGroup 설정을 보시면 “Instances“, “Desired“, “Min“, “Max“가 있습니다. Instances는 현재 서버의 개수, Min은 최소 서버 수, Max는 최대 서버 수를 의미합니다. Desired는 스케일링이 돼서 현재 몇 대를 희망하고 있는지를 의미합니다. Desired와 Instances의 수가 같으면 더는 서버를 띄우거나 내릴 필요가 없다는 의미입니다.

아임에잇에서는 서버를 띄우는 규칙 2지와 서버를 내리는 규칙 2가지를 사용하고 있습니다.

  • Add 1 instance (CPUCreditBalance < 9)

10분안에 CPUCreditBalance의 수치가 9 미만이라는 알람 두번이 울리면 인스턴스를 띄워라!

이 규칙은 CPUCreditBalance 수치에 따라서 동작하게 됩니다. 아임에잇 서버는 현재 t2계열의 인스턴스를 사용하고 있습니다. T2계열의 인스턴스에는  CPUCredit라는 개념이 존재합니다. 크레딧이 부족하면 CPU 연산 능력이 매우 떨어지기 때문에 새로운 서버 한 대를 띄우도록 하고 있습니다.

  • Add 2 instances (CPUUtilization >= 60)

2분안에 CPUUtilization의 수치가 60% 이상이라는 알람 두번이 울리면 인스턴스를 띄워라!

평소에는 CPU 사용률이 평온하다가 피크시간이 되면 갑자기 막 올라갑니다. 그래서 CPU사용률이 60% 이상이 되면 인스턴스 2대를 띄우도록 하고 있습니다. (피크시간이 아니면 올라갈 일이 거의 없기 때문)

  • Delete 1 instance (CPUUtilization < 25), Delete 2 instances (CPUUtilization < 15)

1분에 한번씩 CPU 사용률이 낮다는 알람 10번이 울리면 인스턴스를 내려라 !

피크시간이 지나고 나면 급격히 사용률이 떨어지기 때문에 인스턴스를 내리기 시작합니다. 하지만 인스턴스를 내리는 옵션을 상당히 까다롭습니다. 알람이 울리는 10분은 같지만 연속적으로 10번이 울려야 하기 때문입니다. 이유는 인스턴스를 내려도 되는 상황인지 확실시 하고 싶었기 때문입니다. 대게 인스턴스를 띄워서 생기는 문제보다 내리면서 생기게 되는 문제가 많기 때문입니다..

5. Pre-warm

아마 대부분의 프로그래머분들은 장애를 원하지 않을 것 입니다. 이음소시어스 개발팀도 장애가 나지않게 하기 위해서 노력을 하고 있습니다. 아임에잇 서비스의 경우 12:30, 19:00에 푸시를 보내고 있습니다. 실제로 푸시를 보내고 나서 오토스케일링 룰에 따라 서버를 띄우기 시작하면 대처가 좀 늦습니다. 갑자기 들어오는 트래픽을 버티지 못하고 아예 서버 한대가 죽어버리는 일도 있었습니다. 그래서 12:20, 18:50에 서버를 미리 띄워놓고 있습니다.  서버를 미리 띄우는 코드는 Python으로 작성했고 Crontab에서 실행되도록 하고 있습니다.

 

정리

이번 포스팅에서 아임에잇의 오토스케일링 적용 경험을 적어보았습니다. 사실 이번 포스팅을 작성하면서 너무 뻔한 글이 되지 않을까 걱정이 많았습니다. 이미 오토스케일링에 대한 글도 많고 참고할 부분도 많아서 잘 쓸 수 있을지 고민이 되었기 때문입니다. 그래서 더 자세히, 숨김없이 써보려고 노력했습니다.

오토스케일링을 적용하면서 가장 크게 얻은 두 가지는 비용절감과 불안감이 사라졌다는 것입니다. 기존 서버 비용 보다 45%의 비용을 절감할 수 있었습니다. 그리고 평소보다 가입자가 많은 날은 트래픽에 서버가 죽을까 하는 불안감이 있었습니다. 오토스케일링이 만병통치약은 아니지만 룰에 의해서 확장하고 수축하는 구조 덕분에 더 많은 트래픽을 커버할 수 있게 되어 불안감이 해소되었습니다.

마치며..

이번 오토스케일링을 사용하면서 ELBAS라는 라이브러리가 참 좋다고 느꼈습니다. 너무 쉽게 AMI를 최신으로 유지할 수 있는 방법을 제공해줬기 때문인데요. 그래서 이 라이브러리의 핵심적인 부분을 파이썬 코드로 작성해봤습니다 (파이썬을 오래 쓰다보니 재밌는건 파이썬으로 구현해보고 싶은 맘이 생기네요..)