왜 레진코믹스는 구글앱엔진을 선택했나
DESCRIPTION
프리미엄 웹툰서비스, 레진코믹스는 구글 앱 엔진 위에서 서비스 되고 있습니다. 레진코믹스가 왜 구글 앱 엔진을 선택했는지, 주로 쓰고 있는 기능들과 함께 개발하면서 생긴 다양한 이슈와 팁들에 대해 이야기합니다. (2014.01.22 GDG Seoul Meetup)TRANSCRIPT
왜 레진코믹스는
구글 앱엔진을 선택했나
사용자, 즉 개발자는
개발자 누구
•@curioe , http://curioe.com // 궁금한, 이상한
•레진엔터테인먼트 서버 개발자 1명 (2014.01)
•만명 기업 > 천명 기업 > 600명 기업 > 9명 기업에 코드를 남김
•할 일과 < 역할이 < 점점 더 < 많아짐
다이나믹 타임트리2013년 2월 3월 4월 말
Open PaaS! 백수...
No! 락 인
레진 합류Cloud Foundry
다이나믹 타임트리2013년 2월 3월 4월 말 6월 초
Open PaaS! 백수... 레진 합류
ㅠ.ㅠ
서비스 오픈 40일 후! !
서버 개발자 오직 나 하나... !
기획/요구사항은 개발하면서 진행됨 orz
다이나믹 타임트리
2013/04
레진 합류
인프라는 인프라팀이!!
데이터베이스는 DBA가!!
개발은 분담
Before
다이나믹 타임트리
2013/04
레진 합류
인프라는 인프라팀이!!
데이터베이스는 DBA가!!
개발은 분담
인프라 내가 해야 함!!
데이터베이스 내가 해야 함!!
개발 나 혼자 해야 함
Before After
“앱 엔진 씁시다”
@xguru
구글 앱 엔진
2013/04
레진 합류
인프라는 인프라팀이!!
데이터베이스는 DBA가!!
개발은 분담
PaaS 앱엔진으로
Datastore 앱엔진으로
개발 개발만 하면 됨
Before After
구글 앱 엔진4월 말
레진 합류
PaaS 앱엔진으로!!
Datastore 앱엔진으로!!
개발 개발만 하면 됨
I don’ care!! 락 인
6월 초
일정 / 리소스 해결
미션 컴플리티드!
왜 레진코믹스는
구글 앱엔진을 선택했나
서비스는
레진코믹스 - 프리미엄 웹툰
레진코믹스 - 부분유료화
D - 21
D - 14
D - 7
D - 21
D - 77일후 무료공개 예정
D - 7
“ “7일이나 기다려?? 먼저보는 즐거움!
Auto Scaling
누적 회원 증가
6
12
78
9
10
11
트래픽 - 시간대별 특성
Instance
샘플이미지
Images Service
이미지 서비스
CMS
app engine
blobstore
images service
업로드
일반화질
고화질변환
저장
이미지 서비스 비용
app engine
blobstore
images service
업로드
일반화질
고화질
incoming bandwidth
storage
bandwidth out
image manipulation api
변환
저장
무료유료
CMS
서비스는
구글 앱엔진을 선택했나
왜 레진코믹스는
아마존 EC2 가 아닌,
앱엔진 vs EC2
PaaS 구글 앱 엔진
IaaS EC2
내가 질 책임 애플리케이션애플리케이션, 데이터베이스, 웹서버, 운영체제, 모니터링, 로드밸런싱, 업
그레이드
sysadmin Google 필요
(상대적) 비용 비쌈 쌈
Google Compute Engine vs EC2
Platform as a Service
Frontend Instance Blobstore
Images Services Datastore Memcache
Backend Instance Mail Cron
Task Queue Admin Console
왜 레진코믹스는
구글 앱엔진을 선택했나
해외에서는
Snapchat
http://www.quora.com/Google-App-Engine/What-is-the-highest-traffic-website-built-on-top-of-Google-App-Engine-Python http://gigaom.com/2013/05/07/snapchats-act-of-faith-in-building-on-google-compute-engine/
월 활성 사용자(MAU) 3천만 55% 가 매일 사용함 - 1천6백만 하루에 1억 5천만 사진 업로드
“앱엔진은 우리가 애플리케이션을 개발하는데 집중할 수 있게 해줬어요. 앱엔진이 우리에게 제공해준 개발의 편의없이 이렇게까지 못했을 꺼에요.”
- 바비 머피, CTO 이며 co-Founder
Khan Academy
https://cloud.google.com/files/KhanAcademy.pdf
380만 월 UV 수업일에 150만 프랙티스가 제공 2000 개 이상의 동영상
“만약 구글 앱엔진이 없었다면, 서버를 셋업이나 라우터 설정에 정말 많은 시간을 보냈을꺼에요. 실 서비스에 집중할 수 있게 한 건 구글 앱엔진의 장점입니다.”
- 벤 카멘스, 리드 개발자
“개발 프로세스가 쉬워서 평균 하루에 한번, 많게는 10번도 배포를 합니다.”
Rovio
https://cloud.google.com/files/Rovio.pdf
Angry bird friend for facebook MAU 1천 3백만
“구글 앱엔진은 우리가 게임당 한명 또는 두명의 개발자로만 매우 빠르게 게임을 런칭하도록 해줬어요. 구글이 모든 서버들을 관리해주기 때문에, 유지보수에 있어 우리가 할 일이 거의 없었어요.”
- 스테판 호크, 웹 게임 리드 서버 개발자
왜 레진코믹스는 구글 앱엔진을 선택했나
• 개발이 빠르고 쉬움
• 자동으로 스케일링해줌
• 개발에만 집중
• 유용한 빌트인 기능들 제공
• 구글 노하우가 녹아있는 인프라 위에서 구동 (비즈니스)
좋다고 생각없이 쓰면 패가망신
Generally available features
Language & Runtime
Communications Data Storage, Retrieval, & Search
Java Python PHPpreview
Dedicated Memcache
Datastore
Blobstore
Search
Memcache
Goexperimental
Logs
HRD Migration Tool
Channel
URL Fetch
XMPP
Google Cloud Endpoints
Process Management
Task Queue
Scheduled Tasks
Computation
Backends
Images
App Configuration & Management
App Identity
Capabilities
Traffic Splitting
Multitenancy
Remote
SSL for Custom Domains
Users
Preview features
Modules
Sockets
Cloud SQL
Google Cloud Storage Client Library
Experimental features
OpenID
OAuth
MapReduce
Datastore Admin/Backup/Restore
PageSpeed
Prospective Search
Task Queue REST API
Task Queue Tagging
Appstats
Generally available features
Language & Runtime
Communications Data Storage, Retrieval, & Search
Java Python PHPpreview
Dedicated Memcache
Datastore
Blobstore
Search
Memcache
Goexperimental
Logs
HRD Migration Tool
Channel
URL Fetch
XMPP
Google Cloud Endpoints
Process Management
Task Queue
Scheduled Tasks
Computation
Backends
Images
App Configuration & Management
App Identity
Capabilities
Traffic Splitting
Multitenancy
Remote
SSL for Custom Domains
Users
Preview features
Modules
Sockets
Cloud SQL
Google Cloud Storage Client Library
Experimental features
OpenID
OAuth
MapReduce
Datastore Admin/Backup/Restore
PageSpeed
Prospective Search
Task Queue REST API
Task Queue Tagging
Appstats
드리고 싶은 이야기
참고사항
그 다음
북마크
배포/관리
경험한 기능들정말 팁
App Engine API
Appstats
Blobstore
Logs
URL Fetch
Images
Scheduled Tasks
Dedicated Memcache
Datastore
MailTask Queue
My App
배포/관리
앱엔진 애플리케이션
Application id: gdgseoulmeetup
요청
Application id: gdgseoulmeetup
http://gdgseoulmeetup.appspot.com
Dynamic 인스턴스
Application id: gdgseoulmeetup
Instance
http://gdgseoulmeetup.appspot.com
dynamic
Resident 인스턴스
Application
Instance
id: gdgseoulmeetup
Resident
늘 떠있음
요청
Application
Instance
http://gdgseoulmeetup.appspot.com
id: gdgseoulmeetup
Resident
Auto Scaling
Application
Instance Instance
http://gdgseoulmeetup.appspot.com
id: gdgseoulmeetup
Resident dynamic
Frontend Instance Auto Scaling 돈 먹는 하마
Auto Scaling
Application
Instance Instance Instance Instance Instance Instance
http://gdgseoulmeetup.appspot.com
id: gdgseoulmeetup
Resident dynamic dynamic dynamic dynamic dynamic
앱엔진 애플리케이션
Application
Version Version
Module
http://gdgseoulmeetup.appspot.com
id: gdgseoulmeetup
Instance Instance Instance Instance Instance Instance
Resident dynamic dynamic dynamic dynamic dynamic
Version
Module
앱엔진 애플리케이션
Application
Version Version
Module
http://gdgseoulmeetup.appspot.com
id: gdgseoulmeetup
Instance Instance Instance Instance Instance Instance
Resident dynamic dynamic dynamic dynamic dynamic
Version
Module
Default
배포 - 버전
http://gdgseoulmeetup.appspot.com
http://20140122.gdgseoulmeetup.appspot.com
local
dev server
production
•앱엔진 샌드박스 제공 •/WEB-INF/appengine-generated/local_db.bin
•개발용 app id •개발자 별로 버전 배포 •노출 안되게 앱엔진 admin 권한 처리
•새 버전 배포 -> 확인 -> 디폴트 변경
앱/버전 property 로 관리 - 빌드타임에 정해지도록 함appengine'web.xml.
!<application>${google.app.id}</application>.
<version>${google.app.version}</version>
Datastore
Key Ancestor Index
데이터스토어 - KeyComic
kindidentifierancestor path (optional)
Key - entity 의 ID / 구글앱엔진에서의 유일한 키 값String comicId * String title String artist
UserLong userId * String username boolean adult int coinBalance
PurchaseLong purchaseId * String episodeId int coin long purchaseTime Long userId
Entity
데이터스토어 - KeyComic
kindidentifierancestor path (optional)
Key - entity 의 ID / 구글앱엔진에서의 유일한 키 값String comicId * String title String artist
UserLong userId * String username boolean adult int coinBalance
identifier
PurchaseLong purchaseId * String episodeId int coin long purchaseTime Long userId
자동할당 - 실행시 생성되는 객체들 (long)
지정 - 미리 아는 객체들 (string, long)
Entity
데이터스토어 - KeyComic
kindidentifierancestor path (optional)
Key - entity 의 ID / 구글앱엔진에서의 유일한 키 값String comicId * String title String artist
UserLong userId * String username boolean adult int coinBalance
PurchaseLong purchaseId * String episodeId int coin long purchaseTime Long userId
SELECT.*.FROM.Comic.WHERE.__key__.=.Key('Comic',.'badboss')
SELECT.*.FROM.User.WHERE.__key__.=.Key('User',.1234123412341234)
Entity
Global Query
UserLong userId * String username boolean adult int coinBalance
PurchaseLong purchaseId * String episodeId int coin long purchaseTime Long userId
SELECT.*.FROM.Purchase.WHERE.userId.=.1234123412341234.
AND.episodeId.=.‘badboss'''13’
Eventual Consistency
https://cloud.google.com/developers/articles/balancing-strong-and-eventual-consistency-with-google-cloud-datastore
Eventual Consistency
https://cloud.google.com/developers/articles/balancing-strong-and-eventual-consistency-with-google-cloud-datastore
Purchase
SELECT.*.FROM.Purchase.WHERE.userId.=.1234123412341234.
AND.episodeId.=.‘badboss'''13’
Strong Consistency
https://cloud.google.com/developers/articles/balancing-strong-and-eventual-consistency-with-google-cloud-datastore
Ancestor
UserLong userId * String username boolean adult int coinBalance
PurchaseLong purchaseId * String episodeId int coin long purchaseTime
SELECT.*.FROM.Purchase.WHERE.ancestor)is.Key(‘User’,.1234123412341234).AND.episodeId.=.‘badboss'''13’
Long userId
Entity Group
Ancestor
kindidentifierancestor path
Key - entity 의 IDUserLong userId * String username boolean adult int coinBalance
PurchaseLong purchaseId * String episodeId int coin long purchaseTime
SELECT.*.FROM.Purchase.WHERE.ancestor)is.Key(‘User’,.1234123412341234).AND.episodeId.=.‘badboss'''13’
<Parent>Long userId
데이터스토어 인덱스
UserLong userId * String username boolean adult int coinBalance
PurchaseLong purchaseId * String episodeId int coin long purchaseTime <Parent>Long userId
SELECT.*.FROM.Purchase.WHERE.ancestor)is.Key(‘User’,.1234123412341234).AND.episodeId.=.‘badboss'''13’
index
주의 index 는 entity 를 update 할 때 생김 나중에 추가하려면 entity 를 새로update 해야 함
composite 인덱스 추가
주의 운영 중에 index 조합을 추가한 경우, Serving 상태를 확인한 후에 default 로 배포할 것
카운트1. 카운트하는 엔터티를 둔다. - 동시에 엔터티를 수정하게 되면 Datastore Contention 문제 - 동시에 못하도록 한다면 한번에 한 카운트만 처리 문제 !2. Sharding Counter https://developers.google.com/appengine/articles/sharding_counters - 랜덤으로 카운터를 여러개 두고, 사용하지 않은 카운터를 읽고 씀 - 나중에 합쳐 확인하는 방법 - 정확 - 매 요청마다 데이터스토어 써 느린데다 데이터 엔터티가 무수히 많이 생김 !3. Deferred Task Queue 활용 - 데이터스토어에 permanent 카운트 - Memcache 에 current 카운트 - 주기적으로 Memcache 에서 데이터스토어로 씀 - 성능은 좋지만, 정확하지 않을 수 있다.
비용 줄이기
• Appstats 써서 요청에 대한 비용이 어느정도인지 검토
• Memcache 를 되도록 많이 쓰기
• Urlfetch 는 될 수 있는 한 앱에서 할 것
Appstats
Memcache
•Memcache 쓰면 빠르고 비용도 줄어듬
•제한 1MB 이하 - Object 의 List 를 통째로 넣으려면 주의 (json 변환)
•자바의 경우 Objectify 사용 추천 (Datastore + 기본 Memcache)
팁
메일 Quota 요청 미리미리
세션 삭제, 앱엔진이 안해줌
!/_ah/sessioncleanup com.google.apphosting.utils.servlet.SessionCleanupServlet !cron 으로 주기적으로 호출할 것
http://www.radomirml.com/blog/2011/03/26/cleaning-up-expired-sessions-from-app-engine-datastore/
Cron 문법 주의
•매시간마다 ===> every 1 hours
•매시간인데 15분에 실행되면 좋겠음
===>
!
every 1 hours from 00:15 to 00:14
every 1 hours from 00:15 to 00:15영원히 실행되지 않음
static 파일 만료 시간 조정
•Purge 기능 없음.
•사용자 브라우저에 의존하기 때문에 (자주 바뀐다면) 시간을 조정해둘 것
SSL URL
•http://1.app-id.appspot.com
•https://1-dot-app-id.appspot.com
참고사항
Latency
• 데이터센터 저 멀리
• cdn 이용
• dynamic 데이터는 어쩔 수 없음
• 좋은 소식
• 아시아에 데이터센터 들어감http://googleasiapacific.blogspot.kr/2013/12/our-first-data-centers-in-asia-are-up.html
앱엔진 + 스프링 최적화
• Component Scanning 줄이기/피하기
• Relationship Autowiring 줄이기/피하기
• 상용에서 XML Validation 하지 않기
• Lazy-Initialized Bean 사용하기
• Constructor Injection by Name 피하기
• https://developers.google.com/appengine/articles/spring_optimization
로그 / 파일
• 로그는 좀 많이 불편
• File API 안됨
• PG사 모듈 File 쓰기 안되어서 제외
앱엔진 그 다음
AppScale
http://www.appscale.com/
구글 앱엔진 호환되는 오픈소스 PaaS
AppScale Architecture
북마크
모니터링/커뮤니티• 시스템 상태 페이지https://code.google.com/status/appengine
• 다운타임 알림https://groups.google.com/forum/#!forum/google-appengine-downtime-notify
• issue trackerhttps://code.google.com/p/googleappengine/issues/list
• 토론https://groups.google.com/forum/#!forum/google-appengine
• IRChttp://webchat.freenode.net/ #appengine
앱엔진 시작하기
• 입문자를 위한 앱엔진 튜토리얼http://googcloudlabs.appspot.com/codelabexercise1.html