One place for hosting & domains

      Como adicionar o Sidekiq e o Redis a um aplicativo Ruby on Rails


      Introdução

      Ao desenvolver um aplicativo Ruby on Rails, você pode se deparar com tarefas de aplicativos que devem ser executadas de maneira assíncrona. O processamento de dados, envio de e-mails em lote, ou a interação com APIs externas são todos exemplos de trabalho que podem ser feitos de maneira assíncrona com o background jobs. O uso de jobs em segundo plano pode melhorar o desempenho de seu aplicativo, descarregando tarefas potencialmente demoradas em uma fila de processamento em segundo plano, liberando o ciclo de solicitação/resposta original.

      O Sidekiq é uma das frameworks mais amplamente utilizadas em jobs de segundo plano que você pode implementar em um aplicativo Rails. Ele é apoiado pelo Redis, um armazenamento de valores-chave dentro da memória conhecido pela sua flexibilidade e desempenho. O Sidekiq utiliza o Redis como um armazenamento de gerenciamento de tarefas para processar milhares de tarefas por segundo.

      Neste tutorial, você adicionará o Redis e o Sidekiq a um aplicativo existente do Rails. Você criará um conjunto de classes e métodos de trabalho do Sidekiq para lidar com:

      • Um upload em lote de informações de tubarões ameaçados de extinção para o banco de dados do aplicativo de um arquivo CSV no repositório de projeto.
      • A remoção desses dados.

      Quando terminar, terá um aplicativo de demonstração que utilizará trabalhadores e trabalhos para processar tarefas de maneira assíncrona. Esta será uma boa base para que você adicione trabalhadores e trabalhos ao seu próprio aplicativo, usando este tutorial como um ponto de partida.

      Pré-requisitos

      Para seguir este tutorial, você vai precisar do seguinte:

      Passo 1 — Clonando o projeto e instalando dependências

      Nosso primeiro passo será clonar o repositório de rails-bootstrap da conta do GitHub da Comunidade da DigitalOcean. Esse repositório inclui o código da configuração descrita em Como adicionar o Bootstrap em um aplicativo Ruby on Rails, que explica como adicionar o Bootstrap a um projeto Rails 5 existente.

      Clone o repositório em um diretório chamado rails-sidekiq:

      • git clone https://github.com/do-community/rails-bootstrap.git rails-sidekiq

      Navegue até o diretório rails-sidekiq:

      Para trabalhar com o código, primeiro será necessário instalar as dependências do projeto, que estão listadas em seu Gemfile. Você também precisará adicionar o sidekiq gem ao projeto para trabalhar com o Sidekiq e o Redis.

      Abra o Gemfile do projeto para editá-lo usando o nano ou seu editor favorito:

      Adicione a gem em qualquer lugar das dependências principais do projeto (acima das dependências de desenvolvimento):

      ~/rails-sidekiq/Gemfile

      . . .
      # Reduces boot times through caching; required in config/boot.rb
      gem 'bootsnap', '>= 1.1.0', require: false
      gem 'sidekiq', '~>6.0.0'
      
      group :development, :test do
      . . .
      

      Salve e feche o arquivo quando terminar de adicionar o gem.

      Use o seguinte comando para instalar os gems:

      Você verá na saída que o redis gem também está instalado como um requisito para o sidekiq.

      Em seguida, você instalará suas dependências do Yarn. Como esse projeto Rails 5 foi modificado para atender ativos com o webpack, as dependências de JavaScript dele são gerenciadas agora pelo Yarn. Isso significa que é necessário instalar e verificar as dependências listadas no arquivo do projeto package.json.

      Execute o yarn install para instalar essas dependências:

      Em seguida, execute seu banco de dados de migrações:

      Assim que suas migrações terminarem, teste o aplicativo para garantir que ele esteja funcionando como esperado. Caso esteja trabalhando localmente, inicie seu servidor no contexto do seu pacote local com o seguinte comando:

      Caso esteja trabalhando em um servidor de desenvolvimento, inicie o aplicativo com:

      • bundle exec rails s --binding=your_server_ip

      Navegue até o localhost:3000 ou http://your_server_ip:3000. Você verá a seguinte página inicial:

      Página inicial do aplicativo

      Para criar um novo tubarão, clique no botão Get Shark Info, que levará você até a rota sharks/index:

      Rota do Índice de Tubarões

      Para verificar se o aplicativo está funcionando, podemos adicionar algumas informações de demonstração a ele. Clique em New Shark. Será solicitado que coloque um nome de usuário (sammy) e senha (shark), graças às configurações de autenticação do projeto.

      Na página New Shark, digite “Great White” no campo Name e “Scary” no campo Facts.

      Criar Tubarão

      Clique no botão Create Shark para criar o tubarão. Assim que ver que seu tubarão foi criado, encerre o servidor com CTRL+C.

      Você instalou as dependências necessárias para seu projeto e testou sua funcionalidade. Em seguida, você pode fazer algumas alterações no aplicativo Rails para trabalhar com seus recursos de tubarões ameaçados de extinção.

      Passo 2 — Gerando um controlador para recursos de tubarões ameaçados de extinção

      Para trabalhar com nossos recursos de tubarões ameaçados de extinção, adicionaremos um novo modelo ao aplicativo e um controlador que controlará como as informações sobre tubarões ameaçados de extinção serão apresentadas aos usuários. Nosso objetivo final é tornar possível que os usuários enviem um lote grande de informações sobre tubarões ameaçados de extinção sem bloquear a funcionalidade geral do nosso aplicativo e excluírem essas informações quando não precisarem mais delas.

      Primeiro, vamos criar um modelo Endangered para nossos tubarões ameaçados de extinção. Vamos incluir um campo string em nossa tabela de banco de dados para o nome do tubarão e outro campo string para as categorias da União Internacional para a Conservação da Natureza (IUCN), que determina o grau em que cada tubarão está em risco.

      Por fim, nossa estrutura de modelo corresponderá às colunas do arquivo CSV que usaremos para criar nosso upload do lote. Este arquivo está localizado no diretório db e você pode verificar seu conteúdo com o seguinte comando:

      O arquivo contém uma lista de 73 tubarões ameaçados de extinção e seus status de IUCN – vu para vulneráveis, en para ameaçados de extinção e cr para criticamente ameaçados de extinção.

      Nosso modelo Endangered se correlacionará com esses dados, permitindo-nos criar novas instâncias de Endangered a partir deste arquivo CSV. Crie o modelo com o seguinte comando:

      • rails generate model Endangered name:string iucn:string

      Em seguida, gere um controlador Endangered com uma ação index:

      • rails generate controller endangered index

      Isso nos dará um ponto de partida para desenvolver a funcionalidade do nosso aplicativo, embora também seja necessário adicionar métodos personalizados ao arquivo do controlador que o Rails gerou para nós.

      Abra aquele arquivo agora:

      • nano app/controllers/endangered_controller.rb

      O Rails nos forneceu um rascunho que podemos começar a preencher.

      Primeiro, vamos precisar determinar quais rotas precisamos trabalhar com nossos dados. Graças ao comando generate controller, temos um método index para começar. Isso se correlacionará a uma visualização do index, onde apresentaremos usuários com a opção de fazer o upload de tubarões ameaçados de extinção.

      No entanto, também queremos lidar com casos onde os usuários podem já ter feito upload dos tubarões; eles não precisarão de uma opção de upload neste caso. De algum modo precisaremos avaliar quantas instâncias da classe Endangered já existem, uma vez que mais de uma indica que o upload do lote já ocorreu.

      Vamos começar criando um método private set_endangered que capturará cada instância de nossa classe Endangered a partir do banco de dados. Adicione o código a seguir ao arquivo:

      ~/rails-sidekiq/app/controllers/endangered_controller.rb

      class EndangeredController < ApplicationController
        before_action :set_endangered, only: [:index, :data]
      
        def index
        end
      
        private
      
          def set_endangered
            @endangered = Endangered.all
          end
      
      end
      

      Note que o filtro before_action garantirá que o valor @endangered seja definido apenas para as rotas index e data, que serão as rotas onde lidaremos com os dados dos tubarões ameaçados de extinção.

      Em seguida, adicione o seguinte código ao método index para determinar a rota correta para usuários visitando esta parte do aplicativo:

      ~/rails-sidekiq/app/controllers/endangered_controller.rb

      class EndangeredController < ApplicationController
        before_action :set_endangered, only: [:index, :data]
      
        def index          
          if @endangered.length > 0
            redirect_to endangered_data_path
          else
            render 'index'
          end
        end
      . . .
      

      Caso não exista nenhuma instância de nossa classe Endangered, redirecionaremos os usuários para a rota data, onde podem visualizar informações sobre os tubarões que eles criaram. Caso contrário, eles visualizarão o index.

      Em seguida, abaixo do método index, adicione um método data, que se correlacionará a uma visualização do método data:

      ~/rails-sidekiq/app/controllers/endangered_controller.rb

      . . .
        def index          
          if @endangered.length > 0
            redirect_to endangered_data_path
          else
            render 'index'
          end
        end
      
        def data
        end
      . . .
      

      Em seguida, adicionaremos um método para lidar com o upload de dados propriamente dito. Vamos chamar este método de upload e chamaremos uma classe trabalhadora e um método Sidekiq para realizar o upload de dados do arquivo CSV. Criaremos a definição para essa classe trabalhadora, AddEndangeredWorker, no próximo passo.

      Por enquanto, adicione o seguinte código ao arquivo para fazer o trabalhador do Sidekiq realizar o upload:

      ~/rails-sidekiq/app/controllers/endangered_controller.rb

      . . .
        def data
        end
      
        def upload
          csv_file = File.join Rails.root, 'db', 'sharks.csv'   
          AddEndangeredWorker.perform_async(csv_file)
          redirect_to endangered_data_path, notice: 'Endangered sharks have been uploaded!'
        end
      . . .
      

      Ao chamar o método perform_async da classe AddEndangeredWorker usando o arquivo CSV como um argumento, este código garante que os dados do tubarão e o trabalho de upload sejam passados para o Redis. Os trabalhadores do Sidekiq que vamos configurar monitoram a fila de trabalho e reagirão quando novos trabalhos surgirem.

      Após chamar o perform_async, nosso método de upload redireciona para o caminho data, onde os usuários poderão ver os tubarões carregados.

      Em seguida, adicionaremos um método destroy para destruir os dados. Adicione o seguinte código abaixo do método do upload:

      ~/rails-sidekiq/app/controllers/endangered_controller.rb

      . . .
        def upload
          csv_file = File.join Rails.root, 'db', 'sharks.csv'   
          AddEndangeredWorker.perform_async(csv_file)
          redirect_to endangered_data_path, notice: 'Endangered sharks have been uploaded!'
        end
      
        def destroy
          RemoveEndangeredWorker.perform_async
          redirect_to root_path
        end
      . . .
      

      Da mesma forma que nosso método de upload, nosso método destroy inclui uma chamada do perform_async de uma classe RemoveEndangeredWorker — o outro trabalhador Sidekiq que vamos criar. Após chamar este método, ele redireciona os usuários para o caminho do aplicativo raiz.

      O arquivo final se parecerá com este:

      ~/rails-sidekiq/app/controllers/endangered_controller.rb

      class EndangeredController < ApplicationController
        before_action :set_endangered, only: [:index, :data]
      
        def index          
          if @endangered.length > 0
            redirect_to endangered_data_path
          else
            render 'index'
          end
        end
      
        def data
        end
      
        def upload
          csv_file = File.join Rails.root, 'db', 'sharks.csv'   
          AddEndangeredWorker.perform_async(csv_file)
          redirect_to endangered_data_path, notice: 'Endangered sharks have been uploaded!'
        end
      
        def destroy
          RemoveEndangeredWorker.perform_async
          redirect_to root_path
        end
      
        private
      
          def set_endangered
            @endangered = Endangered.all
          end
      
      end
      

      Salve e feche o arquivo quando você terminar a edição.

      Como passo final na consolidação das rotas do nosso aplicativo, vamos modificar o código em config/routes.rb, o arquivo onde ficam nossas declarações de rota.

      Abra aquele arquivo agora:

      O arquivo se parece com esse:

      ~/rails-sidekiq/config/routes.rb

      Rails.application.routes.draw do
        get 'endangered/index'
        get 'home/index'
        resources :sharks do
                resources :posts
        end
        root 'home#index'
        # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
      end
      

      Vamos precisar atualizar o arquivo para incluir as rotas que definimos em nosso controlador: data, upload e destroy. Nossa rota de data corresponderá a uma solicitação GET para recuperar os dados do tubarão, enquanto nossas rotas de upload e destroy mapearão as solicitações POST, que farão o upload e a destruição desses dados.

      Adicione o código a seguir ao arquivo para definir essas rotas:

      ~/rails-sidekiq/config/routes.rb

      Rails.application.routes.draw do
        get 'endangered/index'
        get 'endangered/data', to: 'endangered#data'
        post 'endangered/upload', to: 'endangered#upload'
        post 'endangered/destroy', to: 'endangered#destroy'
        get 'home/index'
        resources :sharks do
                resources :posts
        end
        root 'home#index'
        # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
      end
      

      Salve e feche o arquivo quando você terminar a edição.

      Com seu modelo e controlador Endangered em funcionamento, continue para definir suas classes de trabalhadores do Sidekiq.

      Passo 3 — Definindo os trabalhadores do Sidekiq

      Chamamos os métodos do perform_async nos trabalhadores do Sidekiq em nosso controlador, mas ainda precisamos criar os trabalhadores em si.

      Primeiro, crie um diretório workers para os trabalhadores:

      Abra um arquivo para o trabalhador AddEndangeredWorker:

      • nano app/workers/add_endangered_worker.rb

      Neste arquivo, adicionaremos códigos que nos permitirá trabalhar com os dados em nosso arquivo CSV. Primeiro, adicione códigos ao arquivo que criarão a classe, inclua a biblioteca Ruby CSV e certifique-se de que essa classe funcione como um trabalhador Sidekiq:

      ~/rails-sidekiq/app/workers/add_endangered_worker.rb

      class AddEndangeredWorker
        require 'csv'
        include Sidekiq::Worker
        sidekiq_options retry: false
      
      end
      

      Também incluiremos a opção retry: false para garantir que o Sidekiq não tente fazer o upload novamente em caso de falha.

      Em seguida, adicione o código para a função perform:

      ~/rails-sidekiq/app/workers/add_endangered_worker.rb

      class AddEndangeredWorker
        require 'csv'
        include Sidekiq::Worker
        sidekiq_options retry: false
      
        def perform(csv_file)
          CSV.foreach(csv_file, headers: true) do |shark|
          Endangered.create(name: shark[0], iucn: shark[1])
        end
       end
      
      end
      

      O método perform recebe argumentos do método perform_async definido no controlador, por este motivo, é importante que os valores do argumento estejam alinhados. Aqui, passamos a csv_file, a variável que definimos no controlador, e usamos o método foreach da biblioteca CSV para ler os valores no arquivo. Definir headers: true para este loop garante que a primeira linha do arquivo seja tratada como uma linha de cabeçalhos.

      Então, o bloco lê os valores do arquivo nas colunas que definimos para nosso modelo Endangered: name e iucn. Executar este loop criará instâncias Endangered para cada uma das entradas em nosso arquivo CSV.

      Assim que terminar a edição, salve e feche o arquivo.

      Em seguida, criaremos um trabalhador para lidar com a exclusão desses dados. Abra um arquivo para a classe RemoveEndangeredWorker:

      • nano app/workers/remove_endangered_worker.rb

      Adicione o código a seguir para definir a classe, e para garantir que ela utilize a biblioteca e as funções CSV como um trabalhador Sidekiq:

      ~/rails-sidekiq/app/workers/remove_endangered_worker.rb

      class RemoveEndangeredWorker
        include Sidekiq::Worker
        sidekiq_options retry: false
      
      end
      

      Em seguida, adicione um método perform para lidar com a destruição dos dados dos tubarões ameaçados de extinção:

      ~/rails-sidekiq/app/workers/remove_endangered_worker.rb

      class RemoveEndangeredWorker
        include Sidekiq::Worker
        sidekiq_options retry: false
      
        def perform
          Endangered.destroy_all
        end
      
      end
      

      O método perform chama a função destroy_all na classe Endangered, que removerá todas as instâncias da classe a partir do banco de dados.

      Salve e feche o arquivo quando você terminar a edição.

      Com seus trabalhadores em funcionamento, continue para a criação de um layout para suas visualizações de endangered e a criação de modelos para a visualização de seus index e data, de modo que os usuários possam fazer upload e visualizar tubarões ameaçados de extinção.

      Passo 4 — Adicionando layouts e modelos de visualização

      Para que os usuários aproveitem suas informações de tubarões ameaçados de extinção, precisaremos abordar duas coisas: o layout para as visualizações definidas em nosso controlador endangered e os modelos de visualização para as visualizações de index e de data.

      Atualmente, nosso aplicativo utiliza um layout em todo o aplicativo, localizado em app/views/layouts/application.html.erb, uma navegação parcial e um layout para visualizações de sharks. O layout do aplicativo verifica se há um bloco de conteúdo que nos permita carregar diferentes layouts com base em qual parte do aplicativo nosso usuário se encontra: para a home index, os usuários verão um layout, e para quaisquer visualizações relacionadas a tubarões individuais, eles verão outro layout.

      Podemos reaproveitar o layout sharks para nossas visualizações endangered, uma vez que este formato também funcionará para a apresentação de dados de tubarões em massa.

      Copie o arquivo de layout de sharks para criar um layout de endangered:

      • cp app/views/layouts/sharks.html.erb app/views/layouts/endangered.html.erb

      Em seguida, vamos trabalhar na criação dos modelos de visualização para nossas visualizações de index e de data.

      Abra o modelo index primeiro:

      • nano app/views/endangered/index.html.erb

      Exclua o código boilerplate e adicione o código a seguir, o qual dará informações gerais aos usuários sobre as categorias ameaçados de extinção e os apresentará com a opção de fazer o upload das informações sobre eles:

      ~/rails-sidekiq/app/views/endangered/index.html.erb

      <p id="notice"><%= notice %></p>
      
      <h1>Endangered Sharks</h1>
      
      <p>International Union for Conservation of Nature (ICUN) statuses: <b>vu:</b> Vulnerable, <b>en:</b> Endangered, <b>cr:</b> Critically Endangered </p>
      
      <br>
      
        <%= form_tag endangered_upload_path do %>
        <%= submit_tag "Import Endangered Sharks" %>
        <% end %>
      
        <br>
      
      <%= link_to 'New Shark', new_shark_path, :class => "btn btn-primary btn-sm" %> <%= link_to 'Home', home_index_path, :class => "btn btn-primary btn-sm" %>
      

      Uma form_tag torna o upload de dados possível, apontando uma ação posterior para a endangered_upload_path — a rota que definimos para nossos uploads. Um botão de enviar, criado com o submit_tag, solicita que os usuários façam o "Import Endangered Sharks​".

      Além deste código, incluímos algumas informações gerais sobre os códigos ICUN, de modo que os usuários possam interpretar os dados que eles verão.

      Salve e feche o arquivo quando você terminar a edição.

      Em seguida, abra um arquivo para a visualização de data:

      • nano app/views/endangered/data.html.erb

      Adicione o código a seguir, que adicionará uma tabela com os dados de tubarões ameaçados de extinção:

      ~/rails-sidekiq/app/views/endangered/data.html.erb

      <p id="notice"><%= notice %></p>
      
      <h1>Endangered Sharks</h1>
      
      <p>International Union for Conservation of Nature (ICUN) statuses: <b>vu:</b> Vulnerable, <b>en:</b> Endangered, <b>cr:</b> Critically Endangered </p>
      
      <div class="table-responsive">
      <table class="table table-striped table-dark">
        <thead>
          <tr>
            <th>Name</th>
            <th>IUCN Status</th>
            <th colspan="3"></th>
          </tr>
        </thead>
      
        <tbody>
          <% @endangered.each do |shark| %>
            <tr>
              <td><%= shark.name %></td>
              <td><%= shark.iucn %></td>
            </tr>
          <% end %>
        </tbody>
      </table>
      </div>
      
      <br>
      
        <%= form_tag endangered_destroy_path do %>
        <%= submit_tag "Delete Endangered Sharks" %>
        <% end %>
      
        <br>
      
      <%= link_to 'New Shark', new_shark_path, :class => "btn btn-primary btn-sm" %> <%= link_to 'Home', home_index_path, :class => "btn btn-primary btn-sm" %>
      

      Esse código inclui os códigos de status da ICUN novamente e uma tabela de Inicialização para os dados produzidos. Ao fazer loop através de nossa variável @endangered, produziremos o nome e o status de ICUN de cada tubarão para a tabela.

      Abaixo da tabela, temos outro conjunto de form_tags e de submit_tags que foram enviados para o caminho destroy, oferecendo aos usuários a opção "Delete Endangered Sharks".

      Salve e feche o arquivo quando você terminar a edição.

      A última modificação que vamos fazer às visualizações será feita na visualização de index associada ao nosso controlador home. Você deve se lembrar de que essa visualização está definida como a raiz do aplicativo em config/routes.rb.

      Abra este arquivo para edição:

      • nano app/views/home/index.html.erb

      Encontre a coluna na linha que afirma que Sharks are ancient​​​:

      ~/rails-sidekiq/app/views/home/index.html.erb

      . . .
              <div class="col-lg-6">
                  <h3>Sharks are ancient</h3>
                  <p>There is evidence to suggest that sharks lived up to 400 million years ago.
                  </p>
              </div>
          </div>
      </div>
      

      Adicione o código a seguir ao arquivo:

      ~/rails-sidekiq/app/views/home/index.html.erb

      . . .
              <div class="col-lg-6">
                  <h3>Sharks are ancient and SOME are in danger</h3>
                  <p>There is evidence to suggest that sharks lived up to 400 million years ago. Without our help, some could disappear soon.</p>
                  <p><%= button_to 'Which Sharks Are in Danger?', endangered_index_path, :method => :get,  :class => "btn btn-primary btn-sm"%>
                  </p>
              </div>
          </div>
      </div>
      

      Incluímos uma chamada de ação para os usuários aprenderem mais sobre os tubarões ameaçados de extinção, primeiramente compartilhando uma mensagem importante e, em seguida, adicionando um auxiliar button_to que envia uma solicitação GET para nossa rota de endangered index, dando acesso a essa parte do aplicativo. A partir daí, eles poderão fazer o upload e visualizar informações sobre tubarões ameaçados de extinção.

      Salve e feche o arquivo quando você terminar a edição.

      Com seu código funcionando, você está pronto para inicializar o aplicativo e fazer o upload de alguns tubarões!

      Passo 5 — Inicializando o Sidekiq e testando o aplicativo

      Antes de inicializar o aplicativo, precisaremos executar as migrações em nosso banco de dados e iniciar o Sidekiq para habilitar nossos trabalhadores. O Redis já deve estar em execução no servidor, mas podemos verificar isso para termos certeza. Com tudo isso funcionando, estaremos prontos para testar o aplicativo.

      Primeiro, verifique se o Redis está funcionando:

      Deverá ver um resultado como o seguinte:

      Output

      ● redis-server.service - Advanced key-value store Loaded: loaded (/lib/systemd/system/redis-server.service; enabled; vendor preset: enabled) Active: active (running) since Tue 2019-11-12 20:37:13 UTC; 1 weeks 0 days ago

      Em seguida, execute seu banco de dados de migrações:

      Agora, você pode iniciar o Sidekiq no contexto do seu pacote atual do projeto, usando o comando bundle exec sidekiq​​​:

      Você verá um resultado como este, indicando que o Sidekiq está pronto para processar trabalhos:

      Output

      m, `$b .ss, $$: .,d$ `$$P,d$P' .,md$P"' ,$$$$$b/md$$$P^' .d$$$$$$/$$$P' $$^' `"/$$$' ____ _ _ _ _ $: ,$$: / ___|(_) __| | ___| | _(_) __ _ `b :$$ ___ | |/ _` |/ _ |/ / |/ _` | $$: ___) | | (_| | __/ <| | (_| | $$ |____/|_|__,_|___|_|__|__, | .d$$ |_| 2019-11-19T21:43:00.540Z pid=17621 tid=gpiqiesdl INFO: Running in ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux] 2019-11-19T21:43:00.540Z pid=17621 tid=gpiqiesdl INFO: See LICENSE and the LGPL-3.0 for licensing details. 2019-11-19T21:43:00.540Z pid=17621 tid=gpiqiesdl INFO: Upgrade to Sidekiq Pro for more features and support: http://sidekiq.org 2019-11-19T21:43:00.540Z pid=17621 tid=gpiqiesdl INFO: Booting Sidekiq 6.0.3 with redis options {:id=>"Sidekiq-server-PID-17621", :url=>nil} 2019-11-19T21:43:00.543Z pid=17621 tid=gpiqiesdl INFO: Starting processing, hit Ctrl-C to stop

      Abra uma segunda janela de terminal, navegue para o diretório rails-sidekiq e inicialize seu servidor do aplicativo.

      Caso esteja executando o aplicativo localmente, use o seguinte comando:

      Caso esteja trabalhando com um servidor de desenvolvimento, execute o seguinte:

      • bundle exec rails s --binding=your_server_ip

      Navegue para o localhost:3000 ou http://your_server_ip:3000 no navegador. Você verá a seguinte página inicial:

      Página inicial do App Sidekiq

      Clique no botão Which Sharks Are in Danger? [Quais tubarões estão em perigo?] . Como não fez upload de nenhum tubarão ameaçado de extinção, isso levará você até a visualização de endangered index:

      Exibição do índice Endangered [Em perigo]

      Clique em Import Endangered Sharks para importar os tubarões. Você verá uma mensagem de status informando que os tubarões foram importados:

      Iniciar importação

      Você verá também o início da importação. Atualize sua página para ver a tabela inteira:

      Atualizar tabela

      Graças ao Sidekiq, o upload de nosso lote grande de tubarões em perigo foi bem-sucedido, sem bloquear o navegador ou interferir com outras funcionalidades do aplicativo.

      Clique no botão Home, no final da página, para voltar à página principal do aplicativo:

      Página inicial do App Sidekiq

      A partir daqui, clique em Which Sharks Are in Danger? novamente. Isso levará você diretamente para a visualização de data, uma vez que você já fez o upload dos tubarões.

      Para testar a funcionalidade de exclusão, clique no botão Delete Endangered Sharks​​​ abaixo da tabela. Você deve ser redirecionado para a página inicial do aplicativo novamente. Clicando em Which Sharks Are in Danger? uma última vez levará você de volta para a visualização do index, onde terá a opção de fazer o upload dos tubarões novamente:

      Exibição do índice Endangered [Em perigo]

      Seu aplicativo agora está funcionando com os trabalhadores Sidekiq, que estão prontos para processar trabalhos e garantir que os usuários tenham uma boa experiência ao trabalhar com seu aplicativo.

      Conclusão

      Agora, você tem um aplicativo Rails em funcionamento com o Sidekiq habilitado, o que permitirá que você descarregue operações dispendiosas em uma fila de trabalho gerenciada pelo Sidekiq e apoiada pelo Redis. Isso permitirá que você melhore a velocidade e a funcionalidade do seu site à medida que desenvolve.

      Caso queira aprender mais sobre o Sidekiq, os docs são um bom lugar para começar.

      Para aprender mais sobre o Redis, confira nossa biblioteca dos recursos do Redis. Você também pode aprender mais sobre a execução de um cluster gerenciado do Redis na DigitalOcean, conferindo a documentação do produto.



      Source link

      How to Deploy Ruby on Rails with One-Click Apps


      Updated by Linode

      Contributed by
      Linode

      Ruby on Rails One-Click App

      Ruby on Rails is a server-side web application framework that allows web designers and developers to implement dynamic, fully featured web applications.

      Deploy a Ruby on Rails One-Click App

      One-Click Apps allow you to easily deploy software on a Linode using the Linode Cloud Manager. To access Linode’s One-Click Apps:

      1. Log in to your Linode Cloud Manager account.

      2. From the Linode dashboard, click on the Create button in the top right-hand side of the screen and select Linode from the dropdown menu.

      3. The Linode creation page will appear. Select the One-Click tab.

      4. Under the Select App section, select the app you would like to deploy:

        Select a One-Click App to deploy

      5. Once you have selected the app, proceed to the app’s Options section and provide values for the required fields.

      The Ruby on Rails Options section of this guide provides details on all available configuration options for this app.

      Ruby on Rails Options

      You can configure your Drupal App by providing values for the following fields:

      FieldDescription
      Rails Application nameThe name for your rails application. Required.

      Linode Options

      After providing the app specific options, provide configurations for your Linode server:

      ConfigurationDescription
      Select an ImageDebian 9 is currently the only image supported by Ruby on Rails One-Click Apps, and it is pre-selected on the Linode creation page. Required.
      RegionThe region where you would like your Linode to reside. In general, it’s best to choose a location that’s closest to you. For more information on choosing a DC, review the How to Choose a Data Center guide. You can also generate MTR reports for a deeper look at the network routes between you and each of our data centers. Required.
      Linode PlanYour Linode’s hardware resources. Required.
      Linode LabelThe name for your Linode, which must be unique between all of the Linodes on your account. This name will be how you identify your server in the Cloud Manager’s Dashboard. Required.
      Root PasswordThe primary administrative password for your Linode instance. This password must be provided when you log in to your Linode via SSH. It must be at least 6 characters long and contain characters from two of the following categories: lowercase and uppercase case letters, numbers, and punctuation characters. Your root password can be used to perform any action on your server, so make it long, complex, and unique. Required.

      When you’ve provided all required Linode Options, click on the Create button. Your Ruby on Rails app will complete installation anywhere between 2-5 minutes after your Linode has finished provisioning.

      Getting Started after Deployment

      Access Ruby on Rails

      After Ruby on Rails has finished installing, you will be able to access Ruby on Rails from the console via ssh with your Linode’s IPv4 address:

      1. SSH into your Linode and create a limited user account.

      2. Log out and log back in as your limited user account.

      3. Update your server:

        sudo apt-get update && apt-get upgrade
        
      4. Ruby comes with some pre-made scripts to get you started. One of these is a blog. To begin with the blog example, use the following command:

        rails new blog
        

        This creates a new Rails application called Blog in the blog directory.

      5. Move into the blog directory:

        cd blog
        
      6. Start the built in server with the following command, replacing the IP address with your Linode’s IP address:

        rails server --binding=198.51.100.0
        
          
        Warning: Running `gem pristine --all` to regenerate your installed gemspecs (and deleting then reinstalling your bundle if you use bundle --path) will improve the startup performance of Spring.
        => Booting WEBrick
        => Rails 4.2.7.1 application starting in development on http://198.51.100.0:3000
        => Run `rails server -h` for more startup options
        => Ctrl-C to shutdown server
        [2020-03-11 14:17:16] INFO  WEBrick 1.3.1
        [2020-03-11 14:17:16] INFO  ruby 2.3.3 (2016-11-21) [x86_64-linux-gnu]
        [2020-03-11 14:17:16] INFO  WEBrick::HTTPServer#start: pid=3089 port=3000
        
        
      7. You can visit your application by visiting the address in the browser.

        Rails Welcome Page

      8. Exit the server process with Ctrl+C.

      Create a Controller and View

      A controller will receive requests which are then routed and served by various actions. A view displays information.

      1. Create a controller called Welcome and an action called index:

        rails generate controller Welcome index
        
          
        create  app/controllers/welcome_controller.rb
        route   get 'welcome/index'
        invoke  erb
        create    app/views/welcome
        create    app/views/welcome/index.html.erb
        invoke  test_unit
        create    test/controllers/welcome_controller_test.rb
        invoke  helper
        create    app/helpers/welcome_helper.rb
        invoke    test_unit
        invoke  assets
        invoke    coffee
        create      app/assets/javascripts/welcome.coffee
        invoke    scss
        create      app/assets/stylesheets/welcome.scss
        
        
      2. With the text editor of your choice, edit the file app/views/welcome/index.html.erb and replace the contents with the following:

        app/views/welcome/index.html.erb
        1
        
        <h1>Hello, World! This is Ruby on Rails!</h1>
      3. Tell Rails where to find the document root. Edit the file config/routes.rb, find and uncomment the line root as shown:

        config/routes
        1
        2
        3
        4
        5
        6
        7
        8
        9
        
        Rails.application.routes.draw do
          get 'welcome/index'
        
        ...
        
          root 'welcome#index'
        
        ...
        end
      4. Start the server again:

        rails server --binding=198.51.100.0
        

        You should see your new welcome page in the web browser.

      For more information on setting up a more substantial application, refer to the Ruby on Rails Getting Started Guide.

      Next Steps

      For more on Ruby on Rails, checkout the following guides:

      More Information

      You may wish to consult the following resources for additional information on this topic. While these are provided in the hope that they will be useful, please note that we cannot vouch for the accuracy or timeliness of externally hosted materials.

      This guide is published under a CC BY-ND 4.0 license.



      Source link

      Conteinerizando um aplicativo Ruby on Rails para desenvolvimento com o Docker Compose


      Introdução

      Se você estiver desenvolvendo ativamente um aplicativo, usar o Docker pode simplificar seu fluxo de trabalho e o processo de implantação do seu aplicativo para produção. Trabalhar com contêineres no desenvolvimento oferece os seguintes benefícios:

      • Os ambientes são consistentes, o que significa que você pode escolher as linguagens e dependências que quiser para seu projeto sem se preocupar com conflitos de sistema.
      • Os ambientes são isolados, tornando mais fácil a resolução de problemas e a adição de novos membros de equipe.
      • Os ambientes são portáteis, permitindo que você empacote e compartilhe seu código com outros.

      Este tutorial mostrará como configurar um ambiente de desenvolvimento para um aplicativo Ruby on Rails usando o Docker. Você criará vários contêineres – para o aplicativo em si, o banco de dados PostgreSQL, o Redis e um serviço Sidekiq — com o Docker Compose. A configuração fará o seguinte:

      • Sincronize o código do aplicativo no host com o código no contêiner para facilitar as alterações durante o desenvolvimento.
      • Mantenha os dados do aplicativo entre as reinicializações do contêiner.
      • Configure os threads de trabalho do Sidekiq para processar os trabalhos, conforme o esperado.

      No final deste tutorial, você terá um aplicativo funcional de informações sobre tubarões sendo executado em contêineres do Docker:

      Página inicial do App Sidekiq

      Pré-requisitos

      Para seguir este tutorial, será necessário:

      Passo 1 — Clonando o projeto e adicionando dependências

      Nosso primeiro passo será clonar o repositório rails-sidekiq da conta da Comunidade DigitalOcean no GitHub. Esse repositório inclui o código da configuração descrita em Como adicionar o Sidekiq e o Redis em um aplicativo Ruby on Rails, que explica como adicionar o Sidekiq a um projeto Rails 5 existente.

      Clone o repositório em um diretório chamado rails-docker:

      • git clone https://github.com/do-community/rails-sidekiq.git rails-docker

      Navegue até o diretório rails-docker:

      Nesse tutorial, usaremos o PostgreSQL como um banco de dados. Para trabalhar com o PostgreSQL, em vez do SQLite 3, será necessário adicionar a gem pgàs dependências do projeto, que estão listadas em seu respectivo Gemfile. Abra aquele arquivo para edição usando o nano ou seu editor favorito:

      Adicione a gem em qualquer lugar das dependências principais do projeto (acima das dependências de desenvolvimento):

      ~/rails-docker/Gemfile

      . . .
      # Reduces boot times through caching; required in config/boot.rb
      gem 'bootsnap', '>= 1.1.0', require: false
      gem 'sidekiq', '~>6.0.0'
      gem 'pg', '~>1.1.3'
      
      group :development, :test do
      . . .
      

      Também podemos comentar a gem sqlite, uma vez que não a iremos usar mais:

      ~/rails-docker/Gemfile

      . . .
      # Use sqlite3 as the database for Active Record
      # gem 'sqlite3'
      . . .
      

      Por fim, comente a gem spring-watcher-listen em development:

      ~/rails-docker/Gemfile

      . . .
      gem 'spring'
      # gem 'spring-watcher-listen', '~> 2.0.0'
      . . .
      

      Se não desativarmos essa gema, veremos mensagens de erro persistentes ao acessar o console do Rails. Essas mensagens de erro resultam do fato de que essa gem permite que o Rails listen para monitorar as alterações em desenvolvimento, em vez de sondar o sistema de arquivos quanto a mudanças. Como essa gem monitora a raiz do projeto, incluindo o diretório node_modules, ela irá gerar mensagens de erro sobre quais diretórios estão sendo monitorados, sobrecarregando o console. No entanto, se estiver preocupado em preservar recursos da CPU, desativar essa gem pode não funcionar para você. Neste caso, pode ser uma boa ideia fazer o upgrade do seu aplicativo Rails para o Rails 6.

      Salve e feche o arquivo quando você terminar a edição.

      Com seu repositório de projeto em vigor, a gem pg adicionada ao seu Gemfile e a gem spring-watcher-listen comentada, você estará pronto para configurar seu aplicativo para trabalhar com o PostgreSQL.

      Passo 2 — Configurando o aplicativo para trabalhar com o PostgreSQL e o Redis

      Para trabalhar com o PostgreSQL e o Redis em desenvolvimento, precisamos fazer o seguinte:

      • Configurar o aplicativo para funcionar com o PostgreSQL como o adaptador padrão.
      • Adicionar um arquivo .env ao projeto com nosso nome de usuário do banco de dados e senha, além do host do Redis.
      • Criar um script init.sql para criar um usuário sammy para o banco de dados.
      • Adicionar um inicializador ao Sidekiq, de modo que ele possa funcionar com nosso serviço redis em contêiner.
      • Adicionar o arquivo .env e outros arquivos relevantes aos arquivos gitignore e dockerignore do projeto.
      • Criar operações de propagação do banco de dados, de modo que o nosso aplicativo tenha alguns registros com os quais poderemos trabalhar quando o inicializarmos.

      Primeiro, abra seu arquivo de configuração do banco de dados, localizado em config/database.yml:

      Atualmente, o arquivo inclui as seguintes configurações default [padrão], que serão aplicadas na ausência de outras configurações:

      ~/rails-docker/config/database.yml

      default: &default
        adapter: sqlite3
        pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
        timeout: 5000
      

      Precisamos alterar essas configurações para refletir o fato de que usaremos o adaptador postgresql, já que vamos criar um serviço do PostgreSQL com o Docker Compose para manter os dados do nosso aplicativo.

      Exclua o código que define o SQLite como o adaptador e substitua-o pelas seguintes configurações, que irão configurar o adaptador de maneira apropriada e as outras variáveis necessárias para a conexão:

      ~/rails-docker/config/database.yml

      default: &default
        adapter: postgresql
        encoding: unicode
        database: <%= ENV['DATABASE_NAME'] %>
        username: <%= ENV['DATABASE_USER'] %>
        password: <%= ENV['DATABASE_PASSWORD'] %>
        port: <%= ENV['DATABASE_PORT'] || '5432' %>
        host: <%= ENV['DATABASE_HOST'] %>
        pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
        timeout: 5000
      . . .
      

      Em seguida, vamos modificar a configuração do ambiente development, já que este é o ambiente que estamos usando nesta configuração.

      Exclua a configuração existente do banco de dados SQLite para que a seção fique parecida com esta:

      ~/rails-docker/config/database.yml

      . . .
      development:
        <<: *default
      . . .
      

      Por fim, exclua também as configurações do database para os ambientes production e test:

      ~/rails-docker/config/database.yml

      . . .
      test:
        <<: *default
      
      production:
        <<: *default
      . . .
      

      Essas modificações nas configurações padrão do banco de dados nos permitirão definir as informações do nosso banco de dados dinamicamente usando variáveis de ambiente definidas nos arquivos .env, as quais não serão aplicadas no controle de versão.

      Salve e feche o arquivo quando você terminar a edição.

      Note que, se você estiver criando um projeto Rails a partir do zero, você poderá definir o adaptador com o comando rails new, como descrito no Passo 3 do artigo sobre Como usar o PostgreSQL com seu aplicativo Ruby on Rails no Ubuntu 18.04. Isso irá definir seu adaptador em config/database.yml e adicionará automaticamente a gem pg ao projeto.

      Agora que referenciamos nossas variáveis de ambiente, podemos criar um arquivo para elas com nossas configurações preferidas. Extrair definições de configuração dessa maneira faz parte da Abordagem de 12 fatores para o desenvolvimento de aplicativos, a qual define as melhores práticas para obtenção de resiliência dos aplicativos em ambientes distribuídos. Agora, quando estivermos definindo nossos ambientes de produção e teste no futuro, configurar nossas definições de banco de dados envolverá a criação de arquivos .env adicionais e o referenciamento do arquivo apropriado nos nossos arquivos do Docker Compose.

      Abra um arquivo .env:

      Adicione os valores a seguir ao arquivo:

      ~/rails-docker/.env

      DATABASE_NAME=rails_development
      DATABASE_USER=sammy
      DATABASE_PASSWORD=shark
      DATABASE_HOST=database
      REDIS_HOST=redis
      

      Além de definir nosso nome de banco de dados, usuário e senha, também definimos um valor para o DATABASE_HOST. O valor, database, refere-se ao serviço de database do PostgreSQL que vamos criar usando o Docker Compose. Também definimos um REDIS_HOST para especificar nosso serviço redis.

      Salve e feche o arquivo quando você terminar a edição.

      Para criar o usuário de banco de dados sammy, podemos escrever um script init.sql o qual poderemos, então, montar no contêiner do banco de dados quando ele for iniciado.

      Abra o arquivo de script:

      Adicione o código a seguir para criar um usuário sammy com privilégios administrativos:

      ~/rails-docker/init.sql

      CREATE USER sammy;
      ALTER USER sammy WITH SUPERUSER;
      

      Esse script criará o usuário apropriado no banco de dados e concederá privilégios administrativos a esse usuário.

      Defina permissões apropriadas no script:

      Em seguida, vamos configurar o Sidekiq para que trabalhe com nosso serviço redis em contêiner. Podemos adicionar um inicializador ao diretório config/initializers, onde o Rails procura as definições de configuração assim que os frameworks e plug-ins estiverem carregados, que define um valor para um host do Redis.

      Abra um arquivo sidekiq.rb para especificar essas configurações:

      • nano config/initializers/sidekiq.rb

      Adicione o código a seguir ao arquivo para especificar valores para um REDIS_HOST e REDIS_PORT:

      ~/rails-docker/config/initializers/sidekiq.rb

      Sidekiq.configure_server do |config|
        config.redis = {
          host: ENV['REDIS_HOST'],
          port: ENV['REDIS_PORT'] || '6379'
        }
      end
      
      Sidekiq.configure_client do |config|
        config.redis = {
          host: ENV['REDIS_HOST'],
          port: ENV['REDIS_PORT'] || '6379'
        }
      end
      

      Assim como acontece com as definições da configuração do nosso banco de dados, tais definições nos dão a capacidade de definir dinamicamente os parâmetros de nosso host e porta. Isso, por sua vez, nos permite substituir os valores apropriados em tempo de execução sem ter que modificar o código do aplicativo propriamente dito. Além de um REDIS_HOST, temos um valor padrão definido para o REDIS_PORT, caso não seja definido em outro lugar.

      Salve e feche o arquivo quando você terminar a edição.

      Em seguida, para garantir que os dados sensíveis do nosso aplicativo não sejam copiados para o controle de versão, podemos adicionar .env ao arquivo .gitignore do nosso projeto, que diz ao Git quais arquivos ignorar no nosso projeto. Abra o arquivo para edição:

      Ao final do arquivo, adicione uma entrada para .env:

      ~/rails-docker/.gitignore

      yarn-debug.log*
      .yarn-integrity
      .env
      

      Salve e feche o arquivo quando você terminar a edição.

      Em seguida, vamos criar um arquivo .dockerignore para definir o que não deve ser copiado para nossos contêineres. Abra o arquivo para edição:

      Adicione o código a seguir ao arquivo, que diz ao Docker para ignorar algumas das coisas que não precisamos que sejam copiadas para nossos contêineres:

      ~/rails-docker/.dockerignore

      .DS_Store
      .bin
      .git
      .gitignore
      .bundleignore
      .bundle
      .byebug_history
      .rspec
      tmp
      log
      test
      config/deploy
      public/packs
      public/packs-test
      node_modules
      yarn-error.log
      coverage/
      

      Adicione também .env ao final deste arquivo:

      ~/rails-docker/.dockerignore

      . . .
      yarn-error.log
      coverage/
      .env
      

      Salve e feche o arquivo quando você terminar a edição.

      Como passo final, criaremos alguns dados de propagação, de modo que o nosso aplicativo tenha alguns registros quando o inicializarmos.

      Abra um arquivo para os dados de propagação no diretório db:

      Adicione o código a seguir ao arquivo para criar quatro tubarões de demonstração e um post de exemplo:

      ~/rails-docker/db/seeds.rb

      # Adding demo sharks
      sharks = Shark.create([{ name: 'Great White', facts: 'Scary' }, { name: 'Megalodon', facts: 'Ancient' }, { name: 'Hammerhead', facts: 'Hammer-like' }, { name: 'Speartooth', facts: 'Endangered' }])
      Post.create(body: 'These sharks are misunderstood', shark: sharks.first)
      

      Esses dados de propagação criarão quatro tubarões e um post que será associado ao primeiro tubarão.

      Salve e feche o arquivo quando você terminar a edição.

      Com seu aplicativo configurado para funcionar com o PostgreSQL e suas variáveis de ambiente criadas, você está pronto para escrever seu aplicativo Dockerfile.

      Passo 3 — Escrevendo os scripts do Dockerfile e de ponto de entrada

      Seu Dockerfile especifica o que será incluído no contêiner do seu aplicativo quando ele estiver criado. Usar um Dockerfile permite que você defina seu ambiente de contêiner e evite discrepâncias com as dependências ou versões de tempo de execução.

      Ao seguir essas diretrizes para criação de contêineres otimizados, tornaremos nossa imagem o mais eficiente possível usando uma base Alpine e tentando minimizar nossas camadas de imagem no geral.

      Abra um Dockerfile em seu diretório atual:

      As imagens do Docker são criadas com uma sucessão de imagens em camadas que são baseadas umas nas outras. Nosso primeiro passo será adicionar a imagem base para nosso aplicativo, que formará o ponto inicial da compilação do aplicativo.

      Adicione o código a seguir ao arquivo para adicionar a imagem alpine do Ruby como uma base:

      ~/rails-docker/Dockerfile

      FROM ruby:2.5.1-alpine
      

      A imagem alpine é derivada do projeto Alpine Linux e nos ajudará a manter nossa imagem com tamanho reduzido. Para obter mais informações sobre se a imagem alpine é a escolha certa para seu projeto, consulte a discussão completa na seção de Variantes de imagem da página de imagens do Docker Hub Ruby.

      Alguns fatores a serem levados em consideração ao se usar o alpine no desenvolvimento:

      • Manter a imagem com tamanho reduzido irá diminuir os tempos de carregamento da página e de recursos, especialmente se você também mantiver os volumes a um mínimo. Isso tornará a experiência do seu usuário – durante o desenvolvimento – rápida e mais próxima da experiência de se trabalhar localmente, em um ambiente não conteinerizado.
      • A paridade entre as imagens de desenvolvimento e de produção facilita implantações bem-sucedidas. Como as equipes optam frequentemente por usar imagens do Alpine na produção – pelos benefícios em termos de velocidade, o desenvolvimento com uma base de Alpine ajuda a compensar os problemas na transição para a produção.

      Em seguida, defina uma variável de ambiente para especificar a versão Bundler:

      ~/rails-docker/Dockerfile

      . . .
      ENV BUNDLER_VERSION=2.0.2
      

      Esse é um dos passos que vamos adotar para evitar conflitos de versão entre a versão bundler padrão, disponível no nosso ambiente e nosso código de aplicativo, que exige o Bundler 2.0.2.

      Em seguida, adicione ao Dockerfile os pacotes necessários para trabalhar com o aplicativo:

      ~/rails-docker/Dockerfile

      . . .
      RUN apk add --update --no-cache 
            binutils-gold 
            build-base 
            curl 
            file 
            g++ 
            gcc 
            git 
            less 
            libstdc++ 
            libffi-dev 
            libc-dev 
            linux-headers 
            libxml2-dev 
            libxslt-dev 
            libgcrypt-dev 
            make 
            netcat-openbsd 
            nodejs 
            openssl 
            pkgconfig 
            postgresql-dev 
            python 
            tzdata 
            yarn
      

      Esses pacotes incluem o nodejs e yarn, entre outros. Como nosso aplicativo atende os ativos com o webpack, precisamos incluir o Node.js e o Yarn para que o aplicativo funcione conforme o esperado.

      Lembre-se de que a imagem alpine tem tamanho diminuto: a lista apresentada aqui não esgota os pacotes que você talvez queira ou precise no desenvolvimento, quando estiver conteinerizando o seu próprio aplicativo.

      Em seguida, instale a versão do bundler apropriada:

      ~/rails-docker/Dockerfile

      . . .
      RUN gem install bundler -v 2.0.2
      

      Esse passo irá garantir a paridade entre o nosso ambiente conteinerizado e as especificações no arquivo Gemfile.lock desse projeto.

      Agora, defina o diretório de trabalho para o aplicativo no contêiner:

      ~/rails-docker/Dockerfile

      . . .
      WORKDIR /app
      

      Copie para lá o seu Gemfile e o Gemfile.lock:

      ~/rails-docker/Dockerfile

      . . .
      COPY Gemfile Gemfile.lock ./
      

      Copiar esses arquivos como um passo independente, seguido de bundle install, significa que as gems do projeto não precisam ser recompiladas toda vez que você fizer alterações no código do seu aplicativo. Isso funcionará em conjunto com o volume da gem que vamos incluir no nosso arquivo Compose, o qual irá montar as gems para o seu contêiner de aplicativo, nos casos em que o serviço for recriado.As gems do projeto, porém, permanecerão as mesmas.

      Em seguida, defina as opções de configuração para a compilação da gem nokogiri:

      ~/rails-docker/Dockerfile

      . . .
      RUN bundle config build.nokogiri --use-system-libraries
      . . .
      

      Esse passo compila a nokigiri com as versões de biblioteca libxml2 e libxslt que adicionamos ao contêiner do aplicativo no passo RUN apk add... acima.

      Em seguida, instale as gems do projeto:

      ~/rails-docker/Dockerfile

      . . .
      RUN bundle check || bundle install
      

      Antes de instalar as gems, essa instrução verifica se elas já estão instaladas.

      Em seguida, vamos repetir o mesmo procedimento que usamos com as gems em relação aos nossos pacotes e dependências do JavaScript. Primeiro, vamos copiar os metadados do pacote; em seguida, vamos instalar as dependências e, por fim, vamos copiar o código do aplicativo na imagem do contêiner.

      Para começar com a seção Javascript do nosso Dockerfile, copie os arquivos package.json e yarn.lock do diretório de seu projeto atual, no host, para o contêiner:

      ~/rails-docker/Dockerfile

      . . .
      COPY package.json yarn.lock ./
      

      Depois, instale os pacotes necessários com yarn install:

      ~/rails-docker/Dockerfile

      . . .
      RUN yarn install --check-files
      

      Essa instrução inclui um sinalizador --check-files com o comando yarn, um recurso que garante que nenhum arquivo instalado anteriormente tenha sido removido. Como no caso das nossas gems, vamos gerenciar a permanência dos pacotes no diretório node_modules com um volume, quando escrevermos nosso arquivo Compose.

      Por fim, copie o resto do código do aplicativo e inicie o aplicativo com um script de ponto de entrada:

      ~/rails-docker/Dockerfile

      . . .
      COPY . ./
      
      ENTRYPOINT ["./entrypoints/docker-entrypoint.sh"]
      

      Usar um script de ponto de entrada nos permite executar o contêiner como um executável.

      O Dockerfile final ficará parecido com este:

      ~/rails-docker/Dockerfile

      FROM ruby:2.5.1-alpine
      
      ENV BUNDLER_VERSION=2.0.2
      
      RUN apk add --update --no-cache 
            binutils-gold 
            build-base 
            curl 
            file 
            g++ 
            gcc 
            git 
            less 
            libstdc++ 
            libffi-dev 
            libc-dev 
            linux-headers 
            libxml2-dev 
            libxslt-dev 
            libgcrypt-dev 
            make 
            netcat-openbsd 
            nodejs 
            openssl 
            pkgconfig 
            postgresql-dev 
            python 
            tzdata 
            yarn
      
      RUN gem install bundler -v 2.0.2
      
      WORKDIR /app
      
      COPY Gemfile Gemfile.lock ./
      
      RUN bundle config build.nokogiri --use-system-libraries
      
      RUN bundle check || bundle install
      
      COPY package.json yarn.lock ./
      
      RUN yarn install --check-files
      
      COPY . ./
      
      ENTRYPOINT ["./entrypoints/docker-entrypoint.sh"]
      

      Salve e feche o arquivo quando você terminar a edição.

      Em seguida, crie um diretório chamado entrypoints para os scripts de ponto de entrada:

      Esse diretório incluirá nosso script de ponto de entrada principal e um script para o nosso serviço Sidekiq.

      Abra o arquivo para o script de ponto de entrada do aplicativo:

      • nano entrypoints/docker-entrypoint.sh

      Adicione o código a seguir ao arquivo:

      rails-docker/entrypoints/docker-entrypoint.sh

      #!/bin/sh
      
      set -e
      
      if [ -f tmp/pids/server.pid ]; then
        rm tmp/pids/server.pid
      fi
      
      bundle exec rails s -b 0.0.0.0
      

      A primeira linha importante é set -e, que diz ao shell /bin/sh – que executa o script – para falhar rapidamente se houver problemas mais tarde no script. Em seguida, o script verifica se o tmp/pids/server.pid não está presente, a fim de garantir que não haverá conflitos de servidor quando iniciarmos o aplicativo. Por fim, o script inicia o servidor Rails com o comando bundle exec rails s. Usamos a opção -b com esse comando para vincular o servidor a todos os endereços IP e não para o localhost padrão. Essa invocação faz com que o servidor Rails encaminhe os pedidos de entrada para o IP do contêiner e não para o localhost padrão.

      Salve e feche o arquivo quando você terminar a edição.

      Crie o executável do script:

      • chmod +x entrypoints/docker-entrypoint.sh

      Em seguida, vamos criar um script para iniciar nosso serviço sidekiq, que irá processar nossas tarefas do Sidekiq. Para obter mais informações sobre como esse aplicativo usa o Sidekiq, consulte o artigo sobre Como adicionar o Sidekiq e o Redis a um aplicativo Ruby on Rails.

      Abra um arquivo para o script de ponto de entrada do Sidekiq:

      • nano entrypoints/sidekiq-entrypoint.sh

      Adicione o código a seguir ao arquivo para iniciar o Sidekiq:

      ~/rails-docker/entrypoints/sidekiq-entrypoint.sh

      #!/bin/sh
      
      set -e
      
      if [ -f tmp/pids/server.pid ]; then
        rm tmp/pids/server.pid
      fi
      
      bundle exec sidekiq
      

      Esse script inicia o Sidekiq no contexto do nosso pacote de aplicativo.

      Salve e feche o arquivo quando você terminar a edição. Torne-o executável:

      • chmod +x entrypoints/sidekiq-entrypoint.sh

      Com seus scripts de ponto de entrada e o Dockerfile instalados, você está pronto para definir seus serviços no seu arquivo Compose.

      Usando o Docker Compose, conseguiremos executar vários contêineres necessários para nossa configuração. Vamos definir nossos serviços do Compose no nosso arquivo principal docker-compose.yml. Um serviço no Compose é um contêiner em execução e as definições de serviço — que você incluirá no seu arquivo docker-compose.yml — contém informações sobre como cada imagem de contêiner será executada. A ferramenta Compose permite que você defina vários serviços para construir aplicativos multi-contêiner.

      Nossa configuração do aplicativo incluirá os seguintes serviços:

      • O aplicativo em si
      • O banco de dados PostgreSQL
      • Redis
      • Sidekiq

      Também vamos incluir uma montagem bind como parte da nossa preparação, de modo que qualquer alteração de código que fizermos durante o desenvolvimento será imediatamente sincronizada com os contêineres que precisam de acesso a esse código.

      Note que não estamos definindo um serviço de test, uma vez que testar está fora do âmbito deste tutorial e desta série. Porém, você pode fazer esse teste, seguindo o anterior que estamos usando aqui para o serviço do sidekiq.

      Abra o arquivo docker-compose.yml:

      Primeiro, adicione a definição de serviço do aplicativo:

      ~/rails-docker/docker-compose.yml

      version: '3.4'
      
      services:
        app:
          build:
            context: .
            dockerfile: Dockerfile
          depends_on:
            - database
            - redis
          ports:
            - "3000:3000"
          volumes:
            - .:/app
            - gem_cache:/usr/local/bundle/gems
            - node_modules:/app/node_modules
          env_file: .env
          environment:
            RAILS_ENV: development
      

      A definição de serviço do app inclui as seguintes opções:

      • build: define as opções de configuração, incluindo o context e dockerfile, que serão aplicadas quando o Compose construir a imagem do aplicativo. Se quisesse usar uma imagem existente de um registro como o Docker Hub, você poderia usar como alternativa a instrução image, com informações sobre seu nome de usuário, repositório e tag da imagem.
      • context: define o contexto de construção para a construção da imagem — neste caso, o diretório atual do projeto.
      • dockerfile: especifica o Dockerfile no diretório atual do seu projeto como o arquivo que o Compose usará para construir a imagem do aplicativo.
      • depends_on: esta opção define primeiro os contêineres database e redis, de modo que eles estejam em funcionamento antes do app.
      • ports: esta opção mapeia a porta 3000 no host até a porta 3000 no contêiner.
      • volumes: estamos incluindo dois tipos de montagens aqui:
        • A primeira é uma montagem bind que monta o código do nosso aplicativo no host para o diretório /app no contêiner. Isso facilitará o desenvolvimento rápido, uma vez que quaisquer alterações que você faça no código do seu host serão povoadas imediatamente no contêiner.
        • A segunda é um volume nomeado, o gem_cache. Quando a instrução bundle install executa no contêiner, ela instala as gems do projeto. Adicionar esse volume significa que, caso você recriar o contêiner, as gems serão montadas no novo contêiner. Essa montagem pressupõe que não houve nenhuma alteração no projeto. Assim, caso você de fato faça alterações nas gems do seu projeto no desenvolvimento, você precisará lembrar de excluir esse volume antes de recriar o serviço do seu aplicativo.
        • O terceiro volume é um volume nomeado para o diretório node_modules. Em vez de ter o node_modules montado no host, o qual pode levar a discrepâncias de pacotes e conflitos de permissões no desenvolvimento, esse volume irá garantir que os pacotes neste diretório sejam mantidos e reflitam o estado atual do projeto. Novamente, se você modificar as dependências do Node do projeto, precisará remover e recriar esse volume.
      • env_file: esta opção diz ao Compose que gostaríamos de adicionar variáveis de ambiente de um arquivo chamado .env, localizado no contexto da compilação.
      • environment: usar esta opção nos permite definir uma variável de ambiente não confidencial, enviando informações sobre o ambiente do Rails para o contêiner.

      Em seguida, abaixo da definição de serviço do app, adicione o código a seguir para definir seu serviço de database:

      ~/rails-docker/docker-compose.yml

      . . .
        database:
          image: postgres:12.1
          volumes:
            - db_data:/var/lib/postgresql/data
            - ./init.sql:/docker-entrypoint-initdb.d/init.sql
      

      Ao contrário do serviço app, o serviço database extrai uma imagem postgres diretamente do Docker Hub. Note que estamos também fixando a versão aqui, em vez de defini-la como latest [mais recente] ou sem especificá-la (que, por padrão, é definida como latest). Dessa forma, podemos garantir que essa configuração funcione com as versões especificadas aqui e evitar surpresas inesperadas com as alterações de falha do código na imagem.

      Também estamos incluindo aqui um volume db_data, o qual manterá os dados do nosso aplicativo entre as inicializações do contêiner. Além disso, montamos nosso script de inicialização init.sql no diretório apropriado, docker-entrypoint-initdb.d/ no contêiner, para criar nosso usuário de banco de dados sammy. Depois que o ponto de entrada da imagem criar o usuário e banco de dados padrão postgres, ele executará quaisquer scripts encontrados no diretório docker-entrypoint-initdb.d/, que você poderá usar para as tarefas de inicialização necessárias. Para obter mais detalhes, examine a seção Inicialização de scripts da documentação da imagem do PostgreSQL.

      Em seguida, adicione a definição de serviço redis:

      ~/rails-docker/docker-compose.yml

      . . .
        redis:
          image: redis:5.0.7
      

      Assim como o serviço de database, o serviço de redis usa uma imagem do Docker Hub. Neste caso, não vamos manter o cache do trabalho do Sidekiq.

      Por fim, adicione a definição de serviço do sidekiq:

      ~/rails-docker/docker-compose.yml

      . . .
        sidekiq:
          build:
            context: .
            dockerfile: Dockerfile
          depends_on:
            - app      
            - database
            - redis
          volumes:
            - .:/app
            - gem_cache:/usr/local/bundle/gems
            - node_modules:/app/node_modules
          env_file: .env
          environment:
            RAILS_ENV: development
          entrypoint: ./entrypoints/sidekiq-entrypoint.sh
      

      Nosso serviço sidekiq se assemelha ao serviço app em alguns aspectos: ele usa o mesmo contexto e imagem de compilação, variáveis de ambiente e volumes. No entanto, ele é depende dos serviços app, redis e database e, portanto, será o último a ser iniciado. Além disso, ele usa um entrypoint que irá substituir o ponto de entrada definido no Dockerfile. Essa configuração do entrypoint aponta para entrypoints/sidekiq-entrypoint.sh, que inclui o comando apropriado para iniciar o serviço sidekiq.

      Como passo final, adicione as definições de volume abaixo da definição de serviço sidekiq:

      ~/rails-docker/docker-compose.yml

      . . .
      volumes:
        gem_cache:
        db_data:
        node_modules:
      

      Nossa chave de nível superior de volumes define os volumes gem_cache, db_data e node_modules. Quando o Docker cria volumes, o conteúdo do volume é armazenado em uma parte do sistema de arquivos do host, /var/lib/docker/volumes/, que é gerenciado pelo Docker. O conteúdo de cada volume é armazenado em um diretório em /var/lib/docker/volumes/ e é montado em qualquer contêiner que utilize o volume. Dessa forma, os dados de informações sobre tubarões – a serem criados por nossos usuários – irão permanecer no volume db_data, mesmo se removermos e recriarmos o serviço de database.

      O arquivo final se parecerá com este:

      ~/rails-docker/docker-compose.yml

      version: '3.4'
      
      services:
        app:
          build:
            context: .
            dockerfile: Dockerfile
          depends_on:     
            - database
            - redis
          ports:
            - "3000:3000"
          volumes:
            - .:/app
            - gem_cache:/usr/local/bundle/gems
            - node_modules:/app/node_modules
          env_file: .env
          environment:
            RAILS_ENV: development
      
        database:
          image: postgres:12.1
          volumes:
            - db_data:/var/lib/postgresql/data
            - ./init.sql:/docker-entrypoint-initdb.d/init.sql
      
        redis:
          image: redis:5.0.7
      
        sidekiq:
          build:
            context: .
            dockerfile: Dockerfile
          depends_on:
            - app      
            - database
            - redis
          volumes:
            - .:/app
            - gem_cache:/usr/local/bundle/gems
            - node_modules:/app/node_modules
          env_file: .env
          environment:
            RAILS_ENV: development
          entrypoint: ./entrypoints/sidekiq-entrypoint.sh
      
      volumes:
        gem_cache:
        db_data:
        node_modules:     
      

      Salve e feche o arquivo quando você terminar a edição.

      Com as definições do seu serviço gravadas, você estará pronto para iniciar o aplicativo.

      Passo 5 — Testando o aplicativo

      Com seu arquivo docker-compose.yml funcionando, você pode criar seus serviços com o comando docker-compose up e propagar o seu banco de dados. Você também pode testar se seus dados serão mantidos, interrompendo e removendo seus contêineres com o docker-compose down e recriando-os.

      Primeiro, compile as imagens de contêiner e crie os serviços, executando o docker-compose up com o sinalizador -d, o qual executará os contêineres em segundo plano:

      Você verá um resultado, confirmando que seus serviços foram criados:

      Output

      Creating rails-docker_database_1 ... done Creating rails-docker_redis_1 ... done Creating rails-docker_app_1 ... done Creating rails-docker_sidekiq_1 ... done

      Você também pode obter informações mais detalhadas sobre os processos de inicialização exibindo o resultado do registro dos serviços:

      Você verá algo simelhante a isso caso tudo tenha iniciado corretamente:

      Output

      sidekiq_1 | 2019-12-19T15:05:26.365Z pid=6 tid=grk7r6xly INFO: Booting Sidekiq 6.0.3 with redis options {:host=>"redis", :port=>"6379", :id=>"Sidekiq-server-PID-6", :url=>nil} sidekiq_1 | 2019-12-19T15:05:31.097Z pid=6 tid=grk7r6xly INFO: Running in ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux-musl] sidekiq_1 | 2019-12-19T15:05:31.097Z pid=6 tid=grk7r6xly INFO: See LICENSE and the LGPL-3.0 for licensing details. sidekiq_1 | 2019-12-19T15:05:31.097Z pid=6 tid=grk7r6xly INFO: Upgrade to Sidekiq Pro for more features and support: http://sidekiq.org app_1 | => Booting Puma app_1 | => Rails 5.2.3 application starting in development app_1 | => Run `rails server -h` for more startup options app_1 | Puma starting in single mode... app_1 | * Version 3.12.1 (ruby 2.5.1-p57), codename: Llamas in Pajamas app_1 | * Min threads: 5, max threads: 5 app_1 | * Environment: development app_1 | * Listening on tcp://0.0.0.0:3000 app_1 | Use Ctrl-C to stop . . . database_1 | PostgreSQL init process complete; ready for start up. database_1 | database_1 | 2019-12-19 15:05:20.160 UTC [1] LOG: starting PostgreSQL 12.1 (Debian 12.1-1.pgdg100+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 8.3.0-6) 8.3.0, 64-bit database_1 | 2019-12-19 15:05:20.160 UTC [1] LOG: listening on IPv4 address "0.0.0.0", port 5432 database_1 | 2019-12-19 15:05:20.160 UTC [1] LOG: listening on IPv6 address "::", port 5432 database_1 | 2019-12-19 15:05:20.163 UTC [1] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432" database_1 | 2019-12-19 15:05:20.182 UTC [63] LOG: database system was shut down at 2019-12-19 15:05:20 UTC database_1 | 2019-12-19 15:05:20.187 UTC [1] LOG: database system is ready to accept connections . . . redis_1 | 1:M 19 Dec 2019 15:05:18.822 * Ready to accept connections

      Você também pode verificar o status dos seus contêineres com o docker-compose ps:

      Você verá um resultado indicando que seus contêineres estão funcionando:

      Output

      Name Command State Ports ----------------------------------------------------------------------------------------- rails-docker_app_1 ./entrypoints/docker-resta ... Up 0.0.0.0:3000->3000/tcp rails-docker_database_1 docker-entrypoint.sh postgres Up 5432/tcp rails-docker_redis_1 docker-entrypoint.sh redis ... Up 6379/tcp rails-docker_sidekiq_1 ./entrypoints/sidekiq-entr ... Up

      Em seguida, crie e propague seu banco de dados e execute migrações nele com o comando docker-compose exec a seguir:

      • docker-compose exec app bundle exec rake db:setup db:migrate

      O comando docker-compose exec permite que você execute comandos nos seus serviços. Aqui, nós o estamos usando para executar rake db:setup e db:migrate, no contexto do pacote do nosso aplicativo, com o objetivo de criar e propagar o banco de dados e executar as migrações. Como você trabalha com desenvolvimento, o docker-compose exec se mostrará útil quando quiser executar migrações em relação ao seu banco de dados de desenvolvimento.

      Você verá o seguinte resultado após executar este comando:

      Output

      Created database 'rails_development' Database 'rails_development' already exists -- enable_extension("plpgsql") -> 0.0140s -- create_table("endangereds", {:force=>:cascade}) -> 0.0097s -- create_table("posts", {:force=>:cascade}) -> 0.0108s -- create_table("sharks", {:force=>:cascade}) -> 0.0050s -- enable_extension("plpgsql") -> 0.0173s -- create_table("endangereds", {:force=>:cascade}) -> 0.0088s -- create_table("posts", {:force=>:cascade}) -> 0.0128s -- create_table("sharks", {:force=>:cascade}) -> 0.0072s

      Com seus serviços em execução, você pode acessar o localhost:3000 ou o http://your_server_ip:3000 no navegador. Você verá uma página de destino que se parece com esta:

      Página inicial do App Sidekiq

      Agora, podemos testar a permanência dos dados. Crie um novo tubarão, clicando no botão** Get Shark Info**, que levará você até a rota shark/index:

      Página do índice de tubarões com os dados propagados

      Para verificar se o aplicativo está funcionando, podemos adicionar algumas informações de demonstração a ele. Clique em New Shark. Será solicitado que coloque um nome de usuário (sammy) e senha (shark), graças às configurações de autenticação do projeto.

      Na página New Shark, digite “Mako” no campo Name e “Fast” no campo Facts.

      Clique no botão Create Shark para criar o tubarão. Assim que tiver criado o tubarão, clique em Home, na barra de navegação do site para voltar à página de destino principal do aplicativo. Agora, podemos testar se o Sidekiq está funcionando.

      Clique no botão Which Sharks Are in Danger? [Quais tubarões estão em perigo?] . Como não fez upload de nenhum tubarão em perigo, isso levará você até a visualização de index endangered [em perigo]:

      Exibição do índice Endangered [Em perigo]

      Clique em Import Endangered Sharks para importar os tubarões. Você verá uma mensagem de status informando que os tubarões foram importados:

      Iniciar importação

      Você verá também o início da importação. Atualize sua página para ver a tabela inteira:

      Atualizar tabela

      Graças ao Sidekiq, o upload de nosso lote grande de tubarões em perigo foi bem-sucedido, sem bloquear o navegador ou interferir com outras funcionalidades do aplicativo.

      Clique no botão Home, no final da página, para voltar à página principal do aplicativo:

      Página inicial do App Sidekiq

      A partir daqui, clique em Which Sharks Are in Danger? novamente. Você verá os tubarões que foram carregados novamente.

      Agora que sabemos que nosso aplicativo está funcionando corretamente, podemos testar a permanência dos nossos dados.

      De volta ao seu terminal, digite o seguinte comando para interromper e remover seus contêineres:

      Note que não estamos incluindo a opção --volumes; desta forma, nosso volume db_data não foi removido.

      O resultado a seguir confirma que seus contêineres e rede foram removidos:

      Output

      Stopping rails-docker_sidekiq_1 ... done Stopping rails-docker_app_1 ... done Stopping rails-docker_database_1 ... done Stopping rails-docker_redis_1 ... done Removing rails-docker_sidekiq_1 ... done Removing rails-docker_app_1 ... done Removing rails-docker_database_1 ... done Removing rails-docker_redis_1 ... done Removing network rails-docker_default

      Recrie os contêineres:

      Abra o console do Rails no contêiner do app com o docker-compose exec e bundle exec rails console:

      • docker-compose exec app bundle exec rails console

      No prompt, inspecione o registro do last [último] do Tubarão no banco de dados:

      Você verá o registro que acabou de criar:

      IRB session

      Shark Load (1.0ms) SELECT "sharks".* FROM "sharks" ORDER BY "sharks"."id" DESC LIMIT $1 [["LIMIT", 1]] => "#<Shark id: 5, name: "Mako", facts: "Fast", created_at: "2019-12-20 14:03:28", updated_at: "2019-12-20 14:03:28">"

      Depois, você poderá verificar se os seus tubarões Endangered permaneceram, usando o seguinte comando:

      IRB session

      (0.8ms) SELECT COUNT(*) FROM "endangereds" => 73

      Seu volume db_data foi montado com sucesso no serviço database recriado, possibilitando que seu serviço app acesse os dados salvos. Se navegar diretamente até a página index shark – visitando o localhost:3000/sharks ou http://your_server_ip:3000/sharks, você também verá aquele registro sendo exibido:

      Página de índice de tubarões com o Mako

      Seus tubarões em perigo também estarão em exibição em localhost:3000/endangered/data ou http://your_server_ip:3000/endangered/data:

      Atualizar tabela

      Seu aplicativo agora está funcionando em contêineres do Docker com persistência de dados e sincronização de código habilitados. Você pode seguir em frente e testar as alterações do código local no seu host, as quais serão sincronizadas com o seu contêiner, graças à montagem bind que definimos como parte do serviço do app.

      Conclusão

      Ao seguir este tutorial, você criou uma configuração de desenvolvimento para o seu aplicativo Rails, usando os contêineres do Docker. Você tornou seu projeto mais modular e portátil, extraindo informações confidenciais e desassociando o estado do seu aplicativo do seu código. Você também configurou um arquivo clichê docker-compose.yml que pode revisar conforme suas necessidades de desenvolvimento e exigências mudem.

      Conforme for desenvolvendo, você pode se interessar em aprender mais sobre a concepção de aplicativos para fluxos de trabalho em contêiner e Cloud Native. Consulte o artigo Arquitetando aplicativos para o Kubernetes e Modernizando aplicativos para o Kubernetes para obter mais informações sobre esses tópicos. Ou, caso queira investir em uma sequência de aprendizagem do Kubernetes, veja o artigo sobre o Kubernetes para obter o currículo dos desenvolvedores de pilha completa.

      Para aprender mais sobre o código do aplicativo em si, consulte os outros tutoriais nesta série:



      Source link