Rode aplicações gráficas em um Docker Container

 Por Ciro Mota |  7, Maio 2021 |  Tempo de leitura aproximadamente 5 minutos.
 Edições: Revisão textual. Em 26, Fevereiro 2022.

Olá amigos, como vão? Espero que estejam todos bem.
Sei que tinha programado para entrar um artigo sobre o OpenWrt e acabei colocando outro na frente e agora esse novo artigo, bem, infelizmente ainda não concluí os testes que gostaria. Desse modo, assim que eu concluir e espero que para muito breve, este artigo sai.

Pois bem, já comentei aqui em outras oportunidades que virei um fã boy dos containers, devido a sua extrema facilidade com que podemos lidar com as aplicações. Eis que em um dos grupos no Telegram em que participo e um dos membros levantou uma questão há alguns dias, sobre o quanto a abordagem de instalação de algumas aplicações podem ser ruins para um usuário final no Linux, o que de fato é verdade para algumas situações onde o app não esteja na loja/repositórios das distribuições.

Com isso resolvi fazer alguns testes e verificar sobre a viabilidade de executar aplicações gráficas em containers. E sim, não só é possível como muito prático e acaba eliminando essa complexidade da instalação para um usuário final, ou se você precisa do deploy dessa mesma aplicação em várias estações ao mesmo tempo.

Vamos lá.

Overview

Tudo o que é necessário nada mais é do que um único arquivo Dockerfile, algo simples de ser executado e um Shell Script para rodar o container. Mesmo que você ainda não manje de Dockerfile e é interessante que você saiba como ele funciona, será possível replicar para boa parte das aplicações.

Se você já tem experiência, basta transcrever todos os passos que você usaria, para o Dockerfile ou para um arquivo de script que poderá também ser utilizado para isso.

Instalação

No meu exemplo optei por usar o Google Chrome, que normalmente não está disponível nos repositórios das distribuições, sua instalação é através de um arquivo .deb fornecido pelo próprio Google, então para um usuário comum, ele teria o trabalho de ir até o site e executar o instalador, onde sabemos que nem toda distro teria um instalador gráfico para apps assim.

Crie um arquivo Dockerfile com o seguinte conteúdo:

FROM ubuntu

LABEL maintainer="ciromota"
LABEL version="Developer"

ADD https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb /tmp/google-chrome-stable.deb

RUN apt-get update && \
    apt-get install wget sudo \
    libcanberra-gtk-module -y && \
    apt-get install -f ./tmp/google-chrome-stable.deb -y

RUN export uid=1000 gid=1000 && \
    mkdir -p /home/usearpp && \
    echo "usearpp:x:${uid}:${gid}:usearpp,,,:/home/usearpp:/bin/bash" >> /etc/passwd && \
    echo "usearpp:x:${uid}:" >> /etc/group && \
    echo "usearpp ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers.d/usearpp && \
    chmod 0440 /etc/sudoers.d/usearpp && \
    chown ${uid}:${gid} -R /home/usearpp

USER usearpp

ENV HOME /home/usearpp

CMD /usr/bin/google-chrome

Explicando cada linha:

  • Linha 1 = Chama a imagem latest do Ubuntu. Poderia ser Debian também.
  • Linha 2 = TAG de mantenedor da imagem. Altere para seu nome ou empresa.
  • Linha 3 = TAG de versionamento. Altere para o que melhor aplicar para seu cenário.
  • Linha 4 = Adiciona o pacote do Google Chrome à imagem.
  • Linha 5 = Atualiza os repositórios e instala o wget e o sudo, uma biblioteca para o tema GTK que serão essenciais e instala o Google Chrome.
  • Linha 6 = Exporta seu gid e uid, cria uma pasta home para o usuário, “cadastra” este usuário no arquivo passwd e group, libera completamente seu uso para o sudo e define as permissões para sua pasta de usuário.
  • Linha 7 = Nome de usuário.
  • Linha 8 = Definição da variável de ambiente para a pasta home do usuário recém criada acima.
  • Linha 9 = Faz com que a aplicação seja executada a cada nova inicialização do container.

Caso você precise executar um script dentro do container, utilize o conteúdo abaixo dentro do Dockerfile:

COPY script.sh .
RUN chmod +x /script.sh && ./script.sh

E por fim, construiremos o container com o comando abaixo, no meu caso optei por chamar o container de chrome:

docker build -t chrome .

Após a construção do container, basta executá-lo com a seguinte linha:

docker container run -ti --privileged --name chrome -e DISPLAY=$DISPLAY \
-v /etc/localtime:/etc/localtime \
-v /tmp/.X11-unix:/tmp/.X11-unix chrome

Explicando cada comando:

  • docker run -ti = Executará o container em modo interativo e com acesso ao terminal.
  • –privileged = Desabilita a marcação de segurança do container, requerido para rodar aplicações que necessitem de sandbox lá dentro do container, como o Chrome requer.
  • –name chrome = Defino o nome do container como “chrome”.
  • -e DISPLAY=$DISPLAY = Defino a variável de ambiente Display.
  • -v /etc/localtime:/etc/localtime = Faz o mapeamento das configurações de horário do Host para o container.
  • -v /tmp/.X11-unix:/tmp/.X11-unix = Mapeia em volume o arquivo “.X11-unix” do host para o container. o Arquivo “.X11-unix” é responsável pelo soquete do Xorg.
  • chrome = A imagem do container que criamos no passo anterior.

Certo, porém sabemos que container são efêmeros, cumprem sua função e são descartáveis, afinal, como faremos para executá-lo novamente após sairmos da aplicação? Com o comando abaixo isso é possível:

docker container start -a chrome

Observem que para executar o container pela primeira vez eu defino em --name o nome de chrome, isso serve não serve só para identificação como para facilitar a execução de comandos. É possível criar um novo arquivo de script (o segundo a que cito acima) e chamar esse arquivo através de um arquivo .desktop, ou seja, a aplicação aparecerá como um atalho do menu de apps da sua distro. Para isso basta este conjunto de linhas abaixo:

#!/usr/bin/env bash

docker container start -a chrome
[Desktop Entry]
Encoding=UTF-8
Name=Chrome
Comment=Rodando Chrome em um Docker Container
Icon=docker.png
Exec=$HOME/aplicação/roda_container_app.sh
Type=Application
Terminal=false
StartupNotify=true

O arquivo .desktop deverá ser salvo no diretório $HOME\.local\share\applications.

E o resultado é isso que podemos ver na imagem abaixo, meu Chrome nativo ao fundo e a janela de execução do Chrome partindo do container.

Google Chrome rodando dentro de um container

Considerações finais

Se você precisa diminuir a complexidade na implantação de uma aplicação, ou utilização de um ambiente em que seja necessário que esta aplicação esteja ainda mais isolada, ou quiçá até mesmo rodando uma aplicação do Windows através do Wine sem que seja necessária a sua instalação no sistema, este tipo de abordagem pode ser uma boa pedida. Mas cuidado, não torne com isso a sua aplicação um monólito hein?!

E você, já tinha pensado em algo parecido ou já implementa esse tipo de implementação em suas instalações? Me deixe saber abaixo nos comentários.

Até a próxima!