Twitter는 서비스에서 어떻게 이미지를 처리했는지에 대해 잘 정리된 글이 있어서 원 저자의 허락하에 제 블로그에 포스팅합니다. 원 글은 How Twitter Handles 3,000 Images Per Second 입니다. 이 글을 통해 퍼블릭 서비스를 만들때 사이트내에서 미디어를 어떻게 처리하면 좋을지에 대한 좋은 힌트를 얻을 수 있으면 더더욱 좋을 거 같습니다.
현재 Twitter는 초당 3,000장의 이미지(약 200GB)가 만들어지고, 저장되고 있다. 더 잘 할 수도 있었지만, 2015년에 Twitter는 이러한 이미지 파일 등 미디어 파일의 저장 방법을 개선한 덕분에 600만 달러를 절약할 수 있었다.
항상 그랬던 것은 아니다. 2012년 Twitter는 주로 텍스트 기반이었다. 이것은 마치 호그와트(해리포터에 나오는 마법학교)의 벽에 멋지게 움직이는 그림이 없는 것과 같았다. 2016년, 지금은 Twitter가 미디어 중심으로 전환하고 있다. Twitter는 미리보기, 멀티 포토, gif, vine, 인라인 비디오 등에서 필요한 사진(이미지)을 지원할 수 있는 새로운 미디어 플랫폼 개발을 통해 변화해 왔다.
Twitter의 소프트웨어 개발 엔지니어인 Henna Kermani가 Mobile Scale London에서 3,000 images per second라는 이야기를 통해 미디어 플랫폼으로의 변화를 말했으며, 그 이야기는 주로 이미지의 파이프 라인에 초점을 맞추고 있었지만, 세부 사항의 대부분은 다른 미디어(동영상이나 음성)에도 적용되어 있다고 말했다.
그 이야기중에서 가장 흥미로웠던 교훈을 몇가지 들어 보자.
- 문제를 해결하려 가장 간단한 방법을 사용하면 항상 망치게 된다. 이미지가 있는 트윗을 하게 하는 것이 고객을 락인시키는 필수적인 기능이었는데, 이것을 잘 못했다. 이 기능으로 인해 확장성도 떨어졌고, 네크워크 성능 저하도 왔으며, 이런 것들로 인해 트위터가 새로운 기능을 추가하는 것을 어렵게 만들었다.
- 디커플링. 그래서 트윗과 미디어 업로드를 분리했다. 이를 통해 각 프로세스를 독립적으로 최적화하는 것이 가능해져, 보다 유연하게 운용을 할 수 있었다.
- 미디어 데이터의 자체가 아니라 참조 처리로 처리 방식을 바꿨다. 시스템에서 큰 데이터 덩어리를 이동시키지 마라. 대역폭을 소비하고, 데이터를 다루는 모든 서비스에서 성능 문제를 일으킬 수 있다. 대신에 미디어 데이터를 저장하고 그 참조를 조작하는 편이 좋다.
- 업로드를 독립시켜 도중에 실패해도 계속할 수 있도록 해, 미디어 업로드 실패율이 크게 줄었다.
- 실험과 조사. Twitter는 실험과 그 결과를 조사하여 20일이라는 기간이 이미지 변형본(썸네일, 작은 이미지, 큰 이미지 등)을 만드는데 들어가는 저장과 저장소 크기 산정에서 가장 균형적이면서 효율적인 곳을 찾았다. 이것은 트윗에서 20일이 지나면 이미지는 액세스 될 가능성이 떨어지기 때문에 변형본 이미지는 삭제할 수 있게 된다. 그리고 삭제하면 필요한 서버의 절반인, 하루에 약 4TB의 데이터 스토리지를 절약할 수 있게 된다. 그리고 1년 동안 수백만 달러를 절약할 수도 있다.
- 온디맨드. (트윗에서 20일 이후) 이전에 변형본 이미지는 삭제할 수 있다. 왜냐하면 사전에 이미지 생성하고 유지하기보다는 필요할 때 즉석에서 이미지를 생성하는 편이 더 낫다. 이처럼 필요에 따라 서비스를 제공함으로써 처리의 유연성이 증가하고, 일을 더 영리하게 수행할 수 있고, 더 일원적으로 관리할 수 있도록 해 준다.
- Progressive JPEG은 진정한 표준 이미지 포멧의 승자다. 이것은 프런트 엔드와 백엔드 지원이 좋고, 저속 네트워크에서 성능이 좋다.
트위터가 미디어 지향으로 변화하면서 많은 좋은 일들이 일어났다. 그래서 그들이 어떻게 대처해왔는지 배워보자.
옛날 방식 - 2012년 Twitter
1. 쓰기 절차
-
사용자가 앱으로 트윗을 작성하고 경우에 따라서는 이미지를 첨부한다.
- 클라이언트는 트윗을 모놀리틱 서버에 게시한다. 이미지는 다른 모든 트윗 메타 데이터와 함께 한꺼번에 업로드 된다. 그리고 일련의 프로세스 과정에 관여하는 모든게 단일 서비스에 의해 돌려진다.
- 이 엔드 포인트는 올드하게 설계되어 있어 많은 문제의 원인이 되고 있었다.
-
문제 1: 네트워크 대역폭의 대량 낭비
- 트윗 작성과 미디어 업로드는 하나의 작업으로 단단히 결합되어 있었다.
- 업로드는 완전히 성공하든 완전히 실패하든 일회성이었다. 네트워크의 약간의 문제나 일시적인 오류 등 실패의 이유가 무엇이었던 간에, 다시 시작하려면 미디어의 업로드를 포함한 업로드 과정을 다시 할 필요가 있었다. 업로드가 95%까지 완료한 상태에서도 뭔가 문제가 있으면 또 업로드를 처음부터 다시 시작해야 했다.
-
문제 2: 큰 치수의 미디어에 대한 충분한 확장성을 제공하지 못했다.
- 이 방법은 동영상처럼 큰 사이즈의 미디어를 위해 충분한 확장성을 제공하지 못했다. 큰 사이즈는 특히 브라질, 인도, 인도네시아 등 네트워크가 느리고 불안정한 신흥 시장에서 실패율이 증가했다. 이 지역은 트윗 업로드 성공률을 정말 높이고 싶은 곳이다.
-
문제 3: 내부 대역폭의 비효율적 사용
- 엔드 포인트는 TFE(Twitter 프런트 엔드)에 연결하고, TFE가 사용자 인증과 라우팅을 처리한다. 그런후, 사용자는 Image Service로 이동한다.
- Image Service는 다양한 크기(소형, 중형, 대형, 썸네일 등)의 이미지 인스턴스를 생성하는 Variant Generator와 통신을 한다. 이들은 사진이나 동영상과 같은 대용량 데이터에 최적화된 키-값(key-value) 저장 장치인 BlobStore에 저장된다. 이미지는 반영구적으로 거기에 저장되어 계속 유지된다.
- 트윗의 생성과 유지 과정에 관련된 서비스는 그 밖에도 많이 있다. 왜냐하면 엔드 포인트가 모놀리틱이었기 때문에, 미디어 데이터와 트윗 메타 데이터를 묶어, 그 정리는 전체 서비스를 통해 처리되었다. 이 큰 데이터 부하는 직접 이미지를 처리하지 않아도 되는 서비스에 처리를 맡겼고, 이 서비스는 미디어 파이프 라인의 일부가 아니었지만, 큰 데이터 처리의 최적화에 사용되었다. 이 방법은 내부 대역폭을 매우 비효율적으로 만들었다.
-
문제 4: 비대해진 스토리지 공간
- 더 이상 요청이 없는, 한 달 혹은 일년 이상 장시간이 지난 트윗 이미지는 BlobStore 저장소에 영구적으로 저장되어 공간을 차지하고 있었다. 때때로 트윗이 삭제되었을 때에도 이미지는 BlobStore에 남아 있었다. 가비지 컬렉션이 없었던 것이다.
2. 읽기 절차
- 사용자는 트윗과 관련 이미지를 본다. 그 이미지는 어디에서 오는 것일까?
- 클라이언트는 CDN에서 클라이언트에 맞는 변형본 이미지를 요구한다. CDN은 이미지가 있는 TFE에 이미지에 대해 요청해야 한다. 결국, 특정 크기의 이미지 URL을 BlobStore에서 직접 찾게 된다.
-
문제 5 부 : 새로운 변화에 도입이 불가능
- 디자인이 별로 유연하지 않다. 새로운 변화, 즉 다양한 크기의 이미지를 추가하기 위해서는, BlobStore의 모든 이미지에 대해 새로운 이미지 크기로 다시 채우는 작업이 필요하다. 주문형 변형 기능이 없었다.
- 유연성 때문에 Twitter가 새로운 기능을 클라이언트에 추가하는 것이 어려워졌다.
새로운 방식 - 2016년 Twitter
1. 쓰기 절차
트윗에서 미디어 업로드를 분리했다.
- 업로드 기능은 일급 객체(First-class object)로 만들어졌다. 업로드 엔드 포인트가 생성되고 원래 미디어를 BlobStore에 넣는 것이 유일하게 하는 일이다.
- 이것이 업로드의 처리 방법에 유연성을 준다.
- 클라이언트는 BlobStore에 이미지를 넣는 기능을 하는 Image Service와 통신하는 TFE로 상호 통신을 해 메타 데이터 저장소에 데이터를 넣는다. 그것이 전부다. 관련된 숨겨진 서비스는 더 이상 없다. 어느 누구도 미디어를 해결하지 않고서는, 어느 누구도 데이터를 처리할 수 없다.
- 미디어 식별자인 mediaId는 Image Service로부터 전달 받는다. 클라이언트가 트윗 또는 DM을 만들거나 프로필 사진을 업데이트하고 싶을때, mediaId는 미디어를 제공하기보다는 미디어를 참조하게 처리하는데 사용된다.
-
그냥 업로드 된지 얼마 안된 미디어를 붙인 트윗을 작성하고 싶다고 하자. 그 흐름은 아래와 같다 :
- 클라이언트는 mediaId을 post로 전달하면서 엔드 포인트를 업데이트 한다. 그리고 Twitter Front End에 도달한다. TFE는 생성된 엔터티에 적합한 서비스로 경로를 정한다. DM이나 프로필용 서비스는 여러 가지가 있다. 모든 서비스가 Image Service와 통신을 한다. Image Server가 얼굴 검출 및 아동 포르노 검색 등의 기능을 처리 후 처리 큐에 던져진다. 그것이 끝나면 Image Service가 이미지 처리용인 ImageBird나 동영상용인 VideoBird와 통신을 한다. ImageBird가 다잉한 크기로 이미지를 변형본을 만든다. VideoBird는 일부분에 대해 트랜스 코딩을 한다. 생성된 미디어가 무엇이든 BlobStore에 저장된다.
- 미디어가 전달되는 것이 아니어서 불필요한 대역폭이 줄어들었다.
세그먼트화 되어 재시도가 가능한 업로드.
- 지하철을 걸어서 10분 후에 나오는 경우, 업로드 프로세스가 앞서 중단된 부분부터 시작된다. 이것은 사용자에게 완전히 매끄럽게 처리된다.
- 클라이언트는 업로드 API를 사용하여 업로드 세션을 초기화 한다. 백엔드는 업로드 세션 전체에서 사용하는 세션을 식별하기 위한 식별자 mediaId를 준다.
- 이미지는 여러 세그먼트로 분할된다. 여기에서는 3개의 세그먼트로 하자. API를 사용하여 세그먼트가 추가되어 각각의 추가 명령을 호출하면 세그먼트 인덱스를 돌려준다. 모든 추가 세그먼트는 같은 mediaId를 가진다. 업로드가 완료되면 최종 업로드가 완료되고, 해당 미디어는 사용할 준비가 된다.
- 이 방법은 네트워크의 문제에 대해 더 탄력성을 가질 수 있다. 각각의 세그먼트가 다시 시도 될 수 있다. 네트워크가 어떤 이유로 다운되면 중단하고 네트워크 상태가 정상으로 돌아왔을 때 중단 된 세그먼트를 선택할 수 있다.
- 간단한 방법으로 막대한 이익을 얻는다. 50KB 이상의 파일에서 이미지 업로드 결함이 브라질에서 33%, 인도에서는 30%, 인도네시아에서는 19%로 감소했다.
2. 읽기 절차
MinaBird라는 CDN Origin Server를 도입했다.
- MinaBird는 ImageBird과 VideoBird와 통신 할 수 있기 때문에 이미지 크기나 동영상 형식의 변화가 없는 경우 즉시 생성하는 것이 가능하다.
- MinaBird는 클라이언트 요청의 처리 방법에 있어서, 더 유연하고, 더 다이나믹하다. DMCA 삭제(미국의 디지털 밀레니엄 저작권법에 따라 저작권 침해 인터넷 콘텐츠를 삭제하는 것)가 있었다고 해도, 예를 들어 미디어의 특정 부분에 대한 액세스를 차단하거나 액세스를 다시 활성화시키는 것이 매우 쉽게 할 수 있다.
-
즉시 이미지의 변형이나 트랜스 코딩을 할 수 있는 것만으로도, Twitter는 스토리지에 대해 더욱 잘 대처할 수 있게 되었다.
- 그때 그때 필요에 따라(스크린의 변화에 따른) 이미지 변형본을 생성한다는 것은 BlobStore에 전체 변형본을 저장할 필요는 없다는 것이다.
- 원본 이미지는 삭제될 때까지 유지된다. 변형본은 20일 밖에 저장되지 않는다. 미디어 플랫폼 팀은 수 많은 연구와 조사를 통해 최선의 만료 기간을 찾았다. 요청된 모든 사진 중 절반은 최대 15일내의 것들이다. 그 이상의 기간 이미지를 유지한다는 것은 수확 체감 현상을 가져온다. 그 어느 누구도 오래된 미디어를 요청하지 않는다. 15일이 지나면 롱테일이 된다.
- TTL(유효 기간)도 없고, 만료 기간도 없다면 미디어 스토리지는 매일 6TB씩 증가해 간다. 필요에 따라 변형본을 만드는 느슨한(Lazy) 방식을 적용하면 하루 스토리지 증가분은 1.5TB 된다. 20일 TTL 방식은 느슨한(Lazy) 방법보다 더 스토리지를 사용하지 않기 때문에 스토리지 비용은 거의 없지만, 계산이라는 관점에서 보면 크다. 느슨한(Lazy) 방식으로 읽을 전체 변형본을 계산하는 것은 데이터 센터 당 150개의 ImageBird 머신이 필요한 반면, 20일 TTL을 적용하면 75개 정도만 필요로 한다. 그래서 20일 TTL 적용 접근법은 스토리지와 계산의 균형점이 되는 최적인 것이다.
- 스토리지와 계산 관점에서 절약이 비용을 줄여주고 있기 때문에, Twitter의 2015년에는 20일 TTL 도입으로 600만 달러를 절약했다.
클라이언트의 개선(Android)
-
Google이 만든 이미지 포맷, WebP를 사용한 실험을 6개월간 하였다.
- 이미지는 PNG나 JPEG을 대응했을때보다 평균 25% 작았다.
- 작은 이미지 크기를 사용하여 네트워크 스트레스를 감소시켜 신흥 시장에서 특히 사용자 참여의 증가가 보고 되었다.
- iOS에 대해서는 지원하지 않았다.
- Android 4.0+만 지원했다.
- 플랫폼의 지원 부족으로 WebP 지원 비용이 늘어났다.
-
Twitter도 시도했지만, 또 다른 옵션은 Progressive JPEG이다. 이것은 연속적인 스캔을 통해 렌더링을 한다. 첫번째 스캔은 렌더링이 고르지 않을수도 있지만, 연속적으로 스캔해 나가면 렌더링이 개선된다.
- 더 나은 성능.
- 백엔드에서 지원하기 쉽다.
- 기존의 JPEG보다 60% 인코딩이 느리다. 인코딩은 1번 밖에 일어나지 않고 처리는 여러번 발생하기 때문에 이것은 큰 문제가 되지는 않는다.
- 투명 부분을 지원하지 않기 때문에 투명 PNG가 남게 되지만, 그 밖의 모든 것은 Progressive JPEG이 커버한다.
- 클라이언트 측에서는 Facebook의 Fresco 라이브러리가 지원을 제공받고 있다. Fresco 대해 좋게 말하고 싶은 것들이 많이 있다. 2G에 연결한 결과는 꽤 좋았다. 첫번째 PJPEG 스캔은 겨우 10kb 밖에 필요로하지 않았기 때문에 로딩 시간도 길지 않았다. 기본 파이프 라인은 PJPEG 파일이 이미지로 인식해 표시되기까지 아무런 표시 없이 대기 타임이 있다.
- 트윗의 상세 뷰에서 로드에 대한 지속적인 실험 결과 50 로드 시간당 9% 감소했다. 95 로드 시간당 27% 감소했다. 액세스 실패율은 74%나 감소했다. 연결 속도가 느린 사용자에게 정말 큰 만족이었다.
용어 정의
- 일급 객체(first class object) : 크리스토퍼 스트래치(Christopher Strachey)라는 영국 컴퓨터 과학자가 만들어낸 용어로, 변수나 데이터 구조 안에 담을 수 있고, 파라미터로 전달할 수 있으며, 반환값(return value)으로 전달할 수 있고, 할당에 사용된 이름과 관계없이 고유하게 식별이 가능하고, 동적으로 프로퍼티 할당이 가능하면 일급 객체라 한다.