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 网络与桌面访问路径必须明确隔离或有序控制。

相关推荐
有续技术15 分钟前
哈斯 (Haas) 机床 IP、端口号配置内容总结
运维·服务器
小张同学a.22 分钟前
LAMP架构2
linux·运维·网络·架构·负载均衡
千河殇 多想和她24 分钟前
如何在Reaonix中使用CodeGraph以及CodeGraph效果实测经验分享
linux·运维·ubuntu
VortMall25 分钟前
出海引流运维双升级|VortMall微服务商城系统v1.3.12 升级更新
运维·微服务·facebook
心念枕惊26 分钟前
新写了个直播录制工具,可录制抖音快手斗鱼直播
运维·服务器·数据库
躺不平的理查德37 分钟前
CMAKE备忘录
linux·运维·服务器
9624561 小时前
跨域与私有网络访问限制排查实录
运维·服务器·网络
雨辰AI1 小时前
K8s 达梦数据守护集群部署|容器化高可用生产落地指南
云原生·容器·kubernetes
发量惊人的中年网工1 小时前
SD-WAN如何赋能工业物联网?打造工厂网络远程连接和数据传输新方案
运维·网络·分布式·物联网
面对疾风叭!哈撒给1 小时前
Linux ARM架构的docker和docker-compose离线安装
linux·docker·架构