大概摸了一下 Incus
需求
之前提到了我迁移到了 Arch,现在能吃到新软件了,但是一些有特定版本限制的软件反而变得困难了。
说的就是你 ROS。于是在朋友的建议下打算用 Incus 来跑 Ubuntu 20.04 的容器。
比如根据 AUR 里的评论,ROS Noetic 现在没办法正常安装了。 |
顺带一提 Incus 是 LXD 的分支,关于这部分参见:
总之你知道的,放这个是为了骂 Canonical。
Incus 设置
Incus 的安装很简单,我还是参照了 ArchWiki 的过程,初始配置基本上全部采用了默认配置。
但是关于创建容器部分的 incus launch ubuntu:20.04
似乎已经不再适用,我使用的命令是 incus launch images:ubuntu/focal/amd64 ros
。这里的 ros
是容器的名称,可以自己根据需要改动。
GUI
然后就是大问题了:GUI 怎么办。
其实这里的思路很清晰,用 X11 Forwarding 转发容器内的内容到本机的 X11 Server(是的我还是在用 X11),然后本机 X11 Server 负责渲染就好了。除了一个问题:跑不起来。
直接说怎么解决吧,首先参考了论坛里的方案,排查出来问题出在没有合适的 Xauthority 导致新版本的 Xorg 上会无法正常连接。
解决的办法分成了两步,虽然理论上可以在 profile 里直接解决的,但是我没成功。
这里是 profile
config:
cloud-init.user-data: |
#cloud-config
package_update: true
package_upgrade: true
package_reboot_if_required: true
packages:
- pulseaudio-utils
- dbus-user-session
write_files:
- path: /var/lib/cloud/scripts/per-boot/set_up_sockets.sh
permissions: 0755
content: |
#!/bin/bash
user_uid=1000
user_gid="$( getent passwd ${user_uid} | cut -d: -f4 )"
if [[ -n ${user_gid} ]]; then
mnt_dir="/mnt/.container_sockets"
run_dir="/run/user/${user_uid}"
[[ ! -d "${run_dir}" ]] && mkdir -m 700 -p "${run_dir}" && chown ${user_uid}:${user_gid} "${run_dir}"
[[ ! -d "${run_dir}/pulse" && -d "${run_dir}" ]] && mkdir -m 700 "${run_dir}/pulse" && chown -R ${user_uid}:${user_gid} "${run_dir}/pulse"
[[ -S "${mnt_dir}/pipewire-0" && -d "${run_dir}" && ! -e "${run_dir}/pipewire-0" ]] && touch "${run_dir}/pipewire-0" && sudo mount --bind "${mnt_dir}/pipewire-0" "${run_dir}/pipewire-0"
[[ -S "${mnt_dir}/pipewire-0-manager" && -d "${run_dir}" && ! -e "${run_dir}/pipewire-0-manager" ]] && touch "${run_dir}/pipewire-0-manager" && sudo mount --bind "${mnt_dir}/pipewire-0-manager" "${run_dir}/pipewire-0-manager"
[[ -S "${mnt_dir}/native" && -d "${run_dir}/pulse" && ! -e "${run_dir}/pulse/native" ]] && touch "${run_dir}/pulse/native" && sudo mount --bind "${mnt_dir}/native" "${run_dir}/pulse/native"
fi
- path: /var/lib/cloud/scripts/per-once/set_up_sockets_service.sh
permissions: 0755
content: |
#!/bin/bash
user_uid=1000
user_name="$( getent passwd ${user_uid} | cut -d: -f1 )"
home_dir="$( getent passwd ${user_uid} | cut -d: -f6 )"
if [[ -d "${home_dir}" && -n ${user_name} ]]; then
run_as_user="runuser -u ${user_name} --"
service_dir="${home_dir}/.config/systemd/user"
target_dir="${service_dir}/default.target.wants"
${run_as_user} mkdir -p "${target_dir}"
${run_as_user} touch "${service_dir}/set_up_sockets.service"
cat >> ${service_dir}/set_up_sockets.service << EOF
[Unit]
Description=Run set_up_sockets.sh on every boot
After=local-fs.target
[Service]
Type=oneshot
ExecStart=/var/lib/cloud/scripts/per-boot/set_up_sockets.sh
[Install]
WantedBy=default.target
EOF
${run_as_user} ln -s "${service_dir}/set_up_sockets.service" "${target_dir}/"
fi
- path: /var/lib/cloud/scripts/per-once/set_up_env_vars.sh
permissions: 0755
content: |
#!/bin/bash
user_uid=1000
user_name="$( getent passwd ${user_uid} | cut -d: -f1 )"
[[ -n ${user_name} ]] && usermod -a -G render,video ${user_name}
mnt_dir="/mnt/.container_sockets"
home_dir="$( getent passwd ${user_uid} | cut -d: -f6 )"
profile="${home_dir}/.profile"
bashrc="${home_dir}/.bashrc"
if [[ -f "${profile}" ]]; then
echo "export DISPLAY=:0" >> "${profile}"
echo "export XAUTHORITY=${mnt_dir}/containercookie" >> "${profile}"
fi
- path: /var/lib/cloud/scripts/per-once/change_gid.sh
permissions: 0755
content: |
#!/bin/bash
user_uid=1000
user_name="$( getent passwd ${user_uid} | cut -d: -f1 )"
user_gid="$( getent passwd ${user_name} | cut -d: -f4 )"
home_dir="$( getent passwd ${user_name} | cut -d: -f6 )"
if [[ -n ${user_name} && ! ${user_uid} == ${user_gid} ]]; then
group_to_move="$( getent group ${user_uid} | cut -d: -f1 )"
if [[ -n ${group_to_move} ]]; then
for gid in {1000..6000}; do
return_value="$( getent group ${gid} )"
if [[ -z ${return_value} ]]; then
groupmod -g ${gid} ${group_to_move}
break
fi
done
fi
users_group="$( getent group ${user_gid} | cut -d: -f1 )"
groupmod -g ${user_uid} ${users_group}
chown -R ${user_uid}:${user_uid} "${home_dir}"
fi
security.nesting: "true"
description: GUI Wayland and xWayland profile with pipewire and pulseaudio, shifting
enabled
devices:
gpu:
gid: "44"
type: gpu
pipewire_manager_socket:
path: /mnt/.container_sockets/pipewire-0-manager
shift: "true"
source: /run/user/1000/pipewire-0-manager
type: disk
pipewire_socket:
path: /mnt/.container_sockets/pipewire-0
shift: "true"
source: /run/user/1000/pipewire-0
type: disk
pulseaudio_socket:
path: /mnt/.container_sockets/native
shift: "true"
source: /run/user/1000/pulse/native
type: disk
x_socket:
bind: container
connect: unix:@/tmp/.X11-unix/X0
listen: unix:@/tmp/.X11-unix/X0
security.gid: "1000"
security.uid: "1000"
type: proxy
xauthority_cookie:
path: /mnt/.container_sockets/containercookie
shift: "true"
source: /home/maary/containercookie
type: disk
name: profile
profile 很长因为混入了 pipewire 的部分,我并没有使用这个的需求但是也懒得再改了(似乎目前也不能正常工作)。
-
第一个关键点在
x_socket
部分的设置,其中connect
部分需要看自己的本地 Xserver 路径。 -
重头戏在于挂载了所谓的
containercookie
到容器的/mnt/.container_sockets/containercookie
下。
这里参考了 x11docker 的 wiki 中 X authentication with cookies and xhost (“No protocol specified” error)。需要使用下面这样的方式生成
containercookie
(名字任意)Cookiefile=~/containercookie Cookie="$(xauth nlist $DISPLAY | sed -e 's/^..../ffff/')" echo "$Cookie" | xauth -f "$Cookiefile" nmerge -
最后在容器的环境里设置 XAUTHORITY,我这里使用了
export XAUTHORITY=/mnt/.container_sockets/containercookie
,省去每次手动设置参数的问题。终于正常运行的 Rviz
最后就是在容器里设置了和宿主机同版本的 Nvidia
驱动,理论上可以用硬件加速了。(我还没试)
总之是 nvidia-smi 能跑了 |
依然没弄完
老生常谈的问题,我现在仍然在 X11 下,Wayland 迟早是要切换的,但不是今天。另外也摸了一下 Sway,从 Regolith 继承下来的 i3 配置大翻车,回头得大改一下,将来的事情了。