El autor seleccionó la Free and Open Source Fund para recibir una donación como parte del programa Write for DOnations.
Aún con la creciente popularidad de los servicios en la nube, la necesidad de ejecutar aplicaciones nativas sigue existiendo.
Con noVNC y TigerVNC, puede ejecutar aplicaciones nativas dentro de un contenedor Docker y acceder a ellas remotamente usando un navegador web. Adicionalmente, puede ejecutar su aplicación en un servidor con más recursos del sistema de los que pueda tener disponibles localmente, lo que puede proporcionar mayor flexibilidad cuando se ejecutan grandes aplicaciones.
En este tutorial, pondrá en un contendor Mozilla Thunderbird, un cliente de correo electrónico, usando Docker. Tras ello, lo protegerá y proporcionará acceso remoto usando el servidor web Caddy.
Cuando haya terminado, podrá acceder a Thunderbird desde cualquier dispositivo usando únicamente un navegador web. Opcionalmente, podrá acceder localmente a los archivos usando WebDAV. También tendrá una imagen Docker completamente autocontenida que puede ejecutar en cualquier lugar.
Antes de iniciar esta guía, necesitará lo siguiente:
sudo
.supervisord
Ahora que su servidor está ejecutándose y Docker está instalado, está listo para comenzar a configurar el contenedor de su aplicación. Ya que su contenedor consta de varios componentes, deberá usar un administrador de procesos para iniciarlos y monitorizarlos. Aquí usará supervisord
. supervisord
es un gestor de procesos escrito en Python que se utiliza a menudo para organizar contenedores complejos.
Primero, cree y entre en un directorio llamado thunderbird
para su contenedor.
- mkdir ~/thunderbird
- cd ~/thunderbird
Ahora cree y abra un archivo llamado supervisord.conf
usando nano
o su editor preferido:
- nano supervisord.conf
Ahora añada este primer bloque de código en supervisord.conf
, lo que definirá las opciones globales para supervisord:
[supervisord]
nodaemon=true
pidfile=/tmp/supervisord.pid
logfile=/dev/fd/1
logfile_maxbytes=0
En este bloque, está configurando supervisord
. Deberá establecer nodaemon
a true
porque se ejecutará dentro de un contenedor Docker como el punto de entrada. Por tanto, querrá que permanezca en ejecución en primer plano. También configura pidfile
a una ruta accesible por un usuario non-root (más sobre esto más tarde), y logfile
to stdout para que pueda ver los registros.
A continuación, añada otro bloque de código pequeño a supervisord.conf
. Este bloque inicia TigerVNC que es un servidor VNC/X11 combinado:
...
[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
En este bloque, está configurando el servidor X11. X11 es un protocolo de servidor de visualización, que es lo que permite que se ejecuten las aplicaciones GUI. Tenga en cuenta que en el futuro se sustituirá con Wayland, pero el acceso remoto aún está en desarrollo.
Para este contenedor, está usando TigerVNC y está integrado en el servidor VNC. Esto tiene varias ventajas sobre el uso de un servidor X11 y VNC:
Si lo desea, puede cambiar el argumento para la opción -desktop
desde Thunderbird
a otra cosa que elija. El servidor mostrará su opción como el título de la página web usada para acceder a su aplicación.
Ahora, vamos a añadir un tercer bloque de código a supervisord.conf
para iniciar easy-novnc
:
...
[program:easy-novnc]
priority=0
command=/usr/local/bin/easy-novnc --addr :8080 --host localhost --port 5900 --no-url-password --novnc-params "resize=remote"
autorestart=true
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
redirect_stderr=true
En este bloque, está configurando easy-novnc
, un servidor independiente que ofrece un envoltorio alrededor de noVNC. Este servidor realiza dos funciones. Primero, proporciona una página sencilla de conexión que le permite configurar opciones para la conexión, y le permite establecer las predeterminadas. Segundo, realiza proxy a VNC sobre WebSocket, lo que permite el acceso a través de un navegador web ordinario.
Normalmente, el cambio de tamaño se realiza en el lado del cliente (es decir, escala de imagen), pero está usando la opción resize=remote
para aprovechar los ajustes de resolución remota de TigerVNC. Esto también proporciona una menor latencia en dispositivos más lentos, como Chromebooks de gama baja:
Nota: Este tutorial utiliza easy-novnc
. Si lo desea, puede usar websockfy
y un servidor web independiente. La ventaja de easy-novnc
es que el uso de memoria y el tiempo de inicio es significativamente menor que si fuese auto-contenido. easy-novnc
también proporciona una página conexión más limpia que la de noVNC predeterminado, y permite establecer opciones predeterminadas que son útiles para esta configuración (como resize=remote
).
Ahora añada el siguiente bloque a su configuración para iniciar OpenBox, el gestor de ventanas:
...
[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
En este bloque, está configurando OpenBox, un gestor de ventanas X11 ligero. Podría omitir este paso, pero sin él no tendría las barras de título o podría cambiar el tamaño de las ventanas.
Finalmente, vamos a añadir el último bloque a supervisord.conf
, que iniciará la aplicación principal:
...
[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
En este bloque final, está configurando priority
a 1
para garantizar que Thunderbird se inicia tras TigerVNC, o encontrará una condición de carrera o fallaría aleatoriamente al inicio. También configuramos autorestart=true
para que vuelva a abrir automáticamente la aplicación si se cierra por error. La variable de entorno DISPLAY
indica a la aplicación que se muestre en el servidor VNC que configuró anteriormente.
Este es el aspecto que tendrá su supervisord.conf
completado:
[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
Si desea poner una aplicación diferente en contenedor, sustituya /usr/bin/thunderbird
con la ruta al ejecutable de su aplicación. De lo contrario, estará listo para configurar el menú principal de su GUI.
Ahora que su gestor de procesos está configurado, vamos a configurar el menú de OpenBox. Este menú nos permite abrir aplicaciones dentro del contenedor. También incluiremos un monitor de terminal y procesos para depurar si es necesario.
Dentro del directorio de la aplicación, utilice nano
o su editor de texto favorito para crear y abrir un nuevo archivo llamado menu.xml
:
- nano ~/thunderbird/menu.xml
Ahora añada el siguiente código a 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>
Este archivo XML contiene los elementos del menú que aparecerán cuando haga clic con el botón derecho sobre el escritorio. Cada elemento consta de una etiqueta y una acción.
Si desea añadir al contenedor una aplicación diferente, sustituya /usr/bin/thunderbird
con la ruta al ejecutable de su aplicación y cambie la etiqueta
del elemento.
Ahora que OpenBox está configurado, creará el Dockerfile, que une todo.
Crear un Dockerfile en el directorio de su contenedor:
- nano ~/thunderbird/Dockerfile
Para comenzar, vamos a añadir algún código para crear easy-novnc
:
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
En la primera etapa, está creando easy-novnc
. Esto se hace en una etapa independiente para mayor simplificada y para ahorrar espacio; no necesita toda la cadena de herramientas de go en su imagen final. Observe @v1.1.0
en el comando de compilación. Esto garantiza que el resultado sea determinista, lo cual es importante porque Docker captura el resultado de cada paso. Si no hubiese especificado una versión explícita, Docker haría referencia a la versión más reciente de easy-novnc
en el momento en que se compiló la imagen por primera vez. Además, desea garantizar que descarga una versión específica de easy-novnc
, en el caso de que se realicen cambios de ruptura en la interfaz CLI.````````
Ahora vamos a crear la segunda etapa que será la imagen final. Aquí usará Debian 10 (buster) como imagen base. Observe que dado que se está ejecutando en un contenedor, funcionará independientemente de la distribución que esté ejecutando en su servidor.
A continuación, añada el siguiente bloque a su 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
En esta instrucción, está instalando Debian 10 como su imagen base y luego instalando el mínimo necesario para ejecutar aplicaciones GUI en su contenedor. Observe que ejecuta apt-get update
como parte de la misma instrucción para evitar problemas de captura desde Docker. Para ahorrar espacio, también está eliminando las listas de paquetes descargadas posteriormente (los paquetes en caché en sí mismos se eliminan por defecto). Está creando /usr/share/desktop-directories
porque algunas aplicaciones dependen de que exista el directorio.
Vamos a añadir otro bloque de código pequeño:
...
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
En esta instrucción, está instalando algunas utilidades y paquetes útiles de uso general. Son interesantes xdg-utils
(que proporciona los comandos base usados por las aplicaciones de escritorio en Linux) y ca-certificates
(que instala los certificados raíz para permitirnos acceder a los sitios HTTPS).
Ahora, podemos añadir las instrucciones para la aplicación principal:
...
RUN apt-get update -y && \
apt-get install -y --no-install-recommends thunderbird && \
rm -rf /var/lib/apt/lists
Como antes, aquí estamos instalando la aplicación. Si añade una aplicación diferente al contenedor, puede sustituir estos comandos con los necesarios para instalar su aplicación específica. Algunas aplicaciones requerirán un poco más de trabajo para ejecutarse dentro de Docker. Por ejemplo, si está instalando una aplicación que utiliza Chrome, Chromium o QtWebEngine, deberá usar el argumento de línea de comando --no-sandbox
porque no será compatible en Docker.
A continuación, vamos a comenzar añadiendo las instrucciones para añadir las últimas líneas al contenedor:
...
COPY /bin/easy-novnc /usr/local/bin/
COPY menu.xml /etc/xdg/openbox/
COPY supervisord.conf /etc/
EXPOSE 8080
Aquí está añadiendo los archivos de configuración que creó anteriormente a la imagen y copiando el binario easy-novnc
de la primera etapa.
Este siguiente bloque de código crea el directorio de datos y añade un usuario dedicado para su aplicación. Esto es importante porque algunas aplicaciones no se ejecutan como root. También es una buena práctica no ejecutar aplicaciones como root, incluso en un contenedor.
...
RUN groupadd --gid 1000 app && \
useradd --home-dir /data --shell /bin/bash --uid 1000 --gid 1000 app && \
mkdir -p /data
VOLUME /data
Para garantizar un UID/GID
consistente para los archivos, está estableciendo explícitamente ambos a 1000
. También está montando un volumen sobre el directorio de datos para garantizar que persiste entre los reinicios.
Finalmente, vamos a añadir las instrucciones para iniciar todo:
...
CMD ["sh", "-c", "chown app:app /data /dev/stdout && exec gosu app supervisord"]
Al establecer el comando predeterminado a supervisord
, el administrador iniciará los procesos requeridos para ejecutar su aplicación. En este caso, está usando CMD
en vez de ENTRYPOINT
. En la mayoría de los casos, no supondría una diferencia, pero usar CMD
es mejor para este fin por algunos motivos. Primero, supervisord
no toma ningún argumento que sería relevante para nosotros, y si proporciona argumentos al contenedor, sustituye CMD
y se anexan a ENTRYPOINT
. Segundo, usar CMD
nos permite proporcionar un comando completamente diferente (que se será ejecutado por /bin/sh -c
) cuando se pasan argumentos al contenedor, lo que hace que la depuración sea más fácil.
Y finalmente, deberá ejecutar chown
como raíz antes de iniciar supervisord
para evitar problemas de permisos sobre el volumen de datos y para permitir que se abran los procesos secundarios stdout
. Esto también significa que deberá usar gosu
en vez de la instrucción USER
para cambiar al usuario.
Este es el aspecto que tendrá su archivo Dockerfile
completado:
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 /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"]
Guarde y cierre su Dockerfile
. Ahora estamos listos para crear y ejecutar nuestro contenedor, y luego acceder a Thunderbird - una aplicación GUI.
El siguiente paso es crear su contenedor y configurarlo para que se ejecute al inicio. También configurará un volumen para conservar los datos de la aplicación entre reinicios y actualizaciones.
Primero, cree su contenedor. Asegúrese de ejecutar estos comandos en el directorio ~/thunderbird
:
- docker build -t thunderbird .
Ahora cree una nueva red que será compartida entre los contenedores de la aplicación:
- docker network create thunderbird-net
A continuación, cree un volumen para almacenar los datos de la aplicación:
- docker volume create thunderbird-data
Finalmente, ejecútelo para que se reinicie automáticamente:
- docker run --detach --restart=always --volume=thunderbird-data:/data --net=thunderbird-net --name=thunderbird-app thunderbird
Tenga en cuenta que si lo desea, puede sustituir thunderbird-app
tras la opción --nombre
con un nombre diferente. Sea cual sea su elección, su aplicación ahora está en contenedor y ejecutándose. Ahora vamos a usar el servidor web Caddy para protegerla y conectar remotamente a ella.
En este paso, configurará el servidor web Caddy para proporcionar autenticación y, opcionalmente, acceso remoto a los archivos sobre WebDAV. Para mayor simplicidad, y para permitirle usarlo con su proxy inverso existente, lo ejecutará en otro contenedor.
Cree un nuevo directorio y muévalo dentro:
- mkdir ~/caddy
- cd ~/caddy
Ahora cree un nuevo Dockerfile
usando nano
o su editor preferido:
- nano ~/caddy/Dockerfile
Luego, añada las siguientes directivas:
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 /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"]
Este Dockerfile crea Caddy con el complemento WebDAV habilitado, y luego lo abre en el puerto 8080
con Caddyfile
en /etc/Caddyfile
. Guarde y cierre el archivo.
A continuación, configurará el servidor web Caddy. Cree un archivo llamado Caddyfile
en el directorio que acaba de crear:
- nano ~/caddy/Caddyfile
Ahora añada el siguiente bloque de código a su 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}
}
}
Este Caddyfile
realiza proxy el directorio raíz al contenedor thunderbird-app
que creó en el Paso 4 (Docker lo resuelve a la IP correcta). También servirá como un navegador de archivo basado en web solo lectura en /files
y para ejecutar el servidor WebDAV en /webdav
que puede montar localmente para acceder a sus archivos. El nombre de usuario y la contraseña se leen desde las variables de entorno APP_USERNAME
y APP_PASSWORD_HASH
.
Ahora cree el contenedor:
- docker build -t thunderbird-caddy .
Caddy v.2 requiere que haga hash a su contraseña deseada. Ejecute el siguiente comando y recuerde sustituir mypass
con una contraseña fuerte que elija:
- docker run --rm -it thunderbird-caddy caddy hash-password -plaintext 'mypass'
Este comando dará como resultado una cadena de caracteres. Copie esto a su portapapeles para prepararse para ejecutar el siguiente comando.
Ahora está listo para ejecutar el contenedor. Asegúrese de sustituir myuser
con un nombre de usuario que elija, y sustituya mypass-hash
con el resultado del comando que ejecutó en el paso anterior. Puede cambiar también el puerto (8080
aquí) para acceder a su servidor en un puerto diferente:
- 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
Ahora está listo para acceder y probar nuestra aplicación.
Vamos a acceder a su aplicación y a asegurarnos de que está funcionando.
Primero, abra http://your_server_ip:8080
en un navegador web, inicie sesión con las credenciales que seleccionó anteriormente y haga clic en Connect.
Ahora debería poder interactuar con la aplicación, y debería cambiar de tamaño automáticamente para que se adapte a la ventana de su navegador.
Si hace clic con el botón derecho en el escritorio en negro, debería ver un menú que le permite acceder a un terminal. Si hace clic en el centro, debería ver una lista de ventanas.
Ahora abra http://your_server_ip:8080/files/
en un navegador web. Debería poder acceder a sus archivos.
Opcionalmente, puede intentar montar http://your_server_ip:8080/webdav/
en un cliente WebDAV. Debería poder acceder y modificar sus archivos directamente. Si utiliza la opción Asignar unidad de red en Windows Explorer, necesitará usar un proxy inverso para añadir HTTPS o establecer HKLM\SYSTEM\CurrentControlSet\Services\WebClient\Parameters\BasicAuthLevel
a DWORD:2
.
En cualquier caso, su aplicación GUI nativa está ahora lista para su uso remoto.
Ahora ha configurado correctamente un contenedor Docker para Thunderbird y a continuación, usando Caddy, ha configurado el acceso a él a través de un navegador web. Si alguna vez necesita actualizar su aplicación, detenga los contenedores, ejecute docker rm thunderbird-app thunderbird-web
, vuelva a crear las imágenes, y vuelva a ejecutar los comandos docker run
de los pasos anteriores. Sus datos se conservarán, ya que se almacenan en un volumen.
Si desea obtener más información sobre los comandos Docker básicos, puede leer este tutorial o esta hoja de trucos. Para un uso a más largo plazo, quizá desee considerar habilitar HTTPS (esto requiere un dominio) para mayor seguridad.
Además, si está implementando más de una aplicación, es posible que desee usar Docker Compose o Kubernetes en vez de iniciar cada contenedor manualmente. Y recuerde, este tutorial puede servir como base para ejecutar cualquier otra aplicación Linux en su servidor, incluyendo:
Esta última opción demuestra el gran potencial de usar contendores y acceder remotamente a aplicaciones GUI. Con esta configuración, puede usar un servidor con una potencia de computación considerablemente mayor que si utilizase localmente herramientas que consumen muchos recursos como Cutter.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!