One place for hosting & domains

      Aplicações

      Como acessar remotamente aplicações GUI usando Docker e Caddy no Ubuntu 18.04


      O autor selecionou o Free and Open Source Fund para receber uma doação como parte do programa Write for DOnations.

      Introdução

      Mesmo com a crescente popularidade dos serviços em nuvem, a necessidade de executar aplicações nativas ainda existe.

      Utilizando o noVNC e o TigerVNC, você pode executar aplicações nativas dentro de um contêiner Docker e acessá-las remotamente usando um navegador Web. Além disso, você pode executar sua aplicação em um servidor com mais recursos de sistema do que você pode ter disponível localmente, o que pode fornecer maior flexibilidade ao se executar grandes aplicações.

      Neste tutorial, você irá conteinerizar o Mozilla Thunderbird, um cliente de e-mail usando o Docker. Depois, você irá protegê-lo e fornecer acesso remoto usando o servidor Web Caddy.

      Quando você terminar, você será capaz de acessar o Thunderbird a partir de qualquer dispositivo usando apenas um navegador Web. Opcionalmente, você também poderá acessar localmente os arquivos dele usando o WebDAV. Você também terá uma imagem Docker totalmente independente que você pode executar em qualquer lugar.

      Pré-requisitos

      Antes de iniciar este guia, você precisará do seguinte:

      • Um servidor Ubuntu 18.04 com pelo menos 2GB RAM e 4GB de espaço em disco.
      • Um usuário non root com privilégios sudo.
      • O Docker configurado em seu servidor. Você pode seguir o tutorial How To Install and Use Docker on Ubuntu 18.04.

      Passo 1 – Criando a configuração do supervisord

      Agora que seu servidor está em execução e o Docker está instalado, você está pronto para começar a configuração do contêiner da sua aplicação. Como seu contêiner consiste em vários componentes, você precisa usar um gerenciador de processos para iniciá-los e monitorá-los. Aqui, você estará usando o supervisord. O supervisord é um gerenciador de processos escrito em Python que é frequentemente usado para orquestrar contêineres complexos.

      Primeiro, crie e entre em um diretório chamado thunderbird para seu contêiner:

      • mkdir ~/thunderbird
      • cd ~/thunderbird

      Agora crie e abra um arquivo chamado supervisord.conf usando o nano ou o seu editor preferido:

      Agora adicione este primeiro bloco de código em supervisord.conf, que definirá as opções globais para o supervisord:

      ~/thunderbird/supervisord.conf

      [supervisord]
      nodaemon=true
      pidfile=/tmp/supervisord.pid
      logfile=/dev/fd/1
      logfile_maxbytes=0
      

      Neste bloco, você está configurando o supervisord propriamente. Você precisa definir o nodaemon para true porque ele estará executando dentro de um contêiner Docker como o entrypoint. Portanto, você vai querer que ele permaneça em execução em primeiro plano. Você também está definindo o pidfile para um caminho acessível por um usuário não-root (mais sobre isso posteriormente), e o logfile para stdout para que você possa ver os logs.

      Em seguida, adicione outro pequeno bloco de código ao supervisord.conf. Este bloco inicia o TigerVNC, que é um servidor VNC/X11 combinado:

      ~/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
      

      Neste bloco, você está configurando o servidor X11. O X11 é um protocolo de servidor de exibição, que é o que permite que as aplicações GUI sejam executadas. Observe que no futuro ele será substituído pelo Wayland, mas o acesso remoto ainda está em desenvolvimento.

      Para este contêiner você está usando o TigerVNC e seu servidor VNC embutido. Isso tem uma série de vantagens em relação ao uso de um servidor X11 e VNC separados:

      • Tempo de resposta mais rápido, uma vez que o desenho da GUI é feito diretamente no servidor VNC em vez de ser feito em um framebuffer intermediário (a memória que armazena o conteúdo da tela).
      • Redimensionamento automático da tela, que permite que a aplicação remota seja redimensionada automaticamente para caber no cliente (nesse caso, na janela do navegador Web).

      Se você desejar, você pode alterar o argumento para a opção -desktop a partir do Thunderbird para outra coisa de sua escolha. O servidor exibirá sua escolha como o título da página Web usada para acessar sua aplicação.

      Agora, vamos adicionar um terceiro bloco de código ao supervisord.conf para iniciar o 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
      

      Neste bloco, você está configurando o easy-novnc, um servidor standalone que fornece um encapsulamento em torno do noVNC. Este servidor executa dois papéis. Primeiro, ele fornece uma página de conexão simples que lhe permite configurar opções para a conexão e definir as opções padrão. Segundo, ele faz proxy do VNC no WebSocket, que o permite ser acessado através de um navegador Web comum.

      Normalmente, o redimensionamento é feito no lado do cliente (ou seja, a escala de imagem), mas você está usando a opção resize=remote para tirar a vantagem total dos ajustes de resolução remota do TigerVNC. Isso também fornece menor latência em dispositivos mais lentos, como os Chromebooks de entrada:

      Nota: Este tutorial usa o easy-novnc. Se você desejar, você pode usar o websockify e um servidor Web separado. A vantagem do easy-novnc é que o uso da memória e o tempo de inicialização são significativamente mais baixos e ele é auto-contido. O easy-novnc também fornece uma página de conexão mais limpa do que o noVNC padrão e permite definir opções padrão que são úteis para esta configuração (como o resize=remote).

      Agora adicione o seguinte bloco à sua configuração para iniciar o OpenBox, o gerenciador de janelas:

      ~/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
      

      Neste bloco, você está configurando o OpenBox, um gerenciador de janelas X11 leve. Você poderia ignorar este passo, mas sem ele, você não teria barras de título ou seria capaz de redimensionar janelas.

      Finalmente, vamos adicionar o último bloco ao supervisord.conf, que irá iniciar a aplicação principal:

      ~/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
      

      Neste bloco final, você está definindo priority para 1 para garantir que o Thunderbird inicie depois do TigerVNC, ou ele encontraria uma condição de corrida e falharia aleatoriamente ao iniciar. Também definimos autorestart=true para reabrir a aplicação automaticamente se ela fechar erroneamente. A variável de ambiente DISPLAY diz à aplicação para exibir no servidor VNC que você configurou anteriormente.

      Veja como seu supervisord.conf finalizado vai ficar:

      ~/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
      

      Se você quiser conteinerizar uma aplicação diferente, substitua /usr/bin/thunderbird pelo caminho para o executável da sua aplicação. Caso contrário, você está pronto para configurar o menu principal da sua GUI.

      Passo 2 – Configurando o menu do OpenBox

      Agora que seu gerenciador de processos está configurado, vamos configurar o menu do OpenBox. Este menu nos permite lançar aplicações dentro do contêiner. Também incluiremos um terminal e monitor de processos para depuração, se necessário.

      Dentro do diretório da sua aplicação, use o nano ou o seu editor de texto favorito para criar e abrir um novo arquivo chamado menu.xml:

      • nano ~/thunderbird/menu.xml

      Agora adicione o seguinte código ao 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>
      

      Este arquivo XML contém os itens de menu que aparecerão quando você clicar com o botão direito no desktop. Cada item consiste em uma etiqueta e em uma ação.

      Se você quiser conteinerizar uma aplicação diferente, substitua /usr/bin/thunderbird pelo caminho para o executável da sua aplicação e altere o label do item.

      Passo 3 – Criando o Dockerfile

      Agora que o OpenBox está configurado, você criará o Dockerfile, que une tudo que vimos.

      Crie um Dockerfile no diretório do seu contêiner:

      • nano ~/thunderbird/Dockerfile

      Para começar, vamos adicionar código para criar o 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/[email protected] && 
          go build -o /bin/easy-novnc github.com/geek1011/easy-novnc
      

      No primeiro estágio, você está compilando o easy-novnc. Isso é feito em um estágio separado por simplicidade e para salvar espaço – você não precisa da cadeia de ferramentas Go inteira em sua imagem final. Observe o @v1.1.0 no comando de compilação. Isso garante que o resultado seja determinístico, o que é importante porque o Docker faz cache do resultado de cada passo. Se você não tivesse especificado uma versão explícita, o Docker faria referência à versão mais recente do easy-novnc no momento em que a imagem foi construída pela primeira vez. Além disso, você deseja garantir que você baixe uma versão específica do easy-novnc, no caso de alterações recentes na interface CLI.

      Agora vamos criar o segundo estágio, que se tornará a imagem final. Aqui você estará usando o Debian 10 (buster) como a imagem base. Observe que como isso está em execução em um contêiner, ele funcionará independentemente da distribuição que você está executando em seu servidor.

      Em seguida, adicione o seguinte bloco ao seu 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
      

      Nesta instrução, você está instalando o Debian 10 como sua imagem base e instalando o mínimo necessário para executar aplicações GUI em um contêiner. Observe que você executa apt-get update como parte da mesma instrução para evitar problemas de cache do Docker. Para economizar espaço, você também está removendo as listas de pacotes baixadas depois (os pacotes em cache são removidos por padrão). Você também está criando /usr/share/desktop-directories porque algumas aplicações dependem do diretório existente.

      Vamos adicionar outro pequeno bloco de código:

      ~/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
      

      Nesta instrução, você está instalando alguns utilitários e pacotes de utilidade geral. De interesse particular aqui estão o xdg-utils (que fornece os comandos base usados pelas aplicações desktop no Linux) e os ca-certificates (que instalam os certificados raiz para nos permitir acessar sites HTTPS).

      Agora, podemos adicionar as instruções para a aplicação principal:

      ~/thunderbird/Dockerfile

      ...
      RUN apt-get update -y && 
          apt-get install -y --no-install-recommends thunderbird && 
          rm -rf /var/lib/apt/lists
      

      Como antes, aqui estamos instalando a aplicação. Se você estiver conteinerizando uma aplicação diferente, você pode substituir esses comandos por aqueles necessários para instalar seu app específico. Algumas aplicações irão exigir um pouco mais de trabalho para executar dentro do Docker. Por exemplo, se você estiver instalando um app que usa o Chrome, Chromium ou o QtWebEngine, você precisará usar o argumento da linha de comando --no-sandbox porque ele não será suportado dentro do Docker.

      Em seguida, vamos começar a adicionar as instruções para adicionar os últimos arquivos ao contêiner:

      ~/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
      

      Aqui você está adicionando os arquivos de configuração que você criou anteriormente à imagem e copiando o binário easy-novnc do primeiro estágio.

      Este próximo bloco de código cria o diretório de dados e adiciona um usuário dedicado para o seu app. Isto é importante porque algumas aplicações se recusam a serem executados como root. Também é uma boa prática não executar aplicações como root, mesmo em um contêiner.

      ~/thunderbird/Dockerfile

      ...
      RUN groupadd --gid 1000 app && 
          useradd --home-dir /data --shell /bin/bash --uid 1000 --gid 1000 app && 
          mkdir -p /data
      VOLUME /data
      

      Para garantir um UID/GID consistente para os arquivos, você está configurando ambos explicitamente para 1000. Você também está montando um volume no diretório de dados para garantir que ele persista entre reinicializações.

      Finalmente, vamos adicionar as instruções de lançamento.

      ~/thunderbird/Dockerfile

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

      Ao definir o comando padrão para supervisord, o gerenciador irá lançar os processos necessários para executar sua aplicação. Neste caso, você está usando CMD em vez de ENTRYPOINT. Na maioria dos casos, isso não faria diferença, mas usar o CMD é mais adequado para este propósito por algumas razões. Primeiro, o supervisord não aceita quaisquer argumentos relevantes para nós, e se você fornecer argumentos ao contêiner eles substituem o CMD e são anexados ao ENTRYPOINT. Segundo, usar o CMD nos permite fornecer um comando inteiramente diferente (que será executado por /bin/sh -c) ao passar argumentos para o contêiner, o que torna a depuração mais fácil.

      E, finalmente, você precisa executar o chown como root antes de iniciar o supervisord para evitar problemas de permissão no volume de dados e para permitir que os processos filhos abram o stdout. Isso também significa que você precisa usar gosu em vez da instrução USER para trocar o usuário.

      Aqui está como o seu Dockerfile finalizado vai parecer:

      ~/thunderbird/Dockerfile

      FROM golang:1.14-buster AS easy-novnc-build
      WORKDIR /src
      RUN go mod init build && 
          go get github.com/geek1011/[email protected] && 
          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"]
      

      Salve e feche seu Dockerfile. Agora estamos prontos para compilar e executar nosso contêiner e, em seguida, acessar o Thunderbird – uma aplicação GUI.

      Passo 4 – Compilando e executando o contêiner

      O próximo passo é compilar seu contêiner e configurá-lo para executar na inicialização. Você também irá configurar um volume para preservar os dados da aplicação entre reinicializações e atualizações.

      Primeiro compile seu contêiner. Certifique-se de executar esses comandos no diretório ~/thunderbird:

      • docker build -t thunderbird .

      Agora crie uma nova rede que será compartilhada entre os contêineres da aplicação:

      • docker network create thunderbird-net

      Em seguida, crie um volume para armazenar os dados da aplicação:

      • docker volume create thunderbird-data

      Finalmente, execute-a e defina-a para reinicializar automaticamente:

      • docker run --detach --restart=always --volume=thunderbird-data:/data --net=thunderbird-net --name=thunderbird-app thunderbird

      Observe que se você quiser, você pode substituir o thunderbird-app após a opção --name por um nome diferente. Seja o que for que você escolheu, sua aplicação está agora conteinerizada e em execução. Agora vamos usar o servidor Web Caddy para protegê-la e conectar-se remotamente a ela.

      Passo 5 – Configurando o Caddy

      Neste passo, você irá configurar o servidor Web Caddy para fornecer autenticação e, opcionalmente, acesso remoto a arquivos através do WebDAV. Por simplicidade, e para lhe permitir utilizá-lo com seu proxy reverso existente, você irá executá-lo em um outro contêiner

      Crie um novo diretório e então mova-se para ele:

      Agora crie um novo Dockerfile usando o nano ou o seu editor preferido:

      Então, adicione as seguintes diretivas:

      ~/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"]
      

      Este Dockerfile compila o Caddy com o plug-in WebDAV habilitado e, em seguida, o lança na porta 8080 com o Caddyfile em /etc/Caddyfile. Salve e feche o arquivo.

      Em seguida, você irá configurar o servidor Web Caddy. Crie um arquivo chamado Caddyfile no diretório que você acabou de criar:

      Agora adicione o seguinte bloco de código ao seu 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}
          }
      }
      

      Este Caddyfile faz proxy do diretório raiz para o contêiner thunderbird-app que você criou no Passo 4 (o Docker o resolve para o IP correto). Ele também irá fornecer um navegador de arquivos baseado em web somente leitura em /files e executar um servidor WebDAV em /webdav, que você pode montar localmente para acessar seus arquivos. O nome de usuário e a senha são lidos a partir das variáveis de ambiente APP_USERNAME e APP_PASSWORD_HASH.

      Agora compile o contêiner:

      • docker build -t thunderbird-caddy .

      O Caddy v.2 requer que você faça um hash da sua senha desejada. Execute o seguinte comando e lembre-se de substituir mypass por uma senha forte da sua escolha:

      • docker run --rm -it thunderbird-caddy caddy hash-password -plaintext 'mypass'

      Este comando irá exibir uma string de caracteres. Copie isso para sua área de transferência em preparação para executar o próximo comando.

      Agora tudo está pronto para executar o contêiner. Certifique-se de substituir myuser por um nome de usuário da sua escolha e substitua mypass-hash pela saída do comando que você executou no passo anterior. Você também pode alterar a porta (8080 aqui) para acessar seu servidor em uma porta 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

      Agora estamos prontos para acessar e testar nossa aplicação.

      Passo 6 – Testando e gerenciando a aplicação

      Vamos acessar sua aplicação e garantir que ela está funcionando.

      Primeiro, abra http://your_server_ip:8080 em um navegador Web, faça login com as credenciais que você escolheu anteriormente, e clique em Connect.

      NoVNC connect page

      Agora você deve ser capaz de interagir com a aplicação, e ela deve redimensionar automaticamente para se encaixar na janela do seu navegador.

      Thunderbird main menu

      Se você clicar com o botão direito no desktop preto, você deve ver um menu que lhe permite acessar um terminal. Se você clicar com o botão do meio, verá uma lista de janelas.

      NoVNC right click

      Agora abra http://your_server_ip:8080/files/ em um navegador. Você deve ser capaz de acessar seus arquivos.

      NoVNC file access webdav

      Opcionalmente, você pode tentar montar http://your_server_ip:8080/webdav/ em um cliente WebDAV. Você deve ser capaz de acessar e modificar seus arquivos diretamente. Se você usar a opção Mapear unidade de rede no Windows Explorer, você precisará usar um proxy reverso para adicionar HTTPS ou definir HKLMSYSTEMCurrentControlSetServicesWebClientParametersBasicAuthLevel para DWORD:2.

      Em ambos os casos, sua aplicação GUI nativa está pronta para uso remoto.

      Conclusão

      Agora você configurou com êxito um contêiner Docker para o Thunderbird e, em seguida, usando o Caddy, você configurou o acesso a ele através de um navegador Web. Se você precisar atualizar sua aplicação, pare os contêineres, execute docker rm thunderbird-app thunderbird-web, recompile as imagens e, em seguida, execute novamente os comandos docker run das etapas anteriores acima. Seus dados ainda serão preservados uma vez que eles são armazenados em um volume.

      Se você quiser aprender mais sobre comandos básicos do Docker, você pode ler este tutorial ou esse guia de consulta rápida. Para uso a longo prazo, convém habilitar o HTTPS (isso requer um domínio) para segurança adicional.

      Além disso, se você estiver implantando mais de uma aplicação, você pode querer usar o Docker Compose ou o Kubernetes em vez de iniciar cada contêiner manualmente. E lembre-se, este tutorial pode servir como base para executar qualquer outra aplicação Linux em seu servidor, incluindo:

      • Wine , uma camada de compatibilidade para executar aplicações Windows no Linux.
      • GIMP, um editor de imagem de código aberto.
      • Cutter, uma plataforma de engenharia reversa de código aberto.

      Esta última opção demonstra o grande potencial de conteinerizar e acessar remotamente aplicações GUI. Com esta configuração, agora você usa um servidor com mais poder de computação do que localmente. Com isso, você executa ferramentas com consumo intensivo de recursos como o Cutter.



      Source link

      Como acessar remotamente aplicações GUI usando Docker e Caddy no Ubuntu 20.04


      O autor selecionou o Free and Open Source Fund para receber uma doação como parte do programa Write for DOnations.

      Introdução

      Mesmo com a crescente popularidade dos serviços em nuvem, a necessidade de executar aplicações nativas ainda existe.

      Utilizando o noVNC e o TigerVNC, você pode executar aplicações nativas dentro de um contêiner Docker e acessá-las remotamente usando um navegador Web. Além disso, você pode executar sua aplicação em um servidor com mais recursos de sistema do que você pode ter disponível localmente, o que pode fornecer maior flexibilidade ao se executar grandes aplicações.

      Neste tutorial, você irá conteinerizar o Mozilla Thunderbird, um cliente de e-mail usando o Docker. Depois, você irá protegê-lo e fornecer acesso remoto usando o servidor Web Caddy.

      Quando você terminar, você será capaz de acessar o Thunderbird a partir de qualquer dispositivo usando apenas um navegador Web. Opcionalmente, você também poderá acessar localmente os arquivos dele usando o WebDAV. Você também terá uma imagem Docker totalmente independente que você pode executar em qualquer lugar.

      Pré-requisitos

      Antes de iniciar este guia, você precisará do seguinte:

      • Um servidor Ubuntu 20.04 com pelo menos 2GB RAM e 4GB de espaço em disco.
      • Um usuário non root com privilégios sudo.
      • O Docker configurado em seu servidor. Você pode seguir o tutorial How To Install and Use Docker on Ubuntu 20.04.

      Passo 1 – Criando a configuração do supervisord

      Agora que seu servidor está em execução e o Docker está instalado, você está pronto para começar a configuração do contêiner da sua aplicação. Como seu contêiner consiste em vários componentes, você precisa usar um gerenciador de processos para iniciá-los e monitorá-los. Aqui, você estará usando o supervisord. O supervisord é um gerenciador de processos escrito em Python que é frequentemente usado para orquestrar contêineres complexos.

      Primeiro, crie e entre em um diretório chamado thunderbird para seu contêiner:

      • mkdir ~/thunderbird
      • cd ~/thunderbird

      Agora crie e abra um arquivo chamado supervisord.conf usando o nano ou o seu editor preferido:

      • nano ~/thunderbird/supervisord.conf

      Agora adicione este primeiro bloco de código em supervisord.conf, que definirá as opções globais para o supervisord:

      ~/thunderbird/supervisord.conf

      [supervisord]
      nodaemon=true
      pidfile=/tmp/supervisord.pid
      logfile=/dev/fd/1
      logfile_maxbytes=0
      

      Neste bloco, você está configurando o supervisord propriamente. Você precisa definir o nodaemon para true porque ele será executado dentro de um contêiner Docker como o entrypoint. Portanto, você vai querer que ele permaneça em execução em primeiro plano. Você também está definindo o pidfile para um caminho acessível por um usuário não-root (mais sobre isso posteriormente), e o logfile para stdout para que você possa ver os logs.

      Em seguida, adicione outro pequeno bloco de código ao supervisord.conf. Este bloco inicia o TigerVNC, que é um servidor VNC/X11 combinado:

      ~/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
      

      Neste bloco, você está configurando o servidor X11. O X11 é um protocolo de servidor de exibição, que é o que permite que as aplicações GUI sejam executadas. Observe que no futuro ele será substituído pelo Wayland, mas o acesso remoto ainda está em desenvolvimento.

      Para este contêiner você está usando o TigerVNC e seu servidor VNC embutido. Isso tem uma série de vantagens em relação ao uso de um servidor X11 e VNC separados:

      • Tempo de resposta mais rápido, uma vez que o desenho da GUI é feito diretamente no servidor VNC em vez de ser feito em um framebuffer intermediário (a memória que armazena o conteúdo da tela).
      • Redimensionamento automático da tela, que permite que a aplicação remota seja redimensionada automaticamente para caber no cliente (nesse caso, na janela do navegador Web).

      Se você desejar, você pode alterar o argumento para a opção -desktop a partir do Thunderbird para outra coisa de sua escolha. O servidor exibirá sua escolha como o título da página Web usada para acessar sua aplicação.

      Agora, vamos adicionar um terceiro bloco de código ao supervisord.conf para iniciar o 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
      

      Neste bloco, você está configurando o easy-novnc, um servidor standalone que fornece um encapsulamento em torno do noVNC. Este servidor executa dois papéis. Primeiro, ele fornece uma página de conexão simples que lhe permite configurar opções para a conexão e lhe permite definir as opções padrão. Segundo, ele faz proxy do VNC sobre o WebSocket, que o permite ser acessado através de um navegador Web comum.

      Normalmente, o redimensionamento é feito no lado do cliente (ou seja, o tamanho de imagem), mas você está usando a opção resize=remote para aproveitar todos os ajustes de resolução remota do TigerVNC. Isso também fornece menor latência em dispositivos mais lentos, como os Chromebooks de entrada:

      Nota: Este tutorial usa o easy-novnc. Se você desejar, você pode usar o websockify e um servidor Web separado. A vantagem do easy-novnc é que o uso da memória e o tempo de inicialização são significativamente mais baixos e ele é auto-contido. O easy-novnc também fornece uma página de conexão mais limpa do que o noVNC padrão e permite definir opções padrão que são úteis para esta configuração (como o resize=remote).

      Agora adicione o seguinte bloco à sua configuração para iniciar o OpenBox, o gerenciador de janelas:

      ~/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
      

      Neste bloco, você está configurando o OpenBox, um gerenciador de janelas X11 leve. Você poderia ignorar este passo, mas sem ele, você não teria barras de título ou seria capaz de redimensionar janelas.

      Finalmente, vamos adicionar o último bloco ao supervisord.conf, que irá iniciar a aplicação principal:

      ~/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
      

      Neste bloco final, você está definindo priority para 1 para garantir que o Thunderbird inicie depois do TigerVNC, ou ele encontraria uma condição de corrida e falharia aleatoriamente ao iniciar. Também definimos autorestart=true para reabrir a aplicação automaticamente se ela fechar erroneamente. A variável de ambiente DISPLAY diz à aplicação para exibir no servidor VNC que você configurou anteriormente.

      Veja como seu supervisord.conf finalizado vai ficar:

      ~/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
      

      Se você quiser conteinerizar uma aplicação diferente, substitua /usr/bin/thunderbird pelo caminho para o executável da sua aplicação. Caso contrário, você está pronto para configurar o menu principal da sua GUI.

      Passo 2 – Configurando o menu do OpenBox

      Agora que seu gerenciador de processos está configurado, vamos configurar o menu do OpenBox. Este menu nos permite lançar aplicações dentro do contêiner. Também incluiremos um terminal e monitor de processos para depuração, se necessário.

      Dentro do diretório da sua aplicação, use o nano ou o seu editor de texto favorito para criar e abrir um novo arquivo chamado menu.xml:

      • nano ~/thunderbird/menu.xml

      Agora adicione o seguinte código ao 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>
      

      Este arquivo XML contém os itens de menu que aparecerão quando você clicar com o botão direito no desktop. Cada item consiste em uma etiqueta e em uma ação.

      Se você quiser conteinerizar uma aplicação diferente, substitua /usr/bin/thunderbird pelo caminho para o executável da sua aplicação e altere o label do item.

      Passo 3 – Criando o Dockerfile

      Agora que o OpenBox está configurado, você estará criando o Dockerfile, que amarra tudo junto.

      Crie um Dockerfile no diretório do seu contêiner:

      • nano ~/thunderbird/Dockerfile

      Para começar, vamos adicionar código para criar o 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/[email protected] && 
          go build -o /bin/easy-novnc github.com/geek1011/easy-novnc
      

      No primeiro estágio, você está compilando o easy-novnc. Isso é feito em um estágio separado por simplicidade e para salvar espaço – você não precisa da cadeia de ferramentas Go inteira em sua imagem final. Observe o @v1.1.0 no comando de compilação. Isso garante que o resultado seja determinístico, o que é importante porque o Docker faz cache do resultado de cada passo. Se você não tivesse especificado uma versão explícita, o Docker faria referência à versão mais recente do easy-novnc no momento em que a imagem foi construída pela primeira vez. Além disso, é preciso que você baixe uma versão específica do easy-novnc, no caso de alterações recentes na interface CLI.

      Agora vamos criar o segundo estágio, que se tornará a imagem final. Aqui você estará usando o Debian 10 (buster) como a imagem base. Observe que como isso está em execução em um contêiner, ele funcionará independentemente da distribuição que você está executando em seu servidor.

      Em seguida, adicione o seguinte bloco ao seu 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
      

      Nesta instrução, você está instalando o Debian 10 como sua imagem base e instalando o mínimo necessário para executar aplicações GUI em um contêiner. Observe que você executa apt-get update como parte da mesma instrução para evitar problemas de cache do Docker. Para economizar espaço, você também está removendo as listas de pacotes baixadas depois (os pacotes em cache são removidos por padrão). Você também está criando /usr/share/desktop-directories porque algumas aplicações dependem do diretório existente.

      Vamos adicionar outro pequeno bloco de código:

      ~/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
      

      Nesta instrução, você está instalando alguns utilitários e pacotes de utilidade geral. De interesse particular aqui estão o xdg-utils (que fornece os comandos base usados pelas aplicações desktop no Linux) e os ca-certificates (que instalam os certificados raiz para nos permitir acessar sites HTTPS).

      Agora, podemos adicionar as instruções para a aplicação principal:

      ~/thunderbird/Dockerfile

      ...
      RUN apt-get update -y && 
          apt-get install -y --no-install-recommends thunderbird && 
          rm -rf /var/lib/apt/lists
      

      Como antes, aqui estamos instalando a aplicação. Se você estiver conteinerizando uma aplicação diferente, você pode substituir esses comandos por aqueles necessários para instalar seu app específico. Algumas aplicações irão exigir um pouco mais de trabalho para executar dentro do Docker. Por exemplo, se você estiver instalando um app que usa o Chrome, Chromium ou o QtWebEngine, você precisará usar o argumento da linha de comando --no-sandbox porque ele não será suportado dentro do Docker.

      Em seguida, vamos começar a adicionar as instruções para adicionar os últimos arquivos ao contêiner:

      ~/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
      

      Aqui você está adicionando os arquivos de configuração que você criou anteriormente à imagem e copiando o binário easy-novnc do primeiro estágio.

      Este próximo bloco de código cria o diretório de dados e adiciona um usuário dedicado para o seu app. Isto é importante porque algumas aplicações se recusam a executar como root. Também é uma boa prática não executar aplicações como root, mesmo em um contêiner.

      ~/thunderbird/Dockerfile

      ...
      RUN groupadd --gid 1000 app && 
          useradd --home-dir /data --shell /bin/bash --uid 1000 --gid 1000 app && 
          mkdir -p /data
      VOLUME /data
      

      Para garantir um UID/GID consistente para os arquivos, você está configurando ambos explicitamente para 1000. Você também está montando um volume no diretório de dados para garantir que ele persista entre reinicializações.

      Finalmente, vamos adicionar as instruções de lançamento.

      ~/thunderbird/Dockerfile

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

      Ao definir o comando padrão para supervisord, o gerenciador irá lançar os processos necessários para executar sua aplicação. Neste caso, você está usando CMD em vez de ENTRYPOINT. Na maioria dos casos, isso não faria diferença, mas usar o CMD é mais adequado para este propósito por algumas razões. Primeiro, o supervisord não aceita quaisquer argumentos relevantes para nós, e se você fornecer argumentos ao contêiner eles substituem o CMD e são anexados ao ENTRYPOINT. Segundo, usar o CMD nos permite fornecer um comando inteiramente diferente (que será executado por /bin/sh -c) ao passar argumentos para o contêiner, o que torna a depuração mais fácil.

      E, finalmente, você precisa executar o chown como root antes de iniciar o supervisord para evitar problemas de permissão no volume de dados e para permitir que aos processos filhos abrir o stdout. Isso também significa que você precisa usar gosu em vez da instrução USER para trocar o usuário.

      Aqui está como o seu Dockerfile finalizado vai parecer:

      ~/thunderbird/Dockerfile

      FROM golang:1.14-buster AS easy-novnc-build
      WORKDIR /src
      RUN go mod init build && 
          go get github.com/geek1011/easy-novn[email protected] && 
          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"]
      

      Salve e feche seu Dockerfile. Agora estamos prontos para compilar e executar nosso contêiner e, em seguida, acessar o Thunderbird – uma aplicação GUI.

      Passo 4 – Compilando e executando o contêiner

      O próximo passo é compilar seu contêiner e configurá-lo para executar na inicialização. Você também irá configurar um volume para preservar os dados da aplicação entre reinicializações e atualizações.

      Primeiro compile seu contêiner. Certifique-se de executar esses comandos no diretório ~/thunderbird:

      • docker build -t thunderbird .

      Agora crie uma nova rede que será compartilhada entre os contêineres da aplicação:

      • docker network create thunderbird-net

      Em seguida, crie um volume para armazenar os dados da aplicação:

      • docker volume create thunderbird-data

      Finalmente, execute-a e defina-a para reinicializar automaticamente:

      • docker run --detach --restart=always --volume=thunderbird-data:/data --net=thunderbird-net --name=thunderbird-app thunderbird

      Observe que se você quiser, você pode substituir o thunderbird-app após a opção --name por um nome diferente. Seja o que for que você escolheu, sua aplicação está agora conteinerizada e em execução. Agora vamos usar o servidor Web Caddy para protegê-la e conectar-se remotamente a ela.

      Passo 5 – Configurando o Caddy

      Neste passo, você irá configurar o servidor Web Caddy para fornecer autenticação e, opcionalmente, acesso remoto a arquivos através do WebDAV. Por simplicidade, e para lhe permitir utilizá-lo com seu proxy reverso existente, você irá executá-lo em um outro contêiner

      Crie um novo diretório e então vá para ele:

      Agora crie um novo Dockerfile usando o nano ou o seu editor preferido:

      Então, adicione as seguintes diretivas:

      ~/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"]
      

      Este Dockerfile compila o Caddy com o plug-in WebDAV habilitado e, em seguida, o lança na porta 8080 com o Caddyfile em /etc/Caddyfile. Salve e feche o arquivo.

      Em seguida, você irá configurar o servidor Web Caddy. Crie um arquivo chamado Caddyfile no diretório que você acabou de criar:

      Agora adicione o seguinte bloco de código ao seu 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}
          }
      }
      

      Este Caddyfile faz proxy do diretório raiz para o contêiner thunderbird-app que você criou no Passo 4 (o Docker o resolve para o IP correto). Ele também irá fornecer um navegador de arquivos baseado em web somente leitura em /files e executar um servidor WebDAV em /webdav, que você pode montar localmente para acessar seus arquivos. O nome de usuário e a senha são lidos a partir das variáveis de ambiente APP_USERNAME e APP_PASSWORD_HASH.

      Agora compile o contêiner:

      • docker build -t thunderbird-caddy .

      O Caddy v.2 requer que você faça um hash da sua senha desejada. Execute o seguinte comando e lembre-se de substituir mypass por uma senha forte da sua escolha:

      • docker run --rm -it thunderbird-caddy caddy hash-password -plaintext 'mypass'

      Este comando irá exibir uma string de caracteres. Copie isso para sua área de transferência em preparação para executar o próximo comando.

      Agora você está pronto para executar o contêiner. Certifique-se de substituir myuser por um nome de usuário da sua escolha e substitua mypass-hash pela saída do comando que você executou no passo anterior. Você também pode alterar a porta (8080 aqui) para acessar seu servidor em uma porta 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

      Agora estamos prontos para acessar e testar nossa aplicação.

      Passo 6 – Testando e gerenciando a aplicação

      Vamos acessar sua aplicação e garantir que ela está funcionando.

      Primeiro, abra http://your_server_ip:8080 em um navegador Web, faça login com as credenciais que você escolheu anteriormente, e clique em Connect.

      NoVNC connect page

      Agora você deve ser capaz de interagir com a aplicação, e ela deve redimensionar automaticamente para se encaixar na janela do seu navegador.

      Thunderbird main menu

      Se você clicar com o botão direito no desktop preto, você deve ver um menu que lhe permite acessar um terminal. Se você clicar com o botão do meio, verá uma lista de janelas.

      NoVNC right click

      Agora abra http://your_server_ip:8080/files/ em um navegador. Você deve ser capaz de acessar seus arquivos.

      NoVNC file access webdav

      Opcionalmente, você pode tentar montar http://your_server_ip:8080/webdav/ em um cliente WebDAV. Você deve ser capaz de acessar e modificar seus arquivos diretamente. Se você usar a opção Mapear unidade de rede no Windows Explorer, você precisará usar um proxy reverso para adicionar HTTPS ou definir HKLMSYSTEMCurrentControlSetServicesWebClientParametersBasicAuthLevel para DWORD:2.

      Em ambos os casos, sua aplicação GUI nativa está pronta para uso remoto.

      Conclusão

      Agora você configurou com êxito um contêiner Docker para o Thunderbird e, em seguida, usando o Caddy, você configurou o acesso a ele através de um navegador Web. Se você precisar atualizar sua aplicação, pare os contêineres, execute docker rm thunderbird-app thunderbird-web, recompile as imagens e, em seguida, execute novamente os comandos docker run das etapas anteriores acima. Seus dados ainda serão preservados uma vez que eles são armazenados em um volume.

      Se você quiser aprender mais sobre comandos básicos do Docker, você pode ler este tutorial ou esse guia de consulta rápida. Para uso a longo prazo, convém habilitar o HTTPS (isso requer um domínio) para segurança adicional.

      Além disso, se você estiver implantando mais de uma aplicação, você pode querer usar o Docker Compose ou o Kubernetes em vez de iniciar cada contêiner manualmente. E lembre-se, este tutorial pode servir como base para executar qualquer outra aplicação Linux em seu servidor, incluindo:

      • Wine , uma camada de compatibilidade para executar aplicações Windows no Linux.
      • GIMP, um editor de imagem de código aberto.
      • Cutter, uma plataforma de engenharia reversa de código aberto.

      Esta última opção demonstra o grande potencial de conteinerizar e acessar remotamente aplicações GUI. Com esta configuração, agora você pode usar um servidor com consideravelmente mais poder de computação do que você pode ter localmente para executar ferramentas com consumo intensivo de recursos como o Cutter.



      Source link

      Arquitetando Aplicações para o Kubernetes


      Introdução

      Projetar e executar aplicações com escalabilidade, portabilidade e robustez pode ser um desafio, especialmente à medida que a complexidade do sistema aumenta. A arquitetura de uma aplicação ou sistema impacta significativamente em como ele deve ser executado, o que ele espera de seu ambiente e o quanto está acoplado aos componentes relacionados. Seguir certos padrões durante a fase de projeto e aderir a certas práticas operacionais pode ajudar a combater alguns dos problemas mais comuns que as aplicações enfrentam ao executar em ambientes altamente distribuídos.

      Embora os padrões de projeto de software e as metodologias de desenvolvimento possam produzir aplicações com as características corretas de escalabilidade, a infraestrutura e o ambiente influenciam a operação do sistema implantado. Tecnologias como o Docker e o Kubernetes ajudam equipes a empacotar software e depois distribuir, fazer o deploy e escalar em plataformas de computadores distribuídos. Aprender a aproveitar melhor o poder dessas ferramentas pode ajudá-lo a gerenciar aplicações com maior flexibilidade, controle e capacidade de resposta.

      Neste guia, vamos discutir alguns dos princípios e padrões que você pode adotar para ajudá-lo a dimensionar e gerenciar suas cargas de trabalho no Kubernetes. Embora o Kubernetes possa executar muitos tipos de cargas de trabalho, as escolhas feitas podem afetar a facilidade de operação e as possibilidades disponíveis no deploy. A maneira como você projeta e constrói suas aplicações, empacota seus serviços em containers e configura o gerenciamento do ciclo de vida e o comportamento dentro do Kubernetes pode, cada uma, influenciar sua experiência.

      Projetando para Escalabilidade das Aplicações

      Ao produzir software, muitos requisitos afetam os padrões e a arquitetura que você escolhe empregar. Com o Kubernetes, um dos fatores mais importantes é a capacidade de escalar horizontalmente, ajustando o número de cópias idênticas da sua aplicação para distribuir a carga e aumentar a disponibilidade. Esta é uma alternativa à escalabilidade vertical, que tenta manipular os mesmos fatores através do deploy em máquinas com maiores ou menores recursos.

      Em particular, microservices ou micro-serviços é um padrão de projeto de software que funciona bem para deployments escaláveis em clusters. Os desenvolvedores criam aplicações pequenas e compostas que se comunicam pela rede através de APIs REST bem definidas, em vez de grandes programas compostos que se comunicam através de mecanismos internos de programação. A decomposição de aplicações monolíticas em componentes discretos de propósito único torna possível dimensionar cada função de forma independente. Grande parte da complexidade e composição que normalmente existiria no nível da aplicação é transferida para o domínio operacional, onde pode ser gerenciada por plataformas como o Kubernetes.

      Além dos padrões de software específicos, as aplicações cloud native ou nativas para nuvem são projetadas com algumas considerações adicionais em mente. Aplicações cloud native são programas que seguem um padrão de arquitetura de microservices com resiliência integrada, observabilidade e recursos administrativos para se adaptar ao ambiente fornecido por plataformas em cluster na nuvem.

      Por exemplo, aplicações cloud native são construídas com métricas de relatórios de integridade para permitir que a plataforma gerencie eventos do ciclo de vida se uma instância se tornar inconsistente. Eles produzem (e tornam disponível para exportação) dados robustos de telemetria para alertar os operadores sobre problemas e permitir que eles tomem decisões esclarecidas. As aplicações são projetadas para lidar com reinicializações e falhas regulares, alterações na disponibilidade do back-end e alta carga, sem corromper os dados ou deixar de responder.

      Seguindo a Filosofia 12 Factor Application

      Uma metodologia popular que pode ajudá-lo a se concentrar nas características mais importantes ao criar aplicações web prontas para a nuvem é a filosofia Twelve-Factor App. Escritos para ajudar os desenvolvedores e as equipes de operações a entender as principais qualidades compartilhadas pelos web services projetados para serem executados na nuvem, os princípios se aplicam muito bem ao software que funcionará em um ambiente em cluster como o Kubernetes. Embora os aplicativos monolíticos possam se beneficiar seguindo estas recomendações, as arquiteturas de microservices projetadas em torno desses princípios funcionam particularmente bem.

      Um breve sumário dos Doze Fatores são:

      1. Base de código: Gerencie todo o código em sistemas de controle de versão (como Git ou Mercurial). A base de código determina de forma abrangente o que é implantado.

      2. Dependências: As dependências devem ser gerenciadas completa e explicitamente pela base de código, seja armazenada com o código ou com a versão fixada em um formato no qual um gerenciador de pacotes possa instalar.

      3. Configuração: Separe os parâmetros de configuração da aplicação e defina-os no ambiente de deployment, em vez de mantê-los dentro da própria aplicação.

      4. Serviços de apoio: Os serviços locais e remotos são abstraídos como recursos acessíveis pela rede, com detalhes de conexão definidos na configuração.

      5. Construa, libere, execute: O estágio de construção da sua aplicação deve ser completamente separado dos processos de liberação e operação da mesma. O estágio de construção cria um artefato de deployment a partir do código-fonte, o estágio de liberação combina o artefato e a configuração, e o estágio de execução executa o release.

      6. Processos: Aplicações são implementadas como processos que não devem contar com o armazenamento de estado localmente. O estado deve ser transferido para um serviço de apoio, conforme descrito no quarto fator.

      7. Ligações à portas: As aplicações devem ligar-se nativamente a uma porta e ouvir conexões. Roteamento e encaminhamento de solicitações devem ser tratados externamente.

      8. Concorrência: As aplicações devem confiar na escalabilidade através do modelo de processo. A execução de várias cópias de uma aplicação simultaneamente, potencialmente em vários servidores, permite o escalonamento sem ajustar o código da aplicação.

      9. Descartabilidade: Os processos devem ser capazes de iniciar rapidamente e parar normalmente sem efeitos colaterais sérios.

      10. Paridade Desenv/prod: Seus ambientes de teste, preparação e produção devem ter uma correspondência próxima e ser mantidos em sincronia. Diferenças entre ambientes são oportunidades para incompatibilidades e configurações não testadas aparecerem.

      11. Logs: As aplicações devem transmitir os logs para a saída padrão, para que os serviços externos possam decidir a melhor forma de lidar com eles.

      12. Processos administrativos: Processos de administração únicos devem ser executados em releases específicos e enviados com o código do processo principal.

      Ao aderir às diretrizes fornecidas pelos Doze Fatores, você pode criar e executar aplicativos usando um modelo adequado ao ambiente de execução do Kubernetes. Os Doze Fatores encorajam os desenvolvedores a se concentrarem na responsabilidade principal de suas aplicações, a considerarem as condições operacionais e as interfaces entre os componentes e a usarem entradas, saídas e recursos de gerenciamento de processos padrão, para serem executados de maneira previsível no Kubernetes.

      Containerizando Componentes da Aplicação

      O Kubernetes usa containers para executar aplicações empacotadas isoladas em seus nodes de cluster. Para executar no Kubernetes, suas aplicações devem ser encapsuladas em uma ou mais imagens de container e executadas usando um runtime de container como o Docker. Embora containerizar seus componentes seja um requisito para o Kubernetes, isso também ajuda a reforçar muitos dos princípios da metodologia de doze fatores para aplicações, discutidos acima, permitindo fácil escalonamento e gerenciamento.

      Por exemplo, os containers fornecem isolamento entre o ambiente da aplicação e o sistema do host externo, suporta uma abordagem em rede, orientada a serviços para comunicação entre aplicações, e normalmente busca a configuração através de variáveis de ambiente e expõe logs gravados na saída de erro e na saída padrão. Os próprios containers encorajam a concorrência baseada em processos e ajudam a manter a paridade desenv/prod sendo escaláveis independentemente e agrupando o ambiente de runtime do processo. Essas características tornam possível empacotar suas aplicações para que elas funcionem sem problemas no Kubernetes.

      Diretrizes para Otimizar Containers

      A flexibilidade da tecnologia de container permite muitas maneiras diferentes de encapsular uma aplicação. Contudo, alguns métodos funcionam melhor do que outros em um ambiente Kubernetes.

      A maioria das boas práticas recomendadas na containerização de suas aplicações tem a ver com a construção de imagens, onde você define como seu software será configurado e executado dentro de um container. Em geral, manter tamanhos de imagem pequenos e compostos oferece vários benefícios. Imagens de tamanho otimizado podem reduzir o tempo e os recursos necessários para iniciar um novo container em um cluster, mantendo o perfil gerenciável e reutilizando as camadas existentes entre as atualizações de imagem.

      Um bom primeiro passo ao criar imagens de container é fazer o seu melhor para separar suas etapas de criação da imagem final que será executada na produção. Construir software geralmente requer ferramental extra, toma tempo adicional e produz artefatos que podem ser inconsistentes de container para container ou desnecessários para o runtime de execução final, dependendo do ambiente. Uma maneira de separar claramente o processo de construção do runtime de execução é usar as construções de vários estágios do Docker. As configurações das construções de vários estágios permitem que você especifique uma imagem de base para usar durante o processo de construção e defina outra para usar no runtime. Isso possibilita a criação de software usando uma imagem com todas as ferramentas de compilação instaladas e copiar os artefatos resultantes para uma imagem simplificada que será usada a cada vez, posteriormente.

      Com esse tipo de funcionalidade disponível, geralmente é uma boa ideia criar imagens de produção em cima de uma imagem matriz mínima. Se você quiser evitar completamente o inchaço encontrado em camadas da imagem matriz do estilo “distro” como ubuntu:16.04 (que inclui um ambiente Ubuntu 16.04 bastante completo), você pode construir suas imagens com o scratch — A imagem de base mais minimalista do Docker — como matriz. Contudo, a camada base scratch não fornece acesso a muitas das ferramentas principais e muitas vezes quebrará as suposições sobre o ambiente que alguns softwares detêm. Como uma alternativa, a imagem alpine Alpine Linux ganhou poularidade por ser um ambiente de base sólido e mínimo, que fornece uma distribuição Linux minúscula, mas com recursos completos.

      Para linguagens interpretadas como o Python ou Ruby, o paradigma muda levemente, já que não há um estágio de compilação e o interpretador deve estar disponível para executar o código em produção. No entanto, como as imagens magras ainda são ideais, muitas imagens otimizadas para linguagens específicas, construídas a partir do Alpine Linux estão disponíveis no Docker Hub. Os benefícios de se usar uma imagem menor para linguagens interpretadas são semelhantes aos das linguagens compiladas: O Kubernetes poderá buscar rapidamente todas as imagens de container necessárias para novos nodes para começar a fazer trabalho significativo.

      Decidindo sobre o Escopo para Containers e Pods

      Embora suas aplicações devam ser containerizadas para executar em um cluster Kubernetes, os pods são a menor unidade de abstração que o Kubernetes pode gerenciar diretamente. Um pod é um objeto Kubernetes composto de um ou mais containers fortemente acoplados. os containers em um pod compartilham um ciclo de vida e são gerenciados juntos como uma única unidade. Por exemplo, o scheduling desses containers é feito sempre no mesmo node, são iniciados e parados em sincronia e compartilham recursos como sistemas de arquivos e espaço de IP.

      No início, pode ser difícil descobrir a melhor maneira de dividir suas aplicações em containers e pods. Isso torna importante entender como o Kubernetes lida com esses componentes e o que cada camada de abstração fornece para seus sistemas. Algumas considerações podem ajudá-lo a identificar alguns pontos naturais de encapsulamento para sua aplicação com cada uma dessas abstrações.

      Uma maneira de determinar um escopo efetivo para seus containers é procurar por limites naturais do desenvolvimento. Se seus sistemas operam utilizando a arquitetura de microservices, containers bem projetados são frequentemente construídos para representar unidades discretas de funcionalidade que podem ser usadas em uma variedade de contextos. Esse nível de abstração permite à sua equipe lançar alterações nas imagens de containers e, em seguida, fazer o deploy dessa nova funcionalidade em qualquer ambiente onde essas imagens sejam utilizadas. As aplicações podem ser construídas através da composição de containers individuais que preencham uma determinada função, mas podem não realizar um processo inteiro sozinhos.

      Em contraste com o exposto cima, os pods são geralmente construídos pensando em quais partes do seu sistema podem se beneficiar mais do gerenciamento independente. Como o Kubernetes usa pods como sua menor abstração de interação com o usuário, essas são as unidades mais primitivas com as quais as ferramentas e a API do Kubernetes podem interagir e controlar diretamente. Você pode iniciar, parar e reiniciar pods, ou utilizar objetos de nível superior construídos em pods para introduzir recursos de replicação e gerenciamento do ciclo e vida. O Kubernetes não lhe permite gerenciar os containers dentro de um pod de forma independente, portanto, você não deve agrupar containers que possam se beneficiar de uma administração separada.

      Como muitos dos recursos e abstrações do Kubernetes lidam diretamente com os pods, faz sentido agrupar itens que devem ser escalados juntos em um único pod e separar aqueles que devem ser escalados independentemente. Por exemplo, separar seus servidores web de seus servidores de aplicação em diferentes pods permite escalar cada camada de forma independente, conforme necessário. No entanto, o empacotamento de um servidor web e de um adaptador de banco de dados no mesmo conjunto pode fazer sentido se o adaptador fornecer a funcionalidade essencial que o servidor web precisa para funcionar corretamente.

      Melhorando a Funcionalidade do Pod através do Empacotamento de Containers de Apoio

      Com isso em mente, quais tipos de containers devem ser agrupados em um único pod? Geralmente, um container primário é responsável pelo cumprimento das funções principais do pod, mas podem ser definidos containers adicionais que modifiquem ou estendam o container principal ou o ajudem a se conectar a um ambiente de deployment exclusivo.

      Por exemplo, em um pod de servidor web, um container Nginx pode escutar solicitações e fornecer conteúdo enquanto um container associado atualiza arquivos estáticos quando um repositório é alterado. Pode ser tentador empacotar esses dois componentes em um único container, mas há benefícios significativos em implementá-los como containers separados. O container do servidor web e o atualizador do repositório podem ser usados independentemente em diferentes contextos. Eles podem ser mantidos por equipes diferentes e cada um deles podem ser desenvolvidos para generalizar seu comportamento para trabalhar com diferentes containers complementares.

      Brendan Burns e David Oppenheimer identificaram três padrões primários para empacotar containers de apoio em seus artigos em design patterns for container-based distributed systems. Estes representam alguns dos casos de uso mais comuns para empacotamento de containers juntos em um pod:

      • Sidecar: Neste padrão, o container secundário estende e aprimora a funcionalidade principal do container primário. Esse padrão envolve a execução de funções não padronizadas ou utilitárias em um container separado. Por exemplo, um container que encaminha logs ou monitora atualizações em valores de configuração pode aumentar a funcionalidade de um pod sem alterar significativamente seu foco principal.
      • Ambassador: O padrão ambassador usa um container suplementar para abstrair recursos remotos para o container principal. O container principal se conecta diretamente ao container ambassador que, por sua vez, se conecta e abstrai os pools de recursos externos potencialmente complexos, como um cluster de Redis distribuído. O container principal não precisa saber ou se preocupar com o ambiente de deployment real para se conectar a serviços externos.
      • Adaptor: O padrão adaptor é usado para converter os dados, protocolos ou interfaces do container primário para alinhar com os padrões esperados por terceiros. Containers Adaptor permitem o acesso uniforme a serviços centralizados, mesmo quando as aplicações que eles atendem podem suportar nativamente somente interfaces incompatíveis.

      Embora a configuração da aplicação possa ser posta em imagens de container, é melhor tornar seus componentes configuráveis no runtime para suportar deployments em vários contextos e permitir uma administração mais flexível. Para gerenciar parâmetros de configuração de runtime, o Kubernetes usa dois objetos chamados ConfigMaps e Secrets.

      ConfigMaps é um mecanismo usado para armazenar dados que podem ser expostos a pods e outros objetos em runtime. Os dados armazenados dentro de ConfigMaps podem ser apresentados como variáveis de ambiente ou montados como arquivos no pod. Ao projetar suas aplicações para ler esses locais, você pode injetar a configuração no runtime usando o ConfigMaps e modificar o comportamento de seus componentes sem precisar reconstruir a imagem do container.

      Secrets são um tipo similar de objeto Kubernetes usado para armazenar dados sigilosos com segurança e seletivamente permitir o acesso a pods e outros componentes conforme necessário. Os secrets são uma maneira conveniente de transmitir material confidencial para aplicações sem armazená-los como texto simples em locais facilmente acessíveis em sua configuração normal. Funcionalmente, eles trabalham da mesma maneira que os ConfigMaps, portanto, as aplicações podem consumir dados de ConfigMaps e Secrets usando os mesmos mecanismos.

      ConfigMaps e Secrets o ajudam a evitar colocar a configuração diretamente nas definições de objeto do Kubernetes. Você pode mapear a chave de configuração em vez do valor, permitindo que você atualize a configuração dinamicamente, através da modificação do ConfigMap ou do Secret. Isso lhe dá a oportunidade de alterar o comportamento do runtime ativo dos pods e outros objetos do Kubernetes sem modificar as definições dos recursos do Kubernetes.

      Implementando as provas Readiness e Liveness

      O Kubernetes inclui uma grande quantidade de funcionalidades prontas para uso para gerenciar ciclos de vida e garantir que suas aplicações estejam sempre íntegras e disponíveis. No entanto, para aproveitar esses recursos, o Kubernetes precisa entender como ele deve monitorar e interpretar a integridade da sua aplicação. Para fazer isto, o Kubernetes lhe permite definir provas liveness e readiness.

      Provas Liveness permitem que o Kubernetes determine se uma aplicação em um container está no ar e funcionando ativamente. O Kubernetes pode executar comandos periodicamente dentro do container para verificar o comportamento básico da aplicação ou pode enviar solicitações de rede HTTP ou TCP para um local designado para determinar se o processo está disponível e é capaz de responder conforme o esperado. Se uma prova liveness falha, o Kubernetes reinicia o container para tentar restabelecer a funcionalidade dentro do pod.

      Provas Readness são um ferramenta similar usada para determinar se um pod está pronto para servir o tráfego. As aplicações em um container podem precisar executar procedimentos de inicialização antes de estarem prontas para aceitar solicitações de clientes ou podem precisar ser recarregadas ao serem notificadas sobre uma nova configuração. Quando uma prova readness falha, em vez de reiniciar o container, O Kubernetes pára de enviar temporariamente solicitações ao pod. Isso permite que o pod complete suas rotinas de inicialização ou manutenção sem afetar a integridade do grupo como um todo.

      Ao combinar as provas liveness e readiness, você pode instruir o Kubernetes a reiniciar automaticamente os pods ou removê-los dos grupos de back-end. Configurar sua infraestrutura para aproveitar esses recursos permite que o Kubernetes gerencie a disponibilidade e a integridade de suas aplicações sem trabalho operacional adicional.

      Usando Deployments para Gerenciar Escalabilidade e Disponibilidade

      Mais cedo, ao discutir alguns fundamentos de projetos de pods, também mencionamos que outros objetos do Kubernetes são construídos com base nessas primitivas para fornecer funcionalidade mais avançada. Um deployment, um desses objetos compostos, é provavelmente o objeto Kubernetes mais comumente definido e manipulado.

      Deployments são objetos compostos que se baseiam em outras primativas do Kubernetes para somar recursos adicionais. Eles adicionam recursos de gerenciamento de ciclo de vida a objetos intermediários chamados replicasets, como a capacidade de executar atualizações contínuas, rollback para versões anteriores e transição entre estados. Esses replicasets permitem que você defina modelos de pod para lançar e gerenciar várias cópias de um único projeto de pod. Isso ajuda você a escalar facilmente sua infraestrutura, gerenciar os requisitos de disponibilidade e reiniciar automaticamente os pods em caso de falha.

      Esses recursos adicionais fornecem uma estrutura administrativa e recursos de autocorreção para a abstração de pods relativamente simples. Embora os pods sejam as unidades que executam as cargas de trabalho que você define, eles não são as unidades que você geralmente deve provisionar e gerenciar. Em vez disso, pense nos pods como um bloco construtivo que pode executar aplicativos de maneira robusta quando provisionados por meio de objetos de nível mais alto, como os deployments.

      Criando Regras de Serviços e de Ingresso para Gerenciar o Acesso às Camadas de Aplicação

      Deployments lhe permitem provisionar e gerenciar conjuntos de pods intercambiáveis para escalar suas aplicações e atender às demandas do usuário. No entanto, o roteamento de tráfego para os pods provisionados é uma preocupação à parte. Como os pods são trocados como parte de atualizações contínuas, reiniciados ou movidos devido a falhas de host, os endereços de rede associados anteriormente ao grupo em execução serão alterados. Os serviços do Kubernetes lhe permitem gerenciar essa complexidade mantendo as informações de roteamento para pools dinâmicos de pods e controlando o acesso a várias camadas da sua infraestrutura.

      No Kubernetes, serviços são mecanismos específicos que controlam como o tráfego é roteado para conjuntos de pods. Seja encaminhando tráfego de clientes externos ou gerenciando conexões entre vários componentes internos, os serviços permitem que você controle como o tráfego deve fluir. O Kubernetes atualizará e manterá todas as informações necessárias para encaminhar conexões para os pods relevantes, mesmo que o ambiente se altere e o cenário de rede mude.

      Acessando Serviços Internamente

      Para usar os serviços efetivamente, primeiro você deve determinar os consumidores presumidos para cada grupo de pods. Se o seu serviço só será usado por outras aplicações implantadas em seu cluster do Kubernetes, o tipo de serviço clusterIP permite que você se conecte a um conjunto de pods usando um endereço IP estável que só pode ser roteado de dentro do cluster. Qualquer objeto com deploy no cluster pode se comunicar com o grupo de pods replicados enviando tráfego diretamente para o endereço IP do serviço. Esse é o tipo de serviço mais simples, que funciona bem para camadas internas das aplicações.

      Um add-on opcional de DNS permite que o Kubernetes forneça nomes DNS para serviços. Isso permite que os pods e outros objetos se comuniquem com os serviços pelo nome, em vez do endereço IP. Esse mecanismo não altera significativamente o uso do serviço, mas os identificadores baseados em nome podem simplificar a conexão de componentes ou a definição de interações sem conhecer o endereço IP do serviço antecipadamente.

      Expondo Serviços para Consumo Público

      Se a interface deve ser publicamente acessível, sua melhor opção geralmente é o tipo de serviço load balancer. Ele usa a API do seu provedor de nuvem específico para provisionar um balanceador de carga, que serve tráfego para os pods de serviço por meio de um endereço IP exposto publicamente. Isso lhe permite rotear solicitações externas para os pods em seu serviço, oferecendo um canal de rede controlado para sua rede interna de clusters.

      Como o tipo de serviço load balancer cria um balanceador de carga para cada serviço, pode se tornar caro expor publicamente os serviços do Kubernetes usando esse método. Para ajudar a aliviar isso, os objetos ingress do Kubernetes podem ser usados para descrever como rotear diferentes tipos de solicitações para diferentes serviços com base em um conjunto predeterminado de regras. Por exemplo, solicitações para “example.com” poderiam ir para o serviço A, enquanto solicitações para “sammytheshark.com” poderiam ser roteadas para o serviço B. Os objetos ingress fornecem uma maneira de descrever como rotear logicamente um fluxo misto de solicitações para seus serviços de destino com base em padrões predefinidos.

      Regras Ingress devem ser interpretadas por um ingress controller — normalmente algum tipo de balanceamento de carga, como o Nginx — implantado no cluster como um pod, que implementa as regras de entrada e encaminha o tráfego apropriadamente para os serviços do Kubernetes. Atualmente, o tipo de objeto ingress está na versão beta, mas há várias implementações de trabalho que podem ser usadas para minimizar o número de balanceadores de carga externos que os proprietários de cluster precisam executar.

      Usando Sintaxe Declarativa para Gerenciar o Estado do Kubernetes

      O Kubernetes oferece bastante flexibilidade na definição e controle dos recursos implantados (com deploy realizado) em seu cluster. Utilizando ferramentas como o kubectl, você pode definir imperativamente objetos ad-hoc para fazer deploy imediatamente em seu cluster. Embora isso possa ser útil para fazer o deploy de recursos rapidamente ao aprender o Kubernetes, há desvantagens nessa abordagem que a tornam indesejável para a administração de produção a longo prazo.

      Um dos principais problemas com o gerenciamento imperativo é que ele não deixa nenhum registro das alterações que você fez deploy em seu cluster. Isso dificulta ou impossibilita a recuperação no caso de falhas ou para acompanhar as mudanças operacionais à medida que são aplicadas aos seus sistemas.

      Felizmente, o Kubernetes fornece uma sintaxe declarativa alternativa que lhe permite definir recursos em arquivos de texto e em seguida usar o kubectl para aplicar a configuração ou alteração. Armazenar esses arquivos de configuração em um repositório de controle de versão é uma maneira simples de monitorar alterações e integrar com os processos de revisão usados para outras áreas de sua organização. O gerenciamento baseado em arquivos também simplifica a adaptação de padrões existentes para novos recursos, copiando e editando definições existentes. Armazenar suas definições de objeto do Kubernetes em diretórios versionados permite manter um instantâneo do estado do cluster desejado em cada instante no tempo. Isso pode ter um valor inestimável durante operações de recuperação, migrações ou ao rastrear a causa raiz de alterações não intencionais introduzidas em seu sistema.

      Conclusão

      Gerenciar a infraestrutura que executará suas aplicações e aprender como aproveitar melhor os recursos oferecidos pelos ambientes modernos de orquestração pode ser assustador. No entanto, muitos dos benefícios oferecidos por sistemas como o Kubernetes e tecnologias como containers ficam mais claros quando suas práticas de desenvolvimento e operações se alinham aos conceitos sobre os quais o ferramental é construído. Arquitetar seus sistemas usando os padrões nos quais o Kubernetes se destaca e entender como certos recursos podem aliviar alguns dos desafios associados a deployments altamente complexos pode ajudar a melhorar sua experiência com a execução na plataforma.



      Source link