Using Guix despite uncooperative HPC admins—the ultimate solution
Published by Arun Isaac on
In other languages: தமிழ்
Guix is extremely useful on HPC. But often, your friendly HPC admins are uncooperative. guix packs are an idea, but they only work from /gnu/store. There are ways to make relocatable packs using -R, -RR, etc. But, when these hacks do not work, there is an ultimate solution—rebuild all packages from scratch to work from your HPC home folder. Here's how.
A guix-daemon with a different store and state directory
First, create the path you want on your HPC (say, /home/hpcuser) on your own machine.
$ sudo mkdir /home/hpcuser
Then, build a guix-daemon that puts its store and state directories in that path. To do so, use the following guix-daemon.scm. Put your path of choice in the %relocate-path variable.
(use-modules (gnu packages package-management) (guix gexp) (guix packages) (guix utils)) (define %relocate-path "/home/hpcuser") (package (inherit guix) (arguments (substitute-keyword-arguments (package-arguments guix) ;; Skip tests to save time. ((#:tests? tests? #f) #f) ;; Set store and state directories to be inside %relocate-path. ((#:configure-flags flags '()) #~(cons #$(string-append "--with-store-dir=" %relocate-path "/store") (map (lambda (flag) (if (string=? flag "--localstatedir=/var") #$(string-append "--localstatedir=" %relocate-path "/var") flag)) #$flags))))))
Build and run the guix-daemon.
$ sudo $(guix build -f guix-daemon.scm)/bin/guix-daemon --build-users-group=guixbuild --listen=/tmp/guix-daemon-socket --no-substitutes
Use the new guix-daemon to build yourself a guix pack
Use the new guix-daemon to build yourself a guix pack; no need to use -R, -RR, etc.
(use-modules ((gnu packages base) #:select (hello)) (guix build-system) (guix packages) (guix utils)) ;; Skip tests to save time. (define (package-without-tests p) ;; Some build systems (raw and trivial) do not support a #:tests? ;; argument. So, don't modify the package. (if (memq (build-system-name (package-build-system p)) (list 'raw 'trivial)) p ;; For all other packages, disable tests. (package/inherit p (arguments (substitute-keyword-arguments (package-arguments p) ((#:tests? _ #f) #f)))))) (packages->manifest (map (package-mapping package-without-tests) (list hello)))
With the above manifest.scm, run guix pack like so.
$ NIX_STORE_DIR=/home/hpcuser/store GUIX_DAEMON_SOCKET=/tmp/guix-daemon-socket guix pack -S /home/hpcuser/bin=bin -m manifest.scm
Now, this guix pack will work on our HPC, specifically from the one path that we chose. This will work no matter what!
$ scp /home/hpcuser/store/…-guix-pack.tar.gz hpc:~ $ ssh hpc [hpc]~$ tar --strip-components=3 xf …-guix-pack.tar.gz [hpc]~$ ./bin/hello Hello, world!
Bonus: Save more build time!
Even though the above manifest.scm will work, we can save a little more build time with the following.
(use-modules ((gnu compression) #:select (compressor)) ((gnu packages base) #:select (hello tar)) ((gnu packages compression) #:select (gzip)) (guix build-system) (guix gexp) (guix monads) (guix packages) (guix profiles) (guix scripts pack) (guix store) (guix utils)) (define %relocate-path "/home/hpcuser") ;; Skip tests to save time. (define (package-without-tests p) ;; Some build systems (raw and trivial) do not support a #:tests? ;; argument. So, don't modify the package. (if (memq (build-system-name (package-build-system p)) (list 'raw 'trivial)) p ;; For all other packages, disable tests. (package/inherit p (arguments (substitute-keyword-arguments (package-arguments p) ((#:tests? _ #f) #f)))))) (define hpc-profile (profile (content (packages->manifest (map (package-mapping package-without-tests) (list hello)))) ;; Disable hooks and locales? to save on package building. Hope ;; that these are irrelevant to us. (hooks '()) (locales? #f) (allow-collisions? #t))) (with-store store (run-with-store store (with-monad %store-monad (>>= (self-contained-tarball "hpc-pack" hpc-profile #:compressor (compressor "gzip" ".gz" #~(list #+(file-append (package-without-tests gzip) "/bin/gzip") "-9n")) #:symlinks `((,(string-append %relocate-path "/bin") -> "/bin")) #:archiver (package-without-tests tar)) (lambda (drv) (return drv))))))
Put this in hpc-pack.scm and build like so.
$ NIX_STORE_DIR=/home/hpcuser/store GUIX_DAEMON_SOCKET=/tmp/guix-daemon-socket guix build -f hpc-pack.scm
A different approach to avoid rebuilding the world
Rebuilding all packages is a waste of time and energy. There is a way to avoid this however—download Guix substitutes and patch them replacing the /gnu/store path with a path of our choice. Patching binaries this way is not bullet-proof. It is hard to support all kinds of binary files. But, it is another worthy choice. Pjotr describes this further.
Acknowledgment
Thanks to Christopher Baines for suggesting this technique in a Guix London meetup!