如何在 Debian 9 上使用 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 等资源密集型工具。

觉得文章有用?

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