فا نیکسی

نحوه کارکرد Nix

ابزار Nix یک مدیر بسته‌‌ی کاملاً تابعی (purely functional package manager) است. این بدان معناست که این ابزار با بسته‌ها مانند مقادیر در زبان‌های برنامه‌نویسی کاملاً تابعی مانند Haskell رفتار می‌کند؛ بسته‌ها توسط توابعی ساخته می‌شوند که هیچ اثر جانبی (side-effect) ندارند و پس از ساخته شدن هرگز تغییر نمی‌کنند. ابزار Nix بسته‌ها را در انبار Nix (Nix store)، که معمولاً دایرکتوری /nix/store است، ذخیره می‌کند؛ جایی که هر بسته زیردایرکتوری منحصر‌به‌فرد خود را دارد، مانند

/nix/store/b6gvzjyb2pg0kjfwrjmg1vfhh54ad73z-firefox-33.1/

که در آن b6gvzjyb2pg0… یک شناسه یکتا برای بسته‌ است که تمامی وابستگی‌های آن را در خود ثبت می‌کند (این شناسه، یک هش رمزنگاری‌شده از گراف وابستگی‌های ساخت بسته است). این ویژگی بسیاری از قابلیت‌های قدرتمند را امکان‌پذیر می‌سازد.

نسخه‌های متعدد

شما می‌توانید چندین نسخه یا نوع مختلف از یک بسته را به‌طور هم‌زمان نصب داشته باشید. این موضوع به‌ویژه زمانی اهمیت پیدا می‌کند که برنامه‌های مختلف به نسخه‌های متفاوتی از یک بسته وابستگی داشته باشند، این امر از بروز «جهنم DLL» جلوگیری می‌کند. به لطف طرح‌بندی هش، نسخه‌های مختلف یک بسته در مسیرهای متفاوتی در Nix store قرار می‌گیرند، بنابراین تداخلی با یکدیگر ندارند.

یک پیامد مهم این است که عملیاتی مانند ارتقا یا حذف نصب یک برنامه نمی‌تواند باعث خرابی سایر برنامه‌ها شود، زیرا این عملیات هرگز فایل‌هایی را که توسط سایر بسته‌ها استفاده می‌شوند، به‌صورت «تخریب‌گر» به‌روزرسانی یا حذف نمی‌کنند.

وابستگی‌های کامل

هنگامی که برای یک سیستم مدیریت بسته، مانند RPM بسته‌ای می‌سازید، موظف هستید وابستگی‌های آن را اعلام کنید، اما نمی‌توانید به آسانی تضمین کنید که اعلامیه وابستگی شما کامل است. اگر وابستگی‌ای را که به‌طور جداگانه روی سیستم خود نصب کرده‌اید فراموش کنید، ممکن است آن مؤلفه روی سیستم شما به درستی ساخته شود و کار کند، اما روی سیستم کاربر نهایی با خطا مواجه شود.

Nix تضمین می‌کند که مشخصات وابستگی بسته کامل هستند.

تحت Nix، یک فرایند ساخت فقط منابعی را پیدا می‌کند که به‌طور صریح به عنوان وابستگی اعلام شده باشند. هیچ راهی برای ساخت وجود ندارد مگر اینکه تمام نیازهای آن به درستی اعلام شده باشد. اگر ساخت با موفقیت انجام شود، می‌دانید که یک اعلامیه کامل ارائه داده‌اید.

هنگامی که ساخت کامل شد، وابستگی‌های زمان اجرای مداوم به‌طور خودکار شناسایی می‌شوند.

پشتیبانی از چند کاربر

از نسخه 0.11 به بعد، Nix از چند کاربر پشتیبانی می‌کند. این یعنی کاربران غیرممتاز می‌توانند به صورت امن نرم‌افزار نصب کنند. هر کاربر می‌تواند پروفایل متفاوتی داشته باشد؛ مجموعه‌ای از بسته‌ها در Nix store که در متغیر PATH کاربر ظاهر می‌شوند. اگر کاربری بسته‌ای را نصب کند که کاربر دیگری قبلاً آن را نصب کرده است، آن بسته برای بار دوم ساخته یا دانلود نخواهد شد. در عین حال، امکان ندارد که یک کاربر بتواند اسب تروا (Trojan horse) را وارد بسته‌ای کند که ممکن است توسط کاربر دیگری استفاده شود.

ارتقاها و بازگشت‌های اتمی (Atomic)

از آنجایی که عملیات مدیریت بسته هرگز بسته‌ها را در Nix store بازنویسی نمی‌کنند بلکه فقط نسخه‌های جدید را در مسیرهای متفاوت اضافه می‌کنند، آن‌ها اتمی هستند. بنابراین در طول ارتقای یک بسته، هیچ پنجره زمانی وجود ندارد که در آن بسته شامل برخی فایل‌ها از نسخه قدیمی و برخی فایل‌ها از نسخه جدید باشد، که این اتفاق بدی خواهد بود زیرا اگر برنامه‌ای در آن بازه زمانی اجرا شود، ممکن است به شدت دچار فروپاشی (Crash) شود.

و از آنجایی که بسته‌ها بازنویسی نمی‌شوند، نسخه‌های قدیمی پس از ارتقا همچنان در جای خود باقی می‌مانند. این یعنی شما می‌توانید به نسخه قدیمی بازگردید (Roll back):

$ nix-env --upgrade _some-packages_
$ nix-env --rollback

جمع‌آوری زباله (Garbage collection)

وقتی بسته‌ای را به این صورت حذف می‌کنید...

$ nix-env --uninstall firefox

بسته بلافاصله از سیستم حذف نمی‌شود (بالاخره ممکن است بخواهید یک بازنشانی (rollback) انجام دهید، یا شاید در پروفایل‌های کاربران دیگر وجود داشته باشد). در عوض، بسته‌های استفاده‌نشده را می‌توان با اجرای جمع‌آوری‌کننده زباله (garbage collector) با خیال راحت حذف کرد:

$ nix-collect-garbage

این کار تمام بسته‌هایی را که توسط هیچ پروفایل کاربری یا برنامه‌ی در حال اجرایی استفاده نمی‌شوند، حذف می‌کند.

زبان بسته‌ی تابعی

بسته‌ها از عبارت‌های Nix (Nix expressions) ساخته می‌شوند که یک زبان تابعی ساده است. یک عبارت Nix همه‌ی مواردی را که در یک عمل ساخت بسته (یک «مشتق» یا derivation) دخیل هستند توصیف می‌کند: بسته‌های دیگر، سورس‌ها، اسکریپت ساخت، متغیرهای محیطی برای اسکریپت ساخت، و غیره. Nix تلاش زیادی می‌کند تا اطمینان حاصل کند که عبارت‌های Nix قطعی (deterministic) هستند: ساخت یک عبارت Nix برای دومین بار باید نتیجه‌ی مشابهی را به همراه داشته باشد.

از آنجا که این یک زبان تابعی است، پشتیبانی از ساخت انواع مختلف (variants) یک بسته بسیار آسان است: عبارت Nix را به یک تابع تبدیل کنید و آن را هر چند بار که لازم است با آرگومان‌های مناسب فراخوانی کنید. به لطف طرح هش‌گذاری، انواع مختلف در Nix store با یکدیگر تداخل پیدا نمی‌کنند.

استقرار شفاف سورس/باینری

عبارت‌های Nix به طور کلی نحوه ساخت یک بسته را از روی سورس توصیف می‌کنند، بنابراین یک عمل نصب مانند

$ nix-env --install firefox

می‌تواند حجم قابل توجهی از فعالیت‌های ساخت (build) را ایجاد کند، زیرا نه تنها فایرفاکس، بلکه تمام وابستگی‌های آن (تا کتابخانه C و کامپایلر) باید ساخته شوند، البته اگر از قبل در Nix store موجود نباشند. این یک مدل استقرار از منبع (source deployment model) است. برای اکثر کاربران، ساخت از روی منبع چندان خوشایند نیست زیرا زمان بسیار زیادی می‌برد. با این حال، Nix می‌تواند به طور خودکار از ساخت از روی منبع صرف‌نظر کرده و به جای آن از یک کش باینری (binary cache) استفاده کند؛ یعنی یک وب‌سایت که فایل‌های باینری از پیش‌ساخته شده را فراهم می‌کند. برای مثال، وقتی از Nix خواسته می‌شود که /nix/store/b6gvzjyb2pg0…-firefox-33.1 را از روی منبع بسازد، ابتدا بررسی می‌کند که آیا فایل http://cache.nixos.org/b6gvzjyb2pg0….narinfo وجود دارد یا خیر، و اگر وجود داشت، باینری از پیش‌ساخته ارجاع داده شده از آنجا را دریافت می‌کند؛ در غیر این صورت، به ساخت از روی منبع بازمی‌گردد.

مجموعه بسته‌های Nix

ما مجموعه بزرگی از عبارات Nix را که شامل هزاران بسته موجود یونیکس است، تحت عنوان مجموعه بسته‌های Nix (Nixpkgs) ارائه می‌دهیم.

قابلیت حمل

Nix روی لینوکس و macOS اجرا می‌شود.

NixOS چگونه کار می‌کند؟

سیستم‌عامل NixOS بر پایه Nix ساخته شده است که یک سیستم مدیریت بسته کاملاً تابعی (purely functional) است. Nix تمام بسته‌ها را ایزوله نسبت به یکدیگر در مسیرهایی مانند زیر ذخیره می‌کند:

/nix/store/5rnfzla9kcx4mj5zdc7nlnv8na1najvg-firefox-3.5.4/

رشته‌ی 5rnf... یک هش رمزنگاری‌شده از تمام ورودی‌های استفاده‌شده برای ساخت بسته است. بسته‌ها هرگز پس از ساخته شدن بازنویسی نمی‌شوند؛ در عوض، اگر توضیحات ساخت یک بسته (یعنی «عبارت Nix» آن) را تغییر دهید، بسته مجدداً ساخته و در مسیری متفاوت در /nix/store نصب می‌شود تا با نسخه‌ی قدیمی تداخل نداشته باشد. NixOS با استفاده از Nix نه تنها برای ساخت بسته‌ها، بلکه برای مواردی مانند فایل‌های پیکربندی، این مفهوم را گسترش می‌دهد. برای مثال، پیکربندی دیمن SSH نیز از یک عبارت Nix ساخته شده و در مسیری مانند

/nix/store/s2sjbl85xnrc18rl4fhn56irkxqxyk4p-sshd\_config

با ساخت کل پیکربندی‌های سیستم از یک عبارت Nix، سیستم NixOS تضمین می‌کند که چنین پیکربندی‌هایی روی یکدیگر بازنویسی نمی‌شوند، قابل بازنشانی (Rollback) هستند و غیره.

یکی از پیامدهای بزرگ نحوه ذخیره‌سازی بسته‌ها توسط Nix/NixOS این است که دایرکتوری‌های /bin، /sbin، /lib، /usr و غیره وجود ندارند. در عوض، تمام بسته‌ها در /nix/store نگهداری می‌شوند. (تنها استثناء، یک پیوند نمادین (Symlink) به نام /bin/sh است که به Bash در فروشگاه Nix اشاره می‌کند.) استفاده نکردن از دایرکتوری‌های «عمومی» مانند /bin همان چیزی است که به نسخه‌های مختلف یک بسته اجازه می‌دهد در کنار یکدیگر همزیستی داشته باشند. Nix دارای یک دایرکتوری /etc برای نگهداری فایل‌های پیکربندی سراسری سیستم است، اما بیشتر فایل‌های موجود در آن دایرکتوری، پیوندهای نمادینی به فایل‌های تولیدشده در /nix/store هستند.

مدل پیکربندی اعلان‌گونه سیستم

در NixOS، کل سیستم‌عامل، هسته، برنامه‌ها، بسته‌های سیستمی، فایل‌های پیکربندی و غیره، توسط مدیر بسته Nix و از روی توصیفی در یک زبان ساختِ کاملاً تابعی ساخته می‌شود. این واقعیت که این زبان کاملاً تابعی است اساساً به این معناست که ساخت یک پیکربندی جدید نمی‌تواند پیکربندی‌های قبلی را بازنویسی کند. بیشتر ویژگی‌های دیگر از همین موضوع ناشی می‌شوند.

شما یک سیستم NixOS را با نوشتن مشخصات عملکردی که برای ماشین خود می‌خواهید در /etc/nixos/configuration.nix پیکربندی می‌کنید. برای نمونه، در اینجا یک پیکربندی مینیمال از ماشینی که یک دیمن SSH را اجرا می‌کند آورده شده است:

{
  boot.loader.grub.device = "/dev/sda";
  fileSystems."/".device = "/dev/sda1";
  services.sshd.enable = true;
}

پس از تغییر فایل /etc/nixos/configuration.nix، پیکربندی را با اجرای این دستور اعمال می‌کنید:

$ nixos-rebuild switch

این دستور هر کاری را که برای اعمال پیکربندی لازم است، از جمله دانلود و کامپایل کردن OpenSSH، تولید فایل‌های پیکربندی برای سرور SSH و غیره انجام می‌دهد.

ارتقاهای مطمئن

مزیت دیگر مدیریت بسته‌های کاملاً تابعی این است که nixos-rebuild switch، صرف نظر از اینکه چه بسته‌ها یا فایل‌های پیکربندی از قبل روی سیستم خود داشته‌اید، همیشه نتیجه‌ی یکسانی تولید خواهد کرد. بنابراین، ارتقای یک سیستم به اندازه‌ی نصب مجدد از صفر مطمئن است.

ارتقاهای اتمی

NixOS دارای رویکردی تراکنشی نسبت به مدیریت پیکربندی است: تغییرات پیکربندی مانند ارتقاها اتمی هستند. این بدان معناست که اگر ارتقا به یک پیکربندی جدید متوقف شود، مثلاً برق در بین کار قطع شود، سیستم همچنان در یک حالت سازگار باقی خواهد ماند: یا با پیکربندی قبلی یا با پیکربندی جدید بوت خواهد شد. در اکثر سیستم‌های دیگر، در نهایت به یک حالت ناسازگار خواهید رسید و ممکن است دستگاه شما دیگر حتی بوت نشود.

بازگردانی‌ها (Rollbacks)

از آنجا که فایل‌های یک پیکربندی جدید، فایل‌های قدیمی را بازنویسی نمی‌کنند، می‌توانید (به صورت اتمی) به یک پیکربندی قبلی بازگردید. برای نمونه، اگر پس از اجرای nixos-rebuild switch متوجه شدید که پیکربندی جدید را دوست ندارید، می‌توانید به سادگی به عقب برگردید:

$ nixos-rebuild switch --rollback

در واقع، تمام پیکربندی‌های قدیمی سیستم به‌طور خودکار در منوی بوت ظاهر می‌شوند. بنابراین، اگر پیکربندی جدید از کار بیفتد یا به درستی بوت نشود، می‌توانید به سادگی با انتخاب یک پیکربندی قدیمی‌تر در منوی بوت، به عقب بازگردید (Rollback کنید). بازگشت به عقب بسیار سریع است: نیازی به بازیابی تعداد زیادی فایل از روی نسخه‌های پشتیبان ندارد.

منوی بوت NixOS

پیکربندی‌های سیستم قابل بازتولید

مدل پیکربندی اعلامی (declarative) در NixOS بازتولید پیکربندی سیستم را روی ماشینی دیگر آسان می‌کند (برای مثال، جهت آزمایش یک تغییر در محیط تست قبل از اعمال آن روی سرور پروداکشن). شما فقط کافی است فایل configuration.nix را به ماشین مقصد NixOS کپی کرده و دستور nixos-rebuild switch را اجرا کنید. این کار همان پیکربندی (هسته، برنامه‌ها، سرویس‌های سیستم و غیره) را به شما می‌دهد، به جز «وضعیت تغییرپذیر» (mutable state) (مانند چیزهایی که در /var قرار دارند).

امنیت در آزمایش تغییرات

NixOS آزمایش تغییرات بالقوه خطرناک روی سیستم را امن می‌سازد، زیرا همیشه می‌توانید به حالت قبلی بازگردید. (البته به شرطی که بوت‌لودر را خراب نکنید...) برای مثال، چه این تغییر به سادگیِ فعال‌سازی یک سرویس سیستم باشد، یا به بزرگیِ بازسازی کل سیستم با نسخه‌ای جدید از Glibc، می‌توانید آن را با اجرای دستور زیر آزمایش کنید:

$ nixos-rebuild test

این کار پیکربندی جدید را ساخته و فعال می‌کند، اما آن را به عنوان پیش‌فرض بوت تنظیم نمی‌کند. بنابراین، راه‌اندازی مجدد سیستم (Reboot) شما را به پیکربندی قبلی و سالم باز می‌گرداند.

راه جذاب‌تر برای آزمایش تغییرات به صورت زیر است:

$ nixos-rebuild build-vm
$ ./result/bin/run-\*-vm

این دستور یک ماشین مجازی را می‌سازد و اجرا می‌کند که حاوی پیکربندی جدید سیستم است (یعنی یک شبیه‌سازی از پیکربندی ماشین میزبان، به همراه هر تغییری که در configuration.nix ایجاد کرده‌اید). این ماشین مجازی هیچ داده‌ای را با میزبان به اشتراک نمی‌گذارد، بنابراین می‌توانید با خیال راحت درون آن آزمایش کنید. دستور build-vm بسیار کارآمد است (برای ایجاد ماشین مجازی نیازی به دیسک تصویر ندارد)، بنابراین روشی بسیار مؤثر برای آزمایش تغییرات است.

nixos.org/guides/how-nix-works

نیکسی · یادداشت‌های فارسی Nix local fonts