On 5/19/25 00:24, James Thomas wrote:
> Naranden writes:
>> On 5/16/25 04:14, James Thomas wrote:
>>> Naranden writes:
>>>> I suppose there must be some way to make this work like Guix system
>>>> containers, where everything the container needs is loaded from the
>>>> store rather than through Docker.
>>>
>>> Why not build the 'container' script for the users and let them run it?
>>
>> I'm not sure what you mean. The goal isn't to ship to other users but
>> to declare a specific, reproducible operating-system with services
>> that can be deployed on a local (real) system, in a VM, or remotely.
>> More specifically, the goal is to include a guix system container
>> service in an operating-system definition--in other words,
> 
>> a Guix operating-system (container) nested in another operating-system
>> (parent).
> 
> That's what running the script of ‘container’ in (info "(guix) Invoking
> guix system") gets you, no?

Yes, running the script written by `guix system container` does provide
a Guix operating-system container on the local system. But the system
must have a read-write /gnu/store and be capable of building/acquiring
the dependencies. For example, it does not work for guix system vm,
which uses a read-only /gnu/store. (See example 1 below.)

> No new 'service' should be required.

The goal is to get something managed by Shepherd (so it is started at
boot, is automatically respawned, etc.). That's why a service is
required (unless there is some other way to accomplish those things).
See example 2 below.

Also I found a project that seems to maybe have a similar goal, but
there hasn't been any activity there since about 1.5 years ago:

https://github.com/BIMSBbioinfo/swineherd

>> 1. I can bundle a package (and its dependencies) and run a command in a
>> container with `guix shell --container $package -- run`. Can I set up
>> the same thing to happen as a Shepherd-managed service (in an
>> operating-system definition)?
>>
>> 2. And if that can be done, can I set the channels and use package
>> inferiors so I would get the equivalent of `guix time-machine -C
>> $channels -- shell ...` but in a Shepherd-managed service as in (1)?
>>
>> 3. And if *that* can be done, can I do the same with an entire nested
>> operating-system as in my original question and example?! That is,
>> equivalent to `guix time-machine -C $channels -- system vm $file` but as
>> a Shepherd-managed service nested inside a parent operating-system
>> definition, where the parent operating-system definition could itself be
>> executed in a VM with `guix time-machine -C $channels -- system vm
>> $file`. (It's amazing that this level of reproducibility might actually
>> be possible with Guix.)
> 
> AFAIU your requirements, they're possible with the shepherd-transient
> service and 'guix time-machine' prefixing the command mentioned above.

I looked at transient services but from what I understand they don't run
on boot (only the parent transient-service does). It just allows the
user to run transient services during run-time. I am trying to run
something at boot, managed by Shepherd.

======================================================================

Example 1: Trying to use `guix system container` in a VM

os.scm:

```
(use-modules (gnu))

(operating-system
 (host-name "os")
 (file-systems (cons (file-system
                      (device (file-system-label "does-not-matter"))
                      (mount-point "/")
                      (type "ext4"))
                     %base-file-systems))
 (bootloader (bootloader-configuration
              (bootloader grub-bootloader))))
```

In the shell:

```
$ $(guix system --no-graphic vm os.scm) -m 1G

## Log in as root

# nano os.scm

## Write the above `os.scm` file inside the VM

# guix system container os.scm
guix system: error: changing ownership of path '/gnu/store': Read-only
file system
```

======================================================================

Example 2: mlauncher (manifest launcher) service

This shows implementation and use of a rudimentary service that:

- is managed by Shepherd,
- uses a profile defined by a package manifest, and
- runs a specified command using that profile.

However:

- there is no Linux container or other isolation and
- it does not accept a Guix operating-system definition.

In the context of this discussion, it may be reasonable to skip the
"Implementing" part and go to the "Using" part.

I have not found out a way to use something like the output of `(gnu
build linux-container)` module's `(container-script os)` in
`mlauncher-shepherd-service` below to get a container as provided by
`guix system container`.

os.scm:

```
(use-modules (gnu bootloader))
(use-modules (gnu bootloader grub))
(use-modules (gnu packages))
(use-modules (gnu services base))
(use-modules (gnu services shepherd))
(use-modules (gnu system file-systems))
(use-modules (guix gexp))
(use-modules (guix profiles))
(use-modules (guix records))

;; Implementing the service.
(define-record-type* <mlauncher-configuration>
  mlauncher-configuration make-mlauncher-configuration
  mlauncher-configuration?
  (command mlauncher-configuration-command)
  (manifest mlauncher-configuration-manifest)
  (name mlauncher-configuration-name))

(define (mlauncher-shepherd-service config)
  "Return a <shepherd-service> for mlauncher with CONFIG."
  (define mlauncher-command
    (cons*
     (file-append (profile
                   (content (mlauncher-configuration-manifest config)))
                  (car (mlauncher-configuration-command config)))
     (cdr (mlauncher-configuration-command config))))
  (list
   (shepherd-service
    (documentation "mlauncher service")
    (provision
     (list
      (string->symbol
       (string-append "mlauncher-"
                      (mlauncher-configuration-name config)))))
    (requirement '(syslogd))
    (start #~(make-forkexec-constructor '#$mlauncher-command)))))

(define mlauncher-service-type
  (service-type
   (name 'mlauncher)
   (extensions
    (list (service-extension shepherd-root-service-type
                             mlauncher-shepherd-service)))
   (description "Run a command in a profile defined by a manifest.")))

;; Using the service.
(define test-manifest
  (specifications->manifest
   (list
    "python-minimal")))

(define test-mlauncher-service
  (service
   mlauncher-service-type
   (mlauncher-configuration
    (command
     (list
      "/bin/python3"
      "-c"
      "import sys,time;print('hi',file=sys.stderr);time.sleep(60)"))
    (manifest test-manifest)
    (name "test"))))

(define runner-mlauncher-service
  (service mlauncher-service-type
           (mlauncher-configuration
            (command (list "/bin/runner"))
            (manifest test-manifest)
            (name "runner"))))

(operating-system
 (host-name "host-os")
 (file-systems (cons (file-system
                      (device (file-system-label "does-not-matter"))
                      (mount-point "/")
                      (type "ext4"))
                     %base-file-systems))
 (bootloader (bootloader-configuration
              (bootloader grub-bootloader)))
 (services
  (cons* test-mlauncher-service %base-services)))
```

In the shell:

```
$ $(guix system --no-graphic vm os.scm) -m 1G

## Log in as root

# herd status mlauncher-test
● Status of mlauncher-test:
  It is running since 07:14:51 PM (38 seconds ago).
  Main PID: 105
  Command:
/gnu/store/yf4k9v2kw3hcy2567pf30m3r2l4800gc-profile/bin/python3 -c
"import sys,time;print('hi',file=sys.stderr);time.sleep(60)"
  It is enabled.
  Provides: mlauncher-test
  Requires: syslogd
  Will be respawned.

Recent messages (use '-n' to view more or less):
  2025-05-21 19:14:52 hi
```

======================================================================

I appreciate your feedback.

Thanks,
Naranden

Reply via email to