Skip to main content

Ruby on Rails con Docker para un entorno de desarrollo local

Escrito por

Mikhail Tereschenko

blog feature ruby security

2 de noviembre de 2022

0 minutos de lectura

¡Hola, desarrolladores de Ruby! Si has estado buscando una forma efectiva de configurar Ruby on Rails con Docker para tu entorno de desarrollo local, esta publicación es para ti. Es la continuación de nuestro artículo anterior sobre cómo instalar Ruby en macOS para el desarrollo local.

Al desarrollar un proyecto de Ruby on Rails, los desarrolladores de Ruby suelen tener que considerar una base de datos y otros requisitos previos del entorno de desarrollo. Sin embargo, puede ser difícil asegurarse de que todo esté instalado en el sistema operativo host y que use las versiones exactas necesarias.

Cada vez que intentaba crear un proyecto nuevo, deseaba tener un botón que creara un entorno nuevo y limpio para el proyecto. Así que decidí crear una configuración con Docker que se puede iniciar y detener fácilmente con docker-compose.yml de Docker.

¿Qué es Docker Compose?

Docker Compose es una herramienta de Infraestructura como Código (IaC) que te permite configurar y conectar aplicaciones y servicios, como proxies, bases de datos y montajes de volúmenes, para aprovisionarlos de forma reutilizable. Se instala como parte del paquete de software de Docker y se define con YAML.

Esta configuración de Docker Compose para un entorno de desarrollo local se puede usar en distintos tipos de proyectos; no es exclusiva de Ruby on Rails. También puede usarse en proyectos personales o proyectos de frontend del lado del cliente. Estas son las principales razones por las que prefiero usar entornos de Docker en lugar de modificar mi propio sistema operativo host:

  • fácil de configurar

  • fácil de separar la lógica del entorno

  • fácil de usar el mismo entorno para distintos proyectos

  • fácil de compartir el entorno con quien necesite ayuda, sin incluir código confidencial

  • la máquina host se mantiene limpia

Una configuración de Ruby on Rails con Docker

Para configurar un entorno de Ruby on Rails con Docker para el desarrollo local, necesitarás lo siguiente:

  • Un sistema operativo tipo Unix (yo uso macOS, pero puedes usar Ubuntu u otra distribución de Linux. Windows también debería funcionar)

  • Git

  • make (versión 3.81 o posterior)

  • Docker (versión 19.03.12 o posterior)

  • docker-compose (versión 1.26.0 o posterior)

  • Tu editor de texto preferido (en mi caso, Visual Studio Code)

Una vez que tengas todo eso, ya puedes empezar. Si no quieres leer, aquí está el repositorio de código abierto con todas las instrucciones necesarias y el código fuente completo de los fragmentos que revisaremos. Pero como ya estás aquí, sigue este artículo paso a paso.

Para empezar, crearé un proyecto de Ruby on Rails llamado foo_bar_project, para simplificar.

1) Obtén la configuración del entorno de Docker Compose y elimina el repositorio de Git. Si quieres mantener todo bajo el seguimiento de un repositorio de código fuente de Git, puedes crear el tuyo más adelante.

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

Los siguientes pasos deben ejecutarse en el directorio ./environment, así que asegúrate de ejecutar los comandos allí:

cd ./environment

2) Ahora, debes asignar un nombre de proyecto a esta configuración del entorno. De forma predeterminada, es test_project. En el ejemplo de código que veremos aquí, debemos cambiarlo a foo_bar_project, como indicamos antes. Para hacerlo, modifica el archivo ./environment/.env.example para que la variable PROJECT_NAME refleje ese nombre. Es importante usar Snake Case en minúsculas como valor de las variables:

PROJECT_NAME=foo_bar_project

3) Ahora puedes crear tu proyecto con el siguiente comando:

make init

Como puedes ver, ahora tienes una carpeta nueva llamada application que contiene código de Ruby on Rails.

4) Ahora debes compilar tu entorno de desarrollo local con Docker Compose. Este paso es obligatorio después de cualquier cambio en el Dockerfile o el Gemfile de tu aplicación.

make build

5) Ahora puedes iniciar el entorno de desarrollo local de Ruby de la siguiente manera:

make start

Ten en cuenta que esto todavía no inicia una aplicación de Ruby on Rails. Para hacerlo, debemos abrir una shell en el entorno de Docker e indicarle que inicie el proceso rails. Lo haremos así:

make shell
rails s -p 3000 –binding=0.0.0.0

Ruby on Rails con Docker para un entorno de desarrollo local. Por último, si abres el navegador y vas a esta URL, http://foo_bar_project.localhost/, deberías ver el proyecto de Ruby on Rails que acabamos de crear.

Entonces, ¿qué tenemos disponible ahora?

  • Tenemos un proyecto con un directorio de entorno que contiene todo lo necesario para ejecutarlo, y un directorio de aplicación que contiene el código de nuestra aplicación. Este directorio está vinculado al contenedor de Docker, por lo que tienes acceso completo desde la imagen de Ruby de Docker.

  • Tenemos acceso rápido a la shell de Ruby, así que no necesitamos instalar localmente un entorno de Ruby, como Rails, Rake, Bundler u otra herramienta de Ruby.

  • Los comandos se pueden ejecutar después del comando make shell.

  • Esta configuración de Docker Compose del proyecto incluye dos etapas preconfiguradas. Si alguna vez necesitas implementar tu aplicación de Ruby en otro lugar, puedes crear tu propia etapa. ¡Así de simple!

Si quieres saber cómo funciona el entorno de Docker para Ruby on Rails que vimos, vamos a desglosarlo en la imagen de Ruby para Docker, el Makefile y la definición de Docker Compose que hace que todo funcione en conjunto.

Una imagen de Ruby para Docker

Para empezar, la imagen de Ruby para Docker es una parte esencial. También es importante crearla correctamente para reutilizar de forma efectiva la caché de las capas de la imagen de Docker y tener en cuenta otros aspectos relacionados con un entorno de desarrollo local.

A continuación se muestra el Dockerfile para la imagen de Ruby de Docker:

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 puedes ver, da por sentada cierta configuración adicional necesaria para tener un entorno funcional de aplicaciones de Ruby on Rails, por ejemplo:

  • El archivo database.yml, que contiene los detalles de conexión de Ruby on Rails a la base de datos

  • development.rb, para la configuración de ejecución de Ruby on Rails en el entorno de desarrollo

Esto se hace con el script runners.sh en el directorio ./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 también usa Docker multietapa, de modo que partes específicas se pueden reutilizar de manera efectiva en distintos entornos si quieres usar la misma configuración en diferentes flujos de trabajo (pruebas, preproducción, etc.).

Un archivo Docker Compose para Rails

El archivo Docker Compose en `./environment/docker-compose.development.yml` ayuda a conectar todos los servicios para que una aplicación de Ruby on Rails funcione en Docker:

  • Un servidor HTTP nginx

  • Una aplicación de Ruby

  • Un servidor de base de datos PostgreSQL

A continuación se muestra el archivo Docker Compose para Rails que usa esta configuración:

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 último, para que los desarrolladores puedan interactuar fácilmente con este entorno de desarrollo local de Ruby mediante una interfaz accesible, se usa un 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

Ejecutar aplicaciones de Ruby de forma eficiente

Esperamos que ahora hayas aprendido a ejecutar aplicaciones de Ruby, como Ruby on Rails, en tu entorno local mediante la configuración de contenedores de Docker. Otra forma de ejecutar aplicaciones de Ruby localmente es usar entornos virtuales de Ruby, un tema que ya abordamos en nuestra publicación sobre cómo instalar Ruby en macOS.

Ahora que ya tienes listo tu entorno de desarrollo de Ruby, quizás también quieras aprender a proteger tus aplicaciones de Ruby. Consulta estos artículos para obtener más información sobre la seguridad de Ruby:

Seguridad de contenedores centrada en los desarrolladores

Snyk encuentra y corrige automáticamente vulnerabilidades en imágenes de contenedores y cargas de trabajo de Kubernetes.

Leer más

feature customer snowflake
Article

Seguridad desde el inicio: presentamos la integración de Snyk Studio con Snowflake Cortex Code

Snyk Studio se integra con Snowflake Cortex Code para analizar código generado por IA, dependencias y contenedores en busca de vulnerabilidades durante el desarrollo.

blog feature pypi spoof
Article

Toda la Snyk AI Security Platform, gratis para quienes mantienen proyectos de código abierto

Quienes mantienen proyectos de código abierto reciben reportes de vulnerabilidades reales sin parar y necesitan ayuda para priorizarlas, corregirlas y publicar soluciones más rápido. El Secure Developer Program de Snyk ofrece a los proyectos que cumplen los requisitos acceso gratis a Snyk AI Security Platform.

feature insights announcement
Blog

Compromiso de la cadena de suministro de node-gyp: un gusano de npm que se propaga solo y se oculta en binding.gyp

Un nuevo gusano de npm abusa de binding.gyp para activar node-gyp durante la instalación y permitir que paquetes maliciosos ejecuten código sin scripts del ciclo de vida. Roba credenciales, persiste en GitHub y se propaga entre mantenedores.