鸿蒙 PC 能跑 Docker 吗?一次从安装失败到成功运行的实测记录

鸿蒙 PC 的融合开发引擎提供了 openEuler 环境,能不能在里面安装 Docker,用来运行开发服务?

这次实测的答案是:可以。我们在 openEuler 中安装了 Moby 25.0.3,并成功拉取、运行 ARM64 版 hello-world 容器。 不过,直接执行 dnf install docker 并没有一次成功,中间遇到了旧引擎与 cgroup v2 不兼容,以及 Docker Hub 访问超时两个问题。

一、测试环境

本次使用的是融合开发引擎内的 openEuler,而不是鸿蒙原生终端。

项目 实测信息
操作系统 openEuler 24.03 LTS-SP3
架构 aarch64(ARM64)
内核 Linux 6.6.0
cgroup v2
最终引擎 Moby 25.0.3
Compose 插件 v5.5.0,版本命令验证成功
存储驱动 vfs

下面所有命令,都在融合开发引擎的 openEuler 终端中执行。这份记录针对本次环境,不代表所有融合开发引擎版本和容器应用都已验证兼容。

二、第一处坑:装上 Docker,不代表引擎能启动

最初通过系统软件源安装:

bash 复制代码
sudo dnf install -y docker
sudo systemctl enable --now docker

安装事务成功,但实际得到的是 docker-engine 18.09.0-354.oe2403sp3,启动时却失败了。查看日志:

bash 复制代码
sudo journalctl -u docker.service -b -n 120 --no-pager -o cat

真正导致退出的是这句:

text 复制代码
Error starting daemon: Devices cgroup isn't mounted

进一步执行以下检查,发现 /sys/fs/cgroup 的类型为 cgroup2fs,系统只有 cgroup v2 挂载:

bash 复制代码
stat -fc %T /sys/fs/cgroup
findmnt -t cgroup,cgroup2
cat /sys/fs/cgroup/cgroup.controllers

问题并非简单的"少了一个目录",而是已安装的引擎在寻找 cgroup v1 的 devices 挂载点。cgroup v2 不在 cgroup.controllers 中列出 devices,这是正常现象,不应通过手动创建目录解决。 Docker 上游从 20.10 开始支持 cgroup v2,本次实际安装的旧分支未能适配该环境。

三、解决办法:使用 openEuler 仓库中的 Moby

不修改内核启动参数,也不混用 CentOS 软件源,我们选择了同一 openEuler 仓库提供的 Moby 25.0.3 。Moby 是 Docker Engine 的开源上游,安装后仍使用 docker 命令。

先查询软件包,并预览替换计划:

bash 复制代码
sudo dnf info moby moby-engine moby-client
sudo dnf install moby --allowerasing --assumeno

本次预览显示:安装 Moby、containerd、runc 等 10 个包,只移除旧 docker-engine。确认清单后正式执行:

bash 复制代码
sudo dnf install moby --allowerasing

已有容器业务的机器,应先备份配置和业务数据,再评估迁移;不要直接照搬卸载操作,也不要删除 /var/lib/docker

全新环境可以优先查询并安装 moby,避免先装旧引擎再替换。

安装完成后,重新加载服务配置并启动 Docker:

bash 复制代码
sudo systemctl daemon-reload
sudo systemctl enable --now docker
sudo docker version
sudo docker info

本次最终能同时看到 25.0.3 的 Client 和 Server,且显示:

text 复制代码
Cgroup Driver: systemd
Cgroup Version: 2

过程中曾出现独立 containerd.service 启动失败,但 Docker 服务端仍能响应,后续也成功运行容器。这项独立服务异常尚未单独定位,不能把它描述成已修复,也不应盲目反复启动或停用该服务。

四、第二处坑:Docker Hub 连接超时

引擎启动后,运行:

bash 复制代码
sudo docker run --rm hello-world

出现访问 https://registry-1.docker.io/v2/ 超时。此时应排查镜像仓库网络,而不是重新安装 Docker。

本次按用户选择,将 https://docker.1panel.live 加入 /etc/docker/daemon.jsonregistry-mirrors已有配置应先备份,并在原 JSON 对象中合并该字段,不能覆盖其他设置。 如果原文件不存在或为空配置,完整内容可写为:

json 复制代码
{
  "registry-mirrors": ["https://docker.1panel.live"]
}

保存后校验、重启并确认生效:

bash 复制代码
sudo dockerd --validate --config-file=/etc/docker/daemon.json &&
sudo systemctl restart docker
sudo docker info --format '{{json .RegistryConfig.Mirrors}}'
sudo docker run --rm hello-world

本次镜像成功拉取,容器输出了:

text 复制代码
Hello from Docker!
This message shows that your installation appears to be working correctly.

这说明客户端通信、镜像下载、容器创建和程序执行的基本链路已经跑通。该地址是第三方服务,本次可用不代表长期可用或适合生产,使用前应评估其可信度。

五、补齐 Docker Compose:引擎可用不等于插件已安装

运行 hello-world 成功后,继续执行 sudo docker compose up -d,却出现:

text 复制代码
unknown shorthand flag: 'd' in -d

再用版本命令检查:

bash 复制代码
sudo docker compose version

输出:

text 复制代码
docker: 'compose' is not a docker command.

这说明当前 Docker CLI 没有识别到 Compose 插件,不是 -d 写错,也不是需要重装 Docker 引擎。 docker compose 是 CLI 插件用法,与带横线的 docker-compose 命令不能简单画等号。

1. 查询软件源,确定安装路线

bash 复制代码
dnf search compose

本次软件源能查到 docker-compose.noarch,但仅凭包名不能确认它的版本及是否提供 CLI 插件。因此本次没有安装这个包,而是采用官方发布的 Linux ARM64 Compose 插件 ,最终实测版本为 v5.5.0。这不是所有环境都必须使用的版本,也不表示与所有 Docker Engine 版本均已验证兼容。

2. 下载插件:使用固定路径,方便断点续传

下面是根据本次操作整理的复用命令。与实测最初使用随机临时目录不同,这里在 openEuler 用户缓存下使用固定的、带版本号的目录,避免关闭终端后丢失变量或误复制示例路径。

先确认下载依赖可用:

bash 复制代码
curl --version
rpm -q ca-certificates

如果缺少依赖,再通过 sudo dnf install curl ca-certificates 安装并检查事务清单。本次安装依赖时,DNF 同时升级了 curl、libcurl 和 ca-certificates;这说明安装命令也可能更新已安装的软件包,不能把它当成纯检查命令。

下载 ARM64 文件:

bash 复制代码
mkdir -p "$HOME/.cache/docker-compose/v5.5.0"

curl -fL -C - --connect-timeout 30 \
  "https://github.com/docker/compose/releases/download/v5.5.0/docker-compose-linux-aarch64" \
  -o "$HOME/.cache/docker-compose/v5.5.0/docker-compose"
  • 文件名为 docker-compose-linux-aarch64,不是 x86_64
  • -f 使 HTTP 错误返回失败,-L 跟随下载重定向。
  • -C - 根据本地已有文件大小尝试断点续传。
  • 不设置总下载时长,避免慢速下载因达到固定总时限而被中止。
  • 镜像加速地址 docker.1panel.live 不会加速 GitHub 二进制文件下载。

3. 下载超时与路径错误的实际排查

最初下载命令设置了 --max-time 600。文件总大小约 44.3 MiB,下载到约 16.2 MiB 时触发了十分钟总时限:

text 复制代码
curl: (28) Operation timed out after 599354 milliseconds
with 17068712 out of 46470638 bytes received

这不是终端会话断线,也不意味着必须从头下载。 保留部分文件,去掉总时限,使用 curl -C - 即可尝试续传。如果采用上面的固定目录,网络中断后重新执行同一条下载命令即可;若服务器不支持续传,应先检查错误,不要盲目覆盖现有文件。

实测续传时还发生过一次路径错误:把 /tmp/tmp.ABC123/docker-compose 这样的示例路径直接复制进命令,导致:

text 复制代码
curl: (23) Failure writing output to destination

这个错误表示输出路径不可写或不存在,并不表示远端文件损坏。改为实际部分文件所在路径后完成续传。示例路径不能直接当作实际路径使用;已有部分文件也不能仅因出现下载错误就当成完整文件安装。

4. 下载完成后安装到系统级目录

确认 curl 正常退出、下载达到 100%,再执行:

bash 复制代码
sudo install -d -m 0755 /usr/local/lib/docker/cli-plugins &&
sudo install -m 0755 "$HOME/.cache/docker-compose/v5.5.0/docker-compose" \
  /usr/local/lib/docker/cli-plugins/docker-compose &&
sudo docker compose version

上述安装命令与本节固定缓存下载路径配套。如果沿用此前的临时文件,源路径必须改成已下载完成的真实文件路径。已有同名插件时,应先检查版本并备份,再决定是否覆盖。本次实测未另行记录发布文件的校验和核验,因此不能宣称已完成供应链完整性验证;正式环境应另按官方发布校验信息验证文件。

选择 /usr/local/lib/docker/cli-plugins 是为了系统级安装,避免仅装在普通用户的 ~/.docker/cli-plugins 后,sudo docker 找不到插件。安装 CLI 插件不需要重启 Docker。

本次安装后的真实输出为:

text 复制代码
Docker Compose version v5.5.0

至此,Compose 插件安装和 CLI 加载验证成功,但还不能据此认定实际 Compose 项目已成功部署。 手动安装的插件不会随 DNF 软件包更新自动升级,需要自行维护。

5. 后续如何验证项目

以下是后续操作指引,并非本次已完成的实测结果。进入实际包含 compose.yamldocker-compose.yml 的项目目录,再执行:

bash 复制代码
sudo docker compose config --quiet &&
sudo docker compose up -d

sudo docker compose ps
sudo docker compose logs --tail=50

config --quiet 用于检查配置,up -d 会真正创建或更新并启动服务。没有 Compose 配置文件时,不要直接在用户主目录执行。若实际部署出现 API 版本不兼容,应根据错误选用匹配的 Compose 或引擎版本,而不是把版本命令成功当作完整兼容性验证。

六、重启后的 Docker 临时恢复脚本

后续重启验证发现,系统处于 SELinux Enforcing 模式时,部分文件标签与策略不匹配,导致 systemctl 异常、Docker 查询超时。定点修复后 Docker API 恢复响应;再次重启时,//etc/usr/etc/ld.so.cache 的修复标签保留,但 /dev/null 又变成了 xserver_misc_device_t,本机策略期望的是 null_device_t

下面将这次排查中使用的恢复步骤整理成脚本。只适用于本文已诊断的融合开发引擎 openEuler 环境,不是通用修复脚本,也不是永久修复。 不要因为其他机器的 Docker 启动失败就直接使用。

1. 保存和运行

将下面完整代码保存为 recover-docker.sh,放入 openEuler。在该文件所在目录打开 openEuler 终端执行:

bash 复制代码
sh recover-docker.sh

无需先设置执行权限,脚本需要时会请求 sudo 授权。它会调整明确列出的五个路径的 SELinux 标签,并提交 Docker 启动请求;不是只读检查。不要添加递归参数 -R-r,也不要在鸿蒙原生终端运行。

2. 完整脚本

以下内容与单独交付的 recover-docker.sh 一致,不包含 Consul 或其他指定容器操作。

sh 复制代码
#!/bin/sh
# 用于已排查的融合开发引擎 openEuler 环境,非通用 Linux 修复脚本。
# 临时恢复已确认异常的 SELinux 标签并启动 Docker,不负责永久修复。
# 不操作任何指定容器,不安装软件,不删除数据,不关闭 SELinux。
# Docker 启动时仍可能按已有容器的 restart 策略自动恢复容器。
set -eu

if [ "$(id -u)" -ne 0 ]; then
    exec sudo /bin/sh "$0" "$@"
fi

for cmd in restorecon systemctl timeout docker getenforce; do
    if ! command -v "$cmd" >/dev/null 2>&1; then
        printf '缺少必要命令:%s。已停止,未安装任何软件。\n' "$cmd" >&2
        exit 1
    fi
done

printf '此脚本仅适用于此前已确认标签异常的 openEuler 环境。\n'
printf '\n[1/3] 定点恢复 SELinux 标签(不递归)\n'
restorecon -v / /etc /usr /dev/null /etc/ld.so.cache
printf '当前 SELinux 模式:'
getenforce

printf '\n[2/3] 清除 Docker 失败状态并提交启动请求\n'
# 外层 timeout 防止服务管理请求长时间阻塞。
if timeout 15s systemctl reset-failed docker.service; then
    :
else
    rc=$?
    printf '清除失败状态未成功,退出码=%s。已停止。\n' "$rc" >&2
    exit 1
fi
if timeout 15s systemctl start --no-block docker.service; then
    :
else
    rc=$?
    printf 'Docker 启动请求未成功,退出码=%s。已停止。\n' "$rc" >&2
    exit 1
fi

printf '\n[3/3] 等待 Docker API 就绪(最多检查 12 次)\n'
attempt=1
while [ "$attempt" -le 12 ]; do
    if timeout 5s docker --host unix:///var/run/docker.sock info >/dev/null 2>&1; then
        printf '\nDocker 本地 API 已恢复响应。\n'
        printf '未执行任何容器启动、停止、删除或重建命令。\n'
        printf '注意:已有容器可能按 restart 策略随 Docker 自动启动。\n'
        printf '此结果不代表业务健康或重启标签问题已永久解决。\n'
        exit 0
    fi
    printf '检查 %s/12:Docker 暂未就绪。\n' "$attempt"
    if [ "$attempt" -lt 12 ]; then
        sleep 2
    fi
    attempt=$((attempt + 1))
done

printf '\nDocker 本地 API 未在等待窗口内就绪,已停止。\n' >&2
printf '未重装软件、删除数据或停止已提交启动的 Docker 服务。\n' >&2
printf '请在 openEuler 终端手动检查:\n' >&2
printf '  sudo systemctl status docker --no-pager -l\n' >&2
printf '  sudo journalctl -u docker -b -n 80 --no-pager -o cat\n' >&2
printf '若命令无输出,请同时检查 SELinux 拒绝日志。\n' >&2
exit 1

3. 如何判断结果及适用边界

  • 出现"Docker 本地 API 已恢复响应"且退出码为 0,说明本地引擎能响应查询,不代表业务健康。
  • 脚本已通过 sh -n 语法检查;组成步骤有对话中的实测依据,但尚未收到整份脚本在目标虚拟机完整运行的验证结果。
  • 脚本不操作指定容器,但 Docker 启动时可能根据已有容器的 restart 策略自动启动容器。
  • 脚本不会关闭 SELinux、递归重标记系统、安装软件或删除数据。中途失败不会自动撤销此前已成功的标签修复或启动请求。
  • 如果服务查询仍无输出,需检查 SELinux 拒绝日志,不能将"没有输出"视为成功。
  • 此脚本不修复 journald、不改容器日志驱动,也不解决系统其他位置的标签或策略不匹配。标签复发的启动层原因仍未定位。

七、跑通之后,还要注意什么?

1. 优先选择 ARM64 镜像

本次运行的是 arm64v8 版本,不能假设所有仅支持 AMD64 的应用都能直接运行。

2. 留意存储驱动

本次实际使用 vfs,基本功能可用,但通常更占磁盘、效率较低。是否可以使用 overlay2,还需要检查内核和文件系统,不能直接改配置了事。

3. 容器端口不等于鸿蒙本机端口

-p 8080:80 首先映射到 openEuler 环境,鸿蒙侧或局域网能否访问,还取决于融合开发引擎的网络与转发配置,本次尚未验证。

结语

这次实测证明:鸿蒙 PC 可以借助融合开发引擎内的 openEuler 运行 Docker 容器。 关键不是反复重装,而是先从日志确认失败点:旧引擎遇到 cgroup v2,就换用适配的 Moby;镜像拉取超时,就处理仓库网络。

最终 hello-world 已成功运行,Compose v5.5.0 插件也已安装并通过版本命令验证。此次补充排查还解决了插件缺失、GitHub 下载超时和续传路径填写错误的问题。

验证项 当前结论
Docker Engine 25.0.3 已运行,Client 与 Server 正常通信
cgroup v2 引擎已正常识别
hello-world ARM64 镜像拉取、容器执行成功
Compose v5.5.0 系统级插件安装成功,sudo docker compose version 可用
Compose 实际项目 后续已通过 Compose 重建业务容器并达到 running;应用健康仍需独立验证
独立 containerd.service 异常 尚未单独定位
重启恢复 已发现 SELinux 标签复发,手动定点修复后 Docker 响应恢复;永久修复未完成
recover-docker.sh 已整理并通过语法检查,整份脚本尚未在目标虚拟机实跑验证
overlay2、鸿蒙侧及局域网端口访问 尚未验证

因此,准确的结论是:Docker 基本运行链路已跑通,Compose 插件已补齐,但重启稳定性、系统策略适配和业务健康不能视为全部通过。第六节脚本只是当前环境的临时恢复工具。

参考资料

相关推荐
Latte Moments开发1 小时前
Harmony鸿蒙实战开发-浏览器app【源码在文末】
华为·harmonyos
用户8181870627461 小时前
第27章 消息丢失/重复消费的全链路排查(生产者→Broker→消费者)
java·后端
程序员cxuan1 小时前
ChatGPT 开启无限 token
人工智能·后端·程序员
Gopher_HBo1 小时前
beego ORM 源码(上):模型元数据与注册
后端
柒儿吖1 小时前
SSCom 重构全记录:用 Rust 把串口调试助手送上鸿蒙 PC、macOS、Windows和Linux
测试工具·rust·harmonyos
索隆zoro1 小时前
Army 对 jOOQ
java·后端
李游Leo1 小时前
HarmonyOS 7 端侧 AI 视觉能力实战 06:把识别、增强与搜索串成完整处理链
harmonyos
泡海椒1 小时前
规则热更新实现:JQuick-Java无需重启更新业务规则实战
后端
大雷神1 小时前
【共创稿事节】ArkGraphics 3D开发环境安装实战:从官网下载编辑器,到 DevEco Studio 离线装插件
harmonyos