بررسی فایل سیستم HDFS از Apache Hadoop و امکان سنجی استفاده از آن به عنوان یک ذخیره ساز برودکست – بخش اول

مقدمه

سرویس هادوپ آپاچی یکی از پایه های سیستم های distributed در قرن اخیر است که پایه بسیاری از کسب و کارهای بزرگ، مانند yahoo با ده هزار هسته cpu و facebook با 100 پتابایت اطلاعات بر روی آن قرار دارد. hadoop که اسم فیل اسباب بازی پسر یکی از توسعه دهندگان اصلی این سرویس بود، اکنون یکی از اسم های پر سر و صدای عرصه کامپیوتر امروز است.

یکی از بخش های مهم هادوپ، سیستم فایل آن است که به نام Hadoop Distributed File System یا بصورت خلاصه HDFS نامیده می شود. HDFS قابلیت های جالبی از جمله طراحی برای فایل های بسیار بزرگ، قابلیت replica گرفتن از فایل برای جلوگیری از دست رفتن آن، قابلیت کار روی سخت افزارهای عادی (commodity hardware) بدون نیاز به سخت افزارهای خاص و قابلیت توزیع شدن روی تعداد زیادی کامپیوتر و نمایش آنها بصورت یک سیستم واحد را دارد.

بسیاری از این قابلیت ها، قابلیت هایی هستند که می توانند برای سیستم های ذخیره سازی ویدئو بسیار مهم باشند. سیستم های ذخیره سازی ویدئو مخصوصا برای آرشیو، نیاز به ذخیره سازهای ارزان قیمتی دارند که که اهمیت حفظ فایل ویدئویی در آن بالاست و با فایل های بزرگ سر و کار دارد، و علاوه بر این از نظر اقتصادی استفاده از دیسک های SAN برای آنها مقرون به صرفه نیست.

در مقاله جاری که برداشتی از فصل سوم با عنوان The Hadoop Distributed Filesystem کتاب Hadoop- The Definitive Guide, 3rd Edition، ما ساختار هادوپ و عملکرد آنرا بررسی می کنیم و در نهایت جمع بندی خود از اینکه آیا استفاده از hdfs برای ذخیره سازی ویدئویی مناسب است یا نه را ارائه خواهیم کرد. علاقمندان می توانند خود برای مطالعه اصل متن به کتاب مذبور مراجعه کنند.

در بخش اول این مقاله ما به معرفی فایل سیستم HDFS و ساختار NameNode های آن و نحوه ایجاد یک کلاستر high availability و روش دسترسی به فایل های ذخیره شده داخل کلاستر می پردازیم. در بخش دوم این مقاله ما عملیات درونی read / write را بررسی می کنیم و به روش چیده شدن بلوک ها داخل کلاستر می پردازیم. علاوه بر این در بخش دوم ما بعضی ابزارهای hdfs را بررسی کرده و در مورد اینکه hdfs آیا به عنوان یک ذخیره ساز برودکست قابل استفاده است یا نه تصمیم می گیریم.

معرفی فایل سیستم HDFS

فایل سیستم HDFS برای فایل های بسیار بزرگ که بصورت استریمینگ به آنها دسترسی داده می شود طراحی شده است و روی سخت افزارهای متداول اجرا می شود. به عبارت دیگر این فایل سیستم برای فایل های چند گیگابایتی تا چند ترابایتی که یک بار نوشته می شوند و بارها خوانده می شوند مناسب است و از آنجا که روی سخت افزارهای عادی اجرا می شود و نیازی به سخت افزارهای خاص ندارد، باید آمادگی مقابله با از کار افتادن دیسک یا نود در خود فایل سیستم وجود داشته باشد.
به همین دلایل hdfs برای عملیات فایلی که نیاز به تاخیر کم (low latency) دارد مناسب نیست. علاوه بر این hdfs برای جایی که تعداد بسیار زیادی فایل های کوچک وجود دارد یا برای جاهایی که مداوم به فایل برای نوشتن در بخش های مختلف آن بصورت تصادفی دسترسی پیدا می شود مناسب نیست.

HDFS فایل ها را بصورت بلوک های 64 یا 128 مگابایتی تقسیم می کند و نگهداری می کند. اگر کسی با ساختار دیتا آشنا باشد، می داند که معمولا سایز بلوک روی یک دیسک 512 تا 4096 بایت (اندازه یک سکتور) و در فایل سیستم های معمولی 4 کیلوبایت تا 16 کیلو بایت است. اما hdfs از بلوک های به این بزرگی (64 MByte) به این خاطر استفاده میکند تا زمان seek time روی کل سیستم را کمتر کند.

ساختار hdfs به این صورت است که یک NameNode و یکسری DataNode وجود دارند. وظیفه NameNode که از اینجا به بعد ما آنرا NN می نامیم، این است که اطلاعات متادیتای فایل را در خود نگهداری کند و DataNode هم اطلاعات بلوکهای فایل را در خود نگهداری می کنند.
HDFS کاملا با استاندارد POSIX سازگار نیست، ولی بخش زیادی از آنرا پیاده سازی می کند. دسترسی های کاربر و گروه شبیه posix است و ACL (Access Control List) در آن وجود ندارد. از آنجا که فایل ها را از روی HDFS نمی توان اجرا کرد گزینه x در دسترسی ها ندیده گرفته می شود.

علاوه بر این HDFS می تواند بک اند (backend) و اینترفیس (interface )های مختلفی داشته باشد که می توانند دیتا را با فرم های مختلف (مثلا نوعی distributed RAID) ذخیره کنند، یا اجازه دسترسی به آن بصورت http, ftp, sftp را بدهند

ساختار NameNode ها و عملکرد داخلی آنها

هر NN اطلاعات درخت فایل سیستم و متادیتای فایل (مثل تاریخ و ساعت ایجاد یا آخرین دسترسی، گروهها و کاربران مجاز به دسترسی، تعداد رپلیکا و مانند این (file mode, file replica number, creat datetime, last modified datetime, replica number, file size, file owner, file group, file absolute name)) را در دو فایل اصلی که namespace image و edit log نامیده می شوند نگهداری می کند. تمام این اطلاعات در حافظه سرور NN نگهداری می شوند و برای هر یک میلیون فایل 300 مگابایت فضای حافظه لازم است (تقریبا 150 بایت به ازای هر فایل و دایرکتوری). یکی از نقاط ضعف hdfs در اینجا خودش را نشان می دهد. به عبارت دیگر از آنجا که فضای رم هر سرور NN به هر حال به یک عددی محدود می شود، تعداد فایل قابل نگهداری در هر کلاستر HDFS محدود است و به همین دلیل HDFS برای نگهداری تعداد بسیار زیاد فایل (چند میلیارد فایل) در یک namespace مناسب نیست.

برای حل این مشکل باید از HDFS Federation استفاده کرد که از چندین NameNode مختلف که هر کدام بخشی از فایل ها را در خود نگه می دارند استفاده می شود. در این حالت مثلا اطلاعات /usr و زیر مجموعه آن در یک NameNode و اطلاعات /doc در یک NameNode دیگر نگهداری می شوند. باید توجه داشت که در این حالت این وظیفه کلاینت است که یک mount table لوکال برای خود نگهداری کند تا بتواند بفهمد برای دسترسی به فایل های هر مجموعه باید با کدام NN تماس بگیرد.

باید توجه داشت که سرور NN اطلاعات اینکه بلوکها در کدام DN ها نگهداری شده اند را در خود نگهداری نمی کند و این اطلاعات هنگامی که سرور NN برای بار اول اجرا می شود با پرس و جو (query) از DN های مجموعه جمع آوری شده و بعد از آن فقط به روزآوری می شود.
نکته مهم دیگری که در اینجا مشخص می شود، این است که NameNode در HDFS یک تک نقطه آسیب پذیری یا SPOF (Single Point Of Failure) است و در صورت از کار افتادن آن، دسترسی به کل فایل های HDFS غیر ممکن می شود. در صورتیکه اطلاعات NN از بین برود از آنجا که متادیتای کل فایل ها هم از بین رفته است، در واقعی دسترسی به کل فایل های ذخیره شده روی کلاستر هم از بین خواهد رفت. بنابراین باید تمام تلاش ممکن برای پایداری این نود (make it resilient) انجام شود، و این کار از طریق بک آپ از اطلاعات و ایجاد یک کلاستر HA (High Availability) انجام می شود.

باید در نظر داشت که تنها داشتن بک آپ برای داشتن یک کلاستر پایدار کافی نیست. در صورتیکه NameNode اصلی از کار بیافتد، و یک NN جدید بالا بیاید، برای راه اندازی آن باید اول: بک آپ namespace image و edit log داخل حافظه بار شود، دوم: این دو فایل با هم ترکیب شوند تا آخرین وضعیت فایل سیستم مشخص شود و سوم: از دیتا نود ها به اندازه کافی گزارش های وضعیت بلوک دریافت شود تا NN جدید از حالت safe mode خارج شده و عملا کنترل را در دسترس بگیرد، که برای یک کلاستر بزرگ ممکن است این بیش از یک ساعت طول بکشد.
(باید در نظر داشت که در HDFS یک secondary NameNode هم وجود دارد، اما عملکرد آن چیز دیگری است).

ساخت یک کلاستر با دسترسی بالا در HDFS

برای ساخت یک کلاستر HA (High Availability) در HDFS باید یک زوج از NameNode ها را بصورت active standby راه اندازی کرد. البته باید توجه داشت این حالت نیازمند پیش زمینه های زیر است:
الف: هر دو سرور باید برای لاگ فایل ها و namespace image فایل ها به یک shared storage مشترک وصل باشند، که می تواند یک فایل سرور NFS باشد.
ب: باید DataNode ها گزارش های تغییرات بلوک را به هردو سرور NN بفرستند.
ج: باید کلاینت هم از این ترکیب جدید با اطلاع باشد تا در صورتیکه با NN اصلی نتوانست تماس بگیرد، بصورت خودکار روی NN دوم Fail Over کند.

در این حالت هر دو NN با هم در حال کار هستند، اما فقط NN اصلی است که تغییرات بلوک ها را عملا ذخیره می کند. به محض اینکه برای این NN مشکلی پیش بیاید، مکانیزم HearBeat (ضربان قلب) در نظر گرفته شده سرور NN دوم را مطلع می کند و سرور دوم مسئولیت را به عهده می گیرد. علاوه بر این سرور NN دوم برای اینکه سرویس معیوب روی NN اول احتمالا فایل های لاگ یا namespace را خراب نکند، عملیاتی به نام محصور کردن (یا fencing) را روی سرور اول اجرا می کند که شامل موارد زیر است:
الف: kill کردن پروسس NN روی NameNode اول.
ب: قطع دسترسی (revoke) سرور NN اول به shared storage که namespace image و فایل های ادیت لاگ در آن نگهداری می شوند.
ج: غیر فعال کردن پورت شبکه NN اول با فرستادن دستورات لازم به سیستم عامل آن.
د: و در نهایت و در صورت لزوم انجام STONITH (Shoot The Other Node In The Head) یا زدن تیر خلاص به نود اول، که با استفاده از فرمان فرستادن به کلیدهای برق مخصوص، برق سرور اول به کل قطع شود.

باید در نظر داشت که failover به NN دوم در شبکه می تواند بصورت graceful، یعنی با فرمان مدیر شبکه و برای مقاصد سرویس دوره ای (routine maintenance) باشد. در واقع بیشتر failover ها به NN دوم از این نوع است، و امکان از کار افتادن NN اول به خاطر ungraceful failover یا مشکلات واقعی شبکه بسیار کمتر است. علاوه بر این تشخیص مشکلات شبکه مانند حالتی که بخاطر از کار افتادن یک سوییچ یک بخش شبکه از یک بخش دیگر جدا می شود (حالت split brain یا network partition) بصورت خودکار بسیار مشکل است.

روش دسترسی به فایل های ذخیره شده داخل HDFS

باید توجه داشت که روش کلی دسترسی به هادوپ می تواند به دو صورت مختلف باشد.

روش اول: خود کامپیوتر کلاینت مستقیما به فایل سیستم دسترسی پیدا می کند و فایل را روی آن کپی می کند یا از آن میخواند. در این حالت یک وب سرور امبدد که داخل خود NameNode روی پورت 50070 کار می کند اطلاعات متادیتای فایل را بصورت json یا xml می دهد و بعد خود بلوک های فایل از DataNode ها بصورت استریم دیتا روی پورت 50075 جابجا می شوند.(direct client access)

روش دوم: یکسری کامپیوتر proxy (یا gateway) وجود دارند که از یک طرف به فایل سیستم هادوپ دسترسی دارند و از طرف دیگر فایل ها را با استانداردهای متداول تر، مانند NFS, SMB, REST به کاربرد می دهند. در واقع در این حالت روی این کامپیوترهای پروکسی یک کلاینت هادوپ و یک سرور سیستم مورد نظر با هم سوار است که امکان دسترسی به فایل سیستم را با کامپیوترهایی که کلاینت HDFS را ندارند می دهد.(access by HDFS proxies). در این حالت چون کل ترافیک از داخل این نود پروکسی رد می شود، می توان آنرا با فایروال کنترل کرد، یا اینکه پهنای باند را کم و زیاد کرد. این معمولا روشی است که برای انتقال فایل بین دو کلاستر مختلف HDFS استفاده می شود.
دسترسی مستقیم کلاینت به hdfs بصورت های مختلف ممکن است. یکی از این روش ها، دسترسی از طریق خود هادوپ است که می توان با زدن hadoop fs -help گزینه های مختلف را مشاهده کرد. به عنوان مثال hadoop fs -copyFromLocal یک فایل را از کلاینت به hdfs کپی می کند یا hadoop fs -copyToLocal یک فایل را از کلاستر hdfs به سیستم جاری کپی می کند. hadoop fs -mkdir یک دایرکتوری در hdfs می سازد. پورت پیش فرض hdfs پورت 8020 است و آدرس آنهم بصورت hdfs://URI شناخته می شود.

روش دیگر دسترسی با استفاده از کلاینت FUSE برای سیستم های لینوکسی است که HDFS را زیر یک شاخه فایل سیستم جاری مونت کرده و عملیات رایج فایل را می توان روی آن انجام داد.

روش سوم، دسترسی از طریق برنامه کلاینتی است که خود کاربر سیستم نوشته است. باید توجه داشت که توسعه اولیه HDFS به عنوان فایل سیستم hadoop و برای انجام عملیات Map/Reduce است و بنابراین بخش مهمی از دسترسی توسط برنامه های کلاینت که از طریق کتابخانه های HDFS و هادوپ به فایل سیستم دسترسی پیدا میکنند است. از آنجا که هادوپ و hdfs با زبان جاوا نوشته شده اند، بیشتر کتابخانه های دسترسی به این سیستم هم برای java پیاده سازی شده است، اما کتابخانه های زیادی برای سایر زبان ها، مانند C و بقیه هم وجود دارد. به عنوان مثال واسط fuse این سیستم روی کتابخانه سی آن (libhdfs) پیاده سازی شده است. در این کتابخانه ها توابع لازم برای callback interface ها، مثلا برای پروگرس عملیات فایل، توابع اضافه کردن به ته فایل (مثلا برای log file ها) یا جابجا شدن روی فایل (seek) در نظر گرفته شده است. باید توجه داشت که در hdfs امکان اضافه کردن به وسط فایل وجود ندارد و به فایل فقط از ته آن می توان اطلاعات اضافه کرد (یعنی در رایت امکان seek وجود ندارد). علاوه بر این با وجود اینکه امکان seek در خواندن وجود دارد، ولی این عملیات resource زیادی از سیستم مصرف می کند (expensive operation) و بنابراین باید بصورت منطقی از آن استفاده کرد.

در hdfs توابع استفاده از wild card کاراکترها مانند * و ? وجود دارند و می توان فقط اسم یکسری فایل مشخص را گرفت.(globbing)

در بخش بعدی ما عملیات درونی read / write را بررسی می کنیم و به روش چیده شدن بلوک ها داخل کلاستر می پردازیم. علاوه بر این در بخش دوم ما بعضی ابزارهای hdfs را بررسی کرده و در مورد اینکه hdfs آیا به عنوان یک ذخیره ساز برودکست قابل استفاده است یا نه تصمیم می گیریم.