[발 번역] 수백대의 장비와 수십가지의 기술, Instagram의 힘

해당 글은 http://instagram-engineering.tumblr.com/post/13649370142/what-powers-instagram-hundreds-of-instances-dozens-of 라는 기사를 발 번역한 것입니다. 오역에 주의하세요.

다른 엔지니어들과의 대화나 만남에서 항상 받는 질문 한가지는 “어떻게 시스템을 구성했는가?” 입니다.  그 질문은 High-Level에서 Instagram의 힘인 시스템에 대한 느낌을 제공해주기 때문에 재미있습니다. 나중에 해당 시스템에 대한 더 심도 있게 찾을 수 있을 것입니다.  겨우 1년동안,  적은 수의 엔지니어 팀으로 1400만명+알파의 유저로 확장가능 하도록 부분부분을 재개발하면서  시스템이 어떻게 발전했는지 보여줍니다.  시스템을 선택하는 중요원칙은 다음과 같습니다.

* 아주 심플하게 유지하라.

* 바퀴를 재발명하지 마라.

* 가능한한 명확하고, 증명된 방법을 이용하라

그럼 위에서 부터 밑에까지 보도록 하겠습니다.

OS / Hosting

아마존 EC2에서 우분투 리눅스 11.04( Natty Narwhal )을  사용합니다.  EC2에서 높은 트래픽 상황에서 이전 우분투 버전에서는  모든 종류의 예측할 수 없는 프리징이 발생했습니다. 그러나 Natty는 문제가 없었습니다.  우리는 오직 3명의 엔지니어만 있고, 여전히 진화가 필요합니다.  그리고 유례없는 성장 중이라 self-hosting 은  지금도 고려사항이 아닙니다.

Load Balancing

모든 Instagram 서버로 오는 요청은 Load Balancing 장비를 거칩니다. 2대의 Nginx 장비를 DNS Round-Robin 으로 이용합니다. 해당 방법의 단점은 장비를 제거해야 할 때, DNS가 변경되는 시간이 든다는 것입니다. 최근에는 아마존 Elastic Load Balancer 와 3대의 Nginx 장비로 변경하였습니다.  자동으로 추가되거나 제거되게 됩니다.( 헬스 체크에 실패하면 자동으로 변경됩니다. ) CPU 로드를 줄이기 위해서 SSL과 ELB level 도 종료하였습니다.  DNS를 위해서는 아마존의 Route53을 이용합니다. 최근에 AWS Console 에 꽤 괜찮은 GUI tool이 추가되었습니다.

Application Servers

다음은 요청을 처리하는 애플리케이션 서버입니다. 아마존 High-CPU Extra-Large 머신에 Django를 올려서 사용하고 있습니다.( 역자 주: High-CPU Extra-Large Instance는 7GB 메모리에 20EC2 Compute Unit-각 2.5EC2 Compute Unit의 8 Core 1690GB 에 64bit 플랫폼이며, 시간당 $0.68 입니다.) 사용량이 늘면서 25대 이상을 사용하고 있습니다.( 운좋게도, Stateless 형태로 동작하므로, 수평적으로 확장하기가 쉽습니다.)  우리의 일반적인 작업은 메모리 사용량이 많은 작업보다는 CPU를 많이 사용하는 작업이라는 것을 알게 되어서 CPU와 메모리의 적당한 균형을 맞춘 High-CPU Extra-Large 를 선택했습니다.( 역자 주: NoSQL 류라면, Memory가 17.1GB 제공하는 High-Memory Extra Large Instance 가 좋아보입니다.  4square 에서는 mongodb 운영을 위해 메모리를 68.4GB 제공하는 High-Memory Quadruple Extra Large Instance를 사용한다고 합니다. )

WSGI 서버로서는 http://gunicorn.org/을 이용합니다. apache에 mod_wsgi 를 사용했었는데, Gunicorn이 훨씬 설정하기가 편하고 CPU를 덜 사용한다는 것을 알았습니다. 여러 장비를 한번에 실행하기 위해서 최근에 몇초만에 배포를 할 수 있는 유용한 병렬 모드를 추가한 Fabric 을 이용합니다.

Data storage

유저정보, 사진의 메타데이터, 태그 정보등, 대부분의 정보는 PostgreSQL에 저장됩니다. 이전에 설명한 글(https://charsyam.wordpress.com/2011/12/04/instagram-%EC%97%90%EC%84%9C-id-%EC%83%A4%EB%94%A9%ED%95%98%EA%B8%B0/) 에서 처럼 Postgres 장비에 데이터들이 샤딩되어 있습니다. 메인 샤딩 클러스터를 포함해서 12개의 Quadruple Extra-Large memory 장비를 사용중입니다. ( 그리고 12개의 리플리케이션 장비가 다른 존에 있습니다. – 역자 주: 아마존은 큰 지역을 나타내는 Region 과 그 안에 여러 개의 utility zone이 있습니다. 비유하자면 Region은 거의 하나의 회사라고보고 utility zone 은 그 회사가 소유한 IDC라고 보시면 됩니다.)

아마존의 네트웍 디스크 시스템인 EBS가 충분한 Seeks 속도가 나오지 않아서, 메로리 사용성이 매우 중요합니다. 충분한 IO 성능을 위해서 mdadm을 이용해서 EBS drive를 Software RAID로 구성했습니다.( 역자 주 – DBMS의 경우 메모리가 충분하면 데이터를 최대한으로 캐시해 둘 수 있습니다. 이를 위해서 메모리가 큰 장비가 유리합니다. mdadm 은 리눅스에서 사용하는 Software RAID 프로그램이네요. http://en.wikipedia.org/wiki/Mdadm )

좋은 팁을 드리자면, 메모리에 어떤 데이터가 있는지 확인하고 관리할 수 있는 vmtouch라는 환상적인 툴이 있습니다. 특히, 장비에 더 이상 active 메모리가 없을 경우 다른 장비로 이동하기 위한 fail over 시에 유용합니다. 여기에 vmtouch 의 결과를 파싱하고 vmtouch의 결과를 보여주며, 현재 메모리 상태에 맞게 다른 시스템에 실행시켜 주는  스크립트가 여기(https://gist.github.com/1424540) 있습니다.

모든 PostgreSQL 장비는 Streaming Replication 을 이용해서 master-replica 방식으로 동작합니다. 장비들의 백업은 EBS snapshotting을 이용합니다. snapshot 할 때, RAID를 freeze & unfreeze 할 수 있는 XFS를 파일시스템으로 사용합니다. 이는 snapshot 이 올바르다는 것을 보장하기 위해서입니다.( ec2-consistent-snapshot 에서 영감을 얻었습니다.  ) 스트리밍 리플리케이션을 시작하기 위해서 가장 선호하는 툴은 2ndQuarant를 포크한 repmgr 입니다.

앱 서버에서 데이터베이스와 연결하기 위해서, PostgreSQL connection pool을 위한 pgbouncer를 사용했는데, 성능에 큰 영향을 미쳤습니다. 우리는  Christophe Pettus’s blog 에서 Django를 위한 좋은 리소스와 PostgreSQL, Pgbouncer 팁들을 얻었습니다.

사진 이미지는 바로 Amazon S3로 저장됩니다.(역자 주: 일반적인 경우, 네트웍 사용으로 인해서 이런 큰 데이터들은 바로 아마존 S3로 저장하는 방법을 사용합니다. 만약 인스타그램으로 저장했다가 다시 아마존 S3로 저장한다면, 그 트래픽이 어마어마할껍니다.)  수 테라바이트의 사진 데이터가 저장되어 있습니다.다른 지역의 유저들의 이미지 로드 시간을 위해서 아마존의 CloudFront를 CDN으로 이용합니다. (일본의 경우, 2번째로 많이 사용하는 국가입니다.)

Redis 도 많이 사용하고 있습니다. main feed 와 activity feed, session system(Django session backend), 다른 연관된 시스템(related systems)에서 사용 중입니다.  Redis 데이터는 메모리 크기 안에 들어갈 필요가 있어서 Redis를 위해서 Quadruple Extra-Large Memory instances 장비를 사용합니다.

때때로, redis를 통해서 몇몇 시스템에 대한 샤딩을 하기도 합니다. redis는 master-replica 형태로 동작하고 있고, replica는 정기적으로 내용을 디스크로 저장합니다.(역자 주: Redis는 replication 기능을 지원하고, 백그라운드 디스크 DUMP 기능을 제공합니다.) 그리고 최종적으로 EBS snapshut을 이용하여 DB 덤프들을 백업합니다.( Master에서 Disk Dump는 성능 저하를 일으킵니다.)  Redis가 replica 에 쓰기를 허용한 이후부터 다운 타임 없이 새로운 Redis 로의 온라인 failover 가 굉장히 쉬워졌습니다.( 역자 주: Online 이란 의미는 동작중에 서비스의 다운 타임 없이 진행된다는 뜻입니다. )

geo-search API 를 위해서 몇 달 동안 PostgreSQL을 사용했습니다만, Media Data 들이 샤딩 되었기 때문에 Apache Solr 로 옮겼습니다. 간단한 JSON 인터페이스를 제공하고 있어서, 그냥 다른 API를 사용하는 것과 별반 다르지 않습니다.

다른 웹 서비스와 비슷하게, 캐싱을 위해서는 Memcached를 이용합니다. 6대의 Memcached 장비를 운영하고 있습니다. pylibmc 와 libmemcached를 이용합니다. 아마존에서는 최근에 Elastic Cache 서비스를 런칭했습니다.(역자 주: 이때만 해도 동부 지역만 되다가 최근에 몇개 지역이 더 추가 되었습니다. 즉 Elastic Cache 를 고려하신다면, 서비스 지역인지 확인이 필요합니다. ) 하지만, 그것은 우리의 장비에서 돌리는 것보다 싸지 않습니다. 그래서 아직 사용하는 것을 고려하지는 않고 있습니다.

Task Queue & Push Notifications

사용자가 Instagram Photo를 트위터나 페이스북에 공유하기로 결정했을 때나, 실시간 가입자(Real-time subscribers )에게 새로운 Photo 포스팅에 대하해 뭔가 노티를 해야할 경우,  Danga에서 만든 태스크 큐잉 시스템인 Gearman 에 해당 태스크를 집어넣습니다. 무거운 작업이 “백그라운드”에서 돌고 있더라도, 비동기적으로  미디어  업로드를 빨리 처리할 수 있습니다. 작업이 주어진 시간에 바로 task queue를 소비하는 200개 정도의 Worker(파이선으로 작성되었습니다.)  가 있습니다. 또한 많은 follower를 가진 유저가 새 유저에게 포스팅을 할 경우에도 Gearman을 통해서  feed 를  내보냅니다.

Push 알림을 하는 가장 가격대 성능비가 좋은 솔루션은  https://github.com/samuraisam/pyapns 입니다. open-source twisted(역자 주:  twisted 는 python  기반의 event-driven 라이브러리 입니다. ) 기반으로 수십억 건의 push 알림을 안정적으로 처리하고 있습니다.

Monitoring

100 대가 넘는 장비가 있다면, 가장 중요한 것은, 전반적으로 무슨 일이 일어나고 있는지 확인하는 일입니다. Munin이라는 모든 시스템을 Graph 형태로 보여주고 정상 범위가 넘어가면 경고를 주는 툴을 이용합니다. Python-Munin 기반으로 많은  Custom Munin plugin을 만들었고, System 수준이 아닌 것들도 그래프 형태로 표현했습니다.( 예를 들어, 분당 등록 유저 수, 초당 사진 등록 수 등 ) 서비스의 외부 모니터링 용도로는 Pingdom 을 이용하고,  PagerDuty 를 사고나 알림을 다루기 위해서 사용합니다. 파이썬 에러 리포팅을 위해서 Sentry 를 사용합니다. Sentry는 Disqus를 포크한 멋진 오픈 소스 Django 앱 입니다. 실시간으로 시스템에 무슨 에러가 발생하고 있는지 등록하고 볼 수 있습니다.

You?

우리의 시스템에 대한 얘기가 흥미가 있다면,  그리고 시스템의 변경에 대해서 뭔가 말할께 있다면, 경청해서 듣겠습니다. 우리는 EC2 장비들을 길들일 DevOps 한 인물을 찾고 있습니다. (We’re looking for a DevOps person to join us and help us tame our EC2 instance herd.)