VMware Horizon 与 Docker 冲突排错记录

因为开发需要使用 带界面的仿真软件,而以往的使用习惯基本都是 SSH 登录虚拟机即可满足需求。

但这次遇到两个现实问题:

  • 现有虚拟机系统为 Ubuntu 18.04
  • 仿真软件要求 Ubuntu 22.04 + GUI

最终选择的方案是:

  • 基于 Docker 构建一套 Ubuntu 22.04 + 仿真软件 的镜像
  • 运行在原有虚拟机中
  • 结合 VMware Horizon,通过浏览器访问桌面,实现软件可视化操作

也正是在这个组合下,踩到了 Docker 与 Horizon 的一个隐藏但非常典型的坑

也尝试过使用 MobaXterm 弹窗,根本弹不了远程虚拟机上的容器程序,AI 说可以是骗人的。


一、Horizon 是什么

VMware Horizon 本质上是一套 VDI(虚拟桌面基础架构) 解决方案。

简单理解就是:

  • 后端运行着大量云服务器 / 虚拟机
  • 虚拟机具备完整桌面环境(Linux / Windows)
  • 用户可通过 浏览器(Web)或客户端 访问远程桌面

在此前的实际使用中:

  • 日常运维、开发:SSH 完全够用
  • Horizon 桌面更多是"存在但几乎不用"的状态

但当 GUI 仿真工具 加入后,情况就完全不同了。


二、问题现象:安装 Docker 后,Horizon Web 桌面不可用

在虚拟机中安装 Docker 并启动后,出现了一个非常反直觉的现象:

  • SSH 登录、命令行操作 一切正常
  • Horizon Web 桌面无法打开

浏览器直接报错:

复制代码
An error has occurred:
{"errno":"ETIMEDOUT","code":"ETIMEDOUT","syscall":"connect","address":"172.17.0.1","port":22443}

关键点非常集中:

172.17.0.1:22443

172.17.0.1,正是 Docker 默认创建的 docker0 网桥地址。


三、问题根因:Docker 默认网桥优先级与 Horizon 网络路径冲突

1️⃣ 先厘清:Horizon Web 桌面是如何建立连接的?

Horizon Web(Blast / HTML Access)并不只有一种连接模式,常见有两种:

模式一:浏览器直连虚拟机(Direct Connection)

  • 浏览器直接访问:虚拟机IP:22443

  • Blast 服务监听地址为:

    0.0.0.0:22443

  • 网络路径:

    浏览器 → 虚拟机 → Blast Server

模式二:通过中转服务器访问(Blast Secure Gateway / Unified Access Gateway)
  • 浏览器先连接 Horizon 中转服务器

  • 由中转服务器 回连虚拟机的 Blast 服务端口

  • 网络路径:

    浏览器 → Horizon 中转服务器 → 虚拟机 → Blast Server

本次问题环境使用的是 第二种(中转模式),该模式可在 Horizon 管理台中配置。

2️⃣ 为什么会"连到" 172.17.0.1?

需要特别强调的是:

这并不是 Blast 监听地址配置错误,而是 Linux 系统选路问题。

Blast 服务本身监听的是:

复制代码
0.0.0.0:22443

这意味着:

  • Blast 并不绑定某个具体 IP
  • 实际使用哪个 IP,对它来说取决于 操作系统的网络选路结果

真正出问题的地方在于:

Linux 在选择"对外通信地址"时,选错了网卡。

3️⃣ Docker 实际造成了什么影响(真正的根因)

Docker 启动后会自动完成以下操作:

  • 创建 docker0 网桥

  • 默认分配地址:

    172.17.0.1/16

  • 同时调整 系统路由表与 iptables 规则

在部分环境中,会出现以下情况:

  • docker0 网卡在路由或接口优先级上
    高于真实业务网卡
  • 系统在选择"回包路径"或"源地址"时
    优先选用了 docker0 的地址(172.17.0.1)

于是,实际发生的网络行为变成了:

复制代码
Horizon 中转服务器 → 虚拟机
                     ↳ 系统选路:docker0 (172.17.0.1)
                     ↳ Blast :22443

结果就是:

  • Horizon 中转服务器尝试连接 172.17.0.1:22443
  • 该地址仅在虚拟机本地 Docker 网桥存在
  • 对外部网络完全不可达

最终表现为:

复制代码
ETIMEDOUT

4️⃣ 补充:虚拟机访问地址的来源

需要注意的是:

虚拟机的访问地址是由 Horizon Agent 上报给 Connection Server / Gateway 的

当系统在某些场景下:

  • 选用了 docker0 对应的地址作为"对外地址"

那么 Agent 上报的地址本身就是不可达的,这也是问题产生的根源之一。

5️⃣ 结论

这并不是:

"Docker 抢占了 Blast 的端口"

而是:

Docker 创建的 docker0 网桥,在系统选路/接口优先级上高于真实网卡,
导致 Horizon 中转回连流量被引导到一个外部不可达的地址。

这也解释了为什么:

  • SSH 完全正常
  • 虚拟机本身运行无异常
  • 只有 Horizon Web 桌面无法访问

因为只有 Horizon(尤其是中转模式)

👉 强依赖"系统最终选择哪个 IP 作为对外通信地址"。


四、解决方案(可行但偏工程化)

最终采用的解决方式并不优雅,但非常稳定:

删除 docker0 → 登录 Horizon 桌面 → 重启 Docker

bash 复制代码
ip link set docker0 down
ip link delete docker0

# 用户成功登录 Horizon 桌面后
systemctl restart docker
# docker0 会被重新创建

后续断开并重新连接 Horizon 桌面也可以正常访问(可能与会话 / 地址缓存有关)。

并不是所有虚拟机都会触发该问题

且实际需要桌面(GUI 仿真)的用户数量有限

同时虚拟机 IP 均为 DHCP 分配,无法批量在 Horizon Agent 中固定地址

因此最终选择了这种 最小改动、可控范围内的工程方案

关于修改 Docker 网桥地址的误区

曾尝试通过修改 Docker 网关配置:

json 复制代码
"bip": "10.99.99.1/24"

来规避 172.17.0.1,但实践发现:

  • 问题并未彻底解决
  • 根因并非"占用了 172.17.0.1 本身"
  • 而是 docker0 作为接口参与系统选路时的优先级问题

另外还踩过一个小坑:

  • Horizon 服务未停止时
  • Docker 启动阶段可能仍会受到影响

最终采用的顺序是:

  1. 停止 Horizon Web / Agent 服务
  2. 启动 Docker
  3. 再启动 Horizon

五、另一类常见报错的排查思路

如果不是 172.17.0.1 相关报错,而是:

"无法进入桌面,请联系管理员"

则可以按以下顺序排查。

检查 Horizon Agent 服务

bash 复制代码
systemctl status viewagent

必要时重启:

bash 复制代码
systemctl restart viewagent

检查 Blast 服务端口

bash 复制代码
netstat -tlnp | grep 22443

期望看到:

复制代码
tcp 0 0 0.0.0.0:22443 LISTEN xxx/VMwareBlastS

检查 iptables 影响

Docker 会自动修改 iptables,可能影响 Blast 通信。

可临时验证:

bash 复制代码
iptables -F
iptables -L

仅用于排错验证,生产环境请谨慎

如果最终浏览器访问 Horizon 出现:

复制代码
ETIMEDOUT 172.17.0.1:22443

基本可以确认仍是 docker0 选路问题,按前述方式处理即可。


六、Docker + GUI:DISPLAY

桌面能正常访问,并不代表容器里的 GUI 程序就能显示。

宿主机(虚拟机)准备

bash 复制代码
echo $DISPLAY

例如:

复制代码
:102

并执行:

bash 复制代码
xhost +

仅限内网 / 临时调试使用

Docker 启动示例

bash 复制代码
docker run --net=host --rm -it \
  -e DISPLAY=$DISPLAY 镜像 bash

测试

bash 复制代码
apt install -y x11-apps
xclock

能够弹窗即说明 GUI 显示链路打通。


七、批量环境治理

既然 Docker 在后续场景中是必需组件,那就统一在虚拟机上提前安装,并批量为现有用户授予 Docker 使用权限,避免后续重复配置和权限问题。

bash 复制代码
ansible 虚拟机组 -m shell -a 'apt install -y docker.io'

ansible 虚拟机组 -m shell -a \
'for user in $(ls /home | grep -vE "^(public|gerrit|lost\+found)$"); do
  usermod -aG docker "$user" && echo "用户 $user 已加入 docker 组"
done'

总结

本文问题本质并非端口或服务异常,而是 Docker 默认网桥参与系统选路,导致 Horizon 回连路径选错接口。

在依赖回连机制的 VDI 场景中,Docker 网络与桌面访问路径必须明确隔离或有序控制。

相关推荐
阿拉伯柠檬2 小时前
应用层协议HTTP
linux·网络·c++·网络协议·http
小李独爱秋2 小时前
Linux操作系统实验报告单(13) 显示进程列表
linux·运维·服务器·操作系统·实验报告单
凸凹恼2 小时前
【每天一个小笔记】01 Docker 部署项目
笔记·docker
阿拉伯柠檬2 小时前
MySQL基本查询
linux·数据库·mysql·面试
Ha_To2 小时前
2025.12.24 Cisco防火墙ASA与动态PAT配置
linux·服务器·网络
艾莉丝努力练剑2 小时前
Al Ping免费上新:GLM-4.7 && MiniMaxM2.1重磅上线,附独家使用教程
java·大数据·linux·运维·人工智能·python
代码游侠3 小时前
学习笔记——TCP 传输控制协议
linux·网络·笔记·网络协议·学习·tcp/ip
莫渊博-天下无病3 小时前
node高版本安装
运维·node.js
zhendianluli3 小时前
为什么fclose处理的是file而不是fd
linux·服务器·网络