把银河麒麟桌面系统装进容器:一条从 debootstrap → rootfs → docker import 的稳路

让一个完整的桌面系统“住进”容器,同时具备远程可用的图形界面,不是猎奇,是生产力。


如何把银河麒麟桌面版 SP1(UKUI)封装为 Docker 镜像,并在容器内通过 TigerVNC + noVNC 提供桌面访问;最终目标是为后续的“容器化云桌面编排系统”打地基——在一台高性能服务器上批量创建不同发行版、带完整桌面的 Linux 容器,随时随地登录,完成日常上网、办公与开发。


总体思路

  • 黄金路径debootstrap → rootfs → docker import。直接用麒麟官方在线源引导,构造最小根文件系统,再一次性把 UKUI 桌面 + TigerVNC + noVNC 安进去。
  • 关键难点:桌面环境包极多且相互依赖复杂。仅靠手撸 --include 往往不全,导致主题、图标、字体、输入法、会话组件等“缺一角”。务实解法:从麒麟 ISO 抽取安装后事实包清单(manifest),以此构造大而全的 --include 集合,再剔除仓库里已废弃或重命名的包
  • 运行层:容器内用一段入口脚本启动 VNC 会话与 UKUI,novnc_proxy/websockify 提供浏览器端访问。
  • 安全与可运维性:不在公网裸露 VNC/WebSocket,反代 + TLS;/home 做持久化;最小权限运行;镜像瘦身与升级策略明确。

准备与约定

  • 构建机示例:CentOS 9(其他发行版亦可)。需要 Docker 正常可用(docker ps 正常)。
  • debootstrap 安装方式:
    • 若用 Debian/Ubuntu 构建机:apt install debootstrap
    • 若用 RHEL/CentOS:建议直接在临时 Debian 容器内执行 debootstrap,更省事: docker run --rm -it -v /:/host debian:bookworm bash 在容器里把目标 rootfs 写到 /host/… 即可。

下文命令默认在“构建环境”可用 debootstrap/tar/rsync 前提下执行;如你在 CentOS,请按上面建议进入 Debian 容器再执行。


Step 1|安装工具 & 设定套件与镜像源

sudo apt update
sudo apt install -y debootstrap tar xz-utils rsync  # 在 Debian/Ubuntu 环境下

# 套件名(麒麟桌面常见为 10.1 或 v101;本文以 10.1 为例)
export SUITE=10.1

# 麒麟官方总源(可改为你的内网镜像)
export MIRROR="http://archive.kylinos.cn/kylin/KYLIN-ALL"

坑位提示SUITE 与镜像源路径必须匹配。如你使用 v101,但镜像目录只提供 10.1,就会出现 Invalid Releaseno valid components 错误。


Step 2|从 ISO 提取“安装后事实包清单”

目标:构造一个尽可能覆盖 UKUI 全家桶的 --include 列表,从源头避免“桌面缺角”。

  1. 从 ISO 抽出 manifest 文件 mkdir -p /tmp/iso 7z x kylin-desktop.iso casper/filesystem.manifest -o/tmp/iso -y # 如存在“安装时移除的包”清单,也一起抽出(没有就算了) 7z x kylin-desktop.iso casper/filesystem.manifest-remove -o/tmp/iso -y || true
  2. 计算“安装后的包集合” cut -d' ' -f1 /tmp/iso/casper/filesystem.manifest | sort -u > /tmp/live.manifest.pkgs if [ -f /tmp/iso/casper/filesystem.manifest-remove ]; then cut -d' ' -f1 /tmp/iso/casper/filesystem.manifest-remove | sort -u > /tmp/remove.pkgs comm -23 /tmp/live.manifest.pkgs /tmp/remove.pkgs > ./kylin-packages.txt else cp /tmp/live.manifest.pkgs ./kylin-packages.txt fi
  3. 清洗不可用包
    由于官方仓库与 ISO 版本差异,常会有百余个包已废弃或重命名。把仓库里不存在的从清单中剔除,得到 pkgs_clean.txt# 伪代码思路(实际可用 apt-cache 或 curl 目录索引来批量校验) # grep -vxF -f not_found_list.txt kylin-packages.txt > pkgs_clean.txt

这一步是成败关键:用事实包清单覆盖桌面组件,极大降低后续“主题丢失/图标缺失/输入法异常/会话拉不起来”的概率。


Step 3|debootstrap 生成 rootfs(一次装齐 UKUI/VNC/noVNC)

export ROOT=/home/wzg/docker_images/kylin-rootfs_1
sudo rm -rf "$ROOT" && sudo mkdir -p "$ROOT"

export INCLUDE=$(paste -sd, pkgs_clean.txt)

sudo debootstrap \
  --arch=amd64 \
  --variant=minbase \
  --components=main,restricted,universe,multiverse \
  --include="$INCLUDE" \
  --no-check-gpg \
  "$SUITE" "$ROOT" "$MIRROR"

如果提示某些关键包(如 ukui-desktop-environmentnovnc未找到

  • 先临时--include 中去掉它们,让 debootstrap 先完成;
  • 导入为镜像后,在容器内 apt-get update && apt-get install 补齐

Step 4|chroot 做最小初始化(locale、用户、VNC 会话)

sudo mount -t proc none "$ROOT/proc"
sudo mount -t sysfs none "$ROOT/sys"
sudo mount -o bind /dev "$ROOT/dev"

# 写 sources.list(按需换成本地镜像)
sudo chroot "$ROOT" bash -lc "cat >/etc/apt/sources.list <<EOF
deb $MIRROR $SUITE main restricted universe multiverse
EOF"

sudo chroot "$ROOT" bash -lc '
set -e
echo "zh_CN.UTF-8 UTF-8" >> /etc/locale.gen || true
locale-gen || true
update-ca-certificates || true

# dev 用户(免密 sudo)
id dev >/dev/null 2>&1 || useradd -m -s /bin/bash dev
echo "dev ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/dev

# VNC xstartup:优先 UKUI,失败回退 openbox
install -d -o dev -g dev /home/dev/.vnc
cat >/home/dev/.vnc/xstartup <<EOF
#!/bin/sh
export LANG=zh_CN.UTF-8
export LC_ALL=zh_CN.UTF-8
export XDG_CURRENT_DESKTOP=UKUI
export DESKTOP_SESSION=ukui
export XDG_DATA_DIRS=/usr/local/share:/usr/share
export GSETTINGS_SCHEMA_DIR=/usr/share/glib-2.0/schemas
export QT_QPA_PLATFORMTHEME=gtk3
export QT_PLUGIN_PATH=/usr/lib/x86_64-linux-gnu/qt5/plugins

export XDG_RUNTIME_DIR=/run/user/\$UID
mkdir -p "\$XDG_RUNTIME_DIR"
chmod 700 "\$XDG_RUNTIME_DIR" || true

if [ -z "\$DBUS_SESSION_BUS_ADDRESS" ]; then
  eval \$(dbus-launch --sh-syntax)
fi

if command -v ukui-session >/dev/null 2>&1; then
  exec ukui-session
fi

# 退化拉起关键组件
command -v ukui-settings-daemon >/dev/null && ukui-settings-daemon >/dev/null 2>&1 &
command -v ukui-panel >/dev/null && ukui-panel >/dev/null 2>&1 &
command -v peony >/dev/null && peony --daemon >/dev/null 2>&1 &
command -v openbox >/dev/null && openbox >/dev/null 2>&1 &

xterm &
EOF
chown dev:dev /home/dev/.vnc/xstartup
chmod +x /home/dev/.vnc/xstartup
'

经验补充

  • 若日志出现 Gtk-WARNING: cannot find theme engine "murrine",请安装 gtk2-engines-murrine;中文字体缺失请安装 fonts-noto-cjk;必要时补充 adwaita-icon-theme
  • 输入法建议核对 fcitx / ibus 组件是否在清单内。

Step 5|入口脚本(自动生成 VNC 密码,启动 noVNC)

为避免在镜像里硬编码密码,这里在 entrypoint按环境变量生成 ~/.vnc/passwd(若不存在):

sudo tee "$ROOT/usr/local/bin/entrypoint.sh" >/dev/null <<'EOF'
#!/usr/bin/env bash
set -e
: "${VNC_PASSWD:=changeme}"
: "${VNC_RESOLUTION:=1920x1080}"
: "${VNC_DEPTH:=24}"
: "${ENABLE_NOVNC:=1}"   # 1=启用 noVNC(6080),0=只开 VNC(5901)

# 生成 VNC 密码(若不存在)
if [ ! -f ~/.vnc/passwd ]; then
  mkdir -p ~/.vnc
  # TigerVNC 支持从 STDIN 生成加密密码文件
  # 注意不同版本的 vncpasswd 行为有差异,必要时使用两次输入或 x11vnc -storepasswd
  echo -n "$VNC_PASSWD" | vncpasswd -f > ~/.vnc/passwd
  chmod 600 ~/.vnc/passwd
fi

# 启动 VNC :1 → 5901
vncserver -kill :1 >/dev/null 2>&1 || true
vncserver :1 -geometry "${VNC_RESOLUTION}" -depth "${VNC_DEPTH}" -localhost no

# === 启动 noVNC(可选)===
if [ "$ENABLE_NOVNC" = "1" ]; then
  if command -v novnc_proxy >/dev/null 2>&1; then
    novnc_proxy --listen 0.0.0.0:6080 --vnc 127.0.0.1:5901 &
  elif [ -x /usr/share/novnc/utils/novnc_proxy ]; then
    /usr/share/novnc/utils/novnc_proxy --listen 0.0.0.0:6080 --vnc 127.0.0.1:5901 &
  elif command -v websockify >/dev/null 2>&1; then
    websockify --web=/usr/share/novnc 0.0.0.0:6080 127.0.0.1:5901 &
  elif python3 - <<PY 2>/dev/null; then
import websockify
PY
    python3 -m websockify --web=/usr/share/novnc 0.0.0.0:6080 127.0.0.1:5901 &
  else
    echo "[FATAL] 未找到 noVNC/websockify。请安装:apt-get install -y websockify 或 python3-websockify" >&2
    exit 1
  fi
fi

# 常驻
tail -f /dev/null
EOF
sudo chmod +x "$ROOT/usr/local/bin/entrypoint.sh"

Step 6|清理与导入为 Docker 镜像

sudo chroot "$ROOT" apt-get clean || true
sudo umount "$ROOT/proc" "$ROOT/sys" "$ROOT/dev" || true

# 清空将由容器运行期挂载/占用的目录
sudo rm -rf "$ROOT"/{boot,proc,sys,dev,run,tmp}/* 2>/dev/null || true
sudo mkdir -p "$ROOT"/{proc,sys,dev,run,tmp}

# 导入为基础镜像
sudo tar --numeric-owner -C "$ROOT" -c . | docker import - kylin:base-ukui

Step 7|Dockerfile 收口 & 运行

Dockerfile

FROM kylin:base-ukui
ENV VNC_RESOLUTION=1920x1080 VNC_DEPTH=24 VNC_PASSWD=changeme ENABLE_NOVNC=1
EXPOSE 5901 6080
USER dev
WORKDIR /home/dev
CMD ["/usr/local/bin/entrypoint.sh"]

构建与运行

docker build -t kylin:ukui-vnc .
docker run -d --name kylin-ukui \
  -p 5901:5901 -p 6080:6080 \
  --shm-size=2g \
  -v kylin_home:/home/dev \        # 建议持久化用户家目录
  kylin:ukui-vnc

访问方式

  • VNC 客户端 → 主机IP:5901(默认密码 changeme,运行时 -e VNC_PASSWD=... 可改)
  • 浏览器 noVNC → http://主机IP:6080/vnc.html

提示:为保证浏览器/IDE 稳定性,--shm-size=2g 很有用(Chromium/VS Code 内嵌等依赖共享内存)。


运行与加固建议

  • 严禁公网直暴露 5901/6080:置于 Nginx/Traefik 反代后做 TLS+身份认证;或仅内网/VPN。
  • 最小权限:容器内使用普通用户(上文为 dev);必要时加上 readOnlyRootFilesystemno-new-privileges、seccomp 配置(K8s/compose 层实现)。
  • 数据持久化:至少把 /home/dev 做为命名卷或挂载目录,避免容器重建造成数据丢失。
  • 镜像瘦身:debootstrap 后尽量 apt-get clean;可考虑拆分为“基础镜像(系统+桌面)+ 功能层(开发工具、浏览器)”两层。
  • 更新策略:容器内 apt-get update && apt-get upgrade;跨大版本建议重建镜像而非就地升级。
  • 常见告警
    • murrine 主题引擎缺失 → 安装 gtk2-engines-murrine
    • UKUI 不启动 → 检查 ~/.vnc/*.log,核对 startukui / ukui-session 是否就绪,确认 DBus、XDG 变量。
    • noVNC 404/空白 → 核对 novnc_proxywebsockify 路径,并确保 --web=/usr/share/novnc 存在静态文件。

扩展:面向“容器化云桌面”的演进路径

当你手里掌握了可复现的封装方法(本文即为模版),就可以并行收集其他发行版:Ubuntu Kylin、Ubuntu GNOME/KDE、Deepin、UOS 等。建议的演进节奏:

  1. 镜像仓库与元数据:为每个桌面镜像建立版本与构建清单(SUITE、镜像源、包差异、补丁)。
  2. 编排与多租:在服务器上建立云桌面管理系统:创建/启动/销毁、资源配额、状态可视化、审计日志。
  3. 网络与网关:统一的 VNC/WebSocket 网关 与反代策略,支持 SSO、访问控制、限速、TLS。
  4. 存储:用户家目录与工作目录的持久化卷与快照备份策略。
  5. 异构与 GPU:按需为特定容器挂载 GPU、USB 外设;抽象成“能力标签”,由调度器匹配。
  6. 合规与审计:会话级操作审计、文件访问记录、镜像白名单。

当这些基石到位,一个“看起来像云桌面,实则是可编排的容器集群”就成型了。


结语

我们不是把一个系统“缩小”进容器,而是把确定性可重复封装成了一个工艺流程。
debootstrap 把系统“装”进目录,docker import 把目录“装”进镜像;一段入口脚本,让 UKUI 与 noVNC 在容器里亮起,像夜航船上一盏稳灯,照见你在任何地方的开发与运维。
先把一件事做到“可复现”,再谈“可规模化”。 银河麒麟这条线走通了,其它发行版不过是时间问题。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注