Architektura serwisu typu YouTube

Witam!
Jest to temat skierowany raczej do bardziej zaawansowanych programistów ruby/rails. Ja się niestety do takich nie do końca zaliczam ale, można powiedzieć, stanąłem przed faktem dokonanym. Otóż jakiś czas temu, dla firmy, w której pracuję, musiałem stworzyć prototyp prostego systemu/api do obróbki video. Obsługa go polega na wysłaniu multipartem pliku video pod odpowiedni endpoint w API wraz z dodatkowymi parametrami (z grubsza - preset, którego system ma użyć podczas obróbki video. Presety różnią się sposobem potraktowania pliku. Np. filmy przychodzące z komórek mają mieć zmienione kodeki [ze względu na wymagania azure media services] i mają być obcięte do kwadratu + mają mieć znacznie gorszą jakość).

Narzędzia

W tworzeniu tej aplikacji używamy:

  • sidekiq - do obsługi asynchronicznych tasków czyli praktycznie cały proces obróbki video obdywa się za jego pomocą.
  • sidekiq-superworker - do obsługi ww. presetów
  • FFMPEG - obróbka plików video
  • Infrastruktura Windows Azure - jako CDN, dostawca VPSów oraz dostawca narzędzia do obróbki i publikacji video (Windows Media Services). Jesteśmy z azurem politycznie związani i użycie innego narzędzia jak np. Amazon nie wchodzi w grę.

Proces

Proces, przez który przechodzi plik wygląda następująco:

  1. Plik wysyłany jest do aplikacji wraz z parametrami (presets)
  2. Aplikacja przetwarza plik (proces przetwarzania odbywa się na serwerze hostującym aplikacje, nie na żadnym amazonie czy innych przeznaczonych do tego serwisach)
  3. Aplikacja wyciąga klatki kluczowe i zapisuje je na CDNie
  4. Aplikacja wysyła plik do WIndows Media Services
  5. Windows Media Services tworzy kilka wersji pliku o różnych jakościach i bitrate i zapisuje je na CDNie

Problem

Ostatnio okazało się, że moja aplikacja będzie użyta w innej produkcyjnej aplikacji. Moja aplikacja jest kompletnie nie skalowalna ani nie gotowa do zastosowania produkcyjnego dlatego musi ulec gruntownemu przerobieniu. Głównym problemem aktualnie jest to, że kroki 2 i 3 są bardzo zasobożerne i jeden serwer może obsłużyć max dwa takie zadania jednocześnie. Prowadzi to do tego, że przykładowo, jeśli 10 osób wrzuciło by 40 minutowe pliki w jakości HD, to ostatnia z tych osób czekała by na jego przetworzenie cały dzień - ale to raczej jasne i byłem tego świadom tworząc prototyp.

Niestety nie jestem w stanie znaleźć żadnych użytecznych materiałów dotyczących infrastruktury używanej przez YouTube dlatego zmuszony jestem odtworzyć coś takiego własnoręcznie. I tutaj właśnie, jako średnio zaawansowany programista ruby/rails, zwracam się z ogromną prośbą do Was. Jeśli macie jakieś pomysły, rady czy jakiekolwiek doświadczenie w podobnej materii, to BŁAGAM o podzielenie się tym ze mną : )

Tak na szybko - mój pomysł polega na tym, aby system ten składał się z jednej aplikacji głównej [master] (która zbierała by requesty o przetworzenie filmu i ogólnie informacje co się aktualnie z którym filmem dzieje) oraz z mini-aplikacji [slave], które zajmowały by się już stricte procesami opisanymi w pkt. 2 i 3. Aby cała ta infrastruktura była elastyczna i odporna na dowolnie duży ruch, proces miał by wyglądać następująco:

  1. Plik video przychodzi wraz z ustawieniami do aplikacji głównej
  2. aplikacja główna sprawdza, czy uruchomione są aktualnie jakieś serwery [slave]. Jeśli nie, to:
  • Stawia nowy serwer z obrazu dysku przy pomocy Windows Azure API
  • Instaluje na nim z gita aplikacje [slave]
  • deleguje tej aplikacji “pracę” nad otrzymanym video
  1. Jeśli istnieje jakiś serwer [slave], który “nie ma nic do roboty”, to deleguje mu obróbkę video
  2. Aplikacja [slave], która skończy pracę nad video powiadamia [master] o zakończonej pracy i cały proces obróbki kończy się.

Do tego wszystkiego, jeśli [master] uzna, że jakiś [slave] zbyt długo podpiera ścianę, to go uśpi (lub zupełnie skasuje) żeby ograniczyć koszta.

Co myślicie o takim rozwiązaniu? Z góry dzięki za jakąkolwiek pomoc. Jestem jedynym programistą w firmie i potrzebuję z kimś o tym pogadać hehe :wink:

1 Like

Nie prościej zamiast push (master sprawdza i rozsyła zadania do slave’ów) zrobić pull (slave’y same sobie pobierają zadania)?

Ja bym pewnie poszedł w kierunku czegoś takiego, że master przyjmuje surowe video, wrzuca na S3 (żeby mieć na zawsze kopię oryginalnego pliku), po czym wrzuca namiary na plik z parametrami, np. do siekiq. Slave’y chodzą same i pobierają zadania z sidekiq i jak skończą wrzucają info na sidekiq, że skończyły, żeby master wiedział. Całą infrastrukturę i tak musisz monitorować, więc będziesz wiedział jakie masz obłożenie slave’ów i możesz je odpowiednio tworzyć/ubijać.

2 Likes

Ale generalnie kierunek dobry, kroki 2 i 3 powinny być robione przez odrębne mikroaplikacje (workery).

Jak ja odbierałem pliki po API to przydało mi się http://wiki.nginx.org/HttpUploadModule

Ja bym jeszcze zasugerował, żeby aplikacja slave (lub jakiś 3 rodzaj miniaplikacji) zajmowała się też przyjmowaniem uploadowanych plików, jeżeli byś się chciał przygotować na upload dużej ilości plików. Pulę serwerów slave można postawić za jakimś load balancerem, wtedy użytkownicy będą wgrywali pliki zawsze pod jeden adres, a load balancer będzie Ci to rozdzielał na różne slavy.

Jeszcze mozesz poczytac o https://coreos.com/docs/distributed-configuration/getting-started-with-etcd/, mozna to bardzo fajnie wykorzystac do ustawiania chmury serverow. Fajnie jest tez pomyslec o jakims centralnym serverze logow - splunk jest genialny i na dodatek darmowy do 500 mb logow dziennie: http://www.splunk.com/ O logowaniu fajnie napisali tutaj: http://googletesting.blogspot.ch/2013/06/optimal-logging.html
Dobrze tez miec centralna baze danych gdzie logujesz status przetwarzania danej pracy. Co do odpalania serverow, mozesz sprawdzac opoznienie - jesli miedzy zleceniem pracy a jej startem jest wiecej niz 10 sec, odpalasz nowy (nowe servery). Do przechowywania listy serverow swietnie nadaje sie etcd, mozesz tez tam trzymac cala konfiguracje.

PS: jakies linki, ktore wygladaja obiecujaco:
http://blog.gopheracademy.com/day-06-service-discovery-with-etcd

1 Like