作为Write for DOnations计划的一部分,作者选择了免费和开源基金来接受捐赠。
介绍
即使云服务越来越流行,运行原生应用程序的需求仍然存在。
通过使用noVNC和TigerVNC,您可以在Docker容器内运行本机应用程序并使用 Web 浏览器远程访问它们。此外,您可以在系统资源多于本地可用资源的服务器上运行您的应用程序,这可以在运行大型应用程序时提供更高的灵活性。
在本教程中,您将使用 Docker容器化电子邮件客户端Mozilla Thunderbird。之后,您将保护它并使用Caddy Web 服务器提供远程访问。
完成后,您只需使用网络浏览器即可从任何设备访问 Thunderbird。或者,您还可以使用WebDAV从本地访问文件。您还将拥有一个可以在任何地方运行的完全独立的 Docker 映像。
先决条件
在开始本指南之前,您需要具备以下条件:
- 一台至少有 2GB RAM 和 4GB 磁盘空间的 Debian 9 服务器。
- 具有
sudo
特权的非 root 用户。 - 在您的服务器上设置 Docker。您可以按照如何在 Debian 9 上安装和使用 Docker 进行操作。
第 1 步 – 创建supervisord
配置
现在您的服务器正在运行并安装了 Docker,您可以开始配置应用程序的容器了。由于您的容器由多个组件组成,您需要使用进程管理器来启动和监控它们。在这里,您将使用supervisord
. supervisord
是一个用 Python 编写的进程管理器,通常用于编排复杂的容器。
首先,创建并输入一个thunderbird
为您的容器命名的目录:
- mkdir ~/thunderbird
- cd ~/thunderbird
现在创建并打开一个名为supervisord.conf
usingnano
或您喜欢的编辑器的文件:
- nano ~/thunderbird/supervisord.conf
现在将第一个代码块添加到 中supervisord.conf
,它将定义 supervisord 的全局选项:
[supervisord]
nodaemon=true
pidfile=/tmp/supervisord.pid
logfile=/dev/fd/1
logfile_maxbytes=0
在此块中,您正在配置supervisord
自身。您需要设置nodaemon
为,true
因为它将作为入口点在 Docker 容器内运行。因此,您希望它保持在前台运行。您还将设置pidfile
为非 root 用户可访问的路径(稍后将详细介绍)和logfile
stdout,以便您可以查看日志。
接下来,将另一个小代码块添加到supervisord.conf
. 此块启动 TigerVNC,它是一个组合的 VNC/X11 服务器:
...
[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
:
...
[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:
...
[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
,这将启动主应用程序:
...
[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
在这最后一个块中,您将设置priority
为1
确保 Thunderbird在TigerVNC之后启动,否则它会遇到竞争条件并随机启动失败。autorestart=true
如果应用程序错误关闭,我们还设置为自动重新打开该应用程序。在DISPLAY
环境变量告诉显示您之前设置的VNC服务器上的应用程序。
这是您完成后的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
:
<?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
:
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
:
...
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
因为某些应用程序依赖于现有目录。
让我们添加另一个小代码块:
...
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 站点)。
现在,我们可以为主应用程序添加指令:
...
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 不支持它。
接下来,让我们开始添加将最后几个文件添加到容器的说明:
...
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 身份运行应用程序,即使在容器中也是如此。
...
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
. 您还在数据目录上安装了一个卷,以确保它在重新启动之间保持不变。
最后,让我们添加启动一切的指令:
...
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
在开始之前supervisord
以root身份运行以防止数据卷上的权限问题并允许子进程打开stdout
. 这也意味着您需要使用gosu
而不是USER
指令来切换用户。
这是您完成后的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
然后添加以下指令:
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
使用Caddyfile
at启动它/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_USERNAME
和APP_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
您现在应该能够与应用程序交互,并且它应该会自动调整大小以适合您的浏览器窗口。
如果在黑色桌面上右键单击,您应该会看到一个允许您访问终端的菜单。如果您单击中键,您应该会看到一个窗口列表。
现在在网络浏览器中打开。您应该能够访问您的文件。http://your_server_ip:8080/files/
或者,您可以尝试在 WebDAV 客户端中安装。您应该能够直接访问和修改您的文件。如果您在 Windows 资源管理器中使用映射网络驱动器选项,您将需要使用反向代理来添加 HTTPS 或设置为.http://your_server_ip:8080/webdav/
HKLM\SYSTEM\CurrentControlSet\Services\WebClient\Parameters\BasicAuthLevel
DWORD:2
无论哪种情况,您的本机 GUI 应用程序现在都可以远程使用了。
结论
您现在已经成功地为 Thunderbird 设置了一个 Docker 容器,然后,您已经使用 Caddy 配置了通过 Web 浏览器访问它的权限。如果您需要升级应用程序,请停止容器,运行docker rm thunderbird-app thunderbird-web
,重新构建映像,然后重新运行docker run
上述步骤中的命令。您的数据仍将保留,因为它存储在一个卷中。
如果您想了解有关基本 Docker 命令的更多信息,可以阅读本教程或本备忘单。对于长期使用,您可能还需要考虑启用 HTTPS(这需要域)以提高安全性。
此外,如果您要部署多个应用程序,您可能希望使用 Docker Compose 或 Kubernetes 而不是手动启动每个容器。请记住,本教程可以作为在您的服务器上运行任何其他 Linux 应用程序的基础,包括:
最后一个选项展示了容器化和远程访问 GUI 应用程序的巨大潜力。通过这种设置,您现在可以使用计算能力比本地强大得多的服务器来运行 Cutter 等资源密集型工具。