Incus 运行 DeepSeek Harness

准备 #

工程配置:

$ incus project show user-1000
config:
  features.images: "true"
  features.networks: "false"
  features.networks.zones: "true"
  features.profiles: "true"
  features.storage.buckets: "true"
  features.storage.volumes: "true"
  restricted: "true"
  restricted.containers.nesting: allow
  restricted.devices.disk: allow
  restricted.devices.disk.paths: /home/king,/run/user/1000
  restricted.devices.gpu: allow
  restricted.devices.proxy: allow
  restricted.devices.usb: allow
  restricted.idmap.gid: "1000"
  restricted.idmap.uid: "1000"
  restricted.networks.access: incusbr-1000
  restricted.snapshots: allow

idmap 配置,参考 映射本地路径:

$ cat /etc/subuid
king:100000:65536
root:1000000:1000000000
root:1000:1
$ cat /etc/subgid
king:100000:65536
root:1000000:1000000000
root:1000:1

需要的目录:

$ mkdir -p ~/Incus/dsh/Default
$ mkdir -p ~/.fonts

创建 profile #

$ incus profile create dsh
$ incus profile edit dsh
config:
  boot.autostart: "true"
  boot.autorestart: "false"
  raw.idmap: both 1000 1000
  cloud-init.vendor-data: |
    #cloud-config
    ssh_genkeytypes: []
    write_files:
      - path: /etc/profile.d/gui.sh
        permissions: '0755'
        content: |
          export LANG=en_US.UTF-8
          export LC_ALL=en_US.UTF-8
          export WAYLAND_DISPLAY=wayland-1
          export PIPEWIRE_REMOTE=unix:/mnt/pipewire-0
          export PULSE_SERVER=unix:/mnt/pulse-native
          if [[ -e /mnt/wayland-1 && ! -e /run/user/$(id -u)/wayland-1 ]]; then
              ln -sf /mnt/wayland-1 /run/user/$(id -u)/wayland-1
          fi
          dbus-update-activation-environment --systemd --all
      - path: /usr/local/bin/dsh-web
        permissions: '0755'
        content: |
          #!/bin/bash
          ulimit -c 0
          if [[ "$(pgrep dsh-web | wc -l)" -gt 2 ]]; then
              echo "dsh-web is already running"
              exit 0
          fi
          pid=$(ss -tlnp | grep 3080 | grep -Po '(?<=pid=)[0-9]+')
          if [[ -n "${pid}" ]]; then
              kill -INT ${pid}
          fi
          coproc dsh {
              dsh web --no-open
          }
          while read line <&"${dsh[0]}"; do
              echo "${line}"
              url=$(echo "${line}" | grep -o 'http://.*token=.*')
              if [[ -n "${url}" ]];then
                  break
              fi
          done
          chromium --enable-features=UseOzonePlatform --ozone-platform=wayland --disable-features=WaylandWpColorManagerV1 --force-color-profile=srgb --app="${url}"
          kill -INT ${dsh_PID} &> /dev/null
          pid=$(ss -tlnp | grep 3080 | grep -Po '(?<=pid=)[0-9]+')
          if [[ -n "${pid}" ]]; then
              kill -INT ${pid}
          fi
    runcmd:
      - loginctl enable-linger arch
      - ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
      - echo -e "en_US.UTF-8 UTF-8\nzh_CN.UTF-8 UTF-8" >> /etc/locale.gen && locale-gen
      - touch /etc/systemd/do-not-udevadm-trigger-on-update
      - echo 'Server = https://mirrors.tuna.tsinghua.edu.cn/archlinux/$repo/os/$arch' > /etc/pacman.d/mirrorlist
      - pacman -Syu --noconfirm
      - pacman -S --noconfirm --needed base-devel pciutils mesa-utils pipewire-audio bash-completion
      - pacman -S --noconfirm --needed git jq unzip zip poppler imagemagick wget ripgrep fd tree bat
      - pacman -S --noconfirm --needed ttf-dejavu wqy-microhei
      - pacman -S --noconfirm --needed nodejs npm pnpm
      - pacman -S --noconfirm --needed chromium foot lxappearance gnome-themes-extra
      - pacman -Scc --noconfirm
      - npm config set registry https://registry.npmmirror.com
      - npm config set allow-scripts=@deepseek-ai/dsh-subprocess-local,koffi,node-pty,@google/genai,protobufjs --location=user
      - npm install -g @deepseek-ai/dsh
devices:
  root:
    path: /
    pool: default
    type: disk
  dsh:
    source: /home/king/Incus/dsh
    path: /home/arch
    type: disk
  fonts:
    source: /home/king/.fonts
    path: /home/arch/.fonts
    readonly: true
    type: disk
  eth0:
    name: eth0
    network: incusbr-1000
    type: nic
  gpu:
    type: gpu
  wayland-socket:
    connect: unix:/run/user/1000/wayland-1
    listen: unix:/mnt/wayland-1
    type: proxy
    bind: instance
    mode: "0700"
    uid: "1000"
    gid: "1000"
    security.gid: "1000"
    security.uid: "1000"
  pipewire-0:
    connect: unix:/run/user/1000/pipewire-0
    listen: unix:/mnt/pipewire-0
    type: proxy
    bind: instance
    mode: "0700"
    uid: "1000"
    gid: "1000"
    security.gid: "1000"
    security.uid: "1000"
  pulse-native:
    connect: unix:/run/user/1000/pulse/native
    listen: unix:/mnt/pulse-native
    type: proxy
    bind: instance
    mode: "0700"
    uid: "1000"
    gid: "1000"
    security.gid: "1000"
    security.uid: "1000"

说明:

loginctl enable-linger arch → 确保用户级 systemd 服务在无登录时存活。

touch /etc/systemd/do-not-udevadm-trigger-on-update → 禁止 systemd 软件包在更新时自动触发 udevadm trigger(会报错)。

创建容器 #

$ incus launch images:archlinux/cloud dsh -p dsh

查看 cloud-init 日志:

$ incus exec dsh -- tail -fn +1 /var/log/cloud-init-output.log
$ incus exec dsh -- tail -fn +1 /var/log/cloud-init.log

查看 cloud-init 运行状态:

$ incus exec dsh -- cloud-init status --long

重启容器:

$ incus restart dsh

配置 #

设置字体和主题:

$ incus exec dsh -- machinectl shell arch@.host /bin/bash -l -c 'lxappearance'

启用鼠标中键粘贴(Chromium 浏览器):

$ incus exec dsh -- machinectl shell arch@.host /bin/bash -l -c 'gsettings set org.gnome.desktop.interface gtk-enable-primary-paste true'

DSH #

安装 dsh-market 插件:

$ incus exec dsh -- machinectl shell arch@.host /bin/bash -l -c 'dsh plugin --profile web add dshmarket'

浏览器打开 dsh web:

$ incus exec dsh -- machinectl shell arch@.host /bin/bash -l -c 'dsh-web'

更新 #

$ incus exec dsh -- pacman -Syu --noconfirm
$ incus exec dsh -- npm install -g @deepseek-ai/dsh