Apa itu build yang dapat diulang?
Deterministic Build atau Reproducible Build sedikit berbeda, tetapi dapat dipahami sebagai hal yang sama dari artikel ini.
Build yang dapat direproduksi mengacu padaBeberapa eksekusi proses build dengan input dan lingkungan build yang sama dapat menghasilkan hasil yang sama persis。 Teknologi ini penting untuk pengembangan perangkat lunak, distribusi, dan validasi keamanan.
Build dapat direproduksi jika memberikan output yang sama persis terlepas dari kapan dan di mana build dijalankan. Tidak peduli komputer mana yang Anda jalankan, jam berapa, dan layanan eksternal apa yang Anda akses melalui jaringan, build yang dapat direproduksi menghasilkan output byte demi byte yang sama. Ini bagus untuk pengembangan (karena build yang dapat direproduksi mudah dibagikan di berbagai perangkat pengembang) dan produksi (karena mudah untuk memastikan bahwa hasil build yang dapat direproduksi tidak dirusak – cukup jalankan ulang build di mesin Anda sendiri dan periksa apakah hasilnya konsisten!). sangat berguna.
Tiga pilar build yang dapat diulang
Pilar 1: Build yang dapat diulang
Pengulangan build mengacu pada apa yang terjadi pada mesin build itu sendiri. Dengan asumsi input build kita tersedia, dan tidak ada yang berubah di dunia di sekitar kita, apakah build kita menghasilkan output yang sama saat diulang?
Rencana instalasi deterministik
Persyaratan pertama, paling sederhana, dan paling jelas dalam build yang dapat diulang adalah rencana penginstalan dependensi deterministik.
Dalam sebagian besar bahasa, semudah memeriksa file yang terkunci. Alat build modern sering kali memungkinkan proyek untuk mengekspresikan persyaratan dependensi langsung sebagai batasan, lalu menyelesaikan batasan tersebut untuk menghasilkan rencana penginstalan (daftar nama dependensi dan pasangan versi untuk diinstal). Banyak dari alat ini juga menghasilkan file kunci untuk rencana instalasi berseri. Pengembang dapat mengirimkan file kunci ini ke kontrol versi sehingga build mendatang menggunakan nama dan versi dependensi yang sama.
Perhatikan bahwa kita juga membutuhkan deterministik dalam build dependensi itu sendiri (bukan hanya pemilihan versi), dan rencana instalasi deterministik tidak memungkinkan kita untuk mencapainya!
Konstruksi deterministik
Setelah kita tahu apa yang harus dibuat, build kita sendiri (termasuk kode kita sendiri dan build kode dependensi) harus benar-benar deterministik.
Ini mungkin sebenarnya tidak menjadi masalah untuk proyek tanpa langkah kompilasi! Misalnya, proyek Node dengan semua dependensi adalah JavaScript murni, dan tidak ada pekerjaan tambahan yang diperlukan untuk mencapai deterministik yang efektif.
Untuk proyek yang menyertakan langkah-langkah kompilasi atau terjemahan (kompilasi sumber-ke-sumber), memastikan determinisme sejauh ini merupakan bagian tersulit dalam membangun build yang dapat direproduksi. Proses kompilasi dapat secara implisit memperkenalkan non-determinisme dalam beberapa cara, termasuk:
- Skrip build program Turing-lengkap dapat mengubah output yang dikompilasi sesuka hati.
- Skrip pasca-instalasi yang mengandalkan pencarian sistem file yang dapat dieksekusi atau panggilan jaringan.
- C mengikat ke paket yang dipasang sistem di mana pengikatan pada sistem yang berbeda dengan header yang berbeda dapat menghasilkan output yang berbeda.
- Langkah-langkah untuk membuat file yang dibaca di luar kontrol versi.
- Bangun langkah-langkah untuk menghasilkan stempel waktu menggunakan waktu sistem.
- Langkah-langkah untuk membangun dependensi yang tidak dinyatakan dalam rencana penginstalan unduhan jaringan (misalnya, unduh dependensi NPM dari GitHub untuk build biner cache yang terikat C).
- Ubah perilaku berdasarkan variabel lingkungan yang saat ini ditetapkan, tetapi jangan mengirimkan build dengan konfigurasi variabel lingkungan.
Tidak semua perilaku ini selalu menimbulkan ketidakpastian saat diatur dengan benar, tetapi mengonfigurasi proses build dengan benar bisa menjadi rumit dan sulit. Misalnya, Anda dapat membaca postingan blog ini tentang ketidakpastian dalam build Chromium. Banyak dari masalah ini dapat dimitigasi dengan mengontrol lingkungan bangunan lokal, yang akan kita bahas di bagian selanjutnya.
Pilar 2: Lingkungan yang tidak dapat diubah
Bahkan dengan build yang dapat diulang, kita perlu memastikan bahwa input build tidak berubah. Seringkali, ini berarti bahwa kita ingin memastikan bahwa kita membangun gambaran lingkungan kita yang tidak dapat diubah.
Lingkungan lokal yang tidak dapat diubah
Seperti yang kita bahas di atas, sumber umum ketidakpastian build adalah mengandalkan "dependensi" yang tidak ditangkap oleh alat build. Pustaka sistem terikat C adalah contoh yang paling umum, tetapi faktor lingkungan lokal lainnya seperti pengaturan variabel lingkungan dan file di luar cakupan kontrol versi juga dapat memengaruhi build.
Cara mudah untuk mengurangi masalah ini adalah dengan menjalankan build dalam kontainer yang diketahui dan tidak dapat diubah. Misalnya, runtime kontainer seperti Docker membantu memastikan bahwa semua orang menggunakan dependensi sistem yang sama, variabel lingkungan yang sama, dan berjalan pada sistem file yang sama. Selain itu, mudah untuk memverifikasi bahwa isi kontainer cocok dengan kontainer build yang diketahui, dan jika diperlukan, kontainer dapat dengan mudah dihapus sepenuhnya dari gambar yang diketahui dan dibuat ulang.
Perhatikan bahwa kami sangat jelas tentang kontainer yang diketahui atau gambar kontainer yang diketahui. Tidak cukup hanya mengirimkan Dockerfile! Mengapa? Karena Dockerfile itu sendiri tidak menjelaskan proses build yang dapat direproduksi sepenuhnya untuk gambar Docker, karena mereka tidak berjalan di lingkungan global yang tidak dapat diubah.
Lingkungan global yang tidak dapat diubah
Sistem build sering berinteraksi dengan layanan eksternal untuk menyelesaikan tugas seperti resolusi versi dan unduhan dependensi. Tetapi layanan eksternal sering berubah.
Menjalankan apt install nodejs hari ini akan memberi Anda hasil yang berbeda dari tahun lalu, dan mungkin tahun depan juga akan mendapatkan hasil yang berbeda. Itu sebabnya Dockerfiles sendiri tidak dapat menggambarkan build yang dapat direproduksi - menjalankan Dockerfile yang sama pada titik waktu yang berbeda akan menghasilkan output build yang berbeda!
Mitigasi sederhana di sini adalah mengonfigurasi build bila memungkinkan, menentukan versi yang tepat (idealnya, hash konten yang tepat juga) sehingga build mendatang menggunakan versi yang sama dengan build saat ini. Tetapi layanan eksternal juga dapat mengubah perilaku mereka secara tak terduga - build yang dapat direproduksi yang benar-benar pesimis menjalankan gambar internal dengan sebanyak mungkin sumber daya jaringannya.
Pilar 3: Ketersediaan sumber daya
Katakanlah bangunan kita dapat diulang dan dunia di bawah kaki kita tidak berubah. Yang kita butuhkan sekarang adalah akses ke input build. Kelihatannya sederhana, bukan? Sumur......
Registri terkadang gagal
Sebagian besar pengembang Node telah mengalami setidaknya satu pemadaman NPM, di mana alur build tanpa caching atau mencerminkan paket NPM terganggu. Banyak pengembang Node juga mengalami penghapusan left-pad dan faker, yang telah merusak ekosistem NPM dan secara efektif berjumlah pemadaman.
Satu-satunya cara andal untuk mengurangi jeda build tersebut adalah dengan menjalankan cermin registri paket Anda sendiri. Ketika layanan eksternal tidak tersedia, gambar dapat tetap online; Ketika registri resmi menghapus paket lama, cermin dapat terus menyediakan layanan. Prinsip yang sama berlaku untuk layanan jarak jauh lainnya: kecuali Anda menjalankan gambar Anda sendiri, ketersediaan alur build hanya sebanding dengan ketersediaan layanannya.
Memilih untuk menjalankan citra layanan selalu merupakan pertukaran yang rumit. Di satu sisi, registri seperti NPM memiliki tim teknik dan operasi khusus yang memiliki keahlian untuk menjaga sistem ini tetap online. Di sisi lain, jauh lebih mudah untuk menjalankan gambar kecil untuk serangkaian kecil dependensi daripada menjalankan semua gambar NPM. Anda harus membuat keputusan pencerminan berdasarkan spesifikasi setiap layanan, dengan mempertimbangkan keandalan layanan eksternal historis dan ketersediaan build tim Anda dan kebutuhan kepegawaian.
Pemasok memastikan ketersediaan maksimum
Cara mudah untuk memastikan ketersediaan maksimum dependensi proyek Anda adalah dengan menambahkannya ke vendor Anda. Sebagian besar pengelola paket mendukung beberapa bentuk "vendoring", yang berarti bahwa alih-alih mengandalkan unduhan dari layanan eksternal, kami menyimpan kode sumber dependensi dalam kontrol versi, hidup berdampingan dengan kode sumber kami. Misalnya, di Node, ini mungkin terlihat seperti melakukan node_modules ke kontrol sumber.
Meskipun solusi ini tidak sempurna (tergantung pada bagaimana vendor dan proyek Anda disiapkan, yang dapat membebani kontrol versi Anda), ini sering kali merupakan solusi paling sederhana dan termudah untuk ketersediaan maksimum.
Referensi:
Login hyperlink terlihat.
Login hyperlink terlihat. |