1 điểm bởi GN⁺ 2025-06-09 | 1 bình luận | Chia sẻ qua WhatsApp
  • Railway đã thiết kế lại builder tạo image container từ mã của người dùng, đưa kinh nghiệm từ Nixpacks — công cụ đã build hơn 14 triệu ứng dụng — sang Railpack
  • Nixpacks đủ tốt với 80% người dùng, nhưng 200.000 người dùng Railway còn lại có thể gặp giới hạn về quản lý phiên bản, kích thước image và caching
  • Railpack nâng cao khả năng tái lập build bằng phiên bản major.minor.patch, khóa dependency và luồng cài đặt dựa trên Mise, thay cho quản lý phiên bản dựa trên commit của Nix
  • Bằng cách trực tiếp tạo BuildKit LLB và Frontend, image Node mặc định nhỏ hơn 38%, image Python mặc định nhỏ hơn 77%, đồng thời có thể dùng cache chia sẻ giữa các môi trường
  • Railpack hiện đang ở Beta và có thể bật trong phần cài đặt dịch vụ; Railway ưu tiên nâng cao độ hoàn thiện cho các ngôn ngữ được dùng thường xuyên hơn là mở rộng hỗ trợ thật nhiều ngôn ngữ

Bối cảnh Railway tạo builder mới

  • Railway công bố Railpack là bước tiếp theo của Railway builder
  • Railpack được phát triển lại từ đầu dựa trên kinh nghiệm thu được khi build hơn 14 triệu ứng dụng bằng Nixpacks
  • Sau khi ra mắt khoảng 3 năm trước, Nixpacks trở thành cách mặc định để build image từ mã người dùng trên Railway
  • Nó hoạt động tốt với 80% tổng số người dùng, nhưng 200.000 người dùng Railway còn lại có thể gặp các giới hạn
  • Railway cho rằng để mở rộng cơ sở người dùng từ 1 triệu lên 100 triệu, builder cần được nâng cấp đáng kể

Những giới hạn Nixpacks gặp phải với Nix

  • Vấn đề lớn nhất là quản lý phiên bản package dựa trên commit của Nix
    • Mỗi package chỉ cung cấp phiên bản major mới nhất
    • Phiên bản bị ràng buộc với một commit cụ thể trong nixpkgs repo
  • Nỗ lực hỗ trợ mọi phiên bản patch đã dẫn đến cấu trúc ánh xạ trực tiếp chuỗi phiên bản với commit SHA, điều này không rõ ràng hoặc khó bảo trì với các contributor không quen với cách quản lý phiên bản của Nix
  • Các ngôn ngữ như Node và Python cuối cùng chỉ hỗ trợ phiên bản major mới nhất
  • Khi cập nhật commit SHA để hỗ trợ phiên bản package mới nhất, các phiên bản package khác cũng có thể thay đổi theo
    • Nếu phiên bản mặc định thay đổi, các bản build của người dùng vốn đang hoạt động có nguy cơ thất bại do lỗi không lường trước
    • Railway xem việc một bản build từng thành công bỗng nhiên bị hỏng là tệ hơn việc người dùng không truy cập được package mới nhất

Vấn đề kích thước image và caching

  • Cách Nixpacks lấy dependency bằng Nix thường tạo ra image có kích thước lớn
  • Nix cùng các package và thư viện liên quan cần cho build và runtime được đưa vào một layer /nix/store duy nhất
  • Không có cách tách dependency Nix thành các layer riêng, nên việc giảm kích thước image cuối bị hạn chế
    • Railway xem đây không phải là vấn đề của bản thân Nix, mà là vấn đề ở cách Nixpacks sử dụng Nix
  • Với caching, cũng khó kiểm soát khi nào layer cache bị vô hiệu hóa
  • Railway chèn biến môi trường deployment ID vào mọi build
    • Trong Dockerfile, các layer chạy sau khi biến này được thêm vào sẽ luôn bị vô hiệu hóa và không thể cache
  • Cách tiếp cận cố gắng che giấu các thành phần cốt lõi của Nix khỏi người dùng cũng không thật sự phù hợp
    • Họ muốn người dùng không cần hiểu derivation là gì, hoặc vì sao Node 22.14.0 lại nằm trong một phiên bản archive cụ thể của kênh unstable

Thay đổi kiến trúc của Railpack

  • Railway tạo Railpack để giải quyết các vấn đề đã gặp trong Nixpacks
  • Khi rời khỏi Nix, tên gọi cũng đổi từ Nixpacks thành Railpack
  • Codebase được chuyển từ Rust sang Go vì thư viện BuildKit
  • Railpack kiểm soát trực tiếp hơn cách tạo image cuối
    • Trực tiếp tạo BuildKit LLBFrontend
    • So với Nixpacks, image Node mặc định nhỏ hơn 38%, image Python mặc định nhỏ hơn 77%
    • Sử dụng Mise để phân giải phiên bản và cài đặt phần lớn package
    • Vẫn để ngỏ khả năng hỗ trợ các nguồn executable khác trong tương lai
  • Có thể khóa các dependency đã dùng trong một bản build thành công
    • Nhờ đó build không bị hỏng ngay cả khi phiên bản Node mặc định đổi từ 22 sang 24
  • Sử dụng BuildKit secrets để biến môi trường bí mật không xuất hiện trong build log hoặc image cuối

Cách build của Railpack hoạt động

  • Quy trình Railpack được chia thành ba bước
    • Analyze: xem mã để xác định package cần cài, lệnh cần chạy và lệnh khởi động
    • Plan: tạo kế hoạch build có thể serialize thành JSON gồm nhiều bước, trong đó mỗi bước lấy input từ bước khác hoặc từ toàn bộ image
    • Generate: cấu thành đồ thị build BuildKit dựa trên input và output của kế hoạch
  • Dockerfile có tính tuyến tính, nhưng đồ thị BuildKit được cấu trúc song song hơn nhiều
  • Mỗi lệnh chạy trong stage riêng của multi-stage build, cho phép kiểm soát chi tiết layer input và cách lắp ráp filesystem cuối
  • Railpack tạo kế hoạch build chứa mọi bước build cần thiết
    • Mỗi bước định nghĩa cụ thể các bước trước đó hoặc image cần dùng
    • Định dạng này ở mức thấp hơn cách từng được dùng trong Nixpacks
    • Kế hoạch được chuyển thành đồ thị ở định dạng LLB rồi được diễn giải
  • BuildKit bắt đầu từ cuối và làm ngược lại, lấy từ cache khi có thể, và chỉ chạy lệnh khi cần để resolve layer được yêu cầu
  • Để vô hiệu hóa layer khi một biến môi trường cụ thể thay đổi, Railpack hash giá trị biến đã dùng và mount một file chứa hash đó vào filesystem input
    • Nếu mã và các biến đã dùng không thay đổi, layer cache sẽ hit
  • Railpack có thể định nghĩa hoàn toàn cách image được tạo ra

Những việc Railpack cho phép thực hiện

  • Có thể build và deploy site tĩnh Vite, Astro, CRA, Angular với zero-config
  • Tích hợp giữa build và Railway UI chặt chẽ hơn
  • Có thể hỗ trợ phiên bản mới nhất của ngôn ngữ mà không cần phát hành Railpack
  • Có thể dùng layer caching được tối ưu hóa trên nhiều môi trường của dự án

Cách sử dụng hiện tại và phạm vi hỗ trợ

  • Railpack hiện được cung cấp ở trạng thái Beta, có thể kích hoạt trong phần cài đặt dịch vụ
  • Nó đã được dùng để build railway.comcentral station
  • Hiện hỗ trợ các mục sau
    • Node
    • Python
    • Go
    • PHP
    • Deploy Static HTML
    • Hỗ trợ cơ bản cho site tĩnh Vite, Astro, CRA, Angular
  • Railway hướng tới một môi trường có thể deploy cả frontend lẫn backend một cách dễ dàng
  • Hỗ trợ framework và ngôn ngữ vẫn tiếp tục được bổ sung
    • Có thể gửi yêu cầu tại Help Station
    • Cho đến khi API lõi và các abstraction được chốt, họ ưu tiên chiều sâu cho các ngôn ngữ được dùng nhiều hơn là hỗ trợ thật rộng
  • Railpack là mã nguồn mở và tài liệu được cung cấp tại railpack.com

1 bình luận

 
GN⁺ 2025-06-09
Ý kiến trên Hacker News
  • Tôi là người thích Nix, nhưng không có ý chỉ trích việc Railway rời khỏi Nix. Chỉ là một số phàn nàn có vẻ cần được giải thích thêm
    Nixpkgs rất tuyệt, nhưng không phải là một với Nix; và nếu muốn lấy các phiên bản toolchain tùy ý thì Nixpkgs chỉ đơn giản là không lý tưởng. Các công cụ Nix để lấy phiên bản Rust tùy ý hiện đã rất tốt, và các công cụ phát triển dựa trên Nix khác cũng đã cho thấy cách xử lý việc này ổn thỏa
    Tôi cũng không hiểu câu “không có cách nào tách dependency Nix thành một layer riêng”. Bạn có thể tách theo bất kỳ cách nào mình muốn, và các công cụ Docker tích hợp trong Nixpkgs cũng có một phần hỗ trợ cho việc này
    Việc chuyển từ Rust sang Go không liên quan trực tiếp đến Nix nhưng khá thú vị, và nghe như Railpacks với Nixpacks do những người khác nhau tạo ra. Tôi từng thấy những chuyện khá tệ xảy ra khi những người không quen với Nix phải gánh một giải pháp Nix chưa hoàn thiện trong tổ chức, nên ở nơi làm việc tôi thường không dùng Nix để tránh tạo ra tình huống như vậy

    • Tôi không dùng Nix, nhưng phản hồi kiểu “Nixpkgs không phải là Nix” nghe hơi mang tính dismissive. Nếu Nixpkgs là mặc định và các lựa chọn thay thế đòi hỏi phải tìm hiểu, nỗ lực thêm, thì với phần lớn người dùng, trên thực tế đó chính là Nix
      Câu “cứ chia layer theo cách bạn muốn” cũng vậy: điều quan trọng là việc đó có rõ ràng, đơn giản và là hành vi mặc định hay không
    • Mỗi lần có vấn đề về khả năng sử dụng cơ bản trong Nix, câu trả lời “có cách обход qua” lại lặp đi lặp lại, thật mệt mỏi. Những cách обход đó cần tri thức truyền miệng, phải viết hàng chục đến hàng trăm dòng bằng một ngôn ngữ hoạt động hoàn toàn khác các ngôn ngữ phổ biến, trong khi thông báo lỗi và tài liệu thư viện chuẩn cũng không tốt
      Lý do mọi người bất mãn với Nix không phải vì nó không Turing-complete, mà vì nó không cung cấp một API hạng nhất đơn giản, khớp ngay với các dự án theo quán lệ của hệ sinh thái tương ứng, khiến nó tạo ra nhiều vấn đề hơn số vấn đề nó giải quyết
      Nếu dự án nào muốn dùng Nix cuối cùng cũng trôi về hướng tự viết module để sửa các vấn đề của Nix, thì lý do để dùng Nix thay vì các công cụ phổ biến có tài liệu tốt là khá yếu. Trường hợp này trông đúng như vậy, và tôi nghĩ phần lớn sẽ chỉ chọn Docker
      Điều gây bực là một sản phẩm dành cho developer lại bám vào flakes thuần khiết về mặt ý thức hệ, thay vì giải quyết các vấn đề thực tế về developer experience với tốc độ không tính bằng đơn vị thời gian địa chất. Tôi hiểu đó là đóng góp tự nguyện, nhưng thật đáng tiếc khi quá nhiều nỗ lực kỹ thuật lại đổ vào một thứ mà trải nghiệm người dùng tệ khiến nó gần như khó dùng trong thực tế
    • Điểm dễ bỏ sót là người dùng của Railway là các developer muốn chỉ định dependency và phiên bản của riêng họ cho những package tùy ý
      Với cách Nix hoạt động cùng cấu trúc Nixpkgs, việc cố định một phiên bản package đồng nghĩa với việc cố định commit của toàn bộ cây nixpkgs. Việc build package node/python/ruby còn phụ thuộc vào trạng thái của cây bên ngoài thư mục package, nên cần có ánh xạ giữa phiên bản và commit
      Abstraction này bị rò rỉ, nên Railway phải phơi bày nó cho người dùng; người dùng vốn chỉ muốn chạy yarn add new-fancy-nodejs-package-with-linked–native-deps nhưng lại có thể bị đẩy vào tình huống phải khớp nhiều trạng thái khác nhau của repo nixpkgs
      Với phạm vi sử dụng hẹp thì dùng Nix mà không có Nixpkgs cũng ổn, nhưng với một nền tảng như Railway thì có vẻ khó biện minh
    • Tôi cũng từng nghe kiểu nói “Nix != Nixpkgs” khi thử FreeBSD, liên quan đến FreeBSD ports. pkg nhìn chung hoạt động tốt, nhưng có lần tôi cố biên dịch vim từ ports với USE flag tùy chỉnh, nó kéo về hơn 20 dependency và cứ mỗi lần make menuconfig lại hỏi tùy chọn; rồi package thứ 16 trong 23 package thất bại theo kiểu “cái này cần cái kia, cái kia cần Fubar3.32.1, nhưng Fubar3 đã bị deprecated để chuyển sang Fubar4”, nên tôi bỏ cuộc
      Tôi hiểu các developer của Core OS không thể hỗ trợ hơn 10.000 package, nhưng cũng cần nói rõ rằng khi thực sự bật các tính năng tùy chỉnh để dùng thì xác suất thất bại là cao. Hoặc tốt hơn là đặt tiêu chí rằng trước khi xuất hiện trong ports, bản build chuẩn được tạo độc lập phải thành công; cái nào không biên dịch được thì loại khỏi danh sách ports
    • Tôi thấy đây là cách diễn đạt tốt. Nói thêm thì dù nixpkgs không phải bản thân Nix, nó cũng là phần tốt của nixpkgs. Dùng NixOS, lần đầu tiên tôi có thể dùng phiên bản mới nhất của Linux kernel ngay trong ngày phát hành, và điều đó khá tuyệt. Khi lớn tuổi hơn tôi cũng chấp nhận Debian Stable, nhưng nó luôn có cảm giác như quay về vài năm trước
      Có thể phê bình ngôn ngữ Nix hàng giờ liền, nhưng nó đã cũ, là nỗ lực tốt nhất vào thời đó, và giờ có thể không còn đáng để thay đổi lớn. Hệ thống build của Nix cho cảm giác khá thô sơ, và thường build lại cả những thứ trông như không cần build lại. Ví dụ, phần lớn quá trình build ISO cài đặt NixOS phụ thuộc vào dòng lệnh truyền cho kernel console=ttyS2,1500000n8, nên chỉ đổi tốc độ cổng serial cũng cần khoảng 3 phút build. Buồn cười thật, nhưng tôi sẽ không vì thế mà bỏ Nix; chỉ là trong build của tôi thì tôi sẽ không cho phép chuyện đó
      Cá nhân tôi cho rằng Nix cho Docker image là lĩnh vực Nix làm kém nhất. Từ lâu, khi làm phần mềm Go, tôi cần thêm binary pg_dump của Postgres vào container image; theo đề xuất của đội hạ tầng, tôi dùng Nix, và image chứa binary Go nén 50MB phình thành 1,5GB không rõ vì sao. pg_dump chỉ 464KB. Cuối cùng tôi dùng Bazel và rules_debian để cài apt package, chạy trên distroless thì gọn gàng và nhỏ hơn nhiều. Dựa trên trải nghiệm Nix thực tế, hệ thống Nix lúc nào cũng có cảm giác thành 1,4GB. ISO cài đặt cũng 1,4GB, máy vừa cài xong cũng 1,4GB
      Tình huống muốn build một dự án C++ lớn vốn đã là con đường được dọn sẵn, và đổi C++ thành Rust về bản chất cũng không khác. Có những build system giúp tình hình thư viện bớt đau đớn hơn; chúng cũng phức tạp như Nix, nhưng có cái phù hợp hơn cho mục đích này. Vì Nix cố trở thành build system để build phần mềm của người khác và nixpkgs, nó nằm ở phía rất tổng quát. Build system được thiết kế để build phần mềm của chính bạn thường làm việc đó tốt hơn. Cá nhân tôi hài lòng với Bazel, và ngoài go build cho dự án chỉ dùng Go thì có lẽ tôi gần như không dùng gì khác; nhưng có rất nhiều lựa chọn. Trong 99% trường hợp, hãy dùng những thứ đó thay Nix, rồi viết flake để mọi người có thể cài phiên bản mới nhất bằng home-manager
  • Phần chọn phiên bản nghe hơi kỳ lạ. Phiên bản của nixpkgs thì có ý nghĩa khi chạy hoặc build hệ thống, nhưng nếu cung cấp runtime hay compiler với tư cách một nền tảng thì cần cách cung cấp phiên bản trực tiếp như devenv
    Nếu build một hệ thống cũ để cung cấp nodejs cũ, bạn sẽ bỏ lỡ các bản vá bảo mật của dependency. Chẳng hạn Devenv xử lý bằng cách dùng https://github.com/cachix/nixpkgs-python để “giữ mọi phiên bản Python luôn mới nhất theo từng giờ bằng Nix”
    Việc Railway inject biến môi trường ID triển khai vào mọi build lẽ ra có thể làm ở một layer sau bước cài đặt. Cũng có thể chia package thành nhiều layer, và còn có tự động hóa theo cụm để giảm số lượng layer

  • “Bản thân Nix không có vấn đề, vấn đề nằm ở cách chúng tôi dùng nó” là một ví dụ hay cho việc dùng đúng công cụ cho đúng việc. Nix tuyệt vời cho một số mục đích, và tệ hại cho những mục đích khác
    Vấn đề là đường cong học Nix quá dốc, nên đến lúc hiểu đủ để đánh giá thì bạn đã đầu tư quá nhiều thời gian, cảm thấy tiếc nếu quay lại, rồi cố gò nó vào để giải quyết nhu cầu ban đầu

    • Tôi cũng cảm thấy tương tự, nhưng ở một khía cạnh nào đó Nix gần với một mô thức lập trình tổng quát hơn so với các hệ điều hành khác. Chỉ là chúng ta chưa quen nghĩ về hệ điều hành theo cách đó. Biểu thức Nix có input, chứa kho package và nhiều cặp key-value, rồi output là một hệ thống Linux. Vài năm nữa có thể nó sẽ cảm thấy bình thường hơn nhiều
      Chính mô thức này khiến AI rất dễ tạo shell.nix hoặc configuration.nix đúng theo đặc tả. Ví dụ có thể chứa package Python, package Linux, biến môi trường, mục đường dẫn, v.v.
      Tôi thường dùng thứ này để đưa vào repository một môi trường hỗ trợ đầy đủ package đó. Dùng flakes thì sẽ tái lập được tốt hơn, nhưng tôi hiểu flake.nix giống như shell.nix có pin phiên bản, và vẫn đang học thêm
  • Trông như đang cố nhét phiên bản vào nơi vốn không có phiên bản. Giống như nhét một khối vuông vào lỗ tròn
    “Phiên bản mặc định” làm hỏng những thứ phụ thuộc vào nó là sao, tôi không hiểu. Nó giống như dùng tag :latest của Docker rồi ngạc nhiên khi mỗi lần server mới bật lên lại hỏng vì khác phiên bản so với image “mặc định” trước đó
    Tôi chẳng hiểu nổi phần giải thích nào trong bài blog này. Trông như những người hoàn toàn không biết “phiên bản” của phần mềm là gì
    Câu “không có cách nào tách dependency Nix thành layer riêng” cũng không hiểu vì sao. Tất nhiên có thể chia /nix/store thành bao nhiêu layer tùy cần. Tôi còn nghi họ có biết container và Nix được dùng thế nào ngay từ đầu không
    Nhìn sự non tay rõ ràng như vậy, cũng không ngạc nhiên khi giải pháp họ đề xuất có mùi cá ươn. Đây là hội chứng NIH điển hình, và rất có khả năng những vấn đề tương tự mà họ không giải được bằng Nix sẽ lan nguyên sang “giải pháp” mới

    • Bản thân việc không dùng Nix thì tôi đồng ý, nhất là ở những nơi không hợp lý. Nhưng chỉ cần bỏ ra vài giờ là có thể thấy người ta đã giải quyết các vấn đề đó ra sao; việc xây lại từ đầu một hệ thống đang hoạt động vì những lý do thực ra không phải vấn đề thì về cơ bản là kỳ lạ
      Như những người khác đã nói, nix2container và flakes có vẻ sẽ giải quyết mọi vấn đề của họ
      Về quản lý phiên bản, flakes tôi viết 3 năm trước đến nay vẫn build ra đúng cùng phiên bản và cùng output như lúc mới viết
      Nghe có vẻ như họ đang muốn ra thị trường như một nền tảng để gọi vốn
      Chỉnh sửa: Tôi vừa xem GitHub của nixpacks, và điều lập tức đập vào mắt là họ dùng rustPlatform của nixpkgs thay vì rust-overlay[0] của oxalica, thứ lẽ ra chỉ cần tìm sơ qua về vấn đề Rust là thấy. rust-overlay là một trong những overlay hữu ích và mạnh mẽ nhất mà tôi từng dùng
      [0] https://github.com/oxalica/rust-overlay
    • Nếu mục tiêu là nhận đầu tư VC, thì nền tảng triển khai dễ bán hơn một wrapper cho Nix
  • nix2container[1] thực sự có thể tách dependency thành layer riêng. Bạn có thể tạo rõ ràng một layer chỉ chứa một phần dependency cần cho image, và ví dụ cũng có trong mục này: https://github.com/nlewo/nix2container?tab=readme-ov-file#is...
    Ví dụ nếu các image dùng bash, bạn có thể tạo rõ ràng một layer chứa closure của bash. Layer này sẽ được tái sử dụng trong mọi image, và chỉ được build lại, push lại khi closure bash đó thay đổi
    Việc image phình to vì một layer /nix/store duy nhất đúng với hàm nixpkgs.dockerTools.buildImage mặc định, nhưng không đúng với nix2container hay nixpkgs.dockerTools.streamLayeredImage. Các công cụ này không ghi layer vào Nix store, mà dùng các store path hiện có để tạo script thực sự push image. Triển khai của nix2container tạo một file JSON mô tả các Nix store path của mọi layer, rồi Skopeo tiêu thụ JSON này để push image lên Docker daemon, registry, podman, v.v.
    Nhân tiện, tôi là tác giả của nix2container
    [1] https://github.com/nlewo/nix2container

    • Nhờ nix2container, tôi dùng nó để triển khai lên AWS ECR, và thời gian lặp giữa các lần build đã giảm xuống còn vài giây một chữ số
    • Tôi đang định dành thời gian thử nghiệm nix2container vì gặp vấn đề kích thước Docker image. Cảm ơn vì công sức của bạn
  • Vấn đề cốt lõi ở đây là bám vào thái độ món súp phiên bản tùy biến mà các trình quản lý package theo ngôn ngữ cổ vũ. Cách này hoàn toàn không bền vững
    Phương án thay thế Mise dường như không có khả năng hiểu ràng buộc phiên bản giữa các package, và có vẻ cũng không chạy test để xem từng package đã cài có hoạt động tốt với các phiên bản xung quanh hay không. Vậy thì bạn không hề nhận được cùng một thứ

  • “Món súp phiên bản tùy biến” là không bền vững, nhưng một trong những lý do mọi người vẫn tiếp tục dùng là vì nhìn chung nó hoạt động tốt. Một trong những lý do nó hoạt động tốt là các thư viện cấp hệ điều hành đến từ một thế giới khác, bảo thủ hơn nhiều, và cố gắng tối đa để tránh phá vỡ tương thích ngược
    Vì vậy, có thể lấy một hệ điều hành ổn định và được quản lý tốt làm nền, rồi chồng lên đó “món súp phiên bản tùy biến” của công cụ và runtime ngôn ngữ bằng các công cụ như mise hoặc asdf, sau đó chạy ứng dụng. Nó hầu như không hỏng. Khi hỏng, ta chỉnh qua lại phiên bản và vài bản sửa nhỏ cho đến khi nó chạy lại rồi tiếp tục. Việc nó hỏng thì khó chịu, nhưng không quan trọng. Những thứ làm tăng ma sát, đòi hỏi học thêm hoặc cần nhiều việc hơn đều là lãng phí thời gian
    Ngược lại, cũng có những người tìm giải pháp để nó không bao giờ hỏng nữa. Với họ, vấn đề này quan trọng, nên giải pháp có đòi hỏi ma sát, học hỏi và thêm công việc cũng không sao. Những người như vậy muốn Nix
    Phần lớn thuộc nhóm đầu tiên, nên một công ty muốn tăng trưởng như Railway rốt cuộc sẽ chọn giải pháp phù hợp với nhóm đó

    • Bạn có thể giải thích thêm thái độ gọi là “món súp phiên bản tùy biến” nghĩa là gì và phương án thay thế là gì không?
    • Nếu làm đúng thì có thể có cả hai. Ví dụ, build package Rust bằng Nix từ file Cargo.lock là chuyện nhỏ. nixpkgs đi theo hướng ngược lại với “món súp phiên bản tùy biến”, nhưng bản thân Nix cũng xử lý được cách đó khá ổn
  • Từ kinh nghiệm làm DevOps/SRE, khi ai đó cố xây một hệ thống quản lý dependency và những thứ tương tự, thường họ đi theo một trong hai hướng. Có thể lấy Python làm ví dụ
    Lựa chọn 1: “Hãy dùng một kho đơn khối dùng chung thật lớn.” Ưu điểm là mọi thứ ở một chỗ, thứ cần có đều được đưa vào, mọi người dùng cùng một thứ nên dễ sửa các vấn đề như lỗ hổng. Nhược điểm là luôn có người muốn một phiên bản đặc biệt, triển khai theo từng giai đoạn khó nên thay đổi dễ thành kiểu big bang, và rồi sẽ xuất hiện câu hỏi “làm bản Docker nhỏ như thế nào?”
    Lựa chọn 2: “Mỗi người có conda/venv của riêng mình.” Ưu điểm là ai cũng có đúng thứ mình muốn, không dùng các package không cần thiết, và nâng cấp từng bước dễ hơn. Nhược điểm là sẽ thành “rốt cuộc có bao nhiêu môi trường conda?”, các thư viện của những nhóm khác nhau có thể không được test với cùng tổ hợp thư viện Python, và thậm chí không biết các môi trường conda khác nhau nằm ở đâu nên quản lý lỗ hổng trở thành ác mộng
    Vì vậy tôi luôn hoài nghi với câu “cách mới này giải quyết được tất cả”. Càng nhiều kinh nghiệm, câu “không có giải pháp, chỉ có đánh đổi” càng ngày càng đúng

  • Dù chỉ có chút kinh nghiệm với Nix, tôi thấy các luận điểm ở đây không có vẻ đúng lắm
    Có câu “không rõ ràng hoặc khó bảo trì đối với contributor chưa quen với cách quản lý phiên bản của Nix”, và “Node và Python đã chỉ còn hỗ trợ các major version mới nhất”, nhưng tôi không hiểu vì sao điều này lại không thể bảo trì. Nếu lý do là phải tạo danh sách các phiên bản có thể dùng, thì tôi thắc mắc liệu không thể tự động hóa được sao
    Hơn nữa, tôi cũng không hiểu vì sao Railway lại định nghĩa cách người dùng dùng Nix. Một trong những điểm cốt lõi của Nix chẳng phải là có thể cấu hình một máy trống với đúng phiên bản các package mình muốn sao. Tôi không hiểu vì sao Railway phải chen vào giữa người dùng và các phiên bản đó để giới hạn
    Nếu kiến trúc là không cho người dùng thấy trực tiếp Nix, thì câu hỏi ban đầu vẫn còn đó. Không thể tự động hóa danh sách phiên bản package sao?

    • Giới hạn phiên bản đến từ việc cache của Nix không giữ các phiên bản cũ. Vì vậy nếu dùng phiên bản cũ thì phải biên dịch từ source. Nghe như họ không muốn tự cung cấp cache cho các phiên bản cũ, nhưng việc đó có vẻ không tốn công lớn đến vậy
      Thành thật mà nói, các lý do đưa ra trông không vững lắm. Có lẽ người đưa Nix vào đã rời đi, và những người còn lại không thích nó cho lắm. Bản thân ngôn ngữ cũng không thật sự tốt, tài liệu cũ cũng không xuất sắc
      Dù vậy, tôi không biết đủ về stack họ chọn, nhưng tôi tò mò liệu nó có cung cấp mức tính quyết định gần với Nix hay không. Nếu không, sau này nó có thể gây vướng víu hoặc làm vận hành khó hơn
    • Trong bài có câu “cách Nixpacks kéo dependency dẫn đến một image khổng lồ với một tầng /nix/store duy nhất, trong đó có tất cả package và thư viện liên quan đến Nix cần cho build và runtime”
      Câu này giống như nói “không thể làm ô tô chạy về phía trước nên bỏ ô tô”. Đây là một trong những việc Nix làm ổn định nhất. Nó tự động phát hiện dependency runtime thực sự được tham chiếu từ binary kết quả bằng cách khớp chuỗi hash trong /nix/store
      Nếu họ không làm được việc đó thì hoặc là đang dùng theo cách khá kỳ lạ, hoặc là đã làm sai nghiêm trọng. Thậm chí tôi khó nghĩ ra cách nào để ngăn Nix tự động giải quyết việc này
      Vì vậy tôi sẽ không quá coi trọng trải nghiệm Nix của họ. Nội dung về quản lý phiên bản là vấn đề rất phổ biến mà ai cũng gặp, nên nếu họ thử giải quyết thì đã thú vị hơn
    • Việc đây là kiến trúc không cho người dùng trực tiếp thấy Nix, ít nhất theo hiểu biết của tôi cũng là vậy
    • Ngay cả khi dự án không có Dockerfile nào, nếu đẩy code lên Railway thì họ sẽ build image bằng Nixpacks. Log build sẽ hiển thị nội dung liên quan đến Nix, nhưng phần lớn chạy phía sau
  • Nix không cung cấp bảo đảm về phiên bản tùy ý, mà cung cấp bảo đảm theo commit. Khi gặp edge case, họ sẽ khổ vì thay đổi glibc hoặc các shared library xung đột
    Có lẽ hơi muộn, nhưng tôi sẵn lòng cung cấp tư vấn để làm cho nó hoạt động theo cách idiomatic của Nix. Sản phẩm trông rất hay

    • Nix giải quyết vấn đề không tương thích shared library theo cách cực kỳ bảo thủ. Bất cứ thứ gì thay đổi, dù là thay đổi có ý nghĩa hay không, chỉ sửa chú thích, đổi tài liệu, thêm test case thôi cũng rebuild toàn bộ các phụ thuộc
      Không chỉ vậy, còn tiếp tục đến dependency của dependency, rồi dependency của dependency đó, nên các đợt rebuild lớn xảy ra thường xuyên
      Nó sẽ tránh được xung đột shared library, nhưng giải pháp này cực kỳ lãng phí và có thể khiến việc phát triển rất đau đớn. Nhìn vào quy trình staging của nixpkgs là thấy
    • Tôi hoàn toàn hiểu value proposition của Nix. Tuy nhiên tôi nghĩ cách nói “sẽ khổ” hơi phóng đại. Nói ở mức cao nhất thì cũng chỉ là “mất một bảo đảm khá quan trọng so với Nix”
      Dù vậy, có lẽ nó vẫn sẽ ở trạng thái “được đóng gói với khả năng hoạt động đúng cao” hơn 95% phần mềm trên đời
  • Tôi không hiểu vì sao họ không thể tạo derivation riêng thay vì phụ thuộc vào hash của nixpkgs