Автор выбрал Фонд свободного и открытого исходного кода для получения пожертвования в рамках программы Write for DOnations.
Даже несмотря на растущую популярность облачных сервисов, необходимость запуска собственных приложений по-прежнему существует.
Используя noVNC и TigerVNC, вы можете запускать собственные приложения внутри контейнера Docker и получать к ним удаленный доступ с помощью веб-браузера. Кроме того, вы можете запустить свое приложение на сервере с большим количеством системных ресурсов, чем у вас может быть доступно локально, что может обеспечить повышенную гибкость при запуске больших приложений.
В этом руководстве вы поместите почтовый клиент Mozilla Thunderbird в контейнер с помощью Docker. После этого вы защитите его и предоставите удаленный доступ с помощью веб-сервера Caddy.
Когда вы закончите, вы сможете получить доступ к Thunderbird с любого устройства, используя только веб-браузер. При желании вы также сможете получить локальный доступ к файлам из него с помощью WebDAV. У вас также будет полностью автономный образ Docker, который вы сможете запускать где угодно.
Прежде чем приступить к работе с этим руководством, вам понадобится следующее:
sudo
.supervisord
Теперь, когда ваш сервер запущен и Docker установлен, вы готовы приступить к настройке контейнера вашего приложения. Поскольку ваш контейнер состоит из нескольких компонентов, вам необходимо использовать диспетчер процессов для их запуска и мониторинга. Здесь вы будете использовать supervisord
. supervisord
— это менеджер процессов, написанный на Python, который часто используется для управления сложными контейнерами.
Сначала создайте и войдите в каталог thunderbird
для вашего контейнера:
1 |
<ol><li data-prefix="$"> <span class="token function">mkdir</span> ~/тандерберд</li><li data-prefix="$"> <span class="token builtin class-name">компакт-диск</span> ~/тандерберд</li></ol> |
Теперь создайте и откройте файл supervisord.conf
с помощью nano
или вашего любимого редактора:
1 |
<ol><li data-prefix="$"> <span class="token function">нано</span> супервизорд.конф</li></ol> |
Теперь добавьте этот первый блок кода в supervisord.conf
, который определит глобальные параметры супервизора:
1 |
<span class="token section"><span class="token punctuation">[</span> <span class="token section-name selector">supervisord</span> <span class="token punctuation">]</span></span> <span class="token key attr-name">nodaemon</span> <span class="token punctuation">=</span> <span class="token value attr-value">true</span> <span class="token key attr-name">pidfile</span> <span class="token punctuation">=</span> <span class="token value attr-value">/tmp/supervisord.pid</span> <span class="token key attr-name">logfile</span> <span class="token punctuation">=</span> <span class="token value attr-value">/dev/fd/1</span> <span class="token key attr-name">logfile_maxbytes</span> <span class="token punctuation">=</span> <span class="token value attr-value">0</span> |
В этом блоке вы настраиваете сам supervisord
. Вам необходимо установить nodaemon
значение true
, поскольку он будет работать внутри контейнера Docker в качестве точки входа. Поэтому вы хотите, чтобы он продолжал работать на переднем плане. Вы также устанавливаете pidfile
в путь, доступный пользователю без полномочий root (подробнее об этом позже), а logfile
— стандартный вывод, чтобы вы могли видеть журналы.
Затем добавьте еще один небольшой блок кода в supervisord.conf
. Этот блок запускает TigerVNC, который представляет собой комбинированный сервер VNC/X11:
1 |
... <span class="token section"><span class="token punctuation">[</span> <span class="token section-name selector">program:x11</span> <span class="token punctuation">]</span></span> <span class="token key attr-name">priority</span> <span class="token punctuation">=</span> <span class="token value attr-value">0</span> <span class="token key attr-name">command</span> <span class="token punctuation">=</span> <span class="token value attr-value">/usr/bin/Xtigervnc -desktop "Thunderbird" -localhost -rfbport 5900 -SecurityTypes None -AlwaysShared -AcceptKeyEvents -AcceptPointerEvents -AcceptSetDesktopSize -SendCutText -AcceptCutText :0</span> <span class="token key attr-name">autorestart</span> <span class="token punctuation">=</span> <span class="token value attr-value">true</span> <span class="token key attr-name">stdout_logfile</span> <span class="token punctuation">=</span> <span class="token value attr-value">/dev/fd/1</span> <span class="token key attr-name">stdout_logfile_maxbytes</span> <span class="token punctuation">=</span> <span class="token value attr-value">0</span> <span class="token key attr-name">redirect_stderr</span> <span class="token punctuation">=</span> <span class="token value attr-value">true</span> |
В этом блоке вы настраиваете сервер X11. X11 — это протокол сервера отображения, который позволяет запускать приложения с графическим интерфейсом. Отметим, что в будущем он будет заменен на Wayland, но удаленный доступ все еще находится в разработке.
Для этого контейнера вы используете TigerVNC и его встроенный VNC-сервер. Это имеет ряд преимуществ по сравнению с использованием отдельного сервера X11 и VNC:
При желании вы можете изменить аргумент опции -desktop
с Thunderbird
на что-то другое по вашему выбору. Сервер отобразит выбранный вами заголовок веб-страницы, используемой для доступа к вашему приложению.
Теперь давайте добавим третий блок кода в supervisord.conf
, чтобы запустить easy-novnc
:
1 |
... <span class="token section"><span class="token punctuation">[</span> <span class="token section-name selector">program:easy-novnc</span> <span class="token punctuation">]</span></span> <span class="token key attr-name">priority</span> <span class="token punctuation">=</span> <span class="token value attr-value">0</span> <span class="token key attr-name">command</span> <span class="token punctuation">=</span> <span class="token value attr-value">/usr/local/bin/easy-novnc --addr :8080 --host localhost --port 5900 --no-url-password --novnc-params "resize=remote"</span> <span class="token key attr-name">autorestart</span> <span class="token punctuation">=</span> <span class="token value attr-value">true</span> <span class="token key attr-name">stdout_logfile</span> <span class="token punctuation">=</span> <span class="token value attr-value">/dev/fd/1</span> <span class="token key attr-name">stdout_logfile_maxbytes</span> <span class="token punctuation">=</span> <span class="token value attr-value">0</span> <span class="token key attr-name">redirect_stderr</span> <span class="token punctuation">=</span> <span class="token value attr-value">true</span> |
В этом блоке вы настраиваете easy-novnc
— автономный сервер, который представляет собой оболочку noVNC. Этот сервер выполняет две роли. Во-первых, он предоставляет простую страницу подключения, которая позволяет вам настроить параметры подключения и установить параметры по умолчанию. Во-вторых, он проксирует VNC через WebSocket, что позволяет получить к нему доступ через обычный веб-браузер.
Обычно изменение размера выполняется на стороне клиента (т. е. масштабирование изображения), но вы используете опцию resize=remote
чтобы в полной мере воспользоваться преимуществами удаленной настройки разрешения TigerVNC. Это также обеспечивает меньшую задержку на более медленных устройствах, таких как Chromebook более низкого уровня:
Note: В этом руководстве используется easy-novnc
. Если хотите, вместо этого вы можете использовать websockify
и отдельный веб-сервер. Преимущество easy-novnc
в том, что использование памяти и время запуска значительно ниже, а также в том, что он автономен. easy-novnc
также предоставляет более чистую страницу подключения, чем страница noVNC по умолчанию, и позволяет устанавливать параметры по умолчанию, которые полезны для этой настройки (например, resize=remote
).
Теперь добавьте следующий блок в вашу конфигурацию, чтобы запустить OpenBox, оконный менеджер:
1 |
... <span class="token section"><span class="token punctuation">[</span> <span class="token section-name selector">program:openbox</span> <span class="token punctuation">]</span></span> <span class="token key attr-name">priority</span> <span class="token punctuation">=</span> <span class="token value attr-value">1</span> <span class="token key attr-name">command</span> <span class="token punctuation">=</span> <span class="token value attr-value">/usr/bin/openbox</span> <span class="token key attr-name">environment</span> <span class="token punctuation">=</span> <span class="token value attr-value">DISPLAY=:0</span> <span class="token key attr-name">autorestart</span> <span class="token punctuation">=</span> <span class="token value attr-value">true</span> <span class="token key attr-name">stdout_logfile</span> <span class="token punctuation">=</span> <span class="token value attr-value">/dev/fd/1</span> <span class="token key attr-name">stdout_logfile_maxbytes</span> <span class="token punctuation">=</span> <span class="token value attr-value">0</span> <span class="token key attr-name">redirect_stderr</span> <span class="token punctuation">=</span> <span class="token value attr-value">true</span> |
В этом блоке вы настраиваете OpenBox, облегченный оконный менеджер X11. Вы можете пропустить этот шаг, но без него у вас не будет заголовков и возможности изменять размеры окон.
Наконец, давайте добавим последний блок в supervisord.conf
, который запустит основное приложение:
1 |
... <span class="token section"><span class="token punctuation">[</span> <span class="token section-name selector">program:app</span> <span class="token punctuation">]</span></span> <span class="token key attr-name">priority</span> <span class="token punctuation">=</span> <span class="token value attr-value">1</span> <span class="token key attr-name">environment</span> <span class="token punctuation">=</span> <span class="token value attr-value">DISPLAY=:0</span> <span class="token key attr-name">command</span> <span class="token punctuation">=</span> <span class="token value attr-value">/usr/bin/thunderbird</span> <span class="token key attr-name">autorestart</span> <span class="token punctuation">=</span> <span class="token value attr-value">true</span> <span class="token key attr-name">stdout_logfile</span> <span class="token punctuation">=</span> <span class="token value attr-value">/dev/fd/1</span> <span class="token key attr-name">stdout_logfile_maxbytes</span> <span class="token punctuation">=</span> <span class="token value attr-value">0</span> <span class="token key attr-name">redirect_stderr</span> <span class="token punctuation">=</span> <span class="token value attr-value">true</span> |
В этом последнем блоке вы устанавливаете priority
на 1
, чтобы гарантировать, что Thunderbird запустится после TigerVNC, иначе он столкнется с состоянием гонки и случайно не запустится. Мы также установили autorestart=true
для автоматического повторного открытия приложения, если оно закрывается по ошибке. Переменная среды DISPLAY
сообщает приложению, что оно должно отображаться на VNC-сервере, который вы настроили ранее.
Вот как будет выглядеть ваш завершенный supervisord.conf
:
1 |
<span class="token section"><span class="token punctuation">[</span> <span class="token section-name selector">supervisord</span> <span class="token punctuation">]</span></span> <span class="token key attr-name">nodaemon</span> <span class="token punctuation">=</span> <span class="token value attr-value">true</span> <span class="token key attr-name">pidfile</span> <span class="token punctuation">=</span> <span class="token value attr-value">/tmp/supervisord.pid</span> <span class="token key attr-name">logfile</span> <span class="token punctuation">=</span> <span class="token value attr-value">/dev/fd/1</span> <span class="token key attr-name">logfile_maxbytes</span> <span class="token punctuation">=</span> <span class="token value attr-value">0</span> <span class="token section"><span class="token punctuation">[</span> <span class="token section-name selector">program:x11</span> <span class="token punctuation">]</span></span> <span class="token key attr-name">priority</span> <span class="token punctuation">=</span> <span class="token value attr-value">0</span> <span class="token key attr-name">command</span> <span class="token punctuation">=</span> <span class="token value attr-value">/usr/bin/Xtigervnc -desktop "Thunderbird" -localhost -rfbport 5900 -SecurityTypes None -AlwaysShared -AcceptKeyEvents -AcceptPointerEvents -AcceptSetDesktopSize -SendCutText -AcceptCutText :0</span> <span class="token key attr-name">autorestart</span> <span class="token punctuation">=</span> <span class="token value attr-value">true</span> <span class="token key attr-name">stdout_logfile</span> <span class="token punctuation">=</span> <span class="token value attr-value">/dev/fd/1</span> <span class="token key attr-name">stdout_logfile_maxbytes</span> <span class="token punctuation">=</span> <span class="token value attr-value">0</span> <span class="token key attr-name">redirect_stderr</span> <span class="token punctuation">=</span> <span class="token value attr-value">true</span> <span class="token section"><span class="token punctuation">[</span> <span class="token section-name selector">program:easy-novnc</span> <span class="token punctuation">]</span></span> <span class="token key attr-name">priority</span> <span class="token punctuation">=</span> <span class="token value attr-value">0</span> <span class="token key attr-name">command</span> <span class="token punctuation">=</span> <span class="token value attr-value">/usr/local/bin/easy-novnc --addr :8080 --host localhost --port 5900 --no-url-password --novnc-params "resize=remote"</span> <span class="token key attr-name">autorestart</span> <span class="token punctuation">=</span> <span class="token value attr-value">true</span> <span class="token key attr-name">stdout_logfile</span> <span class="token punctuation">=</span> <span class="token value attr-value">/dev/fd/1</span> <span class="token key attr-name">stdout_logfile_maxbytes</span> <span class="token punctuation">=</span> <span class="token value attr-value">0</span> <span class="token key attr-name">redirect_stderr</span> <span class="token punctuation">=</span> <span class="token value attr-value">true</span> <span class="token section"><span class="token punctuation">[</span> <span class="token section-name selector">program:openbox</span> <span class="token punctuation">]</span></span> <span class="token key attr-name">priority</span> <span class="token punctuation">=</span> <span class="token value attr-value">1</span> <span class="token key attr-name">command</span> <span class="token punctuation">=</span> <span class="token value attr-value">/usr/bin/openbox</span> <span class="token key attr-name">environment</span> <span class="token punctuation">=</span> <span class="token value attr-value">DISPLAY=:0</span> <span class="token key attr-name">autorestart</span> <span class="token punctuation">=</span> <span class="token value attr-value">true</span> <span class="token key attr-name">stdout_logfile</span> <span class="token punctuation">=</span> <span class="token value attr-value">/dev/fd/1</span> <span class="token key attr-name">stdout_logfile_maxbytes</span> <span class="token punctuation">=</span> <span class="token value attr-value">0</span> <span class="token key attr-name">redirect_stderr</span> <span class="token punctuation">=</span> <span class="token value attr-value">true</span> <span class="token section"><span class="token punctuation">[</span> <span class="token section-name selector">program:app</span> <span class="token punctuation">]</span></span> <span class="token key attr-name">priority</span> <span class="token punctuation">=</span> <span class="token value attr-value">1</span> <span class="token key attr-name">environment</span> <span class="token punctuation">=</span> <span class="token value attr-value">DISPLAY=:0</span> <span class="token key attr-name">command</span> <span class="token punctuation">=</span> <span class="token value attr-value">/usr/bin/thunderbird</span> <span class="token key attr-name">autorestart</span> <span class="token punctuation">=</span> <span class="token value attr-value">true</span> <span class="token key attr-name">stdout_logfile</span> <span class="token punctuation">=</span> <span class="token value attr-value">/dev/fd/1</span> <span class="token key attr-name">stdout_logfile_maxbytes</span> <span class="token punctuation">=</span> <span class="token value attr-value">0</span> <span class="token key attr-name">redirect_stderr</span> <span class="token punctuation">=</span> <span class="token value attr-value">true</span> |
Если вы хотите поместить в контейнер другое приложение, замените /usr/bin/thunderbird
на путь к исполняемому файлу вашего приложения. В противном случае теперь вы готовы настроить главное меню вашего графического интерфейса.
Теперь, когда ваш менеджер процессов настроен, давайте настроим меню OpenBox. Это меню позволяет нам запускать приложения внутри контейнера. При необходимости мы также добавим терминал и монитор процессов для отладки.
В каталоге вашего приложения используйте nano
или ваш любимый текстовый редактор, чтобы создать и открыть новый файл с именем menu.xml
:
1 |
<ol><li data-prefix="$"> <span class="token function">нано</span> ~/thunderbird/menu.xml</li></ol> |
Теперь добавьте следующий код в menu.xml
:
1 |
<span class="token prolog"><?xml version="1.0" encoding="utf-8"?></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> openbox_menu</span> <span class="token attr-name">xmlns</span> <span class="token attr-value"><span class="token punctuation attr-equals">=</span> <span class="token punctuation">"</span> http://openbox.org/ <span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">xmlns:</span> xsi</span> <span class="token attr-value"><span class="token punctuation attr-equals">=</span> <span class="token punctuation">"</span> http://www.w3.org/2001/XMLSchema-instance <span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">xsi:</span> schemaLocation</span> <span class="token attr-value"><span class="token punctuation attr-equals">=</span> <span class="token punctuation">"</span> http://openbox.org/ file:///usr/share/openbox/menu.xsd <span class="token punctuation">"</span></span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> menu</span> <span class="token attr-name">id</span> <span class="token attr-value"><span class="token punctuation attr-equals">=</span> <span class="token punctuation">"</span> root-menu <span class="token punctuation">"</span></span> <span class="token attr-name">label</span> <span class="token attr-value"><span class="token punctuation attr-equals">=</span> <span class="token punctuation">"</span> Openbox 3 <span class="token punctuation">"</span></span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> item</span> <span class="token attr-name">label</span> <span class="token attr-value"><span class="token punctuation attr-equals">=</span> <span class="token punctuation">"</span> Thunderbird <span class="token punctuation">"</span></span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> action</span> <span class="token attr-name">name</span> <span class="token attr-value"><span class="token punctuation attr-equals">=</span> <span class="token punctuation">"</span> Execute <span class="token punctuation">"</span></span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> execute</span> <span class="token punctuation">></span></span> /usr/bin/thunderbird <span class="token tag"><span class="token tag"><span class="token punctuation"></</span> execute</span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span> action</span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span> item</span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> item</span> <span class="token attr-name">label</span> <span class="token attr-value"><span class="token punctuation attr-equals">=</span> <span class="token punctuation">"</span> Terminal <span class="token punctuation">"</span></span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> action</span> <span class="token attr-name">name</span> <span class="token attr-value"><span class="token punctuation attr-equals">=</span> <span class="token punctuation">"</span> Execute <span class="token punctuation">"</span></span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> execute</span> <span class="token punctuation">></span></span> /usr/bin/x-terminal-emulator <span class="token tag"><span class="token tag"><span class="token punctuation"></</span> execute</span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span> action</span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span> item</span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> item</span> <span class="token attr-name">label</span> <span class="token attr-value"><span class="token punctuation attr-equals">=</span> <span class="token punctuation">"</span> Htop <span class="token punctuation">"</span></span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> action</span> <span class="token attr-name">name</span> <span class="token attr-value"><span class="token punctuation attr-equals">=</span> <span class="token punctuation">"</span> Execute <span class="token punctuation">"</span></span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> execute</span> <span class="token punctuation">></span></span> /usr/bin/x-terminal-emulator -e htop <span class="token tag"><span class="token tag"><span class="token punctuation"></</span> execute</span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span> action</span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span> item</span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span> menu</span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span> openbox_menu</span> <span class="token punctuation">></span></span> |
Этот XML-файл содержит пункты меню, которые появляются при щелчке правой кнопкой мыши на рабочем столе. Каждый элемент состоит из метки и действия.
Если вы хотите поместить в контейнер другое приложение, замените /usr/bin/thunderbird
путем к исполняемому файлу вашего приложения и измените label
элемента.
Теперь, когда OpenBox настроен, вы создадите файл Dockerfile, который свяжет все воедино.
Создайте Dockerfile в каталоге вашего контейнера:
1 |
<ol><li data-prefix="$"> <span class="token function">нано</span> ~/thunderbird/Dockerfile</li></ol> |
Для начала давайте добавим немного кода для сборки easy-novnc
:
1 |
<span class="token instruction"><span class="token keyword">FROM</span> golang:1.14-buster <span class="token keyword">AS</span> easy-novnc-build</span> <span class="token instruction"><span class="token keyword">WORKDIR</span> /src</span> <span class="token instruction"><span class="token keyword">RUN</span> go mod init build && <span class="token operator"></span> go get github.com/geek1011/easy-novnc@v1.1.0 && <span class="token operator"></span> go build -o /bin/easy-novnc github.com/geek1011/easy-novnc</span> |
На первом этапе вы создаете easy-novnc
. Это делается на отдельном этапе для простоты и экономии места — вам не понадобится вся цепочка инструментов Go в окончательном изображении. Обратите внимание на @v1.1.0
в команде сборки. Это гарантирует, что результат будет детерминированным, что важно, поскольку Docker кэширует результат каждого шага. Если вы не указали явную версию, Docker будет ссылаться на последнюю версию easy-novnc
на момент первой сборки образа. Кроме того, вам необходимо убедиться, что вы загружаете конкретную версию easy-novnc
на случай, если в интерфейс CLI будут внесены критические изменения.
Теперь давайте создадим второй этап, который станет финальным изображением. Здесь вы будете использовать Debian 10 (buster) в качестве базового образа. Обратите внимание: поскольку он выполняется в контейнере, он будет работать независимо от дистрибутива, который вы используете на своем сервере.
Затем добавьте следующий блок в свой Dockerfile
:
1 |
... <span class="token instruction"><span class="token keyword">FROM</span> debian:buster</span> <span class="token instruction"><span class="token keyword">RUN</span> apt-get update -y && <span class="token operator"></span> apt-get install -y --no-install-recommends openbox tigervnc-standalone-server supervisor gosu && <span class="token operator"></span> rm -rf /var/lib/apt/lists && <span class="token operator"></span> mkdir -p /usr/share/desktop-directories</span> |
В этой инструкции вы устанавливаете Debian 10 в качестве базового образа, а затем устанавливаете минимум, необходимый для запуска приложений с графическим интерфейсом в вашем контейнере. Обратите внимание, что вы запускаете apt-get update
как часть той же инструкции, чтобы предотвратить проблемы с кэшированием в Docker. Для экономии места вы также удаляете загруженные впоследствии списки пакетов (сами кэшированные пакеты удаляются по умолчанию). Вы также создаете /usr/share/desktop-directories
поскольку некоторые приложения зависят от существующего каталога.
Добавим еще один небольшой блок кода:
1 |
... <span class="token instruction"><span class="token keyword">RUN</span> apt-get update -y && <span class="token operator"></span> apt-get install -y --no-install-recommends lxterminal nano wget openssh-client rsync ca-certificates xdg-utils htop tar xzip gzip bzip2 zip unzip && <span class="token operator"></span> rm -rf /var/lib/apt/lists</span> |
В этой инструкции вы устанавливаете несколько полезных утилит и пакетов общего назначения. Особый интерес здесь представляют xdg-utils
(который предоставляет базовые команды, используемые настольными приложениями в Linux) и ca-certificates
(который устанавливает корневые сертификаты, позволяющие нам получать доступ к сайтам HTTPS).
Теперь мы можем добавить инструкции для основного приложения:
1 |
... <span class="token instruction"><span class="token keyword">RUN</span> apt-get update -y && <span class="token operator"></span> apt-get install -y --no-install-recommends thunderbird && <span class="token operator"></span> rm -rf /var/lib/apt/lists</span> |
Как и прежде, здесь мы устанавливаем приложение. Если вы помещаете в контейнер другое приложение, вы можете заменить эти команды теми, которые необходимы для установки вашего конкретного приложения. Для запуска некоторых приложений внутри Docker потребуется немного больше работы. Например, если вы устанавливаете приложение, использующее Chrome, Chromium или QtWebEngine, вам потребуется использовать аргумент командной строки --no-sandbox
, поскольку он не поддерживается в Docker.
Далее давайте начнем добавлять инструкции по добавлению последних нескольких файлов в контейнер:
1 |
... <span class="token instruction"><span class="token keyword">COPY</span> <span class="token options"><span class="token property">--from</span> <span class="token punctuation">=</span> <span class="token string">easy-novnc-build</span></span> /bin/easy-novnc /usr/local/bin/</span> <span class="token instruction"><span class="token keyword">COPY</span> menu.xml /etc/xdg/openbox/</span> <span class="token instruction"><span class="token keyword">COPY</span> supervisord.conf /etc/</span> <span class="token instruction"><span class="token keyword">EXPOSE</span> 8080</span> |
Здесь вы добавляете в образ созданные ранее файлы конфигурации и копируете двоичный файл easy-novnc
с первого этапа.
Следующий блок кода создает каталог данных и добавляет выделенного пользователя для вашего приложения. Это важно, поскольку некоторые приложения отказываются запускаться от имени пользователя root. Также рекомендуется не запускать приложения от имени пользователя root, даже в контейнере.
1 |
... <span class="token instruction"><span class="token keyword">RUN</span> groupadd --gid 1000 app && <span class="token operator"></span> useradd --home-dir /data --shell /bin/bash --uid 1000 --gid 1000 app && <span class="token operator"></span> mkdir -p /data</span> <span class="token instruction"><span class="token keyword">VOLUME</span> /data</span> |
Чтобы обеспечить согласованность UID/GID
для файлов, вы явно устанавливаете для обоих значение 1000
. Вы также монтируете том в каталог данных, чтобы гарантировать его сохранение между перезапусками.
Наконец, добавим инструкции по запуску всего:
1 |
... <span class="token instruction"><span class="token keyword">CMD</span> [ <span class="token string">"sh"</span> , <span class="token string">"-c"</span> , <span class="token string">"chown app:app /data /dev/stdout && exec gosu app supervisord"</span> ]</span> |
Установив команду по умолчанию для supervisord
, менеджер запустит процессы, необходимые для запуска вашего приложения. В этом случае вы используете CMD
, а не ENTRYPOINT
. В большинстве случаев это не имеет никакого значения, но использование CMD
лучше подходит для этой цели по нескольким причинам. Во-первых, supervisord
не принимает никаких аргументов, которые могли бы иметь для нас значение, и если вы предоставляете аргументы контейнеру, они заменяют CMD
и добавляются к ENTRYPOINT
. Во-вторых, использование CMD
позволяет нам предоставить совершенно другую команду (которая будет выполняться /bin/sh -c
) при передаче аргументов в контейнер, что упрощает отладку.
И, наконец, вам необходимо запустить chown
от имени пользователя root перед запуском supervisord
, чтобы предотвратить проблемы с разрешениями на томе данных и позволить дочерним процессам открывать stdout
. Это также означает, что вам нужно использовать gosu
вместо инструкции USER
для переключения пользователя.
Вот как будет выглядеть ваш завершенный Dockerfile
:
1 |
<span class="token instruction"><span class="token keyword">FROM</span> golang:1.14-buster <span class="token keyword">AS</span> easy-novnc-build</span> <span class="token instruction"><span class="token keyword">WORKDIR</span> /src</span> <span class="token instruction"><span class="token keyword">RUN</span> go mod init build && <span class="token operator"></span> go get github.com/geek1011/easy-novnc@v1.1.0 && <span class="token operator"></span> go build -o /bin/easy-novnc github.com/geek1011/easy-novnc</span> <span class="token instruction"><span class="token keyword">FROM</span> debian:buster</span> <span class="token instruction"><span class="token keyword">RUN</span> apt-get update -y && <span class="token operator"></span> apt-get install -y --no-install-recommends openbox tigervnc-standalone-server supervisor gosu && <span class="token operator"></span> rm -rf /var/lib/apt/lists && <span class="token operator"></span> mkdir -p /usr/share/desktop-directories</span> <span class="token instruction"><span class="token keyword">RUN</span> apt-get update -y && <span class="token operator"></span> apt-get install -y --no-install-recommends lxterminal nano wget openssh-client rsync ca-certificates xdg-utils htop tar xzip gzip bzip2 zip unzip && <span class="token operator"></span> rm -rf /var/lib/apt/lists</span> <span class="token instruction"><span class="token keyword">RUN</span> apt-get update -y && <span class="token operator"></span> apt-get install -y --no-install-recommends thunderbird && <span class="token operator"></span> rm -rf /var/lib/apt/lists</span> <span class="token instruction"><span class="token keyword">COPY</span> <span class="token options"><span class="token property">--from</span> <span class="token punctuation">=</span> <span class="token string">easy-novnc-build</span></span> /bin/easy-novnc /usr/local/bin/</span> <span class="token instruction"><span class="token keyword">COPY</span> menu.xml /etc/xdg/openbox/</span> <span class="token instruction"><span class="token keyword">COPY</span> supervisord.conf /etc/</span> <span class="token instruction"><span class="token keyword">EXPOSE</span> 8080</span> <span class="token instruction"><span class="token keyword">RUN</span> groupadd --gid 1000 app && <span class="token operator"></span> useradd --home-dir /data --shell /bin/bash --uid 1000 --gid 1000 app && <span class="token operator"></span> mkdir -p /data</span> <span class="token instruction"><span class="token keyword">VOLUME</span> /data</span> <span class="token instruction"><span class="token keyword">CMD</span> [ <span class="token string">"sh"</span> , <span class="token string">"-c"</span> , <span class="token string">"chown app:app /data /dev/stdout && exec gosu app supervisord"</span> ]</span> |
Сохраните и закройте ваш Dockerfile
. Теперь мы готовы собрать и запустить наш контейнер, а затем получить доступ к Thunderbird — приложению с графическим интерфейсом.
Следующий шаг — создать контейнер и настроить его запуск при запуске. Вы также настроите том для сохранения данных приложения между перезапусками и обновлениями.
Сначала создайте свой контейнер. Обязательно запустите эти команды в каталоге ~/thunderbird
:
1 |
<ol><li data-prefix="$"> <span class="token function">docker</span> build <span class="token parameter variable">-t</span> Thunderbird <span class="token builtin class-name">.</span></li></ol> |
Теперь создайте новую сеть, которая будет использоваться контейнерами приложения:
1 |
<ol><li data-prefix="$"> Сеть <span class="token function">Docker</span> создать Thunderbird-Net</li></ol> |
Затем создайте том для хранения данных приложения:
1 |
<ol><li data-prefix="$"> том <span class="token function">Docker</span> создает данные Thunderbird</li></ol> |
Наконец, запустите его и настройте автоматический перезапуск:
1 |
<ol><li data-prefix="$"> <span class="token function">docker</span> run <span class="token parameter variable">--detach</span> <span class="token parameter variable">--restart</span> <span class="token operator">=</span> всегда <span class="token parameter variable">--volume</span> <span class="token operator">=</span> Thunderbird-data:/data <span class="token parameter variable">--net</span> <span class="token operator">=</span> Thunderbird-net <span class="token parameter variable">--name</span> <span class="token operator">=</span><mark> приложение Thunderbird</mark> громовая птица</li></ol> |
Обратите внимание: если хотите, вы можете заменить thunderbird-app
после опции --name
другим именем. Что бы вы ни выбрали, ваше приложение теперь помещено в контейнер и работает. Теперь давайте воспользуемся веб-сервером Caddy, чтобы защитить его и удаленно подключиться к нему.
На этом этапе вы настроите веб-сервер Caddy для обеспечения аутентификации и, при необходимости, удаленного доступа к файлам через WebDAV. Для простоты и для того, чтобы вы могли использовать его с существующим обратным прокси-сервером, вы запустите его в другом контейнере.
Создайте новый каталог и затем перейдите в него:
1 |
<ol><li data-prefix="$"> <span class="token function">mkdir</span> ~/кадди</li><li data-prefix="$"> <span class="token builtin class-name">компакт-диск</span> ~/кадди</li></ol> |
Теперь создайте новый Dockerfile
, используя nano
или ваш любимый редактор:
1 |
<ol><li data-prefix="$"> <span class="token function">нано</span> ~/caddy/Dockerfile</li></ol> |
Затем добавьте следующие директивы:
1 |
<span class="token instruction"><span class="token keyword">FROM</span> golang:1.14-buster <span class="token keyword">AS</span> caddy-build</span> <span class="token instruction"><span class="token keyword">WORKDIR</span> /src</span> <span class="token instruction"><span class="token keyword">RUN</span> echo <span class="token string">'module caddy'</span> > go.mod && <span class="token operator"></span> echo <span class="token string">'require github.com/caddyserver/caddy/v2 v2.1.1'</span> >> go.mod && <span class="token operator"></span> echo <span class="token string">'require github.com/mholt/caddy-webdav v0.0.0-20200523051447-bc5d19941ac3'</span> >> go.mod</span> <span class="token instruction"><span class="token keyword">RUN</span> echo <span class="token string">'package main'</span> > caddy.go && <span class="token operator"></span> echo <span class="token string">'import caddycmd "github.com/caddyserver/caddy/v2/cmd"'</span> >> caddy.go && <span class="token operator"></span> echo <span class="token string">'import _ "github.com/caddyserver/caddy/v2/modules/standard"'</span> >> caddy.go && <span class="token operator"></span> echo <span class="token string">'import _ "github.com/mholt/caddy-webdav"'</span> >> caddy.go && <span class="token operator"></span> echo <span class="token string">'func main() { caddycmd.Main() }'</span> >> caddy.go</span> <span class="token instruction"><span class="token keyword">RUN</span> go build -o /bin/caddy .</span> <span class="token instruction"><span class="token keyword">FROM</span> debian:buster</span> <span class="token instruction"><span class="token keyword">RUN</span> apt-get update -y && <span class="token operator"></span> apt-get install -y --no-install-recommends gosu && <span class="token operator"></span> rm -rf /var/lib/apt/lists</span> <span class="token instruction"><span class="token keyword">COPY</span> <span class="token options"><span class="token property">--from</span> <span class="token punctuation">=</span> <span class="token string">caddy-build</span></span> /bin/caddy /usr/local/bin/</span> <span class="token instruction"><span class="token keyword">COPY</span> Caddyfile /etc/</span> <span class="token instruction"><span class="token keyword">EXPOSE</span> 8080</span> <span class="token instruction"><span class="token keyword">RUN</span> groupadd --gid 1000 app && <span class="token operator"></span> useradd --home-dir /data --shell /bin/bash --uid 1000 --gid 1000 app && <span class="token operator"></span> mkdir -p /data</span> <span class="token instruction"><span class="token keyword">VOLUME</span> /data</span> <span class="token instruction"><span class="token keyword">WORKDIR</span> /data</span> <span class="token instruction"><span class="token keyword">CMD</span> [ <span class="token string">"sh"</span> , <span class="token string">"-c"</span> , <span class="token string">"chown app:app /data && exec gosu app /usr/local/bin/caddy run -adapter caddyfile -config /etc/Caddyfile"</span> ]</span> |
Этот Dockerfile собирает Caddy с включенным плагином WebDAV, а затем запускает его на порту 8080
с Caddyfile
в /etc/Caddyfile
. Сохраните и закройте файл.
Далее вы настроите веб-сервер Caddy. Создайте файл с именем Caddyfile
в только что созданном каталоге:
1 |
<ol><li data-prefix="$"> <span class="token function">нано</span> ~/caddy/Caddyfile</li></ol> |
Теперь добавьте следующий блок кода в ваш Caddyfile
:
1 |
{ 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-адрес). Он также будет обслуживать веб-браузер файлов только для чтения в /files
и запускать сервер WebDAV в /webdav
, который вы можете смонтировать локально для доступа к своим файлам. Имя пользователя и пароль считываются из переменных среды APP_USERNAME
и APP_PASSWORD_HASH
.
Теперь создайте контейнер:
1 |
<ol><li data-prefix="$"> <span class="token function">docker</span> build <span class="token parameter variable">-t</span> Thunderbird-Caddy <span class="token builtin class-name">.</span></li></ol> |
Caddy v.2 требует, чтобы вы хешировали желаемый пароль. Выполните следующую команду и не забудьте заменить mypass
надежным паролем по вашему выбору:
1 |
<ol><li data-prefix="$"> <span class="token function">docker</span> run <span class="token parameter variable">--rm</span> <span class="token parameter variable">-it</span> Thunderbird-caddy caddy хэш-пароль <span class="token parameter variable">-plaintext</span> <span class="token string">'</span> mypass</mark> '</li></ol> |
Эта команда выведет строку символов. Скопируйте это в буфер обмена, чтобы подготовиться к выполнению следующей команды.
Теперь вы готовы запустить контейнер. Обязательно замените myuser
выбранным вами именем пользователя, а mypass-hash
выводом команды, которую вы выполнили на предыдущем шаге. Вы также можете изменить порт (здесь 8080
), чтобы получить доступ к вашему серверу через другой порт:
1 |
<ol><li data-prefix="$"> <span class="token function">docker</span> run <span class="token parameter variable">--detach</span> <span class="token parameter variable">--restart</span> <span class="token operator">=</span> всегда <span class="token parameter variable">--volume</span> <span class="token operator">=</span> Thunderbird-data:/data <span class="token parameter variable">--net</span> <span class="token operator">=</span> Thunderbird-net <span class="token parameter variable">--name</span> <span class="token operator">=</span> Thunderbird-Web <span class="token parameter variable">--env</span> <span class="token operator">=</span> APP_USERNAME <span class="token operator">=</span> <span class="token string">"</span> myuser</mark> " <span class="token parameter variable">--env</span> <span class="token operator">=</span> APP_PASSWORD_HASH <span class="token operator">=</span> <span class="token string">"</span> mypass-hash</mark> " <span class="token parameter variable">--публиковать</span> <span class="token operator">=</span><mark> <span class="token number">8080</span></mark> :8080 Thunderbird-Caddy</li></ol> |
Теперь мы готовы получить доступ к нашему приложению и протестировать его.
Давайте получим доступ к вашему приложению и проверим, что оно работает.
Сначала откройте http:// your_server_ip : 8080
в веб-браузере, войдите в систему, используя учетные данные, которые вы выбрали ранее, и нажмите Connect .
Теперь вы сможете взаимодействовать с приложением, и его размер должен автоматически измениться в соответствии с размером окна вашего браузера.
Если вы щелкнете правой кнопкой мыши по черному рабочему столу, вы увидите меню, позволяющее получить доступ к терминалу. Если вы щелкните средней кнопкой мыши, вы увидите список окон.
Теперь откройте http:// your_server_ip : 8080 /files/
в веб-браузере. У вас должен быть доступ к вашим файлам.
При желании вы можете попробовать смонтировать http:// your_server_ip : 8080 /webdav/
в клиенте WebDAV. Вы должны иметь возможность напрямую получать доступ к своим файлам и изменять их. Если вы используете параметр Map network drive в проводнике Windows, вам нужно будет либо использовать обратный прокси-сервер для добавления HTTPS, либо установить HKLMSYSTEMCurrentControlSetServicesWebClientParametersBasicAuthLevel
значение DWORD:2
.
В любом случае ваше собственное приложение с графическим интерфейсом теперь готово к удаленному использованию.
Теперь вы успешно настроили контейнер Docker для Thunderbird, а затем с помощью Caddy настроили доступ к нему через веб-браузер. Если вам когда-нибудь понадобится обновить приложение, остановите контейнеры, запустите docker rm thunderbird-app thunderbird-web
, заново создайте образы, а затем повторно запустите команды docker run
из предыдущих шагов, описанных выше. Ваши данные по-прежнему будут сохранены, поскольку они хранятся в томе.
Если вы хотите узнать больше об основных командах Docker, вы можете прочитать это руководство или эту шпаргалку. При долгосрочном использовании вы также можете рассмотреть возможность включения HTTPS (для этого требуется домен) для дополнительной безопасности.
Кроме того, если вы развертываете более одного приложения, вы можете использовать Docker Compose или Kubernetes вместо запуска каждого контейнера вручную. И помните, это руководство может служить основой для запуска любого другого приложения Linux на вашем сервере, включая:
Последний вариант демонстрирует большой потенциал контейнеризации и удаленного доступа к приложениям с графическим интерфейсом. Благодаря этой настройке вы теперь можете использовать сервер со значительно большей вычислительной мощностью, чем у вас локально, для запуска ресурсоемких инструментов, таких как Cutter.