One place for hosting & domains

      доступ

      Доступ к фронтальной и задней камерам с помощью getUserMedia() в JavaScript


      Введение

      В языке HTML5 появились API для доступа к аппаратному обеспечению устройств, включая MediaDevices API. Этот API предоставляет доступ к устройствам ввода мультимедиа. в том числе аудио и видео.

      С помощью этого API разработчики могут получать доступ к устройствам аудио и видео для трансляции и вывода видеопотоков в реальном времени в браузере. В этом учебном модуле вы научитесь получать доступ к видеопотокам с устройства пользователя и выводить его в браузере, используя метод getUserMedia.

      getUserMedia API использует устройства ввода мультимедиа для получения потока MediaStream. Этот поток MediaStream содержит запрошенные типы мультимедиа, то есть аудио или видео. С помощью API можно выводить видеопотоки через браузер, что можно использовать для связи в реальном времени через браузер.

      При использовании вместе с MediaStream Recording API вы можете записывать и сохранять мультимедийные данные, полученные через браузер. Этот API работает только с безопасными источниками, как и остальные новые API, а также работает с localhost и файловыми URL.

      Предварительные требования

      В начале этого учебного модуля мы объясним концепции и продемонстрируем примеры с помощью Codepen. На заключительном шаге вы создадите рабочий видеопоток в браузере.

      Шаг 1 — Проверка поддержки устройств

      Вначале мы проверим, поддерживает ли браузер пользователя mediaDevices API. Этот API существует в интерфейсе navigator и содержит текущее состояние и идентификатор пользовательского агента. Для проверки используется следующий код, который можно вставить в Codepen:

      if ('mediaDevices' in navigator && 'getUserMedia' in navigator.mediaDevices) {
        console.log("Let's get this party started")
      }
      

      Вначале проверяется наличие mediaDevices API в navigator, а затем проверяется доступность getUserMedia API в mediaDevices. Если возвращается результат true, можно начинать работу.

      Шаг 2 — Запрос разрешения пользователя

      После подтверждения поддержки getUserMedia в браузере необходимо запросить разрешение на использование устройств ввода мультимедиа на пользовательском агенте. Обычно после предоставления пользователем разрешения возвращается промис, который разрешается в мультимедийный поток. Если пользователь не дает разрешения, этот промис не возвращается, и доступ к данным устройствам блокируется.

      Вставьте в Codepen следующую строку, чтобы запросить разрешение:

      navigator.mediaDevices.getUserMedia({video: true})
      

      Объект, предоставляемый как аргумент для метода getUserMedia называется constraints (переводится как ограничения). Он определяет, к каким устройствам ввода мультимедиа вы запрашиваете разрешение на доступ. Например, если объект содержит текст audio: true, у пользователя запрашивается доступ к устройству ввода аудио.

      Шаг 3 — Понимание концепции ограничений мультимедиа

      В этом разделе мы расскажем о концепции contraints. Объект constraints — это объект MediaStreamConstraints, который указывает типы мультимедиа для запроса и требования каждого типа мультимедиа. Вы можете использовать объект constraints, чтобы указать требования к запрошенному потоку, например, требуемое разрешение (front, back).

      При отправке этого запроса следует указать аргумент audio или video. Если запрошенные типы мультимедиа не будут найдены в браузере пользователя, будет выведено сообщение об ошибке NotFoundError.

      Если вы планируете запросить видеопоток м разрешением 1280 x 720, вы можете обновить объект constraints, чтобы он выглядел так:

      {
        video: {
          width: 1280,
          height: 720,
        }
      }
      

      С этим обновлением браузер попробует подобрать заданные настройки качества для потока. Если видеоустройство не сможет обеспечить это разрешение, браузер возвратит другие доступные разрешения.

      Чтобы браузер гарантированно возвращал разрешение не ниже указанного, вам нужно будет использовать свойство min. Здесь вы можете обновить объект constraints, добавив в него свойство min:

      {
        video: {
          width: {
            min: 1280,
          },
          height: {
            min: 720,
          }
        }
      }
      

      Это обеспечит возвращаемое разрешение потока не ниже 1280 x 720. Если это минимальное требование не удастся выполнить, промис будет отклонен с сообщением об ошибке OverconstrainedError.

      В некоторых случаях вас может беспокоить сохранение данных и необходимость не превышать заданное разрешение потока. Это может быть полезно, если пользователь использует лимитированный тарифный план. Для реализации этой функции следует обновить объект constraints, добавив в него поле max:

      {
        video: {
          width: {
            min: 1280,
            max: 1920,
          },
          height: {
            min: 720,
            max: 1080
          }
        }
      }
      

      При таких параметрах браузер будет использовать разрешение видеопотока не менее 1280 x 720 и не более 1920 x 1080.

      Также можно использовать аргументы exact и ideal. Параметр ideal обычно используется со свойствами min и max для подбора наилучших возможных настроек, ближайших к идеальному значению.

      Вы можете обновить объект constraints для использования ключевого слова ideal:

      {
        video: {
          width: {
            min: 1280,
            ideal: 1920,
            max: 2560,
          },
          height: {
            min: 720,
            ideal: 1080,
            max: 1440
          }
        }
      }
      

      Чтобы указать браузеру использовать фронтальную или заднюю камеру (на мобильных устройствах), вы можете указать свойство facingMode в объекте video:

      {
        video: {
          width: {
            min: 1280,
            ideal: 1920,
            max: 2560,
          },
          height: {
            min: 720,
            ideal: 1080,
            max: 1440
          },
          facingMode: 'user'
        }
      }
      

      При этой настройке на всех устройствах всегда будет использоваться фронтальная камера. Чтобы использовать заднюю камеру мобильных устройств. вы можете изменить значение свойства facingMode на environment.

      {
        video: {
          ...
          facingMode: {
            exact: 'environment'
          }
        }
      }
      

      Шаг 4 — Использование метода enumerateDevices

      При вызове метода enumerateDevices возвращаются все доступные на компьютере пользователя устройства ввода мультимедиа.

      С помощью этого метода можно дать пользователю возможность выбрать устройство ввода мультимедиа для трансляции аудио- или видеоконтента. Этот метод возвращает промис, разрешающийся в массив MediaDeviceInfo, который содержит информацию о каждом устройстве.

      Ниже приведен фрагмент кода с примером использования этого метода:

      async function getDevices() {
        const devices = await navigator.mediaDevices.enumerateDevices();
      }
      

      Образец ответа для каждого из устройств выглядит следующим образом:

      {
        deviceId: "23e77f76e308d9b56cad920fe36883f30239491b8952ae36603c650fd5d8fbgj",
        groupId: "e0be8445bd846722962662d91c9eb04ia624aa42c2ca7c8e876187d1db3a3875",
        kind: "audiooutput",
        label: "",
      }
      

      Примечание. Ярлык не возвращается, если отсутствует доступный поток, или если пользователь не предоставит разрешения на доступ к устройству.

      Шаг 5 — Отображение видеопотока в браузере

      Вы выполнили процедуру запроса и получения доступа к мультимедийным устройствам, настроили объект constraints для добавления требуемых разрешений и выбрали камеру, которая потребуется для записи видео.

      После выполнения всех этих шагов нам нужно будет посмотреть соответствие потока трансляции заданным параметрам. Для этого мы используем элемент <video>, чтобы вывести видеопоток в браузере.

      Как уже говорилось ранее. метод getUserMedia возвращает промис, который может быть разрешен в поток. Возвращаемый поток можно конвертировать в URL объекта, используя метод createObjectURL. Этот URL будет установлен как источник видео.

      Вы создадите короткую демонстрационную программу, где пользователь сможет выбрать доступное видеоустройство из списка, используя метод enumerateDevices.

      Это метод navigator.mediaDevices. Он перечисляет доступные мультимедийные устройства, в том числе микрофоны и камеры. Он возвращает промис, который разрешается в массив объектов с подробными сведениями о доступных мультимедийных устройствах.

      Создайте файл index.html и обновите его содержимое с помощью следующего кода:

      index.html

      <!doctype html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <meta name="viewport"
                content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
          <meta http-equiv="X-UA-Compatible" content="ie=edge">
          <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
          <link rel="stylesheet" href="style.css">
          <title>Document</title>
      </head>
      <body>
      <div class="display-cover">
          <video autoplay></video>
          <canvas class="d-none"></canvas>
      
          <div class="video-options">
              <select name="" id="" class="custom-select">
                  <option value="">Select camera</option>
              </select>
          </div>
      
          <img class="screenshot-image d-none" alt="">
      
          <div class="controls">
              <button class="btn btn-danger play" title="Play"><i data-feather="play-circle"></i></button>
              <button class="btn btn-info pause d-none" title="Pause"><i data-feather="pause"></i></button>
              <button class="btn btn-outline-success screenshot d-none" title="ScreenShot"><i data-feather="image"></i></button>
          </div>
      </div>
      
      <script src="https://unpkg.com/feather-icons"></script>
      <script src="script.js"></script>
      </body>
      </html>
      

      В приведенном выше фрагменте кода вы настроили требуемые элементы и пару элементов управления видео. Также мы добавили кнопку для снимков экрана текущего видеопотока.

      Теперь применим к этим компонентам стили.

      Создайте файл style.css и скопируйте в него следующие стили. Мы включили Bootstrap, чтобы сократить объем кода CSS, который нужно будет написать для работы компонентов.

      style.css

      .screenshot-image {
          width: 150px;
          height: 90px;
          border-radius: 4px;
          border: 2px solid whitesmoke;
          box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.1);
          position: absolute;
          bottom: 5px;
          left: 10px;
          background: white;
      }
      
      .display-cover {
          display: flex;
          justify-content: center;
          align-items: center;
          width: 70%;
          margin: 5% auto;
          position: relative;
      }
      
      video {
          width: 100%;
          background: rgba(0, 0, 0, 0.2);
      }
      
      .video-options {
          position: absolute;
          left: 20px;
          top: 30px;
      }
      
      .controls {
          position: absolute;
          right: 20px;
          top: 20px;
          display: flex;
      }
      
      .controls > button {
          width: 45px;
          height: 45px;
          text-align: center;
          border-radius: 100%;
          margin: 0 6px;
          background: transparent;
      }
      
      .controls > button:hover svg {
          color: white !important;
      }
      
      @media (min-width: 300px) and (max-width: 400px) {
          .controls {
              flex-direction: column;
          }
      
          .controls button {
              margin: 5px 0 !important;
          }
      }
      
      .controls > button > svg {
          height: 20px;
          width: 18px;
          text-align: center;
          margin: 0 auto;
          padding: 0;
      }
      
      .controls button:nth-child(1) {
          border: 2px solid #D2002E;
      }
      
      .controls button:nth-child(1) svg {
          color: #D2002E;
      }
      
      .controls button:nth-child(2) {
          border: 2px solid #008496;
      }
      
      .controls button:nth-child(2) svg {
          color: #008496;
      }
      
      .controls button:nth-child(3) {
          border: 2px solid #00B541;
      }
      
      .controls button:nth-child(3) svg {
          color: #00B541;
      }
      
      .controls > button {
          width: 45px;
          height: 45px;
          text-align: center;
          border-radius: 100%;
          margin: 0 6px;
          background: transparent;
      }
      
      .controls > button:hover svg {
          color: white;
      }
      

      Следующий шаг — добавление функционала в демонстрацию. Используя метод enumerateDevices, вы получите доступные видеоустройства и зададите их как опции выбранного элемента. Создайте файл script.js и добавьте в него следующий код:

      script.js

      feather.replace();
      
      const controls = document.querySelector('.controls');
      const cameraOptions = document.querySelector('.video-options>select');
      const video = document.querySelector('video');
      const canvas = document.querySelector('canvas');
      const screenshotImage = document.querySelector('img');
      const buttons = [...controls.querySelectorAll('button')];
      let streamStarted = false;
      
      const [play, pause, screenshot] = buttons;
      
      const constraints = {
        video: {
          width: {
            min: 1280,
            ideal: 1920,
            max: 2560,
          },
          height: {
            min: 720,
            ideal: 1080,
            max: 1440
          },
        }
      };
      
      const getCameraSelection = async () => {
        const devices = await navigator.mediaDevices.enumerateDevices();
        const videoDevices = devices.filter(device => device.kind === 'videoinput');
        const options = videoDevices.map(videoDevice => {
          return `<option value="${videoDevice.deviceId}">${videoDevice.label}</option>`;
        });
        cameraOptions.innerHTML = options.join('');
      };
      
      play.onclick = () => {
        if (streamStarted) {
          video.play();
          play.classList.add('d-none');
          pause.classList.remove('d-none');
          return;
        }
        if ('mediaDevices' in navigator && navigator.mediaDevices.getUserMedia) {
          const updatedConstraints = {
            ...constraints,
            deviceId: {
              exact: cameraOptions.value
            }
          };
          startStream(updatedConstraints);
        }
      };
      
      const startStream = async (constraints) => {
        const stream = await navigator.mediaDevices.getUserMedia(constraints);
        handleStream(stream);
      };
      
      const handleStream = (stream) => {
        video.srcObject = stream;
        play.classList.add('d-none');
        pause.classList.remove('d-none');
        screenshot.classList.remove('d-none');
        streamStarted = true;
      };
      
      getCameraSelection();
      

      В приведенном выше фрагменте кода выполняется ряд действий. Давайте рассмотрим их подробнее:

      1. feather.replace(): этот метод создает экземпляр feather, набора иконок для веб-разработки.
      2. Переменная constraints хранит начальную конфигурацию видеопотока. Она будет расширена, и в нее будет добавлено выбранное пользователем мультимедийное устройство.
      3. getCameraSelection: данная функция вызывает метод enumerateDevices. Затем мы выполняем фильтрацию массива на основе разрешенного промиса, и выбираем устройства ввода видео. Из отфильтрованных результатов вы создаете <option> для элемента <select>.
      4. Вызов метода getUserMedia выполняется в средстве прослушивания onclick кнопки play. Здесь перед началом трансляции мы проверяем, поддерживает ли браузер пользователя этот метод.
      5. Затем мы вызываем функцию startStream, принимающую аргумент constraints. Она вызывает метод getUserMedia с указанными constraints. handleStream вызывается на основе трансляции из разрешенного промиса. Этот метод устанавливает возвращаемый поток для объекта srcObject видеоэлемента.

      Далее мы добавим средства прослушивания нажатий в элементы управления кнопками на страницах для приостановки, остановки и снимков экрана. Также вы добавите средство прослушивания в элемент <select>, чтобы обновить ограничения трансляции для выбранного видеоустройства.

      Добавьте в файл script.js следующий код:

      script.js

      ...
      cameraOptions.onchange = () => {
        const updatedConstraints = {
          ...constraints,
          deviceId: {
            exact: cameraOptions.value
          }
        };
        startStream(updatedConstraints);
      };
      
      const pauseStream = () => {
        video.pause();
        play.classList.remove('d-none');
        pause.classList.add('d-none');
      };
      
      const doScreenshot = () => {
        canvas.width = video.videoWidth;
        canvas.height = video.videoHeight;
        canvas.getContext('2d').drawImage(video, 0, 0);
        screenshotImage.src = canvas.toDataURL('image/webp');
        screenshotImage.classList.remove('d-none');
      };
      
      pause.onclick = pauseStream;
      screenshot.onclick = doScreenshot;
      

      Теперь, когда вы откроете файл index.html в браузере, при нажатии Play начнется трансляция.

      Вот полная версия демонстрационной программы:

      Заключение

      В этом учебном модуле мы познакомились с getUserMedia API. Это интересное дополнение HTML5, упрощающее захват мультимедийного контента через интернет.

      Данный API принимает параметр (constraints), который можно использовать для настройки доступа к устройствам ввода звука и видео. Также его можно использовать, чтобы задать требуемое разрешение видео для вашего приложения.

      Вы можете расширить демонстрацию и предоставить пользователю возможность сохранить сделанные снимки экрана, а также записывать и сохранять аудио- и видеоданные с помощью MediaStream Recording API.



      Source link

      Дистанционный доступ к GUI-приложениям с помощью Docker и Caddy в Ubuntu 18.04


      Автор выбрал фонд Free and Open Source Fund для получения пожертвования в рамках программы Write for DOnations.

      Введение

      Несмотря на рост популярности облачных сервисов, необходимость использования локальных приложений все еще существует.

      Используя noVNC и TigerVNC, вы можете запускать локальные приложения в контейнере Docker и удаленно подключаться к ним через браузер. Кроме того, вы можете запускать свои прложения на сервере, где доступно больше системных ресурсов, чем в локальной системе, что обеспечивает дополнительную гибкость при запуске ресурсоемких приложений.

      В этом обучающем модуле мы используем Docker для контейнеризации клиента электронной почты Mozilla Thunderbird. После этого мы защитим контейнер и предоставим удаленной доступ к нему с помощью веб-сервера Caddy.

      После завершения работы вы сможете подключаться к Thunderbird с любого устройства, используя только браузер. Также у вас будет возможность локального доступа к его файлам через WebDAV. У вас также будет автономный образ Docker, который вы сможете использовать где угодно.

      Предварительные требования

      Для прохождения этого обучающего модуля вам потребуется следующее:

      Шаг 1 &mdash; Создание конфигурации </g>supervisord<g id=“1” ctype=“x-CODE” equiv-text=“`”>“

      Мы запустили сервер и установили Docker и теперь можем начать настройку контейнера нашего приложения. Поскольку контейнер состоит из нескольких компонентов, необходимо использовать диспетчер процессов для их запуска и мониторинга. Мы будем использовать supervisord. Диспетчер процессов supervisord написан на языке Python и часто используется для организации сложных контейнеров.

      Вначале создайте для контейнера каталог thunderbird и войдите в него:

      • mkdir ~/thunderbird
      • cd ~/thunderbird

      Затем создайте файл supervisord.conf и откройте его в nano или другом предпочитаемом редакторе:

      Добавьте в файл 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 (подробности ниже), а для параметра logfile — значение stdout, чтобы вы могли просматривать журналы.

      Добавьте еще один небольшой блок кода в файл 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 — это протокол сервера дисплея, позволяющийо запускать приложения с графическим интерфейсом. В будущем он будет заменен протоколом Wayland, однако на данный момент удаленный доступ все еще находится в стадии разработки.

      Для этого контейнера мы используем TigerVNC и его встроенный сервер VNC. Это дает ряд преимуществ по сравнению с использованием отдельных серверов X11 и VNC:

      • Быстрое время отклика, поскольку отрисовка графического интерфейса выполняется непосредственно на сервере VNC, а не в промежуточном буфере кадров (памяти, где хранится содержимое экрана).
      • Автоматическое изменение размера экрана, позволяющее удаленным приложениям автоматически изменять размер экрана в соответствии с клиентом (в данном случае — с окном браузера).

      При желании вы можете изменить аргумент опции -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. Этот сервер выполняет две роли. Во первых, он предоставляет простую страницу подключения, где вы можете настроить параметры подключения и задать параметры по умолчанию. Во вторых, он выступает в качестве прокси для VNC через WebSocket, обеспечивая возможность доступа через обычный браузер.

      Изменение размера обычно выполняется на стороне клиента (т. е. масштабирование изображения), но мы используем опцию resize=remote, чтобы в полной мере воспользоваться преимуществами дистанционной коррекции разрешения TigerVNC. Также при этом обеспечивается более низкое время задержки на медленных устройствах, таких как Chromebook нижнего ценового диапазона:

      Примечание. В этом обучающем модуле используется easy-novnc. При желании вы можете использовать websockify и отдельный веб-сервер. Преимущество 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
      

      В этом последнем блоке мы устанавливаем для параметра priority значение 1, чтобы Thunderbird запускался после TigerVNC, поскольку в ином случае он будет запускаться через очередь, то есть иногда вообще не запускаться. Также мы зададим 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 путем к исполняемому файлу вашего приложения. Если нет, то теперь вы можете настроить главное меню графического пользовательского интерфейса.

      Шаг 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 путем к исполняемому файлу вашего приложения и измените ярлык элемента.

      Шаг 3 — Создание файла Dockerfile

      Мы настроили OpenBox и теперь перейдем к созданию файла Dockerfile, который объединяет все вместе.

      Создайте файл Dockerfile в каталоге вашего контейнера:

      • nano ~/thunderbird/Dockerfile

      Вначале добавим код для создания easy-novnc:

      ~/thunderbird/Dockerfile

      FROM golang:1.14-buster AS easy-novnc-build
      WORKDIR /src
      RUN go mod init build && 
          go get github.com/geek1011/easy-novnc@v1.1.0 && 
          go build -o /bin/easy-novnc github.com/geek1011/easy-novnc
      

      На первом этапе мы создаем easy-novnc. Мы выделили это в отдельный шаг для простоты и экономии места &mdash; в окончательном образе нам не нужна вся цепочка инструментов Go. Обратите внимание на @v1.1.0 в команде build. Это обеспечивает детерминистический результат, что важно, поскольку Docker кэширует результаты каждого шага. Если мы прямо не укажем версию, Docker будет ссылаться на последнюю версию easy-novnc на момент создания образа. Кроме того, нам нужно гарантировать загрузку определенной версии easy-novnc на случай радикальных изменений в интерфейсе командной строки.

      На втором этапе мы создадим окончательный образ. Здесь мы будем использовать Debian 10 (buster) как базовый образ. Обратите внимание, что поскольку приложение работает в контейнере, оно будет работать вне зависимости от версии дистрибутива на вашем сервере.

      Затем добавьте следующий блок в файл Dockerfile:

      ~/thunderbird/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 как базовый образ, а затем устанавливаем минимальный набор средств для запуска в контейнере приложений с графическим интерфейсом. Обратите внимание, что мы запускаем run apt-get update в рамках той же инструкции, чтобы предотвратить проблемы с кэшированием в Docker. Для экономии места мы также удаляем списки пакетов, загруженные после этого (сами кэшированные пакеты удаляются по умолчанию). Также мы создаем каталог /usr/share/desktop-directories, поскольку некоторые приложения зависят от наличия этого каталога.

      Добавим еще один небольшой блок кода:

      ~/thunderbird/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).

      Теперь мы можем добавить инструкции для основного приложения:

      ~/thunderbird/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 это не поддерживается.

      Затем добавим инструкции для добавления в контейнер последних нескольких файлов:

      ~/thunderbird/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 даже в контейнере.

      ~/thunderbird/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. Также мы монтируем том в каталог данных, чтобы он сохранялся при перезапуске системы.

      В заключение добавим инструкции для запуска всех элементов:

      ~/thunderbird/Dockerfile

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

      Если задать команду по умолчанию supervisord, диспетчер будет запускать необходимые процессы для работы вашего приложения. В этом случае мы используем CMD вместо ENTRYPOINT. В большинстве случаев разницы не будет, однако есть несколько причин, по которым CMD лучше подходит для этой цели. Во первых, supervisord не принимает никакие нужные нам аргументы, а если передавать аргументы в контейнер, они заменяют CMD и добавляются к ENTRYPOINT. Во вторых, использование CMD позволяет нам отправлять при передаче аргументов в контейнер совершенно другую команду (которая выполняется /bin/sh -c), что упрощает отладку.

      Кроме того, нам нужно запускать chown с првилегиями root перед запуском supervisord, чтобы предотвратить проблемы с разрешениями доступа к тому данным и дать дочерним процессам возможность открывать stdout. Также это означает, что для переключения пользователя нам нужно использовать gosu вместо инструкции USER.

      Вот так будет выглядеть заполненный файл Dockerfile:

      ~/thunderbird/Dockerfile

      FROM golang:1.14-buster AS easy-novnc-build
      WORKDIR /src
      RUN go mod init build && 
          go get github.com/geek1011/easy-novnc@v1.1.0 && 
          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 — приложение с графическим интерфейсом.

      Шаг 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, чтобы защитить его и удаленно подключаться к нему.

      Шаг 5 — Настройка Caddy

      На этом шаге мы настроим веб-сервер Caddy для аутентификации и (при желании) удаленного доступа к файлам через WebDAV. Для удобства и возможности использования с обратным прокси-сервером мы запустим его в другом контейнере.

      Создайте новый каталог и перейдите в него:

      Создайте новый файл Dockerfile в 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.0.0' >> 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 выполняет сборку Caddy с включенным плагином WebDAV и запускает его на порту 8080 с файлом Caddyfile в каталоге /etc/Caddyfile. Сохраните и закройте файл.

      Далее мы настроим веб-сервер Caddy. Создайте файл с именем Caddyfile в только что созданном вами каталоге:

      Добавьте следующий блок кода в файл Caddyfile:

      ~/caddy/Caddyfile

      {
          order webdav last
      }
      :8080 {
          log
          root * /data
          reverse_proxy thunderbird-app:8080
      
          handle /files/* {
              uri strip_prefix /files
              file_server browse
          }
          redir /files /files/
      
          handle /webdav/* {
              uri strip_prefix /webdav
              webdav
          }
          redir /webdav /webdav/
      
          basicauth /* {
              {env.APP_USERNAME} {env.APP_PASSWORD_HASH}
          }
      }
      

      Этот файл Caddyfile выступает в качестве посредника между корневым каталогом и контейнером thunderbird-app, который мы создали на шаге Step 4 (Docker разрешает его с правильным IP-адресом). Также он выступает в качестве файлового веб-браузера с доступом только для чтения к каталогу /files и запускает сервер WebDAV в каталоге /webdav, который вы можете монтировать в локальную систему для доступа к своим файлам. Имя пользователя и пароль считываются из переменных среды APP_USERNAME и APP_PASSWORD_HASH.

      Выполните сборку контейнера:

      • docker build -t thunderbird-caddy .

      Caddy v.2 требует хэшировать желаемый пароль. Запустите следующую команду, заменив 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 — Тестирование приложения и управление им

      Откроем приложение и проверим его работу.

      Откройте в браузере адрес http://your_server_ip:8080, введите ранее выбранные учетные данные и нажмите Connect.

      Страница подключения NoVNC

      Теперь вы должны иметь возможность взаимодействия с приложением, а его размер экрана должен автоматически измениться для соответствия размеру окна браузера.

      Главное меню Thunderbird

      Если нажать правой кнопкой мыши на черный рабочий стол, вы увидите меню, позволяющее открыть терминал. Если вы нажмете среднюю кнопку мыши, вы увидите список окон.

      Нажатие правой кнопкой мыши на NoVNC

      Откройте в браузере адрес http://your_server_ip:8080/files/. Теперь у вас должен быть доступ к вашим файлам.

      Доступ к файлам NoVNC через webdav

      Также вы можете смонтировать http://your_server_ip:8080/webdav/ в клиент WebDAV. Теперь у вас должен иметься прямой доступ к файлам с возможностью их изменения. Если вы используете опцию «Подключение сетевого диска» в Проводнике Windows, вам нужно будет использовать обратный прокси для добавления HTTPS или задать для параметра HKLMSYSTEMCurrentControlSetServicesWebClientParametersBasicAuthLevel значение DWORD:2.

      В любом случае ваше приложение с графическим интерфейсом теперь доступно для удаленного использования.

      Заключение

      Мы успешно настроили контейнер Docker для приложения Thunderbird и использовали Caddy для настройки доступа к нему через браузер. Если вам потребуется обновить приложение, остановите контейнеры, запустите команду docker rm thunderbird-app thunderbird-web, выполните сборку образов заново и снова запустите команды docker run, как было указано в предыдущих шагах. Ваши данные хранятся на томе, и поэтому они останутся доступными.

      Если вы хотите узнать больше о базовых командах Docker, ознакомьтесь с этим обучающим модулем или этим справочником. Если вы планируете долгосрочное использование, вы можете включить протокол HTTPS (для этого требуется домен) для дополнительной безопасности.

      Если вы развертываете несколько приложений, вы можете использовать Docker Compose или Kubernetes вместо того, чтобы запускать каждый контейнер вручную. Этот обучающий модуль можно использовать для запуска любых других приложений Linux на вашем сервере, в том числе:

      • Wine, слой совместимости для запуска приложений Windows в Linux.
      • GIMP, графический редактор с открытым исходным кодом.
      • Cutter, платформа обратного инжиниринга с открытым исходным кодом.

      Последний вариант открывает большой потенциал контейнеризации приложений с графическим интерфейсом и удаленного доступа к ним. При такой настройке вы можете использовать для запуска ресурсоемких инструментов, таких как Cutter, не локальную систему, а сервер, имеющий большую вычислительную мощность.



      Source link

      Дистанционный доступ к GUI-приложениям с помощью Docker и Caddy в Ubuntu 20.04


      Автор выбрал фонд Free and Open Source Fund для получения пожертвования в рамках программы Write for DOnations.

      Введение

      Несмотря на рост популярности облачных сервисов, необходимость использования локальных приложений все еще существует.

      Используя noVNC и TigerVNC, вы можете запускать локальные приложения в контейнере Docker и удаленно подключаться к ним через браузер. Кроме того, вы можете запускать свои приложения на сервере, где доступно больше системных ресурсов, чем в локальной системе, что обеспечивает дополнительную гибкость при запуске ресурсоемких приложений.

      В этом обучающем модуле мы используем Docker для контейнеризации клиента электронной почты Mozilla Thunderbird. После этого мы защитим контейнер и предоставим удаленной доступ к нему с помощью веб-сервера Caddy.

      После завершения работы вы сможете подключаться к Thunderbird с любого устройства, используя только браузер. Также у вас будет возможность локального доступа к его файлам через WebDAV. У вас также будет автономный образ Docker, который вы сможете использовать где угодно.

      Предварительные требования

      Для прохождения этого обучающего модуля вам потребуется следующее:

      Шаг 1 — Создание конфигурации supervisord

      Мы запустили сервер и установили Docker и теперь можем начать настройку контейнера нашего приложения. Поскольку контейнер состоит из нескольких компонентов, необходимо использовать диспетчер процессов для их запуска и мониторинга. Мы будем использовать supervisord. Диспетчер процессов supervisord написан на языке Python и часто используется для организации сложных контейнеров.

      Вначале создайте для контейнера каталог thunderbird и войдите в него:

      • mkdir ~/thunderbird
      • cd ~/thunderbird

      Затем создайте файл supervisord.conf и откройте его в nano или другом предпочитаемом редакторе:

      • 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 (подробности ниже), а для параметра logfile — значение stdout, чтобы вы могли просматривать журналы.

      Добавьте еще один небольшой блок кода в файл 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 — это протокол сервера дисплея, позволяющий запускать приложения с графическим интерфейсом пользователя. В будущем он будет заменен протоколом Wayland, однако на данный момент удаленный доступ все еще находится в стадии разработки.

      Для этого контейнера мы используем TigerVNC и его встроенный сервер VNC. Это дает ряд преимуществ по сравнению с использованием отдельных серверов X11 и VNC:

      • Быстрое время отклика, поскольку отрисовка графического интерфейса выполняется непосредственно на сервере VNC, а не в промежуточном буфере кадров (памяти, где хранится содержимое экрана).
      • Автоматическое изменение размера экрана, позволяющее удаленным приложениям автоматически изменять размер экрана в соответствии с клиентом (в данном случае — с окном браузера).

      При желании вы можете изменить аргумент опции -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. Этот сервер выполняет две роли. Во-первых, он предоставляет простую страницу подключения, где вы можете настроить параметры подключения и задать параметры по умолчанию. Во-вторых, он выступает в качестве прокси для VNC через WebSocket, обеспечивая возможность доступа через обычный браузер.

      Изменение размера обычно выполняется на стороне клиента (т. е. масштабирование изображения), но мы используем опцию resize=remote, чтобы в полной мере воспользоваться преимуществами дистанционной коррекции разрешения TigerVNC. Также при этом обеспечивается более низкое время задержки на медленных устройствах, таких как Chromebook нижнего ценового диапазона:

      Примечание. В этом обучающем модуле используется easy-novnc. При желании вы можете использовать websockify и отдельный веб-сервер. Преимущество 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
      

      В этом последнем блоке мы устанавливаем для параметра priority значение 1, чтобы Thunderbird запускался после TigerVNC, поскольку в ином случае он будет запускаться через очередь, то есть иногда вообще не запускаться. Также мы зададим 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 путем к исполняемому файлу вашего приложения. Если нет, то теперь вы можете настроить главное меню графического пользовательского интерфейса.

      Шаг 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:

      ~/thunderbird/Dockerfile

      FROM golang:1.14-buster AS easy-novnc-build
      WORKDIR /src
      RUN go mod init build && 
          go get github.com/geek1011/easy-novnc@v1.1.0 && 
          go build -o /bin/easy-novnc github.com/geek1011/easy-novnc
      

      На первом этапе мы создаем easy-novnc. Мы выделили это в отдельный шаг для простоты и экономии места — в окончательном образе нам не нужна вся цепочка инструментов Go. Обратите внимание на @v1.1.0 в команде build. Это обеспечивает детерминистический результат, что важно, поскольку Docker кэширует результаты каждого шага. Если мы прямо не укажем версию, Docker будет ссылаться на последнюю версию easy-novnc на момент создания образа. Кроме того, нам нужно гарантировать загрузку определенной версии easy-novnc на случай радикальных изменений в интерфейсе командной строки.

      На втором этапе мы создадим окончательный образ. Здесь мы будем использовать Debian 10 (buster) как базовый образ. Обратите внимание, что поскольку приложение работает в контейнере, оно будет работать вне зависимости от версии дистрибутива на вашем сервере.

      Затем добавьте следующий блок в файл Dockerfile:

      ~/thunderbird/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 как базовый образ, а затем устанавливаем минимальный набор средств для запуска в контейнере приложений с графическим интерфейсом. Обратите внимание, что мы запускаем apt-get update в рамках той же инструкции, чтобы предотвратить проблемы с кэшированием в Docker. Для экономии места мы также удаляем списки пакетов, загруженные после этого (сами кэшированные пакеты удаляются по умолчанию). Также мы создаем каталог /usr/share/desktop-directories, поскольку некоторые приложения зависят от наличия этого каталога.

      Добавим еще один небольшой блок кода:

      ~/thunderbird/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).

      Теперь мы можем добавить инструкции для основного приложения:

      ~/thunderbird/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 это не поддерживается.

      Затем добавим инструкции для добавления в контейнер последних нескольких файлов:

      ~/thunderbird/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 даже в контейнере.

      ~/thunderbird/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. Также мы монтируем том в каталог данных, чтобы он сохранялся при перезапуске системы.

      В заключение добавим инструкции для запуска всех элементов:

      ~/thunderbird/Dockerfile

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

      Если задать команду по умолчанию supervisord, диспетчер будет запускать необходимые процессы для работы вашего приложения. В этом случае мы используем CMD вместо ENTRYPOINT. В большинстве случаев разницы не будет, однако есть несколько причин, по которым CMD лучше подходит для этой цели. Во-первых, supervisord не принимает никакие нужные нам аргументы, а если передавать аргументы в контейнер, они заменяют CMD и добавляются к ENTRYPOINT. Во-вторых, использование CMD позволяет нам отправлять при передаче аргументов в контейнер совершенно другую команду (которая выполняется /bin/sh -c), что упрощает отладку.

      Кроме того, нам нужно запускать chown с привилегиями root перед запуском supervisord, чтобы предотвратить проблемы с разрешениями доступа к тому данных и дать дочерним процессам возможность открывать stdout. Также это означает, что для переключения пользователя нам нужно использовать gosu вместо инструкции USER.

      Вот так будет выглядеть заполненный файл Dockerfile:

      ~/thunderbird/Dockerfile

      FROM golang:1.14-buster AS easy-novnc-build
      WORKDIR /src
      RUN go mod init build && 
          go get github.com/geek1011/easy-novnc@v1.1.0 && 
          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 — приложение с графическим интерфейсом.

      Шаг 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, чтобы защитить его и удаленно подключаться к нему.

      Шаг 5 — Настройка Caddy

      На этом шаге мы настроим веб-сервер Caddy для аутентификации и (при желании) удаленного доступа к файлам через WebDAV. Для удобства и возможности использования с обратным прокси-сервером мы запустим его в другом контейнере.

      Создайте новый каталог и перейдите в него:

      Создайте новый файл Dockerfile в 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.0.0' >> 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 выполняет сборку Caddy с включенным плагином WebDAV, а затем запускает его на порту 8080 с файлом Caddyfile в каталоге /etc/Caddyfile. Сохраните и закройте файл.

      Далее мы настроим веб-сервер Caddy. Создайте файл с именем Caddyfile в только что созданном вами каталоге:

      Добавьте следующий блок кода в файл Caddyfile:

      ~/caddy/Caddyfile

      {
          order webdav last
      }
      :8080 {
          log
          root * /data
          reverse_proxy thunderbird-app:8080
      
          handle /files/* {
              uri strip_prefix /files
              file_server browse
          }
          redir /files /files/
      
          handle /webdav/* {
              uri strip_prefix /webdav
              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.

      Выполните сборку контейнера:

      • 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 — Тестирование приложения и управление им

      Откроем приложение и проверим его работу.

      Откройте в браузере адрес http://your_server_ip:8080, введите ранее выбранные учетные данные и нажмите Connect (Подключиться).

      Страница подключения NoVNC

      Теперь вы должны иметь возможность взаимодействия с приложением, а его размер экрана должен автоматически измениться для соответствия размеру окна браузера.

      Главное меню Thunderbird

      Если нажать правой кнопкой мыши на черный рабочий стол, вы увидите меню, позволяющее открыть терминал. Если вы нажмете среднюю кнопку мыши, вы увидите список окон.

      Нажатие правой кнопкой мыши на NoVNC

      Откройте в браузере адрес http://your_server_ip:8080/files/. Теперь у вас должен быть доступ к вашим файлам.

      Доступ к файлам NoVNC через webdav

      Также вы можете смонтировать http://your_server_ip:8080/webdav/ в клиент WebDAV. Теперь у вас должен иметься прямой доступ к файлам с возможностью их изменения. Если вы используете опцию «Подключение сетевого диска» в Проводнике Windows, вам нужно будет использовать обратный прокси для добавления HTTPS или задать для параметра HKLMSYSTEMCurrentControlSetServicesWebClientParametersBasicAuthLevel значение DWORD:2.

      В любом случае ваше приложение с графическим интерфейсом теперь доступно для удаленного использования.

      Заключение

      Мы успешно настроили контейнер Docker для приложения Thunderbird и использовали Caddy для настройки доступа к нему через браузер. Если вам потребуется обновить приложение, остановите контейнеры, запустите команду docker rm thunderbird-app thunderbird-web, выполните сборку образов заново и снова запустите команды docker run, как было указано в предыдущих шагах. Ваши данные хранятся на томе, и поэтому они останутся доступными.

      Если вы хотите узнать больше о базовых командах Docker, ознакомьтесь с этим обучающим модулем или этим руководством. Если вы планируете долгосрочное использование, вы можете включить протокол HTTPS (для этого требуется домен) для дополнительной безопасности.

      Если вы развертываете несколько приложений, вы можете использовать Docker Compose или Kubernetes вместо того, чтобы запускать каждый контейнер вручную. Этот обучающий модуль можно использовать для запуска любых других приложений Linux на вашем сервере, в том числе:

      • Wine, слой совместимости для запуска приложений Windows в Linux.
      • GIMP, графический редактор с открытым исходным кодом.
      • Cutter, платформа обратного инжиниринга с открытым исходным кодом.

      Последний вариант открывает большой потенциал контейнеризации приложений с графическим интерфейсом и удаленного доступа к ним. При такой настройке вы можете использовать для запуска ресурсоемких инструментов, таких как Cutter, не локальную систему, а сервер, имеющий большую вычислительную мощность.



      Source link