Container with LXC/LXD (4)

Date: 2023/11/14 (initial publish), 2024/02/23 (last update)

Source: en/note-00055.md

Previous Post Top Next Post

TOC

Here is a series of memos of me trying to use LXC/LXD on Debian 12 (bookworm).

Running GUI application in container

Unlike console applications, running GUI applications in container is a bit complicate. For now, I found some HOWTOs:

  1. Incus / LXD profile for GUI apps: Wayland, X11 and Pulseaudio (2023-11-17)
  2. Use Wayland and Xorg applications (updated 2023-10-07)
  3. LXD Containers for Wayland GUI Apps (2022-08-28)
  4. How to run GUI apps in LXD containers (2022-08-04)
  5. HOWTO: Use the Host’s Wayland and XWayland Servers inside containers (updated 2022-03-18)
  6. GUI application via Wayland from Ubuntu LXD container on Arch Linux host (2018-01-01)

All these interesting references are not always using GNOME wayland under the Debian bookworm environment.

Also, there are subtle differences.

# Wayland X11 PulseA PipeW GPU Note
1 disk disk disk Yes
2 proxy proxy Yes
3.1 proxy basic
3.2 disk for browsers
3.3 disk proxy for audio
4 proxy
5 proxy proxy Yes Xwayland
6 disk disk Yes share /run/user/1000

It seems that the use of the disk device has some advantages.

I will try similar things as above references using GNOME Wayland under the Debian bookworm environment in the followings.

YAML setting for profile=default

Let’s see the default LXD profile:

$ lxc profile show default
config: {}
description: Default LXD profile
devices:
  eth0:
    name: eth0
    network: lxdbr0
    type: nic
  root:
    path: /
    pool: default
    type: disk
name: default
used_by:
- /1.0/instances/d0
- /1.0/instances/d1
- /1.0/instances/d2

The last list for key=used_by changes as I use this profile.

Unless this list is empty, profile can’t be deleted.

User specific sockets used by IPC

For my primary user (name=osamu, UID=1000) on the host system running Debian 12 bookworm with GNOME wayland, I see following user accessible sockets under /run/user/ and /tmp:

 $ ls -l $(find /run/user -type s 2>/dev/null)
srwxrwxrwx 1 osamu osamu 0 Nov 25 07:13 /run/user/1000/at-spi/bus
srw-rw-rw- 1 osamu osamu 0 Nov 25 07:13 /run/user/1000/bus
srw-rw-rw- 1 osamu osamu 0 Nov 25 07:13 /run/user/1000/gcr/ssh
srw------- 1 osamu osamu 0 Nov 25 07:13 /run/user/1000/gnupg/S.dirmngr
srw------- 1 osamu osamu 0 Nov 25 07:13 /run/user/1000/gnupg/S.gpg-agent
srw------- 1 osamu osamu 0 Nov 25 07:13 /run/user/1000/gnupg/S.gpg-agent.browser
srw------- 1 osamu osamu 0 Nov 25 07:13 /run/user/1000/gnupg/S.gpg-agent.extra
srw------- 1 osamu osamu 0 Nov 25 07:13 /run/user/1000/gnupg/S.gpg-agent.ssh
srw-rw-rw- 1 osamu osamu 0 Nov 25 07:13 /run/user/1000/keyring/control
srwxr-xr-x 1 osamu osamu 0 Nov 25 07:13 /run/user/1000/keyring/pkcs11
srw------- 1 osamu osamu 0 Nov 25 07:14 /run/user/1000/keyring/.ssh
srwxr-xr-x 1 osamu osamu 0 Nov 25 07:13 /run/user/1000/keyring/ssh
srwxrwxr-x 1 osamu osamu 0 Nov 25 07:47 /run/user/1000/nvim.11027.0
srw------- 1 osamu osamu 0 Nov 25 07:13 /run/user/1000/openssh_agent
srw-rw-rw- 1 osamu osamu 0 Nov 25 07:13 /run/user/1000/pipewire-0
srw-rw-rw- 1 osamu osamu 0 Nov 25 07:13 /run/user/1000/pk-debconf-socket
srw-rw-rw- 1 osamu osamu 0 Nov 25 07:13 /run/user/1000/pulse/native
srw-rw-rw- 1 osamu osamu 0 Nov 25 07:13 /run/user/1000/snapd-session-agent.socket
s--------- 1 osamu osamu 0 Nov 25 07:13 /run/user/1000/systemd/inaccessible/sock
srwxr-xr-x 1 osamu osamu 0 Nov 25 07:13 /run/user/1000/systemd/notify
srwx------ 1 osamu osamu 0 Nov 25 07:13 /run/user/1000/systemd/private
srwxr-xr-x 1 osamu osamu 0 Nov 25 07:13 /run/user/1000/wayland-0
 $ ls -l $(find /tmp -type s 2>/dev/null)
srwxr-xr-x 1 osamu      osamu      0 Nov 25 07:15 /tmp/.com.google.Chrome.0JD6Mr/SingletonSocket
srwxrwxrwx 1 Debian-gdm Debian-gdm 0 Nov 25 07:13 /tmp/.ICE-unix/1847
srwxrwxrwx 1 osamu      osamu      0 Nov 25 07:13 /tmp/.ICE-unix/2702
srwxr-xr-x 1 osamu      osamu      0 Nov 25 07:13 /tmp/.X11-unix/X0
srwxr-xr-x 1 osamu      osamu      0 Nov 25 07:13 /tmp/.X11-unix/X1
srwxr-xr-x 1 Debian-gdm Debian-gdm 0 Nov 25 07:13 /tmp/.X11-unix/X1024
srwxr-xr-x 1 Debian-gdm Debian-gdm 0 Nov 25 07:13 /tmp/.X11-unix/X1025

Let me check further who uses X sockets while running GNOME desktop on Wayland.

 $ lsof -U |grep -e '[^@]/tmp/\.X11-unix/'
gnome-she 2733 osamu   45u  unix 0x0000000061b9b072      0t0 38083 /tmp/.X11-unix/X1 type=STREAM (LISTEN)
Xwayland  3244 osamu    5u  unix 0x000000005899b02d      0t0 38081 /tmp/.X11-unix/X0 type=STREAM (LISTEN)
 $ ps aux |grep '\(273[3]\|324[4]\)'
osamu       2733  3.1  0.9 5187384 299252 ?      Ssl  07:12   1:04 /usr/bin/gnome-shell
osamu       3244  0.3  0.3 788556 123388 ?       Sl   07:12   0:07 /usr/bin/Xwayland :0 -rootless -noreset -accessx -core -auth /run/user/1000/.mutter-Xwaylandauth.VC1EF2 -listenfd 4 -listenfd 5 -displayfd 6 -initfd 7

So the monitor screen is accessed as /run/user/1000/wayland-0 from Wayland on my environment.

If sound needs to be accessed, sockets such as /run/user/1000/pipewire-0 may be needed. If X program needs to be accessed, sockets such as /tmp/.X11-unix/X0 may be needed.

I will address these more complex cases later. For now, let me focus on to use wayland-0 socket.

YAML to allow display to Wayland using extra disk device

Here is a simple YAML data example (wayland.yaml) to add disk device for the Wayland socket.

config:
  boot.autostart: false
  raw.idmap: both 1000 1000
  user.user-data: |
    #cloud-config
    users:
    - name: osamu
      lock_passwd: True
      groups: [adm, audio, cdrom, dialout, dip, floppy, plugdev, sudo, video]
      sudo: ["ALL=(ALL) NOPASSWD:ALL"]
      shell: /bin/bash
    write_files:
    - path: /usr/local/bin/mystartup.sh
      permissions: 0755
      content: |
        #!/bin/sh
        uid=$(id -u)
        run_dir=/run/user/$uid
        mkdir -p $run_dir && chmod 700 $run_dir && chown $uid:$uid $run_dir
        ln -sf /mnt/runuser/wayland-0 $run_dir/wayland-0
    - path: /usr/local/etc/mystartup.service
      content: |
        [Unit]
        After=local-fs.target
        [Service]
        Type=oneshot
        ExecStart=/usr/local/bin/mystartup.sh
        [Install]
        WantedBy=default.target
    runcmd:
    - mkdir -p /home/osamu/.config/systemd/user/default.target.wants
    - ln -s /usr/local/etc/mystartup.service /home/osamu/.config/systemd/user/default.target.wants/mystartup.service
    - ln -s /usr/local/etc/mystartup.service /home/osamu/.config/systemd/user/mystartup.service
    - chown -R osamu:osamu /home/osamu
    - echo 'export WAYLAND_DISPLAY=wayland-0' >> /home/osamu/.profile    
description: Wayland LXD profile
devices:
  eth0:
    name: eth0
    network: lxdbr0
    type: nic
  root:
    path: /
    pool: default
    type: disk
  wayland-0:
    source: /run/user/1000/wayland-0
    path: /mnt/runuser/wayland-0
    type: disk

Here, raw.idmap: both 1000 1000 ensures permissions of /mnt/runuser/wayland-0 is matches with the host to offer R/W access from osamu UID=1000.

The mystartup.sh script in user.user-data is generated and applied when cloud-init applies user.user-data at the first boot of the instance to set up systemd startup script. This startup script links the Wayland socket to its usual location in the container (/run/user/1000/wayland-0) every time when the primary user osamu logs in.

Running Wayland GUI application eog in container

Let me first create a profile wayland:

 $ lxc profile create wayland
 $ lxc profile edit wayland < wayland.yaml
 $ lxc profile ls
+---------+---------------------+---------+
|  NAME   |     DESCRIPTION     | USED BY |
+---------+---------------------+---------+
| default | Default LXD profile | 3       |
+---------+---------------------+---------+
| wayland | Wayland LXD profile | 0       |
+---------+---------------------+---------+

Let me start wayland instance with wayland profile

 $ lxc launch images:debian/12/cloud wayland --profile wayland

Let me inspect this instance. (We need to expand to see effective configuration settings.)

 $ lxc config show wayland -e
architecture: x86_64
config:
  boot.autostart: "false"
  image.architecture: amd64
  image.description: Debian bookworm amd64 (20231124_05:24)
  image.os: Debian
  image.release: bookworm
  image.serial: "20231124_05:24"
  image.type: squashfs
  image.variant: cloud
  raw.idmap: both 1000 1000
  user.user-data: |
    #cloud-config
    users:
    - name: osamu
      lock_passwd: True
      groups: [adm, audio, cdrom, dialout, dip, floppy, plugdev, sudo, video]
      sudo: ["ALL=(ALL) NOPASSWD:ALL"]
      shell: /bin/bash
    write_files:
    - path: /usr/local/bin/mystartup.sh
      permissions: 0755
      content: |
        #!/bin/sh
        uid=$(id -u)
        run_dir=/run/user/$uid
        mkdir -p $run_dir && chmod 700 $run_dir && chown $uid:$uid $run_dir
        ln -sf /mnt/runuser/wayland-0 $run_dir/wayland-0
    - path: /usr/local/etc/mystartup.service
      content: |
        [Unit]
        After=local-fs.target
        [Service]
        Type=oneshot
        ExecStart=/usr/local/bin/mystartup.sh
        [Install]
        WantedBy=default.target
    runcmd:
    - mkdir -p /home/osamu/.config/systemd/user/default.target.wants
    - ln -s /usr/local/etc/mystartup.service /home/osamu/.config/systemd/user/default.target.wants/mystartup.service
    - ln -s /usr/local/etc/mystartup.service /home/osamu/.config/systemd/user/mystartup.service
    - chown -R osamu:osamu /home/osamu
    - echo 'export WAYLAND_DISPLAY=wayland-0' >> /home/osamu/.profile
  volatile.base_image: 03e51acc22a673de40cf411923bebc3215bea394cf828cafabb6b61925fd722f
  volatile.cloud-init.instance-id: 731cdfd7-9f7e-4e76-a900-9c53b6caf3ba
  volatile.eth0.host_name: veth1db326cc
  volatile.eth0.hwaddr: 00:16:3e:d9:a7:2a
  volatile.idmap.base: "0"
  volatile.idmap.current: '[{"Isuid":true,"Isgid":false,"Hostid":231072,"Nsid":0,"Maprange":1000},{"Isuid":true,"Isgid":true,"Hostid":1000,"Nsid":1000,"Maprange":1},{"Isuid":true,"Isgid":false,"Hostid":232073,"Nsid":1001,"Maprange":9999000},{"Isuid":false,"Isgid":true,"Hostid":231072,"Nsid":0,"Maprange":1000},{"Isuid":true,"Isgid":true,"Hostid":1000,"Nsid":1000,"Maprange":1},{"Isuid":false,"Isgid":true,"Hostid":232073,"Nsid":1001,"Maprange":9999000}]'
  volatile.idmap.next: '[{"Isuid":true,"Isgid":false,"Hostid":231072,"Nsid":0,"Maprange":1000},{"Isuid":true,"Isgid":true,"Hostid":1000,"Nsid":1000,"Maprange":1},{"Isuid":true,"Isgid":false,"Hostid":232073,"Nsid":1001,"Maprange":9999000},{"Isuid":false,"Isgid":true,"Hostid":231072,"Nsid":0,"Maprange":1000},{"Isuid":true,"Isgid":true,"Hostid":1000,"Nsid":1000,"Maprange":1},{"Isuid":false,"Isgid":true,"Hostid":232073,"Nsid":1001,"Maprange":9999000}]'
  volatile.last_state.idmap: '[]'
  volatile.last_state.power: RUNNING
  volatile.uuid: 514a4bb8-aa7d-4b33-b76e-feb8d9d3f3c8
devices:
  eth0:
    name: eth0
    network: lxdbr0
    type: nic
  root:
    path: /
    pool: default
    type: disk
  wayland-0:
    path: /mnt/runuser/wayland-0
    source: /run/user/1000/wayland-0
    type: disk
ephemeral: false
profiles:
- wayland
stateful: false
description: ""

Now, we can see full contents of user.user_data: and devices:.

This user.user_data: YAML data is passed to cloud-init to create files and execute commands as the container starts.

 $ lxc exec wayland -- apt install eog
 $ lxc exec wayland -- sudo -u osamu -i eog

This container environment allowed me to run an Wayland GUI program eog in container and displaying its result to the host GNOME Desktop. Nice.

Running Wayland GUI application firefox in container using disk device

I installed and tried the firefox-esr package.

Then started firefox:

 $ lxc exec wayland -- apt install firefox-esr
 $ lxc exec wayland -- sudo -u osamu -i firefox
[GFX1-]: glxtest: libpci missing

This can be fixed by installing libpci3 package (some web article implies to install libpci-dev which installs more packages in addition to libpci3 as a cure.):

 $ lxc exec wayland -- apt install libpci3
 $ lxc exec wayland -- sudo -u osamu -i firefox

Normally, pciutils package which has “priority: standard” is installed and pulls in libpci3. But firefox-esr doesn’t list libpci3 explicitly as Depends: nor Recommends:. I suppose this is a bug.

Previous Post Top Next Post