Rodando aplicações GUI em Docker

Já roda serviços usando containers em Docker?
Que tal rodar também as suas aplicações GUI com ele? Isso mesmo, estou falando de rodar seu browser, editor de texto, jogos, enfim…

Porque? X11 em Docker parece ser complicado

Isso é bem simples (ou ao menos mais do que se imagina) e te dá um controle absurdo sobre a aplicação… Não quer que o aplicativo acesse a rede/internet por algum motivo? Só usar a flag --net null. De repente rodar em uma rede isolada? --net nomedarede (Neste caso precisa criar a rede Docker antes). Quer limitar a quantidade de ram que ela vai usar? --memory mem (exemplo: --memory 1gb). Quer definir que cores do processador vão rodar a aplicação? --cpuset-cpus cpus (ex: --cpuset-cpus 0,3 ou --cpuset-cpus 0-3). Enfim… Sinta-se livre para usar a imaginação. :)
(sim, eu sei que é possível fazer esse tipo de coisa no linux… mas não é tão fácil quanto colocar uma flag em um comando)

Outra vantagem é que assim instalações de um programa acabam sendo simplesmente “mandar abrir o programa”, quando você roda um “docker run [...] nomedaimagem” o Docker engine verifica se essa imagem está disponível no repositório local, e em caso negativo, baixa a mesma e então roda.

Isso sem falar de que a aplicação já fica com as suas dependencias no container, ou seja, nada de “sujeira” ficando pra trás na forma de pacotes órfãos (e arquivos de configuração) quando a aplicação for removida por algum motivo ;)

Essa experiência além de divertida, proporciona uma imersão total no funcionamento do Docker, simplesmente não há como usar as suas ferramentas diariamente sem acabar precisando usar vários recursos diferentes proporcionados pelo Docker.

Ah tá que é tudo fácil, simples e a vida vira um arco-íris… Fala a real aí

Ok, ok… Nem tudo são flores, principalmente quando chega no ponto em que não há uma imagem pronta pro aplicativo que tu queres…

Nem sempre as dependencias listadas pelos desenvolvedores são as únicas dependencias… O que é aceitável se pensar bem, porque são dependencias que geralmente já estão instaladas nos computadores, mas no caso de containers acaba não sendo bem assim. Isso acaba sendo bem frustrante às vezes, há outros momentos que podem te frustrar um pouco, mas este é o principal pra mim (porém quando se consegue descobrir o que era o problema, é indescritível a sensação :p)

Ok, então se eu só rodar aplicações que já tem imagem, não vou ter problema algum?

Bem, como eu disse antes, os problemas são principalmente quando se monta uma imagem, não os únicos…
Algumas integrações com outros softwares são bem difíceis (na verdade até agora eu não consegui resolver nenhum desses casos, mas como “nada é impossível”…), por exemplo: alguns softwares permitem que se clique em um link e o navegador padrão abre com a página do link, mas quando se tem um container de digamos “Telegram-desktop”, não há um navegador instalado na máquina (na maioria das vezes nem o software que faz essa verificação sobre qual é o navegador padrão e que chama ele).

Nada é “bala de prata” e é quase certo que alguns problemas irão acontecer… E às vezes vai ficar uma verdadeira “pulga atrás da orelha” em relação ao problema ser exatamente por ser em um container (ao menos no meu caso nunca era, mas sempre vinha a dúvida).

Não gosto muito de pular de cara em novidades…

Bem, depende do que tu considera como novidade, hehe…
O próprio Docker não é exatamente velho (na verdade é bem novo até… 3 aninhos), mas muita gente já usa inclusive em produção (e diz que há alguns desafios, mas que vale o esforço) e… (rufem os tambores)
…isso que eu estou falando começou praticamente junto com o lançamento público do Docker :)

Sério?

Sim! Aqui vai a timeline que eu consegui levantar:

Porque os últimos itens foram incluidos? Bem, basicamente porque sempre que se fala sobre GUI em Docker, ou a pessoa fala sobre a Jess ou lembra dela (há outros vídeos dela falando sobre isso, mas achei melhor incluir só o primeiro)
Como pode ver, apenas 1 mês depois do lançamento público do Docker já haviam pessoas querendo rodar aplicações GUI no Docker :)

Certo, me convenceu… Conta mais aí, como funciona? Quão simples (ou complicado) vai ser pra mim?

Funciona basicamente assim, é montado um volume do arquivo de socket do X11 da tua máquina (/tmp/.X11-unix) no mesmo caminho no container e é definida a variável de ambiente DISPLAY do container como o display do servidor X do teu host, assim quando o aplicativo dentro do container enviar as instruções de como renderizar ele no ambiente gráfico, estará enviando elas para o X do host.

É… meio que parece complicado isso aí, hein?

Relaxa que eu só estava explicando a teoria por trás da coisa, na prática vamos colocar duas flags: -e DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix e a “mágica” está feita :)

Ok, vamos aos comandos?

Antes de mais nada… Em 99% dos casos é preciso liberar ,o acesso ao X: xhost local (essa liberação ficará vigente até desligar/reiniciar o host)

Comando mais simples o possível, monta o socket X11 do host no container e define o display (note que vamos “evoluindo” o comando aos poucos, mas pode usar apenas as flags que achar necessário):

docker run [--rm [-it]|-d] \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-e DISPLAY \
imagem [comando]

Em alguns casos a variável DISPLAY tem de ser DISPLAY=unix$DISPLAY (mas pra ser sincero até agora eu não sei bem o motivo, somente que era o indicado pela pessoa que fez a imagem)
Obs: “[--rm [-it]|-d]” quer dizer “Opcionalmente pode usar o --rm que pode ou não estar em conjunto com -it OU pode (ou não :p) usar o -d

Com suporte a aceleração 3D por hardware:

docker run [--rm [-it]|-d] \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-e DISPLAY \
--device /dev/dri \
imagem [comando]

Adicionando audio:

docker run [--rm [-it]|-d] \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-e DISPLAY \
--device /dev/dri \
--device /dev/snd \
imagem [comando]

Adicionando webcam:

docker run [--rm [-it]|-d] \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-e DISPLAY \
--device /dev/dri \
--device /dev/snd \
--device /dev/video0 \
imagem [comando]

Usando a mesma data/hora do host:

docker run [--rm [-it]|-d] \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-e DISPLAY \
--device /dev/dri \
--device /dev/snd \
--device /dev/video0 \
-v /etc/localtime:/etc/localtime:ro \
imagem [comando]

Atenção: dependendo da distribuição, não há um /etc/localtime, tem de averiguar como ela define o timezone e “replicar” no container.

Mantendo as configurações do aplicativo:

docker run [--rm [-it]|-d] \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-e DISPLAY \
--device /dev/dri \
--device /dev/snd \
--device /dev/video0 \
-v /etc/localtime:/etc/localtime:ro \
-v $HOME/.config/app:/root/.config/app \
imagem [comando]

Obs: o caminho é apenas um exemplo :p

Bônus: Controle do video-game :) (na real qualquer dispositivo de entrada)

docker run [--rm [-it]|-d] \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-e DISPLAY \
--device /dev/dri \
--device /dev/snd \
--device /dev/video0 \
-v /etc/localtime:/etc/localtime:ro \
-v $HOME/.config/app:/root/.config/app \
--device /dev/input \
imagem [comando]

Certo, acho que consigo começar por aí… Alguma dica pra me dar?

  • Organize as flags em ordem alfabética (facilita bastante em troubleshootings) - sim, eu sei que nos exemplos eu não fiz isso, mas foi mais para facilitar o reconhecimento da flag adicionada
  • Crie um script com os comandos que rodam os containers separados em funções, assim se pode incluir o script ao perfil do bash e rodar como se fosse um comando (além de facilitar o uso de launchers) - mais abaixo vou colocar um link para o script que eu uso
  • Utilize o detach (–detach ou -d) e uma função para apagar o container que esteja parado ao executar a função do aplicativo desejado novamente - isso também estará no meu script ;)

Beleza! Mais alguma coisa? Algum outro lugar que eu possa ler sobre o assunto?

Bem, claro que tem outros lugares pra ler :)
MAS antes aqui vai um “bônus”: https://github.com/somatorio/dockercompose-sample-workbench
Se podemos rodar as aplicações em container, que tal rodá-las através do Docker Compose? Assim se pode definir todas as ferramentas a serem utilizadas e elas estarão disponíveis após um único comando :)
Essa ideia é do @gomex, não minha… Ele falou disso durante uma conversa em um meetup sobre GUI em Docker (se olharem o link verão que tem um belo tempo entre a conversa e este post… foi mal =p) e eu apenas “executei” ela.

Buenas, quanto a recomendações de leitura:

AH, alguns amigos meus usam Mac e Windows, eles podem aproveitar isso?

Claro, só precisa de umas gambiarras (não que isso que fizemos até agora não tenha sido, hehe)
* Mac OS X
Instalar o Docker for Mac

brew install socat
brew cask install xquartz
open -a XQuartz

socat TCP-LISTEN:6000,reuseaddr,fork UNIX-CLIENT:\"$DISPLAY\"
docker run -e DISPLAY=hostip:0 [...] image OU DISPLAY=hostip:0 docker-compose up [-d]
  • Windows
    Instalar o xming
    Instalar o Docker for Windows
xming :0 -ac -clipboard -multiwindow
docker run -e DISPLAY=hostip:0 [...] image OU DISPLAY=hostip:0 docker-compose up [-d]

E funciona tudo direitinho?

Infelizmente nem tudo (ao menos até agora…), o som por exemplo não funciona e como o Docker for windows/Mac roda o Docker em uma VM, espere uma perda de desempenho…

Então é melhor não falar pra eles sobre isso?

Ei, eu só estava falando o que eles podem esperar, não para evitar isso :)
Dentro do que eu testei afetou mais jogos e programas que exigem muito do hardware.
Eles podem inclusive encontrar uma solução para o que eu comentei… maneiro, hein?

Para editar este post basta clicar aqui.
 
comments powered by Disqus