Skip to main content

Docker para Ruby on Rails em um ambiente de desenvolvimento local

Escrito por

Mikhail Tereschenko

blog feature ruby security

2 de novembro de 2022

0 minutos de leitura

Olá, pessoal que desenvolve com Ruby! Se você está procurando uma maneira eficaz de configurar o Docker para Ruby on Rails no seu ambiente de desenvolvimento local, este post é para você. Ele dá continuidade ao nosso artigo anterior sobre como instalar Ruby no macOS para desenvolvimento local.

Quem desenvolve com Ruby frequentemente precisa considerar um banco de dados ao criar um projeto Ruby on Rails, além de outros pré-requisitos do ambiente de desenvolvimento. Mas garantir que tudo isso esteja instalado no sistema operacional da máquina host — e com as versões exatas exigidas — pode ser um desafio.

Sempre que tentava criar um projeto novo, sentia falta de um botão que preparasse um ambiente novo e limpo para ele. Então, decidi criar uma configuração baseada em Docker que pode ser iniciada e interrompida facilmente com o docker-compose.yml do Docker.

O que é o Docker Compose?

O Docker Compose é uma ferramenta de Infraestrutura como Código (IaC) que permite configurar e conectar aplicativos e serviços, como proxies, bancos de dados e montagem de volumes, para provisioná-los de forma reutilizável. Ele é instalado como parte do pacote de software do Docker e definido usando YAML.

Esta configuração do Docker Compose para um ambiente de desenvolvimento local pode ser usada em diferentes tipos de projeto, não apenas em projetos Ruby on Rails. Ela também pode ser usada em projetos paralelos ou projetos de front-end do lado do cliente. Estas são as principais razões pelas quais prefiro usar ambientes Docker em vez de alterar o sistema operacional da minha máquina host:

  • fácil de configurar

  • fácil de separar a lógica do ambiente

  • fácil de usar o mesmo ambiente em diferentes projetos

  • fácil de compartilhar o ambiente com quem precisar de ajuda, sem expor código confidencial

  • a máquina host permanece limpa

Uma configuração de Docker para Ruby on Rails

Para configurar um ambiente Docker para Ruby on Rails para desenvolvimento local, você precisará de:

  • Um sistema operacional semelhante ao Unix (uso o macOS, mas você pode usar Ubuntu ou qualquer outra distribuição Linux. O Windows também deve funcionar)

  • Git

  • make (3.81 ou posterior)

  • Docker (19.03.12 ou posterior)

  • docker-compose (1.26.0 ou posterior)

  • Seu editor de texto preferido (no meu caso, o Visual Studio Code)

Com tudo isso em mãos, você já pode começar. Se não quiser ler, aqui está o repositório de código aberto com todas as instruções necessárias e o código-fonte completo de todos os trechos que vamos analisar. Mas, como você já está aqui, basta acompanhar este artigo para seguir o passo a passo.

Para começar, vou criar um projeto Ruby on Rails chamado foo_bar_project, para simplificar.

1) Obtenha a configuração do ambiente do Docker Compose e remova o repositório Git. Se quiser manter tudo rastreado em um repositório de código-fonte Git, você poderá criar o seu próprio mais tarde.

git clone git@github.com:mtereschenko/simple_ror_environment.git && cd simple_ror_environment && rm -rf .git

As próximas etapas devem ser executadas no diretório ./environment. Portanto, vamos garantir que os comandos sejam executados dentro dele:

cd ./environment

2) Agora, você precisa definir um nome de projeto para esta configuração de ambiente. Por padrão, o nome é test_project. Neste exemplo de código, precisamos alterá-lo para foo_bar_project, como mencionado anteriormente. Para isso, edite o arquivo ./environment/.env.example e atualize a variável PROJECT_NAME. É importante usar valores em Snake Case, com letras minúsculas:

PROJECT_NAME=foo_bar_project

3) Agora você já pode criar seu projeto executando o seguinte comando:

make init

Como você pode ver, agora há uma nova pasta chamada application, com o código do Ruby on Rails dentro dela.

4) Agora você precisa criar seu ambiente de desenvolvimento local com o Docker Compose. Esta etapa é obrigatória sempre que houver alterações no Dockerfile ou no Gemfile do seu aplicativo.

make build

5) Agora você pode iniciar o ambiente de desenvolvimento local do Ruby da seguinte forma:

make start

Observe que isso ainda não inicia um aplicativo Ruby on Rails. Para isso, precisamos abrir um shell no ambiente Docker e instruí-lo a iniciar o processo rails. Veja como:

make shell
rails s -p 3000 –binding=0.0.0.0

Docker para Ruby on Rails em um ambiente de desenvolvimento local. Por fim, se você abrir o navegador e acessar este URL, http://foo_bar_project.localhost/, deverá ver o projeto Ruby on Rails que acabamos de criar.

Então, o que temos disponível agora?

  • Temos um projeto com um diretório de ambiente que contém tudo o que precisamos para executá-lo e um diretório de aplicativo com o código da aplicação. Esse diretório é mapeado para o contêiner Docker, dando a você acesso total a partir da imagem Docker do Ruby.

  • Temos acesso rápido ao shell do Ruby, sem precisar instalar localmente um ambiente Ruby com Rails, Rake, Bundler ou qualquer outra ferramenta do ecossistema Ruby.

  • Os comandos podem ser executados depois do comando make shell.

  • Esta configuração do projeto com Docker Compose tem dois estágios predefinidos. Assim, se você precisar implantar seu aplicativo Ruby em algum lugar, poderá criar seu próprio estágio. É simples assim!

Se você quiser entender como funciona o ambiente Docker para Ruby on Rails descrito acima, vamos detalhar a imagem Docker do Ruby, o Makefile e a definição do Docker Compose que faz tudo funcionar em conjunto.

Uma imagem Docker do Ruby

Para começar, a imagem Docker do Ruby é essencial. Criá-la corretamente também é importante para garantir o reaproveitamento eficiente do cache das camadas da imagem Docker e considerar outros aspectos de um ambiente de desenvolvimento local.

Veja a seguir o Dockerfile da imagem Docker do Ruby:

FROM ruby:2.7.6-alpine3.16 as base_image
RUN apk add --no-cache git \
    build-base \
    libpq-dev \
    tzdata

FROM base_image as development
COPY ./artifacts/rails/Gemfile /tmp/Gemfile
COPY ./artifacts/rails/Gemfile.lock /tmp/Gemfile.lock
COPY ./containers/ruby/runners/runner.development.sh /rdebug_ide/runner.sh
RUN cd /tmp && \
  gem install ruby-debug-ide && \
  gem install debase && \
  bundle install && \
  chmod +x /rdebug_ide/runner.sh && \
  apk add --no-cache git \
  nodejs \
  yarn
WORKDIR /app

ENTRYPOINT ["tail", "-f", "/dev/null"]

FROM base_image as init
COPY ./containers/ruby/initializers/runner.init.sh /tmp/runner.sh
COPY ./containers/ruby/initializers/database.yml /tmp/database.yml
COPY ./containers/ruby/initializers/.gitignore /tmp/.gitignore
COPY ./containers/ruby/initializers/development.rb /tmp/development.rb
RUN chmod +x /tmp/runner.sh

WORKDIR /app

ENTRYPOINT ["/tmp/runner.sh"]

Como você pode ver, ele parte de algumas suposições sobre configurações complementares necessárias para ter um ambiente funcional de aplicativo Ruby on Rails, como:

  • arquivo database.yml, com os dados de conexão do Ruby on Rails com o banco de dados

  • development.rb, para a configuração do Ruby on Rails em tempo de execução no ambiente de desenvolvimento

Isso é feito usando o script runners.sh no diretório ./environment/containers/ruby/initializers:

#!/bin/ash

cd /app

gem install rails

rails new . -d=postgresql --skip-git

yes | cp -rf /tmp/database.yml /app/config/database.yml
yes | cp -rf /tmp/development.rb /app/config/environments/development.rb
cp /tmp/.gitignore /app/.gitignore

Este Dockerfile também usa builds Docker em múltiplas etapas, permitindo reutilizar partes específicas com eficiência em diferentes ambientes, caso você queira usar a mesma configuração em vários fluxos de trabalho, como testes e homologação.

Docker Compose para Rails

O arquivo Docker Compose em `./environment/docker-compose.development.yml` conecta todos os serviços para que um aplicativo Ruby on Rails funcione no Docker:

  • Um servidor HTTP nginx

  • Um aplicativo Ruby

  • Um servidor de banco de dados PostgreSQL

Veja a seguir o arquivo Docker Compose para Rails usado nesta configuração:

version: '3.7'
services:
  nginx:
    image: "${PROJECT_NAME}/nginx:development"
    container_name: ${PROJECT_NAME}-nginx
    build: 
      context: ./
      dockerfile: ./containers/nginx/Dockerfile
    depends_on:
      - ruby
    tty: true
    ports:
      - 80:80
    volumes:
      - ./`artifacts`/nginx/:/`var`/log/nginx:cached
   ruby:
    image: "${PROJECT_NAME}/ruby:development"
    container_name: ${PROJECT_NAME}-ruby
    depends_on:
      - postgres
    build:
      context: ./
      dockerfile: ./containers/ruby/Dockerfile
      target: development
    ports:
      - 13030:13030
    volumes:
      - ${APP_PATH}:/app:cached
    environment:
      DB_NAME: ${DB_NAME}
      PROJECT_NAME: ${PROJECT_NAME}
      DB_USER: ${DB_USER}
      DB_PASSWORD: ${DB_PASSWORD}
      DB_PORT: ${DB_PORT}
      PUMA_WORKERS: 0
      RAILS_MAX_THREADS: 1
     postgres:
    image: "${PROJECT_NAME}/postgres:development"
    container_name: ${PROJECT_NAME}-postgres
    environment:
      POSTGRES_DB: ${DB_NAME}
      POSTGRES_USER: ${DB_USER}
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    build:
      context: ./
      dockerfile: ./containers/postgres/Dockerfile
    ports:
      - ${DB_PORT}:5432
    volumes:
      - postgres_volume:/var/lib/postgresql/data

volumes:
  postgres_volume:

Makefile para Docker

Por fim, para criar uma interface acessível que facilite a interação de quem desenvolve com este ambiente de desenvolvimento local em Ruby, usamos um Makefile:

.PHONY: help
# Make stuff

-include .env

export DOCKER_BUILDKIT=1
export COMPOSE_DOCKER_CLI_BUILD=1

.DEFAULT_GOAL := help

ARTIFACTS_DIRECTORY := "./artifacts"

CURRENT_PATH :=${abspath .}

SHELL_CONTAINER_NAME := $(if $(c),$(c),ruby)
BUILD_TARGET := $(if $(t),$(t),development)

help: ## Help.
@grep -E '^[a-zA-Z-]+:.*?## .*$$' Makefile | awk 'BEGIN {FS = ":.*?## "}; {printf "[32m%-27s[0m %s\n", $$1, $$2}'

init: ## Project installation.
@rm -f ./.env
@cp .env.example .env
@make init_app_directory
@make create_postgress_volume
@docker-compose -f docker-compose.init.yml build
@docker-compose -f docker-compose.init.yml up

build: ## Build images.
@make create_project_artifacts
@cp ${APP_PATH}/Gemfile "${ARTIFACTS_DIRECTORY}/rails/Gemfile"
@cp ${APP_PATH}/Gemfile.lock "${ARTIFACTS_DIRECTORY}/rails/Gemfile.lock"
@docker-compose -f docker-compose.$(BUILD_TARGET).yml build

shell: ## Internal image bash command line.
@if [[ -z `docker ps | grep ${SHELL_CONTAINER_NAME}` ]]; then \
echo "${SHELL_CONTAINER_NAME} is NOT running (make start)."; \
else \
docker-compose -f docker-compose.$(BUILD_TARGET).yml exec $(SHELL_CONTAINER_NAME) /bin/ash; \
fi

start: ## Start previously builded application images.
@make create_project_artifacts
@make start_postgres
@make start_ruby
@make start_nginx

run: ## Run ruby debugger session.
@docker-compose -f docker-compose.$(BUILD_TARGET).yml exec ruby /bin/ash /rdebug_ide/runner.sh

start_ruby: ## Start ruby image.
@if [[ -z `docker ps | grep ruby` ]]; then \
docker-compose -f docker-compose.$(BUILD_TARGET).yml up -d ruby; \
else \
echo "Ruby is running."; \
fi

start_postgres: ## Start postgres image.
@if [[ -z `docker ps | grep postgres` ]]; then \
docker-compose -f docker-compose.$(BUILD_TARGET).yml up -d postgres; \
else \
echo "Postgres is running."; \
fi

start_nginx: ## Start nginx image.
@if [[ -z `docker ps | grep nginx` ]]; then \
docker-compose -f docker-compose.$(BUILD_TARGET).yml up -d nginx; \
else \
echo "Nginx is running."; \
fi

stop: ## Stop all images.
@docker-compose -f docker-compose.$(BUILD_TARGET).yml stop

create_project_artifacts:
mkdir -p ./artifacts/rails
mkdir -p ./artifacts/db

init_app_directory:
@mkdir -p ${APP_PATH}

create_postgress_volume:
@sed -i '' -r  "s/postgres_volume:/${PROJECT_NAME}_db_volume:/g" docker-compose.development.yml

Executando aplicativos Ruby com eficiência

Esperamos que agora você tenha aprendido a executar aplicativos Ruby, como Ruby on Rails, no seu ambiente local usando uma configuração de contêineres Docker. Outra maneira de executar aplicativos Ruby localmente é usar ambientes virtuais de Ruby, tema que abordamos anteriormente no post sobre como instalar Ruby no macOS.

Agora que seu ambiente de desenvolvimento Ruby está pronto, talvez você também queira aprender a proteger seus aplicativos Ruby. Confira os artigos a seguir para saber mais sobre segurança em Ruby:

Segurança de contêineres que prioriza os desenvolvedores

O Snyk encontra e corrige automaticamente vulnerabilidades em imagens de contêiner e workloads do Kubernetes.

Leia mais

feature customer snowflake
Article

Segurança desde o início: conheça a integração do Snyk Studio com o Snowflake Cortex Code

O Snyk Studio se integra ao Snowflake Cortex Code para detectar vulnerabilidades em código gerado por IA, dependências e contêineres durante o desenvolvimento.

blog feature pypi spoof
Article

A Snyk AI Security Platform completa, grátis para mantenedores de projetos open source

Mantenedores de projetos open source estão sobrecarregados com relatórios reais de vulnerabilidades e precisam de ajuda para priorizar, corrigir e lançar correções mais rápido. O Secure Developer Program da Snyk dá aos projetos qualificados acesso gratuito à Snyk AI Security Platform.

feature insights announcement
Blog

Comprometimento da cadeia de suprimentos do node-gyp: um worm npm que se autopropaga e se esconde em binding.gyp

Um novo worm do npm está explorando binding.gyp para acionar o node-gyp durante a instalação, permitindo que pacotes maliciosos executem código sem scripts de ciclo de vida. Ele rouba credenciais, mantém persistência no GitHub e se propaga entre contas de mantenedores.