وقتی یک پایگاه کد بزرگ دارید، زنجیرههای وابستگی میتوانند بسیار عمیق شوند. حتی باینری های ساده اغلب می توانند به ده ها هزار هدف ساخت وابسته باشند. در این مقیاس، به سادگی غیرممکن است که یک ساخت را در مدت زمان معقولی روی یک ماشین انجام دهیم: هیچ سیستم ساختنی نمی تواند قوانین اساسی فیزیک تحمیل شده بر سخت افزار ماشین را دور بزند. تنها راه برای انجام این کار با یک سیستم ساخت است که از ساخت های توزیع شده پشتیبانی می کند که در آن واحدهای کاری که توسط سیستم انجام می شود در تعداد دلخواه و مقیاس پذیری از ماشین ها پخش می شوند. با فرض اینکه کار سیستم را به واحدهای به اندازه کافی کوچک تقسیم کرده ایم (در ادامه در این مورد بیشتر توضیح خواهیم داد)، این به ما این امکان را می دهد که هر ساختنی با هر اندازه ای را به همان سرعتی که مایل به پرداخت آن هستیم تکمیل کنیم. این مقیاس پذیری جام مقدسی است که ما با تعریف یک سیستم ساخت مبتنی بر مصنوع روی آن کار کرده ایم.
ذخیره سازی از راه دور
ساده ترین نوع ساخت توزیع شده، ساختی است که فقط از حافظه پنهان از راه دور استفاده می کند، که در شکل 1 نشان داده شده است.
شکل 1 . یک ساخت توزیعشده که ذخیرهسازی از راه دور را نشان میدهد
هر سیستمی که ساختها را انجام میدهد، از جمله ایستگاههای کاری توسعهدهنده و سیستمهای ادغام پیوسته، ارجاعی به یک سرویس حافظه پنهان از راه دور مشترک دارد. این سرویس ممکن است یک سیستم ذخیره سازی کوتاه مدت سریع و محلی مانند Redis یا یک سرویس ابری مانند Google Cloud Storage باشد. هر زمان که کاربر نیاز به ساخت مصنوع داشته باشد، چه به طور مستقیم یا به صورت وابستگی، سیستم ابتدا با حافظه پنهان از راه دور بررسی میکند تا ببیند آیا آن آرتیفکت قبلاً در آنجا وجود دارد یا خیر. اگر چنین است، میتواند به جای ساختن مصنوع، آن را دانلود کند. در غیر این صورت، سیستم خود آرتیفکت را میسازد و نتیجه را در حافظه پنهان بارگذاری میکند. این بدان معناست که وابستگیهای سطح پایین که اغلب تغییر نمیکنند، میتوانند یک بار ساخته شوند و بین کاربران به اشتراک گذاشته شوند نه اینکه توسط هر کاربر بازسازی شوند. در Google، بسیاری از مصنوعات به جای اینکه از ابتدا ساخته شوند، از حافظه پنهان ارائه میشوند، که هزینه اجرای سیستم ساخت ما را بسیار کاهش میدهد.
برای اینکه یک سیستم کش از راه دور کار کند، سیستم ساخت باید تضمین کند که بیلدها کاملاً قابل تکرار هستند. یعنی برای هر هدف ساختی، باید بتوان مجموعه ورودیهای آن هدف را تعیین کرد به طوری که همان مجموعه ورودیها دقیقاً همان خروجی را در هر ماشینی تولید کند. این تنها راهی است که می توان اطمینان حاصل کرد که نتایج دانلود یک مصنوع با نتایج ساخت خود یکسان است. توجه داشته باشید که این امر مستلزم آن است که هر مصنوع در حافظه پنهان هم روی هدف و هم هش از ورودیهایش کلید زده شود - به این ترتیب، مهندسان مختلف میتوانند در همان زمان تغییرات مختلفی را در یک هدف انجام دهند و حافظه پنهان از راه دور همه موارد را ذخیره میکند. مصنوعات به دست آمده و به طور مناسب و بدون درگیری به آنها خدمت می کنند.
البته، برای اینکه یک حافظه پنهان از راه دور سودی داشته باشد، دانلود یک مصنوع باید سریعتر از ساخت آن باشد. این همیشه صادق نیست، به خصوص اگر سرور کش از ماشینی که ساخت را انجام می دهد دور باشد. شبکه و سیستم ساخت گوگل به دقت تنظیم شده است تا بتواند به سرعت نتایج ساخت را به اشتراک بگذارد.
اجرای از راه دور
ذخیره سازی از راه دور یک ساخت توزیع شده واقعی نیست. اگر حافظه پنهان از بین رفت یا اگر تغییری در سطح پایین ایجاد کردید که نیاز به بازسازی همه چیز دارد، همچنان باید کل ساخت را به صورت محلی روی دستگاه خود انجام دهید. هدف واقعی پشتیبانی از اجرای از راه دور است، که در آن کار واقعی انجام ساخت می تواند در بین هر تعداد کارگر پخش شود. شکل 2 یک سیستم اجرای از راه دور را نشان می دهد.
شکل 2 . یک سیستم اجرای از راه دور
ابزار ساخت که بر روی ماشین هر کاربر اجرا می شود (که در آن کاربران یا مهندسان انسانی یا سیستم های ساخت خودکار هستند) درخواست ها را به یک استاد ساخت مرکزی ارسال می کند. استاد ساخت، درخواستها را به اکشنهای اجزای آنها تقسیم میکند و اجرای آن اقدامات را روی یک مجموعه مقیاسپذیر از کارگران زمانبندی میکند. هر کارگر با ورودی های مشخص شده توسط کاربر اقدامات خواسته شده از خود را انجام می دهد و مصنوعات حاصل را می نویسد. این مصنوعات در میان ماشینهای دیگر به اشتراک گذاشته میشوند که اقداماتی را انجام میدهند که به آنها نیاز دارند تا زمانی که خروجی نهایی تولید و برای کاربر ارسال شود.
مشکلترین بخش پیادهسازی چنین سیستمی، مدیریت ارتباط بین کارگران، استاد و ماشین محلی کاربر است. کارگران ممکن است به مصنوعات میانی تولید شده توسط سایر کارگران وابسته باشند و خروجی نهایی باید به ماشین محلی کاربر ارسال شود. برای انجام این کار، میتوانیم روی حافظه پنهان توزیعشده که قبلاً توضیح داده شد، بسازیم و از هر کارگر بخواهیم نتایج خود را در حافظه پنهان بنویسد و وابستگیهایش را از حافظه پنهان بخواند. Master از ادامه کار کارگران جلوگیری می کند تا زمانی که همه چیزهایی که به آنها وابسته هستند تمام شود، در این صورت آنها می توانند ورودی های خود را از حافظه پنهان بخوانند. محصول نهایی نیز در حافظه پنهان ذخیره می شود و به دستگاه محلی اجازه می دهد آن را دانلود کند. توجه داشته باشید که ما همچنین به یک ابزار جداگانه برای صادرات تغییرات محلی در درخت منبع کاربر نیاز داریم تا کارگران بتوانند آن تغییرات را قبل از ساخت اعمال کنند.
برای این کار، همه بخشهای سیستمهای ساخت مبتنی بر مصنوع که قبلاً توضیح داده شد، باید با هم جمع شوند. محیط های ساختمانی باید کاملاً خودتوصیف شوند تا بتوانیم کارگران را بدون دخالت انسان به کار بگیریم. خود فرآیندهای ساخت باید کاملاً مستقل باشند زیرا هر مرحله ممکن است در ماشین متفاوتی اجرا شود. خروجی ها باید کاملاً قطعی باشند تا هر کارگر بتواند به نتایجی که از سایر کارگران دریافت می کند اعتماد کند. ارائه چنین تضمین هایی برای یک سیستم مبتنی بر وظیفه بسیار دشوار است، که ساختن یک سیستم اجرای از راه دور قابل اعتماد را در بالای یک سیستم تقریبا غیرممکن می کند.
ساخت های توزیع شده در Google
از سال 2008، گوگل از یک سیستم ساخت توزیع شده استفاده میکند که هم از حافظه پنهان و هم اجرای از راه دور استفاده میکند، که در شکل 3 نشان داده شده است.
شکل 3 . سیستم ساخت توزیع شده گوگل
حافظه پنهان گوگل از راه دور ObjFS نام دارد. این شامل یک Backend است که خروجیهای Bigtables را در سراسر ناوگان ماشینهای تولید ما توزیع میکند و یک شبح FUSE به نام objfsd که روی ماشین هر توسعهدهنده اجرا میشود. شبح FUSE به مهندسان اجازه می دهد تا خروجی های ساخت را به گونه ای مرور کنند که گویی فایل های معمولی ذخیره شده در ایستگاه کاری هستند، اما محتوای فایل بر حسب تقاضا دانلود می شود فقط برای چند فایلی که مستقیماً توسط کاربر درخواست می شود. ارائه محتویات فایل بر اساس تقاضا، استفاده از شبکه و دیسک را تا حد زیادی کاهش می دهد، و سیستم قادر است دو برابر سریعتر در مقایسه با زمانی که تمام خروجی های ساخت را روی دیسک محلی توسعه دهنده ذخیره می کردیم، بسازد.
سیستم اجرای از راه دور گوگل Forge نام دارد. یک کلاینت Forge در Blaze (معادل داخلی Bazel) به نام Distributor درخواستهایی را برای هر عمل به یک کار در حال اجرا در مرکز داده ما به نام Scheduler ارسال میکند. Scheduler یک حافظه پنهان از نتایج عملکرد نگهداری می کند و به آن اجازه می دهد در صورتی که اقدام قبلاً توسط هر کاربر دیگری از سیستم ایجاد شده باشد، بلافاصله پاسخی را بازگرداند. اگر نه، عمل را در یک صف قرار می دهد. مجموعه بزرگی از وظایف Executor به طور مداوم اقدامات را از این صف می خوانند، آنها را اجرا می کنند و نتایج را مستقیماً در Bigtables ObjFS ذخیره می کنند. این نتایج برای اقدامات آتی در اختیار مجریان قرار می گیرد یا توسط کاربر نهایی از طریق objfsd دانلود می شود.
نتیجه نهایی سیستمی است که مقیاس آن برای پشتیبانی کارآمد از تمام ساختهای انجامشده در Google است. و مقیاس ساختهای Google واقعاً عظیم است: Google میلیونها بیلد را اجرا میکند که میلیونها نمونه آزمایشی را اجرا میکند و هر روز پتابایت خروجی ساخت از میلیاردها خط کد منبع تولید میکند. چنین سیستمی نه تنها به مهندسان ما اجازه میدهد تا به سرعت پایگاههای کد پیچیده بسازند، بلکه به ما امکان میدهد تا تعداد زیادی ابزار و سیستم خودکار را پیادهسازی کنیم که به ساخت ما متکی هستند.