使用 Docker 部署 DotNetBrowser 应用程序

DotNetBrowser 是一个基于 Chromium 的 .NET WebView。它允许您在 .NET 桌面应用中嵌入现代网页内容,并通过代码对其进行控制。

与普通的 .NET 应用不同,运行 Chromium 的应用依赖一些系统级组件,这些组件并不包含在您的项目中。应用可能在您的电脑上运行良好,但在一个干净的服务器或 CI 环境中却无法运行。

Docker 通过将应用程序与其所需的完整运行环境一起打包来解决这个问题。本文将介绍如何使用 Docker 部署 DotNetBrowser 应用。

为什么选择 Docker

DotNetBrowser 构建于 Chromium 之上,这意味着它继承了 Chromium 的系统要求。在 Linux 上,这包括原生库和用于渲染内容的 X 服务器。在典型的桌面设置中,这些部分已经由操作系统提供。但在 CI 或服务器环境中,它们通常是缺失的。

Docker 允许您显式地定义和打包环境。这使您的应用程序更具可预测性,并减少了经典的"在我机器上能跑"的问题。当您需要在官方支持列表之外的 Linux 发行版上运行时,它也能派上用场。

准备工作

我们将构建一个小型的 .NET 应用程序,并使用 Docker 进行打包。要跟着操作,您需要:

创建 DotNetBrowser 应用

对于本例,我们将使用以下项目结构:

text 复制代码
Example.Docker/
├── <App files>            # 应用程序源文件
├── Dockerfile             # Docker 构建文件
└── startup.sh             # 用于启动应用的脚本

让我们从创建一个简单的 Avalonia 桌面应用开始。为了简化操作,我们将使用官方的 DotNetBrowser Avalonia 模板,而不是从头开始设置一切。

首先,安装模板:

bash 复制代码
dotnet new install DotNetBrowser.Templates

然后创建一个新项目:

bash 复制代码
dotnet new dotnetbrowser.avalonia.app -o Example.Docker -li <your_license_key>

这将在 Example.Docker 文件夹中生成一个可立即运行的 Avalonia 应用程序。开箱即用,它会打开一个窗口并在 DotNetBrowser 中加载一个网页。

由于应用将在 Docker 容器中运行,我们需要一种方式来确认它在没有可见窗口时也能正常运行。可以在页面加载完成后将网页标题打印到控制台。

MainWindow.axaml.cs 中添加代码:

csharp 复制代码
// 页面加载完成后打印标题。
browser.Navigation.FrameLoadFinished += (_, e) =>
{
    if (e.Frame.IsMain)
    {
        Console.WriteLine($"Title: {browser.Title}");
    }
};

// 加载网页。
browser.Navigation.LoadUrl("https://www.google.com");

完成此操作后,应用程序就可以打包了。下一步是生成 Release 构建:

bash 复制代码
dotnet publish -c Release -o out

生成的 out/ 目录将在运行时被容器使用。

创建 Dockerfile

现在,我们需要定义应用程序运行的环境。在 Docker 中,这是通过 Dockerfile 完成的。

我们的目标是构建一个包含以下内容的镜像:

  • .NET 运行时
  • Chromium 的 Linux 原生依赖项
  • 一个虚拟显示服务器
  • 已发布的应用程序文件

让我们一步步构建它。

基础环境配置

以基础镜像开始您的 Dockerfile

dockerfile 复制代码
FROM mcr.microsoft.com/dotnet/runtime:8.0

此镜像基于 Debian,并且已经包含了 .NET 运行时,因此无需手动安装。

请记住,Chromium 依赖于 glibc。因此,您不能使用 Alpine 或任何其他依赖于 musl 或其他标准 C 库的 Linux 发行版。

接下来,为安装软件包设置一个非交互式环境:

dockerfile 复制代码
ENV DEBIAN_FRONTEND=noninteractive

这可以确保 apt-get 在安装过程中不会暂停并等待输入。

安装 Chromium 依赖项

Chromium 依赖于几个原生 Linux 库。它们负责处理窗口、图形、声音和安全性等基本系统功能。在最小的 Docker 镜像中,这些库默认不会安装。

dockerfile 复制代码
RUN apt-get update && \
    apt-get -y install --no-install-recommends \
        libgtk2.0-0 \
        libgtk-3-0 \
        libgbm1 \
        libnotify4 \
        libgconf-2-4 \
        libnss3 \
        libxss1 \
        libasound2 \
        libxtst6 \
        xauth \
        xvfb \
        x11-xserver-utils && \
    rm -rf /var/lib/apt/lists/*

我们还安装了 Xvfb,一个轻量级的虚拟 X 服务器。它允许 DotNetBrowser 在没有物理显示器可用时运行。

添加应用程序

现在环境已准备就绪,我们可以添加应用程序了。我们需要定义它的启动方式并将其文件复制到容器中。

  1. 创建启动脚本。

我们不直接启动应用程序,而是使用一个小的包装脚本:

bash 复制代码
#!/bin/sh

set -e

if [ -z "${DISPLAY:-}" ]; then
  Xvfb :0 -screen 0 1920x1080x24 &
  export DISPLAY=:0
fi

exec dotnet Example.Docker.dll

此脚本通过 DISPLAY 变量检查是否有 X 服务器可用:

  • 如果未设置 DISPLAY,它会启动一个虚拟显示器,并将应用程序指向它。
  • 如果设置了 DISPLAY,则假定有真实的 X 服务器可用并正常运行。

即使显示器是虚拟的,Chromium 也会像对待真实屏幕一样对待它。所以我们使用 -screen 选项设置分辨率和色深。

  1. 将脚本复制到镜像中:
dockerfile 复制代码
WORKDIR /Example.Docker

COPY startup.sh .
RUN chmod +x startup.sh

我们设置一个工作目录,复制脚本,并使其可执行。

  1. 复制先前发布的应用程序文件:
dockerfile 复制代码
COPY out/ .
  1. 定义入口点:
dockerfile 复制代码
ENTRYPOINT ["/Example.Docker/startup.sh"]

这告诉 Docker 在容器启动时执行什么。

至此,Docker 镜像已准备就绪。下一步是构建它并观察其运行。

构建并运行容器

确保 Docker Engine 正在运行,然后构建镜像:

bash 复制代码
docker build -t dnb-app -f Dockerfile .

运行容器通常有两种方式:

  • 无头模式 --- 适用于自动化、测试和服务器端运行,不需要用户界面。
  • 桌面模式 --- 适用于希望查看应用程序窗口并进行可视化调试的场景。

以下是每种模式的工作原理。

无头模式

无头模式是 CI 或服务器环境中最常见的设置。在这种模式下,应用程序使用虚拟 X 服务器运行,而不是真实的显示器。

运行容器:

bash 复制代码
docker run --rm --shm-size=1g dnb-app

由于没有设置 DISPLAY,启动脚本会启动 Xvfb 并在无头模式下运行应用程序。

--shm-size=1g 选项增加了分配给容器的共享内存量。Chromium 使用共享内存在其进程之间进行通信。在大多数 Linux 系统上,默认情况下共享内存足够大。但在 Docker 中,它被限制为 64 MB ------ 对于 Chromium 来说太小了。

在示例应用程序中,我们加载 Google 并打印页面标题。当容器启动时,您应该看到:

text 复制代码
Title: Google

桌面模式

桌面模式适用于您希望看到容器内的应用程序窗口的情况------例如,调试渲染问题或测试用户输入。在这种情况下,容器需要访问您主机的 X 服务器。

这种方法适用于使用 X11 的 Linux 系统。在 Linux 上,图形应用程序通过 Unix 套接字与 X 服务器通信,DISPLAY 变量告诉它们使用哪个显示器。我们将此信息传递给容器,以便应用程序能够在您的主机桌面上显示其窗口。

首先,允许本地连接到您的 X 服务器:

bash 复制代码
xhost +local:root

然后启动容器:

bash 复制代码
docker run --rm \
  --shm-size=1g \
  -e DISPLAY=$DISPLAY \
  -v /tmp/.X11-unix:/tmp/.X11-unix \
  dnb-app

这里发生了什么:

  • -e DISPLAY=$DISPLAY 告诉应用程序要使用哪个显示器。
  • -v /tmp/.X11-unix:/tmp/.X11-unix 挂载了 X11 通信套接字。

应用程序窗口应该会出现:

由容器渲染的应用程序窗口。

完成后,您可以撤销 X 服务器的权限:

bash 复制代码
xhost -local:root

完整的示例应用程序可在 GitHub 仓库 中找到。

远程调试

当出现问题时,您需要查看 Chromium 内部发生了什么------检查 DOM、调试 JavaScript 或监控网络请求。

在 DotNetBrowser 中,通过引擎选项启用远程调试:设置端口并添加 --remote-allow-origins 开关。

将以下配置添加到 EngineOptions.Builder 中:

csharp 复制代码
IEngine engine = EngineFactory.Create(new EngineOptions.Builder
{
    ChromiumSwitches = { "--remote-allow-origins=http://localhost:9222" },
    RemoteDebuggingPort = 9222
}.Build());

修改应用程序后,不要忘记重新构建 Docker 镜像。

当 Chromium 在容器内运行时,DevTools 无法直接从您的主机访问。Chromium 在本地端口上暴露 DevTools ------但这里的"本地"指的是容器内部。要从主机访问 DevTools,请使用 SSH 本地端口转发。

在主机上,启动容器并发布 SSH 端口:

shell 复制代码
docker run -d -p 2222:22 --shm-size=1g dnb-app

在正在运行的容器内打开一个 shell:

shell 复制代码
docker exec -it <container_id> /bin/bash

<container_id> 替换为正在运行的容器的 ID。您可以通过运行 docker ps 找到它。

在容器内部安装并启动 SSH 服务器:

shell 复制代码
apt install -y openssh-server
service ssh start

在容器内部,创建一个用于 SSH 访问的用户:

shell 复制代码
useradd --create-home --shell /bin/bash dnb-app
passwd dnb-app

在主机上,转发远程调试端口:

shell 复制代码
ssh -L 9222:localhost:9222 -p 2222 dnb-app@localhost

此命令创建了一个从主机的 localhost:9222 到容器内部的 localhost:9222 的隧道。在使用 DevTools 期间,请保持此 SSH 会话打开。

在主机上,打开 Google Chrome 并加载 chrome://inspect 以访问 DevTools。

总结

DotNetBrowser 应用程序不仅仅是一个 .NET 进程------它嵌入了一个完整的 Chromium Engine。该 Engine 期望特定的系统库和一个显示服务器。Docker 允许您显式定义该环境,并在本地、CI 或生产环境中运行相同的镜像,获得一致的结果。

相关推荐
什么时候才能变强2 小时前
如何设计一个自动化压测平台:从需求到落地的完整思考
运维·自动化
xiaogai_gai2 小时前
跨境电商ERP系统自动化集成方案
运维·自动化
赛博云推-Twitter热门霸屏工具2 小时前
2026年Twitter自动化营销新趋势:用赛博云推实现热门霸屏与精准获客
运维·自动化·twitter
翘着二郎腿的程序猿2 小时前
自动化 CI/CD 从 0 到 1:Jenkins 本地化部署全指南
运维·jenkins
ErizJ2 小时前
面试 | Docker K8S
docker·面试·kubernetes
食指Shaye2 小时前
docker的学习日记
学习·docker·eureka
postfxj2 小时前
解决其它虚拟机(深信服超融合)系统导出的ovf导入到vmware esxi6.5虚拟机鼠标用不了(不听例唤)的问题
运维·服务器
℘团子এ2 小时前
什么是Docker
前端·docker·容器
在路上~~~~2 小时前
EBS AR接口表数据跑【自动开票主程序】报错
运维·oracle·ar