Dockerizar su Ruby on Rails con una aplicación front-end de React puede mejorar drásticamente su flujo de trabajo de desarrollo y su proceso de implementación. Al crear un entorno estandarizado para su aplicación, garantiza un comportamiento coherente en las diferentes etapas de desarrollo, pruebas, producción e incluso en diferentes sistemas. De hecho, está diseñado para minimizar los problemas relacionados con las diferencias del sistema. Esta guía lo guiará a través de los pasos esenciales para que su aplicación Rails y React funcione sin problemas en contenedores Docker.
Docker garantiza que la aplicación se ejecute de la misma manera independientemente de dónde se implemente, ya sea en la máquina de un desarrollador, un entorno de prueba o un servidor de producción. Esta coherencia se logra al contener todas las dependencias y configuraciones.
Los contenedores Docker incluyen todas las dependencias necesarias para que se ejecute la aplicación. Esto significa que las variaciones en las bibliotecas del sistema o las dependencias faltantes en diferentes sistemas no afectan la funcionalidad de la aplicación.
Los contenedores Docker se ejecutan de forma aislada entre sí y del sistema host. Este aislamiento evita conflictos entre diferentes aplicaciones y sus dependencias en el mismo sistema.
NB: se requiere conocimiento de la sintaxis de Docker
La dockerización implica dos conceptos clave: imágenes y contenedores. Las imágenes sirven como planos para contenedores y contienen toda la información necesaria para crear un contenedor, incluidas las dependencias y las configuraciones de implementación. Un contenedor es una instancia en tiempo de ejecución de una imagen, que comprende la imagen misma, un entorno de ejecución e instrucciones en tiempo de ejecución. Docker en general establece un estándar para el envío de software.
Para explicar Docker con una analogía simple: piense en los contenedores como los contenedores de envío en un patio, las imágenes como los elementos colocados dentro de estos contenedores y el buque de envío como el sistema sobre el cual se ejecutan los contenedores.
Siempre que configura y construye su aplicación, ciertas configuraciones del entorno son necesarias. Por ejemplo, no puede ejecutar una aplicación Rails sin un entorno Ruby instalado en su sistema. De manera similar, no puede ejecutar una aplicación React sin Node.js
y no puede instalar paquetes de React sin un administrador de paquetes de Node como npm
o Yarn
, etc.
Dado que el contenedor se ejecuta de forma aislada del sistema del usuario, haremos que todos estos paquetes estén disponibles en nuestro contenedor tal como lo habríamos hecho si lo hubiéramos creado directamente en nuestro sistema, por lo tanto, el contenedor actuará como un sistema en él. propio, como una máquina virtual. Existen diferencias entre Docker y la máquina virtual, pero este ejemplo es solo para explicarlo más.
Ahora, sigamos adelante y dockericemos la aplicación Rails. Para hacer esto, necesitaremos tres archivos en nuestra aplicación Rails: un Dockerfile
, un docker-compose.yml
y un bin/docker-entrypoint
. Examinemos cada uno de estos archivos en detalle.
NB: se requiere conocimiento de la sintaxis de Docker
Dockerfile
es un modelo para crear un contenedor Docker. Contiene una serie de instrucciones que Docker utiliza para crear una imagen, que luego se puede utilizar para ejecutar contenedores. Analicemos un Dockerfile
para una aplicación Ruby on Rails y React:
ARG RUBY_VERSION=3.1.4 FROM ruby:$RUBY_VERSION
ARG RUBY_VERSION=3.1.4
: define un argumento de compilación denominado RUBY_VERSION
con un valor predeterminado de 3.1.4
. Esto se puede anular en el momento de la compilación.
FROM ruby:$RUBY_VERSION
: utiliza la imagen base ruby
con la versión especificada por RUBY_VERSION
. Esto configura el contenedor con el tiempo de ejecución de Ruby. Tal como mencioné anteriormente, para ejecutar una aplicación Rails, necesitas tener Ruby instalado. RUN apt-get update -qq && \ apt-get install -y build-essential libvips bash bash-completion libffi-dev tzdata postgresql curl && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* /usr/share/doc /usr/share/man
apt-get update -qq
: actualiza la lista de paquetes de los repositorios, con -qq
para una salida silenciosa.
apt-get install -y
... : Instala varios paquetes: build-essential
: paquetes esenciales para crear software (como GCC).
libvips
: Biblioteca para procesamiento de imágenes.
bash
, bash-completion
: Bash Shell y su autocompletado.
libffi-dev
: biblioteca de interfaz de funciones externas.
tzdata
: datos de zona horaria.
postgresql
: Cliente de base de datos PostgreSQL.
curl
: Herramienta para transferir datos desde URL.
apt-get clean
: limpia el repositorio local de archivos de paquetes recuperados.
rm -rf /var/lib/apt/lists/ /usr/share/doc /usr/share/man
: Elimina listas de paquetes y documentación para reducir el tamaño de la imagen. RUN curl -fsSL https://deb.nodesource.com/setup_current.x | bash - && \ apt-get install -y nodejs && \ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \ echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \ apt-get update && \ apt-get install -y yarn
curl -fsSL https://deb.nodesource.com/setup_current.x | bash -
: Descarga y ejecuta el script de instalación de NodeSource para instalar Node.js.
apt-get install -y nodejs
: Instala Node.js.
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
: Agrega la clave Yarn GPG para verificar sus paquetes.
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
: agrega el repositorio de Yarn a la lista de fuentes.
apt-get update && apt-get install -y yarn
: actualiza la lista de paquetes e instala Yarn. ENV NODE_OPTIONS=--openssl-legacy-provider
ENV NODE_OPTIONS=--openssl-legacy-provider
: establece una variable de entorno para habilitar la compatibilidad con OpenSSL heredado para Node.js. WORKDIR /rails
WORKDIR /rails
: establece el directorio de trabajo para instrucciones posteriores en /rails
. ARG RAILS_ENV ENV RAILS_ENV=$RAILS_ENV
ARG RAILS_ENV
: define un argumento de compilación llamado RAILS_ENV
para especificar el entorno Rails (como development
, test
, production
).
ENV RAILS_ENV=$RAILS_ENV
: establece la variable de entorno RAILS_ENV
en el valor del argumento de compilación. COPY Gemfile Gemfile.lock ./ RUN bundle install
COPY Gemfile Gemfile.lock ./
: Copia Gemfile
y Gemfile.lock
al directorio de trabajo.
RUN bundle install
: Instala las gemas Ruby especificadas en el Gemfile
. COPY package.json yarn.lock ./ RUN yarn install --frozen-lockfile
COPY package.json yarn.lock ./
: Copia el package.json
y yarn.lock
al directorio de trabajo.
RUN yarn install --frozen-lockfile
: Instala las dependencias de front-end usando Yarn, asegurándose de que use las versiones exactas en yarn.lock
. COPY . .
COPY . .
: Copia todo el código de la aplicación al directorio de trabajo. RUN bundle exec bootsnap precompile --gemfile app/ lib/
RUN bundle exec bootsnap precompile --gemfile app/ lib/
: Precompila el caché Bootsnap para tiempos de arranque más rápidos de la aplicación Rails. Bootsnap es una joya que acelera los tiempos de arranque de Ruby y Rails al almacenar en caché cálculos costosos. RUN if [ "$RAILS_ENV" = "production" ]; then \ SECRET_KEY_BASE=1 bin/rails assets:precompile; \ fi
RUN if [ "$RAILS_ENV" = "production" ]; then
... : Ejecuta condicionalmente la precompilación de activos sólo si RAILS_ENV
está configurado en production
. Este paso es crucial para preparar los activos para un entorno de producción. COPY bin/docker-entrypoint /rails/bin/ RUN chmod +x /rails/bin/docker-entrypoint
COPY bin/docker-entrypoint /rails/bin/
: copia un script de punto de entrada personalizado en el contenedor.
RUN chmod +x /rails/bin/docker-entrypoint
: hace que el script del punto de entrada sea ejecutable. ENTRYPOINT ["/rails/bin/docker-entrypoint"] EXPOSE 5000 // you can use any port of your choice CMD ["./bin/rails", "server"]
ENTRYPOINT ["/rails/bin/docker-entrypoint"]
: establece el script del punto de entrada que se ejecutará cuando se inicie el contenedor. Este script normalmente configura el entorno, prepara la base de datos e inicia la aplicación.
EXPOSE 5000
: indica que el contenedor escucha en el puerto 5000. Esta es una función de documentación y no publica el puerto.
CMD ["./bin/rails", "server"]
: especifica el comando predeterminado que se ejecutará cuando se inicia el contenedor, que es iniciar el servidor Rails. El archivo docker-compose.yml
se utiliza para definir y ejecutar aplicaciones Docker de múltiples contenedores. Le permite configurar los servicios, redes y volúmenes de su aplicación en un solo archivo. En este caso vamos a utilizar dos servicios. Aquí está el archivo docker-compose.yml
para la aplicación Rails:
db
) codedb: image: postgres:14.2-alpine container_name: demo-postgres-14.2 volumes: - postgres_data:/var/lib/postgresql/data command: "postgres -c 'max_connections=500'" environment: POSTGRES_DB: ${POSTGRES_DB} POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} ports: - "5432:5432"
image: postgres:14.2-alpine
: especifica la imagen de Docker que se utilizará para este servicio. En este caso, se trata de la imagen de PostgreSQL 14.2 basada en la distribución Alpine Linux. Las imágenes alpinas son conocidas por su pequeño tamaño, lo que puede ayudar a mantener bajo el tamaño general de la imagen.
container_name: demo-postgres-14.2
: nombra el contenedor demo-postgres-14.2
. Este nombre se utiliza para hacer referencia al contenedor en comandos y registros.
volumes
: postgres_data:/var/lib/postgresql/data:
monta un volumen con nombre postgres_data
en /var/lib/postgresql/data
dentro del contenedor. Este directorio es donde PostgreSQL almacena sus datos, asegurando que los datos de la base de datos persistan entre reinicios del contenedor.
command: "postgres -c 'max_connections=500'"
: anula el comando predeterminado de la imagen de PostgreSQL. Inicia PostgreSQL con una opción de configuración para aumentar el número máximo de conexiones a 500.
environment
: POSTGRES_DB: ${POSTGRES_DB}
: establece el nombre de la base de datos predeterminada a crear, utilizando una variable de entorno POSTGRES_DB
.
POSTGRES_USER: ${POSTGRES_USER}
: establece el nombre de usuario predeterminado para acceder a la base de datos PostgreSQL, utilizando la variable de entorno POSTGRES_USER
.
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
: establece la contraseña para el usuario predeterminado, utilizando la variable de entorno POSTGRES_PASSWORD
.
ports
:"5432:5432"
: asigna el puerto 5432 en el host al puerto 5432 en el contenedor. Esto permite el acceso a PostgreSQL en la máquina host a través del puerto 5432.demo-web
) codedemo-web: build: context: . args: - RAILS_ENV=${RAILS_ENV} command: "./bin/rails server -b 0.0.0.0" environment: - RAILS_ENV=${RAILS_ENV} - POSTGRES_HOST=${POSTGRES_HOST} - POSTGRES_DB=${POSTGRES_DB} - POSTGRES_USER=${POSTGRES_USER} - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - RAILS_MASTER_KEY=${RAILS_MASTER_KEY} volumes: - .:/rails - app-storage:/rails/storage depends_on: - db ports: - "3000:3000"
build:
context: .
: Especifica el contexto de compilación para la imagen de Docker. En este caso, .
se refiere al directorio actual. Esto significa que Docker utilizará el Dockerfile en el directorio actual para crear la imagen.args
: RAILS_ENV=${RAILS_ENV}
: pasa el argumento de compilación RAILS_ENV
al proceso de compilación de Docker, lo que le permite especificar el entorno Rails (como development
, test
o production
).
command: "./bin/rails server -b 0.0.0.0"
: anula el comando predeterminado de la imagen de Docker. Inicia el servidor Rails y lo vincula a todas las interfaces de red ( 0.0.0.0
), lo cual es necesario para que se pueda acceder al servicio desde fuera del contenedor.
environment:
RAILS_ENV=${RAILS_ENV}
: establece el entorno Rails dentro del contenedor utilizando la variable de entorno RAILS_ENV
.
POSTGRES_HOST=${POSTGRES_HOST}
: establece la dirección del host PostgreSQL.
POSTGRES_DB=${POSTGRES_DB}
: establece el nombre de la base de datos.
POSTGRES_USER=${POSTGRES_USER}
: establece el usuario de PostgreSQL.
POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
: establece la contraseña del usuario de PostgreSQL.
RAILS_MASTER_KEY=${RAILS_MASTER_KEY}
: establece la clave maestra de Rails, que se utiliza para cifrar credenciales y otros secretos.
volumes
:
.:/rails
: monta el directorio actual (donde se encuentra el archivo docker-compose.yml
) en /rails
dentro del contenedor. Esto le permite editar archivos en su host y reflejar esos cambios dentro del contenedor.
app-storage:/rails/storage
: monta un volumen con nombre app-storage
en /rails/storage
dentro del contenedor. Normalmente se utiliza para almacenar archivos específicos de Rails, como registros, cargas y archivos en caché.
depends_on
:
db
: garantiza que el servicio demo-web
espere a que el servicio db
esté listo antes de comenzar. Docker Compose maneja el orden de inicio de los servicios según esta configuración. ports:
"3000:3000"
: asigna el puerto 3000 en el host al puerto 3000 en el contenedor. Esto le permite acceder a la aplicación Rails en la máquina host a través del puerto 3000. codevolumes: postgres_data: app-storage:
postgres_data
: define un volumen con nombre postgres_data
utilizado por el servicio db
para conservar los datos de PostgreSQL.app-storage
: define un volumen app-storage
con nombre utilizado por el servicio demo-web
para conservar datos específicos de la aplicación, como cargas y registros. El script bin/docker-entrypoint
es una parte crucial de la configuración de Docker. Se ejecuta cuando se inicia el contenedor y normalmente maneja la configuración del entorno, la preparación de la base de datos y otras tareas de inicialización necesarias antes de iniciar la aplicación principal. Aquí hay un ejemplo de script bin/docker-entrypoint
y una explicación detallada de cada parte:
bashCopy code#!/bin/bash set -e
#!/bin/bash
: esta línea especifica que el script debe ejecutarse utilizando el shell Bash.
set -e
: Esto indica al script que salga inmediatamente si algún comando devuelve un código de salida distinto de cero. Esto ayuda a garantizar que, si algún paso falla, el script detenga la ejecución, lo que puede evitar que los pasos posteriores se ejecuten en un estado no válido.
Creación o migración de bases de datos condicionales
# If running the rails server then create or migrate existing database if [ "${*}" == "./bin/rails server" ]; then ./bin/rails db:create ./bin/rails db:prepare fi
"${*}"
) es ./bin/rails server
. El *
es un parámetro especial que contiene todos los parámetros posicionales pasados al script.
./bin/rieles de base de datos
: Si se cumple la condición, este comando intentará crear la base de datos. Es equivalente a ejecutar rails db:create
, que configura la base de datos como se define en el archivo de configuración de la base de datos ( config/database.yml
).
./bin/rieles de base de datos
: Este comando ejecutará rails db:prepare
, lo que garantiza que la base de datos esté configurada y migrada. Creará la base de datos si no existe y ejecutará migraciones si la base de datos ya está creada. Esta es una combinación de rails db:create
y rails db:migrate
.
bashCopy codeexec "${@}"
exec "${@}"
: esto reemplaza el proceso de shell actual con el comando pasado como argumentos al script. El símbolo @
contiene todos los parámetros posicionales pasados al script. Por ejemplo, si el script se llama con ./bin/rails server
, esta línea ejecuta efectivamente ./bin/rails server
como el proceso principal del contenedor. Un Dockerfile
bien diseñado es esencial para crear un entorno confiable y consistente para su aplicación Ruby on Rails y React. Al definir la imagen base, configurar variables de entorno e instalar dependencias, se asegura de que su aplicación se ejecute sin problemas en varios entornos.
Docker no solo agiliza su proceso de desarrollo sino que también mejora la confiabilidad de su aplicación en producción. Hay áreas de optimizaciones, pero esto es sólo una descripción general de cómo dockerizar la aplicación Rails.
Dockerfile
resultante, docker-compose.yml
y bin/docker-entrypoint
ARG RUBY_VERSION=3.1.4 FROM ruby:$RUBY_VERSION # Install dependencies RUN apt-get update -qq && \ apt-get install -y build-essential libvips bash bash-completion libffi-dev tzdata postgresql curl && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* /usr/share/doc /usr/share/man # Install Node.js and Yarn RUN curl -fsSL https://deb.nodesource.com/setup_current.x | bash - && \ apt-get install -y nodejs && \ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \ echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \ apt-get update && \ apt-get install -y yarn # Set environment variable to enable legacy OpenSSL support ENV NODE_OPTIONS=--openssl-legacy-provider # Rails app lives here WORKDIR /rails # Set environment variable for the build ARG RAILS_ENV ENV RAILS_ENV=$RAILS_ENV # Install application gems COPY Gemfile Gemfile.lock ./ RUN bundle install # Install frontend dependencies COPY package.json yarn.lock ./ RUN yarn install --frozen-lockfile # Copy application code COPY . . # Precompile bootsnap code for faster boot times RUN bundle exec bootsnap precompile --gemfile app/ lib/ # Precompiling assets for production without requiring secret RAILS_MASTER_KEY RUN if [ "$RAILS_ENV" = "production" ]; then \ SECRET_KEY_BASE=1 bin/rails assets:precompile; \ fi # Entrypoint prepares the database. COPY bin/docker-entrypoint /rails/bin/ RUN chmod +x /rails/bin/docker-entrypoint # Use an absolute path for the entry point script ENTRYPOINT ["/rails/bin/docker-entrypoint"] # Start the server by default, this can be overwritten at runtime EXPOSE 5000 CMD ["./bin/rails", "server"]
services: db: image: postgres:14.2-alpine container_name: demo-postgres-14.2 volumes: - postgres_data:/var/lib/postgresql/data command: "postgres -c 'max_connections=500'" environment: POSTGRES_DB: ${POSTGRES_DB} POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} ports: - "5432:5432" demo-web: build: context: . args: - RAILS_ENV=${RAILS_ENV} command: "./bin/rails server -b 0.0.0.0" environment: - RAILS_ENV=${RAILS_ENV} - POSTGRES_HOST=${POSTGRES_HOST} - POSTGRES_DB=${POSTGRES_DB} - POSTGRES_USER=${POSTGRES_USER} - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - RAILS_MASTER_KEY=${RAILS_MASTER_KEY} volumes: - .:/rails - app-storage:/rails/storage depends_on: - db ports: - "3000:3000" volumes: postgres_data: app-storage:
#!/bin/bash set -e # If running the rails server then create or migrate existing database if [ "${*}" == "./bin/rails server" ]; then ./bin/rails db:create ./bin/rails db:prepare fi exec "${@}"