Um ambiente de desenvolvimento Rails com Docker

Recentemente eu comecei a estudar Ruby, linguagem que eu já tinha ouvido falar mas sabia um total de vários nada sobre. Comecei devagar, com um curso básico no Codecademy e então fui avançando mais e mais (inclusive, estou documentando esse processo e pretendo explaná-lo aqui em breve!). Porém, chegou o momento que eu queria aprender, de fato, como desenvolver uma aplicação com a linguagem e veio o desafio de montar o ambiente. Logo que tive alguns problemas na instalação do Ruby, pensei: “ué, por que eu não tento solucionar isso com o Docker?” Eventualmente, foi por esse caminho (extremamente tortuoso) que decidi seguir! Venho aqui documentar essa jornada de montar um ambiente de desenvolvimento Rails com Docker!

Uma baleia, representando o Docker, tocando bongos.

Antes: uma reclamação…

Assim como qualquer pessoa que vai começar uma nova jornada em qualquer tecnologia, a primeira coisa que eu fiz foi buscar um tutorial que me guiasse nessa empreitada. Eu não me considero uma especialista em Docker, apesar de saber um tanto de coisa, então gostaria muito de ter uma espécie de linha-guia, já montada por alguém, para que eu pudesse seguir, e confiava que conseguiria resolver possíveis problemas conforme eles aparecessem.

Contudo, trombei com um monte de tutorial ruim! Poxa pessoal, se você for fazer um tutorial, pense em primeiro lugar que a pessoa mais leiga do mundo pode estar te lendo. Quando eu fui tentar pela primeira vez, precisei ir costurando TRÊS tutoriais para ver se conseguia fazer alguma coisa sair. Foi então que eu desisti. Mas, teimosa que sou, pensei: não vou deixar essa bagaça me vencer não! E fui buscar mais um tutorial que, dessa vez, estava BEM melhor explicado. Mesmo assim, precisei buscar muita informação ainda.

… e um pedido de desculpas

O que me leva à já pedir desculpa para vocês pelo seguinte motivo: eu passei muita raiva enquanto construía esse ambiente. Muita mesmo. E dessa forma acontece uma coisa que é: eu saio tentando várias coisas diferentes antes de ir documentando o que deu certo e o que não deu. Então podem ter passos faltando nesse tutorial porque eu simplesmente esqueci de anotá-los, tem mensagens de erro que eu esqueci de copiar e só salvei a solução… enfim. Eu juro que fiz o meu melhor tentando documentar o passo a passo de como montar esse ambiente, mas talvez algum detalhezinho tenha me passado batido. Desculpas desde já.

Caso você tenha algum problema enquanto estiver seguindo os passos deste post, fique à vontade para me comunicar. Pode ser que eu lembre de alguma coisa que eu fiz e não escrevi e ficarei feliz em (tentar) te ajudar, bem como em atualizar esse post para que o próximo leitor não tenha esse percalço.

Um cachorrinho tentando se esconder com a legenda "Sorry!"

Requisitos para esse tutorial

Para que possamos começar esse tutorial, temos somente um pré-requisito: ter o Docker instalado e funcionando na sua máquina. O Community Edition já satisfaz as nossas necessidades. Em síntese, relacionados à essa ferramenta, vamos utilizar o Dockerfile para criar uma imagem relacionada à nossa aplicação Rails e o Docker Compose para orquestrar os outros containers que utilizaremos como “infraestrutura”.


Primeiros arquivos

Primeiramente, para começarmos esse tutorial, precisamos configurar quatro arquivos iniciais no diretório do nosso projeto. Antes disso, já acho válido avisar: é bom você separar esse ambiente/projeto em uma pastinha exclusiva dele, porque vai vir uma porrada de arquivo ali pra dentro.

Observação: deixarei todos os códigos “originais” em um repositório no meu GitHub. Você pode encontrá-lo aqui.

Os arquivos que precisamos configurar são os seguintes:

1. Dockerfile

A princípio, começamos por aqui, onde vamos especificar tudo o que vai acontecer dentro do container que conterá nossa aplicação, incluindo as dependências do próprio Rails. Ou seja, essa será a imagem a partir de onde o seu container será construído. Seu arquivo Dockerfile vai ficar parecido com isso:

FROM ruby:2.5.8
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
RUN mkdir /minha-aplicacao
WORKDIR /minha-aplicacao
ADD Gemfile /minha-aplicacao/Gemfile
ADD Gemfile.lock /minha-aplicacao/Gemfile.lock
RUN bundle install
ADD . /minha-aplicacao

O que você precisa alterar dentro deste arquivo Dockerfile: é preciso inserir o nome da sua aplicação em todos os lugares que dizem “minha-aplicacao”. Certamente que sua aplicação pode chamar minha-aplicacao também, se você quiser 😅 Aqui, no meu caso, chamarei de TutorialAmbienteRuby.

2. Gemfile

Vamos precisar de um Gemfile dentro do nosso projeto para, em primeiro lugar, somente carregar o Rails para dentro dele. Depois, ele será devidamente substituído com todas as gems que vamos precisar para dar o start na nossa aplicação. Seu arquivo deve ficar desta forma:

source 'https://rubygems.org'

gem 'rails', '5.0.0.1'

Para que serve o Gemfile?

Ele é a lista de dependências do Ruby, usado justamente para o gerenciamento das mesmas. Nessa linguagem, cada pacote e/ou biblioteca usado em um projeto é chamado de gem (fofo né?) O Gemfile deve sempre estar na raiz do seu projeto para que o Bundler (o gerenciador de pacotes do Ruby) possa encontrá-lo.

O Bundler é quem vai trackear e instalar as gems descritas no seu Gemfile nas versões necessárias para o seu projeto, provendo para todos os devs de uma mesma equipe um ambiente de desenvolvimento consistente. Sem o Bundler, teríamos que checar essas versões “na mão” e não haveria muitas garantias de que todos estão alinhados em questões de versões de gems.

3. Gemfile.lock

Precisamos também criar um arquivo nomeado como Gemfile.lock, vazio. Só isso.

Para que serve o Gemfile.lock?

Ele vai transformar o seu aplicativo em um único pacote a partir de duas coisas: o seu próprio código mais os códigos de terceiros (vindo das gems do Gemfile). Como no Gemfile muitas vezes nós especificamos somente intervalos de versões, ele vai considerar nesse pacote o código dos terceiros que ele executou na última vez em que tudo funcionou corretamente.

4. docker-compose.yml

É aqui que a magia vai acontecer; ou seja, é este arquivo que vai configurar e arquitetar toda a infraestrutura necessária para que você consiga rodar a sua aplicação. Vamos declarar dois serviços nesse arquivo: o serviço de banco de dados (que aqui estaremos utilizando o PostgreSQL) e o serviço de web, que vai conectar tudo que está relacionado à sua aplicação e vai prover um servidorzinho para que esta possa ser executada.

Seu docker-compose precisa ficar desta forma:

version: '3'
services:
    db:
        image: postgres
        environment:
            POSTGRES_PASSWORD: escolha_uma_senha
    web:
        build: .
        command: bundle exec rails s -p 3000 -b '0.0.0.0'
        volumes:
            - .:/minha-aplicacao
        ports:
            - '3000:3000'
        depends_on: 
            - db

O que você precisa alterar dentro deste arquivo docker-compose: o nome do seu volume (que ali está como “minha-aplicacao”) para que seja igual ao nome da sua aplicação. Além disso, é necessário também escolher uma senha para o seu PostgreSQL: o campo POSTGRES não pode ficar vazio.


Vamos subir essa aplicação!

Observação: este tutorial foi feito e está organizado de forma a ser realizado no Windows. Se você está tentando fazer isso no MacOS ou em alguma distribuição Linux, talvez mais ou menos coisas serão necessárias para que tudo dê certo.

Construindo os alicerces

Com o Docker rodando no seu computador, vamos fazer uma primeira compilação da nossa imagem Dockerfile para nos certificar que está tudo certo. Abra um terminal na pasta do seu projeto e comece com o seguinte comando:

docker build .

Não é para ter acontecido nenhum problema e tampouco vamos notar qualquer diferença significativa nos nossos arquivos. Esse foi um primeiro passo para nos certificarmos que está tudo bem com o nosso Dockerfile.

Depois disso, conseguimos gerar as estruturas para a nossa aplicação executando o YAML do Docker Compose que já temos construído. No mesmo terminal que já está aberto na pasta, execute este comando:

docker-compose run web rails new . — force — database=postgresql

🚨 Importante! Em determinado momento, durante as construções feitas pelo Docker Compose, ele vai perguntar se desejamos sobrescrever o Gemfile que já temos. E sim, temos que sobrescrevê-lo para ficar tudo certinho.

Inclusive, depois de termos o Gemfile substituído, precisamos fazer um novo build à imagem que criamos:

docker-compose build

Ajeitando o banco e subindo

As aplicações Rails requerem, por padrão, que tenhamos um banco de dados rodando em localhost. No caso deste tutorial, estamos usando o PostgreSQL, como você já deve ter notado. Precisamos, então, antes de subir toda a nossa estrutura para finalmente termos tudo “em cima”, setar o banco de dados dentro da nossa aplicação Rails através de dois passos.

Primeiro, precisamos mexer em um dos vários arquivos que foram adicionados ao nosso projeto: o database.yml, dentro da pasta config. É aqui que vamos configurar o banco de dados, deixando-o dessa forma:

default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  username: postgres
  password: aquela_senha_do_docker_compose
  pool: 5

development:
  <<: *default
  database: minha_aplicacao_development

test:
  <<: *default
  database: minha_aplicacao_test

A única coisa que você precisa alterar nesse código que deixei acima é a senha do PostgreSQL, que precisa ser a mesma que você colocou no Docker Compose antes. Também é de bom mudar os nomes onde está “minha_aplicacao_” para o nome da sua, mas como eu vou desenvolver localmente por enquanto, não me preocupei com isso.

Também precisamos adicionar a gem do PostgreSQL no Gemfile, caso contrário não conseguiremos criar o nosso banco de dados no final desse tutorial. É preciso adicionar a seguinte linha (eu posicionei abaixo da gem jbuilder):

gem 'pg', '0.18.4'

Depois de ter atualizado o Gemfile, precisamos novamente fazer um build ao nosso Docker Compose, para que o Bundler possa buscar essa gem e adicioná-la ao nosso projeto:

docker-compose build

Falta pouco! Agora vamos de fato subir o Docker Compose para que sua aplicação esteja no ar. Depois disso, só precisaremos criar o banco de dados bonitinho para que tudo funcione corretamente. Começamos com o seguinte comando:

docker-compose up

E depois, em um outro terminal aberto na mesma pasta, execute o comando de criação do banco de dados:

docker-compose run web rake db:create

E feito! Agora é só abrir o http://localhost:3000/ e se divertir!

Opcional: que tal uma limpeza?

Nesse meio tempo, aqui no meu Docker, vendo pela interface gráfica, vários containers temporários ficam lá existindo junto com os containers que são realmente necessários para a minha aplicação. Tipo assim:

Print do Docker mostrando vários containers temporários parados junto com os containers úteis relacionados à minha aplicação

Eu gosto de, no final desse processo todo de montar esse ambiente, ir apagando esses containers que eu sei que nunca mais serão utilizados. E como boa entusiasta de interfaces que facilitem o nosso processo, eu vou apagando na mão mesmo 😅:

Print da imagem mostrando como apagar um container dentro da interface gráfica do Docker: procurar o botão com uma lixeira e com o tooltip DELETE e pressioná-lo.

E está feito! Agora fica bem mais visualmente bonitinho (e, provavelmente, te devolve algum espacinho aí).

Imagem mostrando todos os containers existentes e úteis da aplicação.

Resolução de problemas

Este tutorial foi montado depois de muita raiva e frustração, porque fui encontrando diversos problemas enquanto seguia o passo-a-passo original e não havia nada explicando como solucionar os problemas que por ventura poderiam vir a aparecer. Por isso, abri essa seção aqui, onde vou falar de alguns problemas que eu tive e como eu os resolvi. Talvez esses problemas não aconteçam com você, o que é ótimo! Mas caso aconteça, talvez aqui esteja a sua solução.

Instalação do Yarn

Em determinado momento, eu precisei instalar o Yarn na minha máquina, pois havia um erro relacionado à falta deste componente na hora de executar alguma coisa relacionada ao Ruby ou ao Rails (eu não salvei a mensagem de erro, perdão). Instalar ele é relativamente tranquilo, é só seguir o passo a passo do site oficial deles.

Porém não pra mim não foi tão simples assim: ao tentar instalar o Yarn, recebi uma mensagem de erro relacionada à execução de scripts de terceiros. Desta forma, tive que habilitar isso via o terminal. É necessário que este esteja aberto em modo de administrador e você precisa executar o seguinte comando:

Set-ExecutionPolicy RemoteSigned

Depois disso, você vai conseguir finalizar a instalação do Yarn normalmente. Caso você queira mais detalhes sobre a resolução deste problema, você pode encontrar neste link.

Erro com o tz-info

Não, eu não sei o que é o tz-info e nem fui procurar saber o que era. Somente lembro que tive um erro relacionado à ele na primeira vez que fui tentar montar o ambiente, se não me engano na hora de rodar a imagem do Ruby. O que resolveu esse problema: apagar a gem do tz-info do meu Gemfile e fazer um novo build na imagem (docker-compose build). Depois disso, consegui seguir com os outros passos do tutorial. Mais informações sobre o erro e outras possíveis soluções neste link.

O Homem de Ferro dizendo "No Problem" enquanto abana a mão
Não queremos nenhum problema sem solução aqui!

📖 Links consultados

📕 Desvendando o Dockerfile – Alura

📘 Vamos falar sobre dependências de projeto? – Wellington Avelino

📗 Construindo aplicações em Rails com Docker – Alexandre Dias

📙 Compreendendo o arquivo Gemfile.lock

📕 StackOverflow: Docker | Postgres Database is uninitialized and superuser password is not specified

📘 StackOverflow: Error loading the ‘postgresql’ Active Record adapter. Missing a gem it depends on? pg is not part of the bundle. Add it to your Gemfile


📜 Posts mais recentes

Atalhos de teclado para desenvolvedores .NET

🧼 Algumas questões sobre o Clean Code – parte 1



💌 Recadinhos

Gostou do texto? Tem algo a adicionar? Alguma crítica construtiva? Feedbacks? Sugestões? Pedidos? Fique à vontade para me contatar via email (oli.pmatt@gmail.com), Twitter (@oliviamattiazzo), LinkedIn (/oliviamattiazzo) ou pela caixa de comentários aqui embaixo! Vai ser um prazer conversar contigo! ✨

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.