如何在 Debian 10 上使用 Docker 和 Caddy 远程访问 GUI 应用程序

作为Write for DOnations计划的一部分,作者选择了免费和开源基金来接受捐赠

介绍

即使云服务越来越流行,运行原生应用程序的需求仍然存在。

通过使用noVNCTigerVNC,您可以在Docker容器内运行本机应用程序并使用 Web 浏览器远程访问它们。此外,您可以在具有比本地可用资源更多的系统资源的服务器上运行您的应用程序,这可以在运行大型应用程序时提供更高的灵活性。

在本教程中,您将使用 Docker容器化电子邮件客户端Mozilla Thunderbird之后,您将保护它并使用Caddy Web 服务器提供远程访问

完成后,您只需使用网络浏览器即可从任何设备访问 Thunderbird。或者,您还可以使用WebDAV从本地访问文件您还将拥有一个可以在任何地方运行的完全独立的 Docker 映像。

先决条件

在开始本指南之前,您需要具备以下条件:

第 1 步 – 创建supervisord配置

现在您的服务器正在运行并安装了 Docker,您可以开始配置应用程序的容器了。由于您的容器由多个组件组成,您需要使用进程管理器来启动和监控它们。在这里,您将使用supervisord. supervisord是一个用 Python 编写的进程管理器,通常用于编排复杂的容器。

首先,创建并输入一个thunderbird为您的容器命名的目录

  • mkdir ~/thunderbird
  • cd ~/thunderbird

现在创建并打开一个名为supervisord.confusingnano或您喜欢的编辑器的文件

  • nano ~/thunderbird/supervisord.conf

现在将第一个代码块添加到 中supervisord.conf,它将定义 supervisord 的全局选项:

~/thunderbird/supervisord.conf
[supervisord]
nodaemon=true
pidfile=/tmp/supervisord.pid
logfile=/dev/fd/1
logfile_maxbytes=0

在此块中,您正在配置supervisord自身。您需要设置nodaemon为,true因为它将作为入口点在 Docker 容器内运行。因此,您希望它保持在前台运行。您还将设置pidfile为非 root 用户可访问的路径(稍后将详细介绍)和logfilestdout,以便您可以查看日志。

接下来,将另一个小代码块添加到supervisord.conf. 此块启动 TigerVNC,它是一个组合的 VNC/X11 服务器:

~/thunderbird/supervisord.conf
...
[program:x11]
priority=0
command=/usr/bin/Xtigervnc -desktop "Thunderbird" -localhost -rfbport 5900 -SecurityTypes None -AlwaysShared -AcceptKeyEvents -AcceptPointerEvents -AcceptSetDesktopSize -SendCutText -AcceptCutText :0
autorestart=true
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
redirect_stderr=true

在此块中,您将设置 X11 服务器。X11是一个显示服务器协议,它允许 GUI 应用程序运行。请注意,将来它将被Wayland取代,但远程访问仍在开发中。

对于此容器,您使用的是TigerVNC及其内置的 VNC 服务器。与使用单独的 X11 和 VNC 服务器相比,这具有许多优点:

  • 响应时间更快,因为 GUI 绘图直接在 VNC 服务器上完成,而不是在中间帧缓冲区(存储屏幕内容的内存)上完成。
  • 自动调整屏幕大小,允许远程应用程序自动调整大小以适应客户端(在本例中为您的 Web 浏览器窗口)。

如果您愿意,您可以将-desktop选项的参数更改Thunderbird为您选择的其他内容。服务器会将您的选择显示为用于访问您的应用程序的网页的标题。

现在,让我们添加第三个代码块supervisord.conf开始easy-novnc

~/thunderbird/supervisord.conf
...
[program:easy-novnc]
priority=0
command=/usr/local/bin/easy-novnc --addr :8080 --host localhost --port 5900 --no-url-password --novnc-params "resize=remote"
autorestart=true
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
redirect_stderr=true

在此块中,您将设置easy-novnc一个独立的服务器,它提供了围绕noVNC的包装器该服务器执行两个角色。首先,它提供了一个简单的连接页面,允许您配置连接选项,并允许您设置默认选项。其次,它通过WebSocket代理 VNC ,允许通过普通的 Web 浏览器访问它。

通常,调整大小是在客户端完成的(即图像缩放),但您正在使用该resize=remote选项来充分利用 TigerVNC 的远程分辨率调整。这还可以在较慢的设备上提供更低的延迟,例如低端 Chromebook:

注意:本教程使用easy-novnc. 如果您愿意,您可以使用websockify和 一个单独的 Web 服务器。的优点easy-novnc是内存使用量和启动时间显着降低,并且它是独立的。easy-novnc还提供了一个比默认 noVNC 更清晰的连接页面,并允许设置有助于此设置的默认选项(例如resize=remote)。

现在将以下块添加到您的配置中以启动窗口管理器 OpenBox:

~/thunderbird/supervisord.conf
...
[program:openbox]
priority=1
command=/usr/bin/openbox
environment=DISPLAY=:0
autorestart=true
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
redirect_stderr=true

在这个块中,您将设置OpenBox,一个轻量级的 X11 窗口管理器。您可以跳过此步骤,但没有它,您将没有标题栏或无法调整窗口大小。

最后,让我们将最后一个块添加到supervisord.conf,这将启动主应用程序:

~/thunderbird/supervisord.conf
...
[program:app]
priority=1
environment=DISPLAY=:0
command=/usr/bin/thunderbird
autorestart=true
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
redirect_stderr=true

在这最后一个块中,您将设置priority1确保 ThunderbirdTigerVNC之后启动,否则它会遇到竞争条件并随机启动失败。autorestart=true如果应用程序错误关闭,我们还设置为自动重新打开该应用程序。DISPLAY环境变量告诉显示您之前设置的VNC服务器上的应用程序。

这是您完成的supervisord.conf样子:

~/thunderbird/supervisord.conf
[supervisord]
nodaemon=true
pidfile=/tmp/supervisord.pid
logfile=/dev/fd/1
logfile_maxbytes=0

[program:x11]
priority=0
command=/usr/bin/Xtigervnc -desktop "Thunderbird" -localhost -rfbport 5900 -SecurityTypes None -AlwaysShared -AcceptKeyEvents -AcceptPointerEvents -AcceptSetDesktopSize -SendCutText -AcceptCutText :0
autorestart=true
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
redirect_stderr=true

[program:easy-novnc]
priority=0
command=/usr/local/bin/easy-novnc --addr :8080 --host localhost --port 5900 --no-url-password --novnc-params "resize=remote"
autorestart=true
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
redirect_stderr=true

[program:openbox]
priority=1
command=/usr/bin/openbox
environment=DISPLAY=:0
autorestart=true
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
redirect_stderr=true

[program:app]
priority=1
environment=DISPLAY=:0
command=/usr/bin/thunderbird
autorestart=true
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
redirect_stderr=true

如果要容器化不同的应用程序,请替换/usr/bin/thunderbird为应用程序可执行文件的路径。否则,您现在已准备好配置 GUI 的主菜单。

步骤 2 — 设置 OpenBox 菜单

现在您的流程管理器已配置,让我们设置 OpenBox 菜单。这个菜单允许我们在容器内启动应用程序。如果需要,我们还将包括用于调试的终端和进程监视器。

在您的应用程序目录中,使用nano或您喜欢的文本编辑器创建并打开一个名为 的新文件menu.xml

  • nano ~/thunderbird/menu.xml

现在将以下代码添加到menu.xml

~/thunderbird/menu.xml
<?xml version="1.0" encoding="utf-8"?>
<openbox_menu xmlns="http://openbox.org/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://openbox.org/ file:///usr/share/openbox/menu.xsd">
    <menu id="root-menu" label="Openbox 3">
        <item label="Thunderbird">
            <action name="Execute">
                <execute>/usr/bin/thunderbird</execute>
            </action>
        </item>
        <item label="Terminal">
            <action name="Execute">
                <execute>/usr/bin/x-terminal-emulator</execute>
            </action>
        </item>
        <item label="Htop">
            <action name="Execute">
                <execute>/usr/bin/x-terminal-emulator -e htop</execute>
            </action>
        </item>
    </menu>
</openbox_menu>

此 XML 文件包含右键单击桌面时将出现的菜单项。每个项目由一个标签和一个动作组成。

如果要容器化不同的应用程序,请替换/usr/bin/thunderbird为应用程序可执行文件的路径并更改label项目的 。

第 3 步 – 创建 Dockerfile

现在配置了 OpenBox,您将创建 Dockerfile,它将所有内容联系在一起。

在容器目录中创建一个 Dockerfile:

  • nano ~/thunderbird/Dockerfile

首先,让我们添加一些代码来构建easy-novnc

〜/雷鸟/Dockerfile
FROM golang:1.14-buster AS easy-novnc-build
WORKDIR /src
RUN go mod init build && \
    go get github.com/geek1011/[email protected] && \
    go build -o /bin/easy-novnc github.com/geek1011/easy-novnc

在第一阶段,您正在构建easy-novnc. 为了简单起见并节省空间,这是在单独的阶段完成的——您不需要在最终图像中使用整个 Go 工具链。请注意@v1.1.0构建命令中的 。这确保了结果是确定性的,这很重要,因为 Docker 会缓存每个步骤的结果。如果您没有指定明确的版本,Docker 将引用easy-novnc首次构建映像时的最新版本此外,您要确保下载特定版本的easy-novnc,以防对 CLI 界面进行重大更改。

现在让我们创建第二个阶段,它将成为最终图像。在这里,您将使用 Debian 10 (buster) 作为基础镜像。请注意,由于这是在容器中运行,因此无论您在服务器上运行的发行版如何,它都可以工作。

接下来,将以下块添加到您的Dockerfile

〜/雷鸟/Dockerfile
...
FROM debian:buster
RUN apt-get update -y && \
    apt-get install -y --no-install-recommends openbox tigervnc-standalone-server supervisor gosu && \
    rm -rf /var/lib/apt/lists && \
    mkdir -p /usr/share/desktop-directories

在本说明中,您将安装 Debian 10 作为基础映像,然后安装在容器中运行 GUI 应用程序所需的最低要求。请注意,您apt-get update作为同一指令的一部分运行以防止来自 Docker 的缓存问题。为了节省空间,您还将删除之后下载的软件包列表(默认情况下删除缓存的软件包本身)。您也在创建,/usr/share/desktop-directories因为某些应用程序依赖于现有目录。

让我们添加另一个小代码块:

〜/雷鸟/Dockerfile
...
RUN apt-get update -y && \
    apt-get install -y --no-install-recommends lxterminal nano wget openssh-client rsync ca-certificates xdg-utils htop tar xzip gzip bzip2 zip unzip && \
    rm -rf /var/lib/apt/lists

在本说明中,您将安装一些有用的通用实用程序和软件包。这里特别有趣的是xdg-utils(它提供了 Linux 上的桌面应用程序使用的基本命令)和ca-certificates(它安装了根证书以允许我们访问 HTTPS 站点)。

现在,我们可以为主应用程序添加指令:

〜/雷鸟/Dockerfile
...
RUN apt-get update -y && \
    apt-get install -y --no-install-recommends thunderbird && \
    rm -rf /var/lib/apt/lists

和以前一样,我们在这里安装应用程序。如果您正在容器化不同的应用程序,您可以将这些命令替换为安装特定应用程序所需的命令。有些应用程序需要做更多的工作才能在 Docker 中运行。例如,如果您要安装使用 Chrome、Chromium 或 QtWebEngine 的应用程序,则需要使用命令行参数,--no-sandbox因为 Docker 不支持它。

接下来,让我们开始添加将最后几个文件添加到容器的说明:

〜/雷鸟/Dockerfile
...
COPY --from=easy-novnc-build /bin/easy-novnc /usr/local/bin/
COPY menu.xml /etc/xdg/openbox/
COPY supervisord.conf /etc/
EXPOSE 8080

在这里,您将之前创建的配置文件添加到映像并easy-novnc从第一阶段复制二进制文件。

下一个代码块创建数据目录并为您的应用程序添加一个专用用户。这很重要,因为某些应用程序拒绝以 root 身份运行。最好不要以 root 身份运行应用程序,即使在容器中也是如此。

〜/雷鸟/Dockerfile
...
RUN groupadd --gid 1000 app && \
    useradd --home-dir /data --shell /bin/bash --uid 1000 --gid 1000 app && \
    mkdir -p /data
VOLUME /data

为确保UID/GID文件一致,您将两者都明确设置为1000. 您还在数据目录上安装了一个卷,以确保它在重新启动之间保持不变。

最后,让我们添加启动一切的指令:

〜/雷鸟/Dockerfile
...
CMD ["sh", "-c", "chown app:app /data /dev/stdout && exec gosu app supervisord"]

通过将默认命令设置为supervisord,管理器将启动运行应用程序所需的进程。在这种情况下,您使用的是CMD而不是ENTRYPOINT. 在大多数情况下,它不会有什么不同,但CMD出于一些原因,使用更适合此目的。首先,supervisord不接受任何与我们相关的参数,如果您向容器提供参数,它们将替换CMD并附加到ENTRYPOINT. 其次, usingCMD允许我们在向/bin/sh -c容器传递参数时提供完全不同的命令(将由 执行),这使得调试更容易。

最后,您需要chown在开始之前supervisordroot身份运行以防止数据卷上的权限问题并允许子进程打开stdout. 这也意味着您需要使用gosu而不是USER指令来切换用户。

这是您完成的Dockerfile样子:

〜/雷鸟/Dockerfile
FROM golang:1.14-buster AS easy-novnc-build
WORKDIR /src
RUN go mod init build && \
    go get github.com/geek1011/[email protected] && \
    go build -o /bin/easy-novnc github.com/geek1011/easy-novnc

FROM debian:buster

RUN apt-get update -y && \
    apt-get install -y --no-install-recommends openbox tigervnc-standalone-server supervisor gosu && \
    rm -rf /var/lib/apt/lists && \
    mkdir -p /usr/share/desktop-directories

RUN apt-get update -y && \
    apt-get install -y --no-install-recommends lxterminal nano wget openssh-client rsync ca-certificates xdg-utils htop tar xzip gzip bzip2 zip unzip && \
    rm -rf /var/lib/apt/lists

RUN apt-get update -y && \
    apt-get install -y --no-install-recommends thunderbird && \
    rm -rf /var/lib/apt/lists

COPY --from=easy-novnc-build /bin/easy-novnc /usr/local/bin/
COPY menu.xml /etc/xdg/openbox/
COPY supervisord.conf /etc/
EXPOSE 8080

RUN groupadd --gid 1000 app && \
    useradd --home-dir /data --shell /bin/bash --uid 1000 --gid 1000 app && \
    mkdir -p /data
VOLUME /data

CMD ["sh", "-c", "chown app:app /data /dev/stdout && exec gosu app supervisord"]

保存并关闭您的Dockerfile. 现在我们准备好构建和运行我们的容器,然后访问 Thunderbird——一个 GUI 应用程序。

第 4 步 – 构建和运行容器

下一步是构建您的容器并将其设置为在启动时运行。您还将设置一个卷以在重新启动和更新之间保留应用程序数据。

首先构建您的容器。确保在~/thunderbird目录中运行这些命令

  • docker build -t thunderbird .

现在创建一个将在应用程序容器之间共享的新网络:

  • docker network create thunderbird-net

然后创建一个卷来存储应用程序数据:

  • docker volume create thunderbird-data

最后,运行它并将其设置为自动重启:

  • docker run --detach --restart=always --volume=thunderbird-data:/data --net=thunderbird-net --name=thunderbird-app thunderbird

请注意,如果需要,您可以用不同的名称替换选项thunderbird-app后的--name选项。无论您选择什么,您的应用程序现在都已容器化并正在运行。现在让我们使用 Caddy Web 服务器来保护它并远程连接到它。

第 5 步 — 设置 Caddy

在此步骤中,您将设置 Caddy Web 服务器以提供身份验证和(可选)通过 WebDAV 的远程文件访问。为简单起见,并允许您将其与现有的反向代理一起使用,您将在另一个容器中运行它。

创建一个新目录,然后在其中移动:

  • mkdir ~/caddy
  • cd ~/caddy

现在创建一个新的Dockerfile使用nano或您喜欢的编辑器:

  • nano ~/caddy/Dockerfile

然后添加以下指令:

~/球童/Dockerfile
FROM golang:1.14-buster AS caddy-build
WORKDIR /src
RUN echo 'module caddy' > go.mod && \
    echo 'require github.com/caddyserver/caddy/v2 v2.1.1' >> go.mod && \
    echo 'require github.com/mholt/caddy-webdav v0.0.0-20200523051447-bc5d19941ac3' >> go.mod
RUN echo 'package main' > caddy.go && \
    echo 'import caddycmd "github.com/caddyserver/caddy/v2/cmd"' >> caddy.go && \
    echo 'import _ "github.com/caddyserver/caddy/v2/modules/standard"' >> caddy.go && \
    echo 'import _ "github.com/mholt/caddy-webdav"' >> caddy.go && \
    echo 'func main() { caddycmd.Main() }' >> caddy.go
RUN go build -o /bin/caddy .

FROM debian:buster

RUN apt-get update -y && \
    apt-get install -y --no-install-recommends gosu && \
    rm -rf /var/lib/apt/lists

COPY --from=caddy-build /bin/caddy /usr/local/bin/
COPY Caddyfile /etc/
EXPOSE 8080

RUN groupadd --gid 1000 app && \
    useradd --home-dir /data --shell /bin/bash --uid 1000 --gid 1000 app && \
    mkdir -p /data
VOLUME /data

WORKDIR /data
CMD ["sh", "-c", "chown app:app /data && exec gosu app /usr/local/bin/caddy run -adapter caddyfile -config /etc/Caddyfile"]

这个 Dockerfile 在启用 WebDAV 插件的情况下构建 Caddy,然后在端口上8080使用Caddyfileat启动它/etc/Caddyfile保存并关闭文件。

接下来,您将配置 Caddy Web 服务器。Caddyfile在刚刚创建的目录中创建一个名为的文件

  • nano ~/caddy/Caddyfile

现在将以下代码块添加到您的Caddyfile

〜/球童/球童文件
{
    order webdav last
}
:8080 {
    log
    root * /data
    reverse_proxy thunderbird-app:8080

    handle_path /files/* {         
        file_server browse
    }
    redir /files /files/

    handle /webdav/* {
        webdav {             
            prefix /webdav         
        }
    }
    redir /webdav /webdav/

    basicauth /* {
        {env.APP_USERNAME} {env.APP_PASSWORD_HASH}
    }
}

会将Caddyfile根目录代理到thunderbird-app您在步骤 4 中创建容器(Docker 将其解析为正确的 IP)。它还将在基于 Web 的只读文件浏览器上提供服务,/files并运行 WebDAV 服务器/webdav,您可以服务器上本地安装以访问您的文件。用户名和密码是从环境变量APP_USERNAMEAPP_PASSWORD_HASH.

现在构建容器:

  • docker build -t thunderbird-caddy .

Caddy v2 要求您散列所需的密码。运行以下命令并记住mypass用您选择的强密码替换

  • docker run --rm -it thunderbird-caddy caddy hash-password -plaintext 'mypass'

该命令将输出一串字符。将此复制到剪贴板以准备运行下一个命令。

现在您已准备好运行容器。确保替换myuser为您选择的用户名,并替换mypass-hash为您在上一步中运行的命令的输出。您还可以更改端口(8080此处)以通过其他端口访问您的服务器:

  • docker run --detach --restart=always --volume=thunderbird-data:/data --net=thunderbird-net --name=thunderbird-web --env=APP_USERNAME="myuser" --env=APP_PASSWORD_HASH="mypass-hash" --publish=8080:8080 thunderbird-caddy

我们现在准备访问和测试我们的应用程序。

步骤 6 — 测试和管理应用程序

让我们访问您的应用程序并确保它正常工作。

首先,在 Web 浏览器中打开,使用
您之前选择的凭据登录,然后单击
连接
http://your_server_ip:8080

NoVNC 连接页面

您现在应该能够与应用程序交互,并且它应该会自动调整大小以适合您的浏览器窗口。

雷鸟主菜单

如果您在黑色桌面上右键单击,您应该会看到一个允许您访问终端的菜单。如果您单击中键,您应该会看到一个窗口列表。

NoVNC 右键单击

现在在网络浏览器中打开您应该能够访问您的文件。http://your_server_ip:8080/files/

NoVNC 文件访问 webdav

或者,您可以尝试在 WebDAV 客户端中安装您应该能够直接访问和修改您的文件。如果您在 Windows 资源管理器中使用映射网络驱动器选项,您将需要使用反向代理来添加 HTTPS 或设置.http://your_server_ip:8080/webdav/HKLM\SYSTEM\CurrentControlSet\Services\WebClient\Parameters\BasicAuthLevelDWORD:2

无论哪种情况,您的本机 GUI 应用程序现在都可以远程使用了。

结论

您现在已经成功地为 Thunderbird 设置了一个 Docker 容器,然后,您已经使用 Caddy 配置了通过 Web 浏览器访问它的权限。如果您需要升级应用程序,请停止容器,运行docker rm thunderbird-app thunderbird-web,重新构建映像,然后重新运行docker run上述步骤中命令。您的数据仍将保留,因为它存储在一个卷中。

如果您想了解有关基本 Docker 命令的更多信息,可以阅读本教程本备忘单对于长期使用,您可能还需要考虑启用 HTTPS(这需要域)以提高安全性。

此外,如果您要部署多个应用程序,您可能希望使用 Docker Compose 或 Kubernetes 而不是手动启动每个容器。请记住,本教程可以作为在您的服务器上运行任何其他 Linux 应用程序的基础,包括:

  • Wine,用于在 Linux 上运行 Windows 应用程序的兼容层。
  • GIMP,一个开源图像编辑器。
  • Cutter,一个开源逆向工程平台。

最后一个选项展示了容器化和远程访问 GUI 应用程序的巨大潜力。通过这种设置,您现在可以使用计算能力比本地强大得多的服务器来运行 Cutter 等资源密集型工具。

觉得文章有用?

点个广告表达一下你的爱意吧 !😁