4 poin oleh GN⁺ 2024-03-17 | 1 komentar | Bagikan ke WhatsApp
  • Jika image Docker sudah digunakan sebagai unit deployment, Nix menjadi titik masuk untuk mencoba build deterministik dan penguncian dependensi tanpa banyak mengubah workflow
  • docker build pada umumnya mudah bergantung pada kondisi internet publik, seperti repositori Ubuntu atau unduhan eksternal, sehingga sulit mereproduksi image yang sama setelah waktu berlalu
  • Nix mengetahui dependensi yang diperlukan pada level paket, sehingga dengan dockerTools.buildLayeredImage dimungkinkan membuat konfigurasi yang hanya mengunggah ulang layer yang berubah
  • Contoh douglas-adams-quotes berbasis Go menunjukkan bahwa binary bisa dibuat dengan pkgs.buildGoModule, lalu dimuat dan dideploy seperti image Docker biasa melalui nix build.#docker dan docker load <./result
  • Jika beberapa service dalam monorepo berbagi layer dan cache Nix, waktu serta biaya build dapat dikurangi saat mereproduksi image dari commit lama dan melakukan deployment berulang

Mengapa memasang Nix ke proses build image Docker

  • Nix memiliki tiga karakter sekaligus: package manager, bahasa, dan sistem operasi
    • Instruksi build paket ditulis dengan bahasa Nix
    • Package manager Nix dapat membuat software, tool, image NixOS, image container, dan lainnya berdasarkan instruksi tersebut
  • Mengadopsi Nix ke pipeline CI/CD yang sudah ada tidak mudah
    • Nix berbeda dari cara kerja yang familier bagi banyak developer
    • Kecuali berada di lingkungan yang bisa memulai semuanya dari awal, seperti startup baru atau homelab, hambatan adopsinya besar
  • Organisasi yang sudah menggunakan Docker dapat mulai menerapkan Nix dari build image container untuk bereksperimen tanpa banyak merusak alur deployment yang ada

Titik rapuh dalam build Docker

  • Docker dan containerization telah diadopsi secara luas, sehingga image Docker digunakan seperti format paket universal de facto di internet
    • Platform seperti Fly.io, Railway, dan Render menggunakan image Docker, bukan image VM sembarang atau program Linux berbentuk tarball
  • Build Docker tidak selalu deterministik
    • Dockerfile yang umum terlihat di internet kebanyakan berhasil dibuild, tetapi persentase kecil kegagalan dapat berujung pada masalah operasional
  • Salah satu kelemahan terbesar adalah build Docker mengakses internet publik
    • Ini diperlukan untuk mengunduh paket dari repositori Ubuntu
    • Saat perlu membuat ulang image yang sama di kemudian hari, sulit mengembalikan kondisi persis repositori Ubuntu pada saat itu
    • Ubuntu 18.04 akan mencapai akhir masa dukungan pada tahun terkait, sehingga sesuatu yang bergantung pada versi itu mungkin baru diketahui setelah terjadi gangguan
  • Menambahkan paket ke image Docker dengan cara sederhana dapat menciptakan ruang terbuang
    • Jika apt-get upgrade dijalankan di awal build, file di dalam image container dapat diganti
    • File sebelum diganti dapat tetap berada di layer dan menumpuk seperti salinan bayangan

Cara Nix menangani layer Docker

  • Nix mengetahui dependensi yang diperlukan sebelumnya, dan dapat membaginya ke dalam layer Docker sesedikit mungkin
    • Mengubah satu baris kode tidak membuat apt atau npm perlu memasang ulang dependensi
    • Hanya bagian minimum yang benar-benar berubah yang dapat direfleksikan dalam pembaruan image
  • dockerTools adalah kumpulan tool untuk memasukkan paket Nix dan dependensinya ke dalam image Docker
  • Image Docker yang dibuat dengan Nix secara garis besar terbagi menjadi dua pendekatan
    • Image tanpa layer: program, dependensi, dan item tambahan seperti sertifikat root TLS dimasukkan ke satu folder dan diekspos sebagai image satu layer
    • Image berlayer: tiap dependensi ditempatkan sebagai layer image terpisah, sehingga hanya bagian yang benar-benar berubah yang perlu diunggah
  • Docker sendiri juga memiliki penyimpanan berbasis konten, tetapi sulit memanfaatkannya secara penuh hanya dengan docker build
    • Image berlayer dari Nix menggunakan layer per paket, sehingga dependensi seperti glibc cukup diunggah sekali
    • Namun, jika library tersebut perlu dimodifikasi, layer itu harus diunggah ulang

Contoh service Go: Douglas Adams Quotes

bin = pkgs.buildGoModule {
  pname = "douglas-adams-quotes";
  inherit version;
  src = ./.;
  vendorHash = null;
};
  • Definisi ini menggunakan template Go module untuk mengatur compiler Go, compiler C untuk CGo, dan unduhan dependensi eksternal
    • pname adalah nama paket
    • version dibuat otomatis dari commit Git service
    • src = ./.; menggunakan kode sumber dari direktori kerja saat ini
    • Jika tidak ada dependensi selain standard library, vendorHash = null dapat digunakan
    • Jika ada dependensi eksternal, hash semua dependensi harus ditentukan atau gomod2nix dapat digunakan
  • Paket dibuild dengan perintah berikut
nix build .#bin
  • Image berlayer Docker dapat dibuat dengan dockerTools.buildLayeredImage
docker = pkgs.dockerTools.buildLayeredImage {
  name = "registry.fly.io/douglas-adams-quotes";
  tag = "latest";
  config.Cmd = "${bin}/bin/douglas-adams-quotes";
};
  • Jika binary server yang sudah dibuild ditentukan pada config.Cmd, item yang diperlukan ikut disalin
    • glibc dan item yang dibutuhkan untuk menjalankannya juga disertakan
  • Paket tambahan seperti sertifikat root CA dapat dimasukkan ke contents
docker = pkgs.dockerTools.buildLayeredImage {
  name = "registry.fly.io/douglas-adams-quotes";
  tag = "latest";
  contents = with pkgs; [ cacert ];
  config.Cmd = "${bin}/bin/douglas-adams-quotes";
};
  • Image dibuat dan dimuat ke daemon Docker dengan perintah berikut
nix build .#docker
docker load < ./result
  • Jika dibuka dengan dive, tiap layer terlihat menambahkan paket berbeda dari nixpkgs, lalu pada akhirnya item-item tersebut di-symlink ke root image

Efek berbagi layer dalam monorepo

  • Jika ada beberapa service dalam repositori yang sama, image Docker yang dibuat dengan Nix dapat berbagi layer
    • Dibagikan tanpa konfigurasi terpisah
    • Jika ingin melakukan ini hanya dengan Docker, biasanya perlu membuat beberapa base image bersama, yang dapat berisi tool dan konten tidak perlu yang tidak digunakan sebagian service
  • Sebagai contoh, repositori Xe/x adalah monorepo berisi 10 tahun side project, eksperimen, dan tool
    • Berbagai proyek dideploy ke sekitar tiga platform
    • Pekerjaan untuk memasukkannya ke image Docker telah dilakukan agar mengerucut ke basis deployment bersama
  • Saat pembaruan satu service di-push, sebagian pembaruan yang dibagikan dengan kebanyakan service lain juga ikut di-push
    • Pendekatan ini menghemat waktu dan biaya di berbagai proyek

Bagian yang sulit dikejar hanya dengan cache Docker

  • Dengan caching Docker, secara teori image juga bisa dibuild seefisien Nix
  • Namun untuk menghasilkan efek yang sama dengan pendekatan Docker, tahap build dapat menjadi sulit dipelihara
    • Shared library harus dipasang ke layer terpisah
    • Network stack harus dinyalakan kembali selama build, sehingga reproduksibilitas melemah
    • Search path, flag compiler, dan pengaturan terkait CGo harus disesuaikan lagi
  • dockerTools.buildLayeredImage dari Nix menangani detail memasukkan paket ke container, sehingga konfigurasi menjadi lebih sederhana

Reproduksi titik waktu lampau dan cache Nix

  • Salah satu keunggulan besar Nix adalah kemampuannya membangun ulang secara persis software dari titik waktu lampau
    • Saat perlu mereproduksi bug pada lingkungan pelanggan tertentu di kemudian hari, image Docker yang sama dapat dibuat ulang
    • Build paket berperan mengunci seluruh kondisi software dan dependensi pada satu titik waktu
  • XeDN adalah proyek yang telah dikerjakan selama beberapa tahun, dan versi 14 bulan lalu dapat dibuild dengan perintah berikut
nix build github:Xe/x/567fdc2#xedn-docker
  • Perintah ini membuild target xedn-docker dari commit tertentu di GitHub repo Xe/x
    • Jika dimuat ke daemon Docker, image dengan byte yang sama seperti saat itu akan dibuat
    • Contoh ini juga menyertakan Go 1.19
  • Untuk melakukan reproduksi yang sama hanya dengan build Docker biasa, mungkin diperlukan biaya penyimpanan yang besar
  • Cache Nix menyimpan output dari perintah Nix agar tidak perlu dibuild ulang nanti
    • Paket seperti nokogiri tidak perlu dibuild ulang di tiap laptop developer
    • Hasil yang sudah dibuild di cloud dapat diambil
  • Garnix digunakan untuk CI semua proyek flakes, dan melaporkan status build pada tiap commit
    • Konfigurasi mesin homelab juga dibuild dengan Garnix, sehingga saat memperbaruinya setiap malam, konfigurasi terbaru diambil dari cache Garnix alih-alih dibuild sendiri

Kesimpulan sebagai artefak deployment

  • Nix bisa dipandang sebagai builder image Docker yang lebih baik daripada builder image Docker milik Docker
  • Nix membuat kita menentukan hasil, bukan menuliskan langsung detail langkah-langkah untuk mencapai hasil itu
  • Di lingkungan yang sudah menggunakan Docker, menerapkan Nix mulai dari build image Docker menjadi jalur adopsi yang realistis
  • Image Docker yang dibuat dengan Nix dapat berbagi layer antarbagian dalam monorepo
  • Berkat cache binary, kode yang sudah pernah dibuild di masa lalu tidak perlu dibuild ulang
  • Artefak akhirnya adalah image container biasa yang dapat dideploy ke platform seperti AWS, Google Cloud, dan Fly.io

1 komentar

 
GN⁺ 2024-03-17
Opini Hacker News
  • Saya sudah beberapa kali mencoba menyukai Nix, tetapi sekarang rasanya saya harus menyerah
    Ada 2 sistem yang menjalankan Nix, dan saya takut menyentuhnya; dulu keduanya pernah rusak sampai harus saya instal ulang dari nol. Secara teori Nix itu idempoten dan deterministik, tetapi dalam praktiknya kita harus memahami deterministik terhadap apa, dan kalau tidak benar-benar memahami perilaku semua bagian yang menjadi dependensi, kita akan berhadapan dengan hasil aneh, error yang ganjil dan tidak membantu, atau bahkan tidak ada umpan balik sama sekali
    Dokumentasinya, meski lengkap dan secara teknis benar, sangat sulit dipahami, dan tutorialnya hanya mengantar sampai 80% jalan. Begitu keluar dari jalur itu, kita harus merakit sendiri, dan itu menuntut pengetahuan dari pengalaman bertahun-tahun serta banyak frustrasi, yang sudah tidak ingin saya tanggung lagi. Keunggulan Docker justru ada pada kekacauannya: cukup tahu kira-kira soal shell dan package manager distro, hampir apa pun bisa dibuat; kalau ada masalah, berkat tool yang sudah berumur puluhan tahun, diagnosis dan perbaikannya jauh lebih mudah
    Nix itu seperti Emacs: dengan kesabaran serta pengetahuan yang dalam dan visioner, kita bisa melakukan apa saja, tetapi tampaknya pilihannya hanya benar-benar tenggelam di dalamnya atau menjaga jarak sambil mengamati

    • Saya pernah melalui jalur yang sama, dan meski saya menyukai build deterministik, menurut saya masalah terbesar Dockerfile bagi developer rata-rata adalah bahwa ia terlihat deterministik
      Kalau build dua kali berturut-turut di mesin yang sama, hasilnya sama, tetapi seiring waktu ketika package manager dan tag base image diperbarui, hasil rebuild sebulan kemudian bisa sepenuhnya berbeda. Kalau tim mengelola sekitar 40 container, memperbaiki container menjadi bagian besar dari pekerjaan
      Karena itu, secara teori Nix seharusnya sempurna, tetapi ia terlalu berbeda sehingga tool vendor tidak berjalan di Nix, dan saat terjadi error pun sulit menemukan solusi cepat di web. Karena frustrasi itu, saya membuat https://www.stablebuild.com, yang menyediakan build deterministik berbasis Docker di atas container Ubuntu, Debian, dan Alpine. Ini terdiri dari cache pull-through Docker Hub yang immutable, salinan harian lengkap repository paket Ubuntu/Debian/Alpine, salinan harian PPA populer dan indeks PyPI, serta cache immutable untuk file/URL arbitrer
      Dalam praktiknya mudah digunakan dan mudah di-debug, dengan kompatibilitas software yang luas, sehingga pada container yang dipindahkan ke StableBuild, jumlah masalah akibat nondeterminisme adalah 0
    • Dokumentasinya bukan sekadar sulit dipahami; khususnya di CLI baru dan flakes, sering kali memang salah, dan itu bukan edge case
      Saya pernah menghabiskan waktu untuk memahami mengapa nix develop tidak berperilaku seperti dokumentasinya, dan bagaimana membuatnya berperilaku demikian. Dokumentasi mengatakan bahwa secara default kita masuk ke environment waktu build, tetapi default sebenarnya tidak tersegel, dan nama opsi command line juga membingungkan, sehingga saya harus menggali pengetahuan dari source
      Saya juga berharap edge case yang merusak reproduksibilitas dibahas lebih eksplisit. Kode floating-point sensitif terhadap urutan operasi, dan state juga bisa bocor karena preemption sistem operasi; kalau hal-hal yang obvious pun tidak dinyatakan, orang akan menembak kakinya sendiri
    • Tidak seburuk itu, tetapi bahkan pada konfigurasi NixOS standar pun ada sangat banyak state yang tidak dapat direproduksi yang tersisa di akun pengguna dan bagian internal sistem
      Jika memakai konfigurasi “Erase your darlings”, sebagian besar state yang tidak dapat direproduksi di luar akun pengguna bisa dihilangkan. Memang agak merepotkan, tetapi rasanya apa sih yang tidak merepotkan di NixOS
      https://grahamc.com/blog/erase-your-darlings/
      Saya tidak memedulikan bagian dalam akun pengguna, dan saya tidak menyukai Home Manager
    • Di tempat kerja saya memakai Docker dan NixOS, tetapi belum pernah mengalami masalah yang disebutkan di atas
      Docker baik-baik saja, tetapi di Mac performanya kurang bagus; Nix menyenangkan karena membuat instalasi dan perilaku yang sama di banyak mesin menjadi hal sepele. Dokumentasi Nix buruk, tetapi ChatGPT-4 sangat bagus untuk menyelesaikan masalah Nix
      Rasanya 90% masalah Nix muncul karena saya mencoba melakukan sesuatu yang bukan “cara Nix”
    • Distro immutable seperti Fedora Atomic layak dicoba
      Saya sudah mencoba hampir semua package manager distro, dan tanpa melakukan hal khusus pun entah bagaimana semuanya pernah saya rusakkan. Fedora Kinoite tetap tangguh meski menambah/menghapus layer, update harian, bahkan rebase dari Silverblue. Secara pribadi, saya rasa rpm-ostree akan menggantikan Nix
  • Saya melihat Nix dan NixOS berada dalam kondisi yang mirip dengan git sebelum GitHub
    Ide dasarnya berlandaskan ilmu komputer yang lebih serius dibandingkan pendekatan lama seperti SVN atau Docker, dan meski plumbing internalnya masih bermasalah, itu tidak lebih buruk; namun tool dan dokumentasi yang user-facing belum berada di level adopsi arus utama
    Peluncuran flox mungkin sudah mengubah hal ini: https://flox.dev. Hampir mulus, dan itu tidak mengherankan karena berasal dari orang-orang eks DE Shaw
    Nix tidak terlalu berarti tanpa flakes dan nix-command, tetapi keduanya didokumentasikan sebagai eksperimental dan secara default juga nonaktif. Dokumentasinya membaik, tetapi masih kurang; nixlang keren jika dipelajari dengan baik, tetapi tidak bisa diterima sebagai hambatan masuk ke arus utama. nix-env -iA foo pada praktiknya sering kali bukan perilaku yang diinginkan, jadi Nix lebih mirip senjata rahasia bagi perusahaan yang mampu menanggung keahlian internal daripada package manager seperti yang diiklankan
    flox menurunkan hambatan untuk “coba sekali dan langsung mendapat pengalaman yang lebih baik”. Nix/NixOS atau sesuatu yang serupa pada akhirnya akan mendorong Docker ke belakang seperti Subversion, tetapi itu tidak akan terjadi sampai ada momen seperti GitHub, lalu akan terjadi sekaligus
    Sebagian besar keluhan tentang Nix di thread ini secara teknis keliru, tetapi sangat bisa dipahami, dan yang lebih penting, bukan salah pengguna. Saya tahu generasi yang mengenal dunia tanpa git/GitHub makin berkurang, tetapi ada baiknya mendengar Linus menjelaskan di depan orang-orang yang lolos standar rekrutmen awal Google mengapa mereka perlu peduli pada tool yang terasa rumit
    https://youtu.be/MjIPv8a0hU8?si=QC0UnHXRdMpp2tI4

    • Saya Ron dari flox.dev, dan tulisan ini membuat seluruh tim tertawa lepas
      Kami sudah cukup lama membangunnya, dan kami ingin mendengar jika ada hal yang layak diprioritaskan agar bisa membuatnya lebih baik
    • Menurut saya, agar tool developer berhasil, untuk tugas yang paling umum harus ada setidaknya tiga cara bagi engineer untuk menggunakan tool dengan salah, tetapi tetap bisa “selesai” sambil meninggalkan technical debt implisit
      git seperti itu, tetapi Nix belum, jadi saya tidak begitu yakin apakah momen seperti GitHub akan membantu
  • Tulisan blog ini melewatkan penjelasan mengapa layer Docker bersama berguna
    Alasannya adalah caching: makin banyak image yang berbagi layer yang sama, makin banyak hal yang bisa di-cache, sehingga startup container menjadi lebih cepat
    Alasan Docker lemah dalam hal ini adalah, untuk mendapat manfaat caching, saat membangun image layer yang dihasilkan harus semirip mungkin dengan layer yang sudah ada. Misalnya, jika hari ini menjalankan apt-get install python3 dan tidak ada update baru, seharusnya layer yang dihasilkan persis sama seperti kemarin; tetapi layer Docker di-cache berdasarkan hash file, jadi semua file harus persis sama, termasuk metadata seperti waktu pembuatan
    Nix sudah menyimpan dependency sebagai hash, jadi jika versinya sama dan konfigurasinya sama, layer-nya akan selalu sama

    • Sepertinya lebih tepat diungkapkan begini: format Dockerfile memaksakan hubungan hierarkis antar-layer
      Dependency biasanya membentuk graf, bukan tree, sehingga ini cepat merepotkan. Tool alternatif seperti Nix atau mungkin Bazel tidak punya batasan ini, dan dapat memetakan graf dependency ke layer Docker untuk mencapai caching yang granular. Itu cara yang tidak bisa diekspresikan dengan Dockerfile
    • Di Docker, jika layer sudah di-cache, layer yang berisi apt-get tidak otomatis diinvalidasi
      Itu hanya berubah saat memakai --no-cache atau ada perubahan pada layer di atasnya
  • Selama 2–3 hari terakhir saya bergulat untuk membangun image Docker di Darwin, dan tulisan ini terasa seperti semesta sedang mengolok-olok saya
    Nix jelas merupakan alat terbaik untuk tujuan yang diinginkan, tetapi ada sudut-sudut gelap dan terbengkalai yang seperti menyedot jiwa. Saya menyukainya, tetapi kadang rasanya seperti Morty yang terseret ke petualangan compiler-land milik Rick

    • Masalah besarnya adalah desain Docker itu sendiri
      Docker pada dasarnya lebih mirip penjara yang berisi binary Linux. Di Linux, Anda cukup membangun binary, memasukkannya ke container, selesai; kode Nix-nya juga sederhana. Jika Anda bisa membangun kodenya, membuat container hanyalah satu langkah tambahan
      Namun Docker membutuhkan binary Linux, dan di Mac, Docker Desktop menjalankan VM Linux, melakukan semua pekerjaan di dalamnya, lalu menyembunyikan fakta itu. Nix tidak melakukan hal seperti itu, jadi pilihannya ada dua
      Pertama, melakukan cross-compilation, tetapi glibc pun harus bisa di-cross-compile, dan meski sebagian besar dependency komunitas mungkin bisa, beberapa paket bisa saja tidak diperhatikan oleh penulisnya untuk cross-compilation. Selain itu, Hydra yang mengisi cache Nix standar tidak melakukan build cross-compilation, sehingga Anda bisa saja membangun lama lalu gagal
      Kedua, memasang builder Linux ke Mac dan mengirim pekerjaan build x86_64-linux ke sana. Bisa berupa mesin fisik, VM, bahkan container Docker NixOS
      Opsi pertama tampak seperti cara yang benar, tetapi opsi kedua lebih praktis. Masalah yang Anda alami kemungkinan besar karena mencoba opsi pertama, dan ini menuntut banyak pengalaman bukan hanya dengan Nix, tetapi juga cross-compilation. Akan bagus jika Hydra juga membangun cross-compilation dari Darwin ke Linux sehingga menyediakan cache dan mencegah kerusakan, tetapi biayanya juga akan meningkat. Sepertinya lebih baik mencoba solusi kedua
      Sepertinya pernah ada solusi resmi: https://ryantm.github.io/nixpkgs/builders/special/darwin-bui...
    • Jika memakai Orbstack, pekerjaan ini berjalan mulus tanpa cela
      Pada beberapa stack, cross-compilation tidak terlalu tangguh, jadi kami melakukan cross-compilation {aarch64,amd64} x {linux,darwin} dengan Docker. Kami menjalankan beberapa Docker di atas Darwin aarch64 untuk mengompilasi semuanya, dan pengalamannya bagus
    • Apakah maksudnya petualangan 20 menit seperti ini?
      https://github.com/gytis-ivaskevicius/high-quality-nix-conte...
    • macOS memang jelas lebih kasar, dan di sana saya memakai colima, yang berjalan lumayan baik
      Ada satu-dua bug, tetapi tampaknya terutama terkait volume, dan build image Docker ditangani dengan cukup baik. Bagian yang lebih kasar adalah kecepatannya, karena kombinasi hardware dan fakta bahwa Docker harus mengemulasi VM Linux membuatnya terasa cukup terpukul
  • Pengalaman membangun image Docker untuk aplikasi Java dengan Nix tidak terlalu bagus
    Setelah gradle2nix dihentikan, tampaknya tidak ada alternatif yang jelas untuk aplikasi Java berbasis Gradle. Dulu saya pernah mengajak seorang teman untuk mencoba membuat image Docker sekecil mungkin untuk aplikasi Spring Boot sederhana, tetapi hasil yang dibuat dengan Nix berukuran dua kali lebih besar daripada image yang dibuat tanpa Nix
    Kodenya bisa dilihat di sini: https://github.com/jossephus/Docker_challenge/blob/main/flak...

    • Alasannya karena ada dua JDK di dalamnya
      Zulu dan JDK yang disertakan Gradle sebagai argumen jdk sama-sama masuk. Jika melihat gradleGen di nixpkgs, maksudnya akan terlihat. Maaf soal gradle2nix; sedang ada upaya perbaikan yang tidak terlalu terasa seperti hack
    • Saya sudah tidak memakai Java lebih dari 10 tahun, jadi tidak bisa banyak membantu, tetapi saya pernah memasukkan aplikasi ke dalam container 70MB yang mencakup Python beserta semua dependensi, busybox, dan tini
      Kira-kira seperti ini: https://gist.github.com/takeda/17b6b645ad4758d5aaf472b84447b...
      Semuanya ditautkan dengan musl, saat mengompilasi Python saya menonaktifkan semua paket yang tidak dipakai aplikasi, dan menghapus bagian boto3/botocore yang tidak digunakan. boto3/botocore saja sudah lebih dari 100MB
      Paket Nix pada dasarnya menargetkan sistem operasi NixOS, jadi dalam situasi umum dengan ruang disk yang cukup, biasanya diinginkan semua fitur aktif. Akibatnya banyak dependensi yang tidak diperlukan ikut terbawa. Sebaliknya, image Alpine dirancang untuk Docker, dengan tujuan mematikan fitur tambahan pada paket, sehingga hasilnya lebih kecil
      Untuk membuat image kecil, hal-hal yang tidak diperlukan harus dimatikan dengan override. Misalnya zulu menyertakan hal-hal seperti alsa, fontconfig, freetype, xorg, cups, gtk, cairo, dan ffmpeg. Teman saya dengan hati-hati hanya mengekstrak file yang diperlukan lalu memasukkannya ke container, sedangkan di sisi Nix, seluruh paket zulu beserta semua dependensinya dibundel
      Pertama, perbaiki agar hanya satu JDK yang disertakan, lalu ukuran JDK bisa diperkecil lagi dengan metode di atas. Menggunakan openjdk_headless mungkin akan lebih sederhana
      https://github.com/NixOS/nixpkgs/blob/master/pkgs/developmen...
    • Untuk container OCI Java minimal, sulit mengalahkan jib
      https://github.com/GoogleContainerTools/jib/tree/master/jib-...
    • openjdk_headless melewati dependensi GTK dan X yang tidak diperlukan Spring
    • Saya ikut langsung dalam tantangan ini dan sedikit merapikan kode Nix; intinya tampaknya adalah membuat JRE yang sangat kecil
      Alih-alih Zulu, saya mengubahnya agar berbasis build OpenJDK headless dari Nixpkgs untuk menghapus dependensi library GUI, lalu membuat JRE minimal khusus dengan pkgs.jre_minimal dan jlink
      Ukuran image menjadi 161MB, sedikit lebih besar daripada image demo_jlink. Ini karena JRE menjadi sekitar 90MB setelah benar-benar menyertakan semua modul yang diperlukan untuk menjalankan aplikasi. Pemanggilan jdeps di Dockerfile_jlink tidak mendeteksi semua modul, sehingga JRE yang dibuat hanya berisi java.base. Jika JRE minimal saya juga hanya memasukkan java.base, ukuran JRE menjadi sekitar 50MB, dan image container yang rusak menjadi 117MB menurut Podman
      Saya juga menghapus copyToRoot yang keliru pada pemanggilan dockerTools.buildImage. Hanya dengan konteks string di config.Cmd, app sudah masuk ke image, tetapi kode sebelumnya menyalin app sekali lagi. Selain itu, saya mengubahnya ke dockerTools.buildLayeredImage agar setiap store path dimasukkan ke layer image terpisah; ini bagus untuk skalabilitas ruang saat berbagi dependensi di antara beberapa image container, tetapi tidak berpengaruh pada eksperimen satu image
      Yang tersisa sebagian besar adalah optimasi ukuran JRE. glibc adalah dependensi terbesar berikutnya, sekitar 30MB, tampaknya karena Nixpkgs membangun glibc agar mendukung banyak locale dan encoding karakter. Saya tidak tahu apakah memungkinkan, atau praktis, untuk memisahkannya menjadi derivation atau output tersendiri agar bisa dipilih. Jika beberapa image dibuat dengan dockerTools.buildLayeredImage, dengan asumsi memakai commit Nixpkgs yang sama, glibc dan dependensi lainnya akan dibagikan
      https://github.com/max-privatevoid/hackernews-docker-challen...
  • Jika Anda sudah mengadopsi Nix, itu bagus, dan semoga manajemen paket deklaratif seperti Nix atau Guix menjadi lebih luas digunakan
    Jika Anda sudah memakai Docker tetapi ingin mengadopsi Nix secara bertahap, ada juga pendekatan dalam presentasi ini: https://youtu.be/l17oRkhgqHE
    Alih-alih langsung memindahkan konfigurasi dan build container sepenuhnya ke Nix, Anda bisa mempertahankan Dockerfile sambil membuatnya membangun konfigurasi Nix. Kekurangan terbesarnya adalah layer sama sekali tidak bisa dimanfaatkan, sedangkan kelebihannya adalah Anda bisa mengubah Dockerfile secara bertahap sambil menggunakan ulang infrastruktur atau otomasi Docker yang sudah ada

    • Tulisan ini juga memakai pendekatan yang sama
      [1] https://mitchellh.com/writing/nix-with-dockerfiles
    • Salah satu sumbu argumen tulisan ini adalah build Docker tidak dapat direproduksi, sedangkan Nix dapat direproduksi
      Namun saya merasa sebagian besar ketidakreproduktifan itu mungkin karena tidak ada jaminan bahwa salah satu layer Docker akan terus tersedia. Kalau begitu, saya juga penasaran apakah Nix punya jaminan bahwa versi paket akan tetap ada di repositori selamanya
  • Agak di luar topik, tetapi ilustrator yang membuat gambar slide ini, Annie Ruygt, juga membuat logo dan art brand untuk dua startup YC, RethinkDB(rethinkdb.com) dan Pachyderm(pachyderm.io)
    Ia pernah bekerja di Pachyderm, dan Pachyderm adalah perusahaan yang didirikan oleh mantan engineer RethinkDB. Karyanya benar-benar bagus

  • Belum lama ini, atas rekomendasi tim infrastruktur, saya menghabiskan sekitar setengah hari mencoba membangun image dasar CI dengan Nix, tetapi image-nya sangat besar dan sebagian tidak berjalan karena masalah linking
    Yang terutama menjengkelkan adalah saat membuat image multi-arsitektur, ia mencoba benar-benar menjalankan sesuatu dari arsitektur lain, dan hanya mendukung virtualisasi hardware qemu. Mesin build dan workstation kami adalah VM, jadi tidak punya itu, meskipun ada binfmt-misc. Kalau ia fork/exec mkdir arm64 untuk membuat /tmp, mungkin akan berjalan, tetapi layer Docker hanyalah file tar, jadi direktori bisa dibuat seperti di bawah ini
    echo "tmp uid=0 gid=0 time=0 mode=0755 type=dir" | bsdtar -cf - @-
    Layer persis ini kemungkinan besar sudah ada di suatu tempat, sehingga pengguna bahkan mungkin tidak perlu mengunduhnya
    Setiap kali mencoba Nix, saya merasa beberapa bulan lagi ia akan bisa saya pakai secara rutin. nixpkgs memiliki hampir semua paket yang saya inginkan dan terpasang dengan baik di workstation. Namun kebutuhan seperti “perlu bash, python, build-essential, Bazel” rasanya bukan tujuan dari Docker image builder. Untuk sekadar memasukkan satu binary Go ke dalam image Docker, Nix tidak diperlukan. Ambil distroless dan masukkan aplikasi ke file tar, maka itu sudah menjadi container. Secara pribadi saya memakai rules_oci dari Bazel; secara internal yang dilakukan juga kira-kira sebatas itu, dengan sedikit kecerdasan tambahan untuk membangun binary bagi beberapa arsitektur, membuat YAML image index, lalu mendorongnya ke registry

    • Binary untuk arsitektur lain seharusnya bisa di-cross-compile tanpa benar-benar dijalankan
      Tentu saja file build paketnya harus mendukung hal itu. Pernyataan bahwa qemu hanya mendukung virtualisasi hardware juga tampaknya tidak tepat. qemu juga bisa digunakan untuk arsitektur yang hanya memungkinkan emulasi software
      Contoh minimal dibahas di sini: https://discourse.nixos.org/t/how-do-i-get-a-shell-nix-with-...
      Saya tidak sampai ingin mengatakan bahwa ini seharusnya sesederhana memakai pkgCross, tetapi saya penasaran masalah konkret apa yang Anda temui dalam proses umumnya
      https://nix.dev/tutorials/cross-compilation.html
    • Yang dimaksud mungkin image Docker Nix resmi, dan itu memang besar
      Saya sudah memakainya selama bertahun-tahun di beberapa proyek, tetapi jika ukuran menjadi masalah, dengan cara yang disebutkan dalam tulisan, Anda bisa membuat image yang sangat kecil dan hanya berisi hal-hal yang ditentukan
      [1] https://hub.docker.com/r/nixos/nix/tags
    • Cross-compilation ke image Docker justru merupakan alasan saya memakai Nix
      Saya bahkan memakai musl, membangun image paling kecil dibandingkan alat lain mana pun secara cepat dan konsisten di CI, dan caching juga berjalan baik. Saya kurang paham apa yang dimaksud dengan ada sesuatu yang dijalankan
    • Saya penasaran seperti apa file Nix dan Docker akhirnya, serta apakah Anda harus memakai buildFHSEnv untuk mendukung binary pihak ketiga yang aneh
      Sepertinya Nix membutuhkan lebih banyak tulisan yang menjelaskan cara bermigrasi secara halus dan bertahap dari sistem yang sudah ada
  • Sebagai platform engineer, saya ingin menyukai Nix, tetapi itu tidak mudah bagi semua orang
    Menurut saya pengalaman pengembangnya juga masih cukup buruk. Misalnya, saya lebih menyukai pengalaman pengembang devbox karena bisa menambahkan paket seperti devbox add python@3.11
    Melihat flake.nix sepanjang 120 baris, sulit juga untuk mengatakan bahwa itu benar-benar “lebih mudah”
    https://github.com/Xe/douglas-adams-quotes/blob/main/flake.n...

    • Perbandingan itu agak tidak adil
      Flake yang ditautkan mendefinisikan cara membangun binary Go, mendefinisikan image Docker yang memakai binary itu sebagai entry point, dan juga mendefinisikan modul NixOS yang membuat layanan systemd untuk menjalankan binary Go tersebut. Selain itu, ia juga menyertakan pengujian NixOS untuk memastikan modul NixOS itu membuat layanan systemd sesuai harapan
      Framework pengujian NixOS cukup mengesankan, dan pengujiannya berjalan di dalam VM QEMU yang menjalankan NixOS bersama-sama. Yang relevan dengan artikel yang ditautkan hanyalah definisi binary Go dan image Docker, serta sebagian boilerplate di sekitarnya
    • 120 baris itu sulit dianggap sebagai contoh yang representatif, dan untuk satu layanan saja kemungkinan akan ditulis inline tanpa membuat modul baru
      Selain itu, banyak barisnya berkorespondensi 1:1 dengan deskripsi layanan systemd inline, jadi tidak akan hilang apa pun sistem yang dipakai. Di dalamnya juga ada cara yang keren untuk mendeklarasikan beberapa sistem lewat override
      Contoh ini lebih dekat dengan “mari melakukan hal sepele seperti dalam proyek besar yang serius”, dan jika diperlakukan sebagai pekerjaan sekali pakai, sepertinya bisa dipangkas menjadi sekitar 40 baris
    • 120 baris ini melakukan cukup banyak hal
    • Untuk menangani satu binary dan konfigurasi systemd, dibutuhkan 120 baris kode
      Binary dan file konfigurasi itu adalah bentuk yang paling mendekati “flat pack” yang bisa didapat di Linux, dan contoh ini menunjukkan dengan baik apa yang kita lewatkan antara melihat hutan dan pohonnya
  • Guix juga punya opsi Docker yang mudah dan bagus bernama guix pack -f docker
    Guix juga punya keunggulan karena memakai bahasa yang sudah digunakan, yaitu Guile/Scheme, alih-alih bahasa khusus buatannya sendiri
    [1] https://guix.gnu.org/manual/en/html_node/Invoking-guix-pack....