Jika Anda memiliki codebase yang besar, rantai dependensi dapat menjadi sangat dalam. Bahkan biner sederhana sering kali bergantung pada puluhan ribu target build. Pada skala ini, tidak mungkin menyelesaikan build dalam waktu yang wajar di satu mesin: tidak ada sistem build yang dapat menghindari hukum fisika mendasar yang diterapkan pada hardware mesin. Satu-satunya cara agar hal ini berfungsi adalah dengan sistem build yang mendukung build terdistribusi di mana unit pekerjaan yang dilakukan oleh sistem tersebar di sejumlah mesin yang arbitrer dan dapat diskalakan. Dengan asumsi kita telah memecah pekerjaan sistem menjadi unit yang cukup kecil (akan dibahas lebih lanjut nanti), hal ini akan memungkinkan kita menyelesaikan build ukuran apa pun secepat yang kita inginkan. Skalabilitas ini adalah tujuan utama yang telah kami upayakan dengan menentukan sistem build berbasis artefak.
Penyimpanan cache jarak jauh
Jenis build terdistribusi yang paling sederhana adalah build yang hanya memanfaatkan penyimpanan cache jarak jauh, yang ditunjukkan pada Gambar 1.
Gambar 1. Build terdistribusi yang menampilkan caching jarak jauh
Setiap sistem yang melakukan build, termasuk workstation developer dan sistem integrasi berkelanjutan, berbagi referensi ke layanan cache jarak jauh umum. Layanan ini dapat berupa sistem penyimpanan jangka pendek yang cepat dan lokal seperti Redis atau layanan cloud seperti Google Cloud Storage. Setiap kali pengguna perlu membangun artefak, baik secara langsung maupun sebagai dependensi, sistem akan terlebih dahulu memeriksa dengan cache jarak jauh untuk melihat apakah artefak tersebut sudah ada di sana. Jika ya, aplikasi tersebut dapat mendownload artefak, bukan membangunnya. Jika tidak, sistem akan membuat artefak itu sendiri dan mengupload hasilnya kembali ke cache. Artinya, dependensi tingkat rendah yang tidak terlalu sering berubah dapat dibangun satu kali dan dibagikan kepada pengguna, bukan harus dibangun ulang oleh setiap pengguna. Di Google, banyak artefak ditayangkan dari cache, bukan dibangun dari awal, sehingga mengurangi biaya menjalankan sistem build kami secara signifikan.
Agar sistem caching jarak jauh berfungsi, sistem build harus menjamin bahwa build dapat direproduksi sepenuhnya. Artinya, untuk target build apa pun, harus memungkinkan untuk menentukan set input ke target tersebut sehingga set input yang sama akan menghasilkan output yang sama persis di komputer mana pun. Ini adalah satu-satunya cara untuk memastikan bahwa hasil mendownload artefak sama dengan hasil membangunnya sendiri. Perhatikan bahwa setiap artefak dalam cache harus diberi kunci berdasarkan targetnya dan hash inputnya. Dengan begitu, berbagai engineer dapat melakukan berbagai modifikasi pada target yang sama secara bersamaan, dan cache jarak jauh akan menyimpan semua artefak yang dihasilkan dan menayangkannya dengan tepat tanpa konflik.
Tentu saja, agar ada manfaat dari cache jarak jauh, mendownload artefak harus lebih cepat daripada membangunnya. Hal ini tidak selalu terjadi, terutama jika server cache jauh dari mesin yang melakukan build. Jaringan dan sistem build Google disetel dengan cermat agar dapat membagikan hasil build dengan cepat.
Eksekusi jarak jauh
Penyimpanan dalam cache jarak jauh bukanlah build terdistribusi yang sebenarnya. Jika cache hilang atau jika Anda melakukan perubahan tingkat rendah yang mengharuskan semuanya dibangun ulang, Anda tetap perlu melakukan seluruh build secara lokal di mesin Anda. Tujuan sebenarnya adalah mendukung eksekusi jarak jauh, yang memungkinkan pekerjaan sebenarnya dalam melakukan build didistribusikan ke sejumlah pekerja. Gambar 2 menggambarkan sistem eksekusi jarak jauh.
Gambar 2. Sistem eksekusi jarak jauh
Alat build yang berjalan di komputer setiap pengguna (tempat pengguna adalah engineer manusia atau sistem build otomatis) mengirimkan permintaan ke master build pusat. Build master memecah permintaan menjadi tindakan komponennya dan menjadwalkan eksekusi tindakan tersebut melalui kumpulan pekerja yang dapat diskalakan. Setiap pekerja melakukan tindakan yang diminta dengan input yang ditentukan oleh pengguna dan menulis artefak yang dihasilkan. Artefak ini dibagikan di seluruh mesin lain yang menjalankan tindakan yang memerlukannya hingga output akhir dapat dihasilkan dan dikirim ke pengguna.
Bagian paling rumit dalam menerapkan sistem semacam itu adalah mengelola komunikasi antara pekerja, master, dan mesin lokal pengguna. Pekerja mungkin bergantung pada artefak perantara yang dihasilkan oleh pekerja lain, dan output akhir harus dikirim kembali ke mesin lokal pengguna. Untuk melakukannya, kita dapat membangun di atas cache terdistribusi yang dijelaskan sebelumnya dengan membuat setiap pekerja menulis hasilnya ke dan membaca dependensinya dari cache. Blok master mencegah pekerja melanjutkan hingga semua yang mereka andalkan selesai, sehingga mereka dapat membaca input dari cache. Produk akhir juga di-cache, sehingga komputer lokal dapat mendownloadnya. Perhatikan bahwa kita juga memerlukan cara terpisah untuk mengekspor perubahan lokal di pohon sumber pengguna sehingga pekerja dapat menerapkan perubahan tersebut sebelum membangun.
Agar dapat berfungsi, semua bagian sistem build berbasis artefak yang dijelaskan sebelumnya harus digabungkan. Lingkungan build harus sepenuhnya mendeskripsikan dirinya sendiri sehingga kita dapat meluncurkan pekerja tanpa intervensi manusia. Proses build itu sendiri harus sepenuhnya mandiri karena setiap langkah mungkin dijalankan di mesin yang berbeda. Output harus sepenuhnya deterministik sehingga setiap pekerja dapat memercayai hasil yang diterimanya dari pekerja lain. Jaminan seperti itu sangat sulit diberikan oleh sistem berbasis tugas, sehingga hampir mustahil untuk membangun sistem eksekusi jarak jauh yang andal di atas sistem tersebut.
Build terdistribusi di Google
Sejak tahun 2008, Google telah menggunakan sistem build terdistribusi yang menggunakan penyimpanan dalam cache jarak jauh dan eksekusi jarak jauh, yang diilustrasikan pada Gambar 3.
Gambar 3. Sistem build terdistribusi Google
Cache jarak jauh Google disebut ObjFS. Objfs terdiri dari backend yang menyimpan output build di Bigtable yang didistribusikan ke seluruh mesin produksi kami dan daemon FUSE frontend bernama objfsd yang berjalan di setiap mesin developer. Daemon FUSE memungkinkan engineer menjelajahi output build seolah-olah output tersebut adalah file normal yang disimpan di workstation, tetapi dengan konten file yang didownload sesuai permintaan hanya untuk beberapa file yang diminta langsung oleh pengguna. Menayangkan konten file sesuai permintaan akan sangat mengurangi penggunaan jaringan dan disk, dan sistem dapat membangun dua kali lebih cepat dibandingkan saat kita menyimpan semua output build di disk lokal developer.
Sistem eksekusi jarak jauh Google disebut Forge. Klien Forge di Blaze (setara internal Bazel) yang disebut Distributor mengirimkan permintaan untuk setiap tindakan ke tugas yang berjalan di pusat data kami yang disebut Scheduler. Scheduler mempertahankan cache hasil tindakan, sehingga dapat langsung menampilkan respons jika tindakan telah dibuat oleh pengguna sistem lainnya. Jika tidak, tindakan akan dimasukkan ke dalam antrean. Kumpulan besar tugas Executor terus-menerus membaca tindakan dari antrean ini, mengeksekusinya, dan menyimpan hasilnya langsung di Bigtable ObjFS. Hasil ini tersedia untuk eksekutor untuk tindakan mendatang, atau untuk didownload oleh pengguna akhir melalui objfsd.
Hasil akhirnya adalah sistem yang dapat diskalakan untuk mendukung semua build yang dilakukan di Google secara efisien. Skala build Google benar-benar sangat besar: Google menjalankan jutaan build yang mengeksekusi jutaan kasus pengujian dan menghasilkan output build sebesar petabyte dari miliaran baris kode sumber setiap hari. Sistem tersebut tidak hanya memungkinkan engineer kami membangun codebase yang kompleks dengan cepat, tetapi juga memungkinkan kami menerapkan sejumlah besar alat dan sistem otomatis yang mengandalkan build kami.