Ejecuta Rails en el entorno de Cloud Run

Aprende a implementar una aplicación de Rails de muestra en Cloud Run y a integrar bases de datos administradas, almacenamiento de objetos, secretos encriptados y canalizaciones de compilación con procesamiento sin servidores.

La implementación de aplicaciones de Rails implica integrar varios servicios para formar un proyecto cohesivo. En este instructivo, se supone que estás familiarizado con el desarrollo web de Rails.

Para este instructivo debes tener Ruby 3.0 o posterior (también se admite Ruby 2.7. Consulta la sección Comprende el código) y Rails 6 o posterior.

Diagrama que muestra la arquitectura de la implementación.
El sitio de Rails se entrega desde Cloud Run, que usa varios servicios de copia de seguridad para almacenar diferentes tipos de datos (información de bases de datos relacionales, elementos multimedia, imágenes de contenedores y secretos de configuración). Cloud Build actualiza los servicios de backend como parte de una tarea de compilación y migración.

Objetivos

  • Crear y conectar una base de datos de Cloud SQL a Active Record
  • Crea y usa Secret Manager para almacenar y acceder de forma segura a una clave maestra de Rails
  • Aloja archivos multimedia subidos por el usuario en Cloud Storage desde Active Storage
  • Usa Cloud Build para automatizar las migraciones de compilación y base de datos
  • Implementa una app de Rails en Cloud Run

Costos

Antes de comenzar

  1. Accede a tu cuenta de Google Cloud. Si eres nuevo en Google Cloud, crea una cuenta para evaluar el rendimiento de nuestros productos en situaciones reales. Los clientes nuevos también obtienen $300 en créditos gratuitos para ejecutar, probar y, además, implementar cargas de trabajo.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  3. Asegúrate de que la facturación esté habilitada para tu proyecto de Google Cloud.

  4. Habilita las API de Cloud Run, Cloud SQL, Cloud Build, Secret Manager, and Compute Engine .

    Habilita las API

  5. Instala Google Cloud CLI.
  6. Para inicializar la CLI de gcloud, ejecuta el siguiente comando:

    gcloud init
  7. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  8. Asegúrate de que la facturación esté habilitada para tu proyecto de Google Cloud.

  9. Habilita las API de Cloud Run, Cloud SQL, Cloud Build, Secret Manager, and Compute Engine .

    Habilita las API

  10. Instala Google Cloud CLI.
  11. Para inicializar la CLI de gcloud, ejecuta el siguiente comando:

    gcloud init
  12. Asegúrate de otorgarle los permisos suficientes a la cuenta que se usa en este instructivo.

Prepara tu entorno

Configura el proyecto predeterminado

Establece la configuración predeterminada del proyecto para la CLI de gcloud mediante la ejecución del siguiente comando:

gcloud config set project PROJECT_ID

Reemplaza PROJECT_ID por el ID del proyecto de Google Cloud recién creado.

Clona la app de Rails

El código de la app de muestra de Rails está en el repositorio GoogleCloudPlatform/ruby-docs-samples en GitHub.

  1. Clone el repositorio:

    git clone https://github.com/GoogleCloudPlatform/ruby-docs-samples.git
    
  2. Ve al directorio que contiene el código de muestra y ejecuta los siguientes comandos para asegurarte de que la aplicación esté configurada correctamente con las gemas y las dependencias requeridas:

    Linux/macOS

    cd ruby-docs-samples/run/rails
    bundle install
    

    Windows

    cd ruby-docs-samples\run\rails
    bundle install
    

Prepara los servicios de copia de seguridad

En este instructivo, se usan varios servicios de Google Cloud para proporcionar la base de datos, el almacenamiento multimedia y el almacenamiento secreto que admiten el proyecto de Rails implementado. Estos servicios se implementan en una región específica. Para la eficiencia entre servicios, es mejor que todos los servicios se implementen en la misma región. Para obtener más información sobre la región más cercana a tu ubicación, consulta Productos disponibles por región.

Configura una instancia de Cloud SQL para PostgreSQL

Rails admite varias bases de datos relacionales, incluidas varias que ofrece Cloud SQL. En este instructivo, se usa PostgreSQL, una base de datos de código abierto que las apps de Rails usan con frecuencia.

En las siguientes secciones, se describe la creación de una instancia de PostgreSQL, una base de datos y un usuario de base de datos para tu app de Rails.

Crear una instancia de PostgreSQL

Console

  1. En Google Cloud Console, ve a la página Instancias de Cloud SQL.

    Ir a la página Instancias de Cloud SQL

  2. Haga clic en Crear instancia.

  3. Haz clic en Elegir PostgreSQL.

  4. En el campo ID de instancia, ingresa un nombre para la instancia (INSTANCE_NAME).

  5. En el campo Contraseña, ingresa una contraseña para el usuario de postgres.

  6. Usa los valores predeterminados en los otros campos.

  7. Haga clic en Crear instancia.

gcloud

  • Crea la instancia de PostgreSQL:

    gcloud sql instances create INSTANCE_NAME \
        --database-version POSTGRES_12 \
        --tier db-f1-micro \
        --region REGION
    

    Reemplaza lo siguiente:

    La instancia demora unos minutos en crearse y estar lista para su uso.

Crea una base de datos

Console

  1. En Google Cloud Console, ve a la página Instancias de Cloud SQL.

    Ir a la página Instancias de Cloud SQL

  2. Selecciona la instancia INSTANCE_NAME.

  3. Ve a la pestaña Bases de datos.

  4. Haga clic en Create database.

  5. En el diálogo Nombre de la base de datos, ingresa DATABASE_NAME.

  6. Haga clic en Crear.

gcloud

  • Crea la base de datos dentro de la instancia recién creada:

    gcloud sql databases create DATABASE_NAME \
        --instance INSTANCE_NAME
    

    Reemplaza DATABASE_NAME por un nombre para la base de datos dentro de la instancia.

Crea un usuario

Genera una contraseña aleatoria para el usuario de la base de datos y escríbela en un archivo llamado dbpassword:

cat /dev/urandom | LC_ALL=C tr -dc '[:alpha:]'| fold -w 50 | head -n1 > dbpassword

Console

  1. En Google Cloud Console, ve a la página Instancias de Cloud SQL.

    Ir a la página Instancias de Cloud SQL

  2. Selecciona la instancia INSTANCE_NAME.

  3. Ve a la pestaña Usuarios.

  4. Haz clic en Agregar cuenta de usuario.

  5. En el cuadro de diálogo Autenticación integrada, realiza lo siguiente:

    1. Ingresa el nombre de usuario DATABASE_USERNAME.
    2. Ingresa el contenido del archivo dbpassword como contraseña PASSWORD.
  6. Haga clic en Agregar.

gcloud

  • Crea el usuario en la instancia creada recientemente y establece su contraseña para que sea el contenido de dbpassword:

    gcloud sql users create DATABASE_USERNAME \
       --instance=INSTANCE_NAME --password=$(cat dbpassword)
    

    Reemplaza DATABASE_USERNAME por un nombre para el usuario dentro de la instancia.

Configure un bucket de Cloud Storage

Puedes alojar elementos estáticos de Rails y medios subidos por el usuario en el almacenamiento de objetos con alta disponibilidad a través de Cloud Storage.

Console

  1. En la consola de Google Cloud, ve a la página Buckets de Cloud Storage.

    Ir a la página Buckets

  2. Haga clic en Crear bucket.
  3. En la página Crear un bucket, ingresa la información de tu bucket. Para ir al paso siguiente, haz clic en Continuar.
  4. Haga clic en Crear.

gcloud

La herramienta de línea de comandos de gsutil se instaló como parte de la instalación de la CLI de gcloud.

  • Crear un bucket de Cloud Storage Para crear un nombre de depósito único de Cloud Storage, usa el PROJECT_ID y un sufijo de tu elección, MEDIA_BUCKET_SUFFIX. En Cloud Storage, los nombres de los depósitos deben ser únicos a nivel global.

    gsutil mb -l REGION gs://PROJECT_ID-MEDIA_BUCKET_SUFFIX
    

Después de crear un bucket, cambia los permisos de los objetos de imagen para que todos los usuarios puedan leerlos con el fin de hacer públicas las imágenes.

Console

  1. En Google Cloud Console, ve a la página Depósitos de Cloud Storage.

    Ir a Buckets

  2. En la lista de bucket s, haz clic en el nombre del bucket que deseas hacer público.

  3. Selecciona la pestaña Permisos cerca de la parte superior de la página.

  4. Haz clic en el botón Agregar miembros.

    Aparece el cuadro de diálogo Agregar miembros.

  5. En el campo Miembros nuevos, ingresa allUsers.

  6. En el menú desplegable Seleccionar una función, selecciona el submenú Cloud Storage y haz clic en la opción Visualizador de objetos de almacenamiento.

  7. Haz clic en Guardar.

Una vez que se comparte de forma pública, aparece un ícono de vínculo para cada objeto en la columna de acceso público. Puedes hacer clic en este ícono para obtener la URL del objeto.

Para obtener información detallada sobre los errores en las operaciones de Ruby con errores en Google Cloud Console, consulta Solución de problemas.

gcloud

  • Usa el comando gsutil iam ch para hacer públicos todos los objetos. Usa el valor de MEDIA_BUCKET_SUFFIX que usaste cuando creaste el depósito.

    gsutil iam ch allUsers:objectViewer gs://PROJECT_ID-MEDIA_BUCKET_SUFFIX
    

Almacena valores secretos en el Secret Manager

Ahora que los servicios de copia de seguridad están configurados, Rails necesita información segura, como contraseñas, para acceder a estos servicios. En lugar de colocar estos valores directamente en el código fuente de Rails, en este instructivo se usan credenciales de Rails y Secret Manager para almacenar esta información de forma segura.

Crea un archivo de credenciales encriptado y almacena la clave como secreto del administrador de secretos

Rails almacena los secretos en un archivo encriptado llamado 'config/credentials.yml.enc'. El archivo se puede desencriptar con el config/master.key local o la variable de entorno ENV[“RAILS_MASTER_KEY”]. En el archivo de credenciales, puedes almacenar la contraseña de la base de datos de la instancia de Cloud SQL y otras claves de acceso para las API externas.

Puedes almacenar esta clave de forma segura en Secret Manager. Luego, puedes otorgar a la clave y Cloud Run acceso a la clave si otorgas acceso a sus respectivas cuentas de servicio. Las cuentas de servicio se identifican mediante una dirección de correo electrónico que contiene el número de proyecto.

  1. Genera el archivo config/credentials.yml.enc con el siguiente comando:

    bin/rails credentials:edit
    

    El comando creará un config/master.key si no se define una clave maestra y creará un archivo config/credentials.yml.enc si el archivo no existe. Se abrirá un archivo temporal en tu $EDITOR predeterminado con el contenido desencriptado para agregar los secretos.

  2. Copia y pega la contraseña de la base de datos de la instancia de PostgreSQL recién creada del archivo dbpassword al archivo de credenciales:

    secret_key_base: GENERATED_VALUE
    gcp:
      db_password: PASSWORD
    

    Se puede acceder a los secretos con Rails.application.credentials. Por ejemplo, Rails.application.credentials.secret_key_base debe mostrar la base de clave secreta de la aplicación y Rails.application.credentials.gcp[:db_passsword] debe mostrar la contraseña de la base de datos.

  3. config/credentials/yml.enc se almacena encriptado, pero config/master.key se puede almacenar en Secret Manager.

    Console

    1. En Google Cloud Console, ve a la página Secret Manager.

      Ir a la página Secret Manager

    2. Haz clic en Crear Secreto.

    3. En el campo Nombre, ingresa un nombre para el secreto RAILS_SECRET_NAME.

    4. En el cuadro de diálogo Valor del secreto, pega el valor mater.key en el cuadro.

    5. Haz clic en Crear secreto.

    6. En la página de detalles de Secret de tu secreto, toma nota del número del proyecto:

      projects/PROJECTNUM/secrets/RAILS_SECRET_NAME

    7. En la pestaña Permisos, haz clic en Agregar miembro.

    8. En el campo Miembros nuevos, ingresa PROJECTNUM-compute@developer.gserviceaccount.com y, luego, presiona Enter.

    9. En el campo Miembros nuevos, ingresa PROJECTNUM@cloudbuild.gserviceaccount.com y, luego, presiona Enter.

    10. En el menú desplegable Función, selecciona Descriptor de acceso a secretos de Secret Manager.

    11. Haz clic en Guardar.

    gcloud

    1. Crea un secreto nuevo con el valor de config/master.key:

      gcloud secrets create RAILS_SECRET_NAME --data-file config/master.key
      

      Reemplaza RAILS_SECRET_NAME por un nombre para el secreto nuevo.

    2. Para confirmar la creación del secreto, revísalo:

      gcloud secrets describe RAILS_SECRET_NAME
      
      gcloud secrets versions access latest --secret RAILS_SECRET_NAME
      
    3. Obtén el valor del número de proyecto:

      gcloud projects describe PROJECT_ID --format='value(projectNumber)'
      
    4. Otorga acceso al secreto a la cuenta de servicio de Cloud Run:

      gcloud secrets add-iam-policy-binding RAILS_SECRET_NAME \
          --member serviceAccount:PROJECTNUM-compute@developer.gserviceaccount.com \
          --role roles/secretmanager.secretAccessor
      

      Reemplaza PROJECTNUM por el valor del número de proyecto anterior.

    5. Otorga acceso al secreto a la cuenta de servicio de Cloud Build:

      gcloud secrets add-iam-policy-binding RAILS_SECRET_NAME \
          --member serviceAccount:PROJECTNUM@cloudbuild.gserviceaccount.com \
          --role roles/secretmanager.secretAccessor
      

      En el resultado, confirma que bindings enumere las dos cuentas de servicio como miembros.

Conecta la app de Rails a la base de datos de producción y al almacenamiento

En este instructivo, se usa una instancia de PostgreSQL como la base de datos de producción y Cloud Storage como el backend de almacenamiento. Para que Rails se conecte a la base de datos y al depósito de almacenamiento recién creados, debes especificar toda la información necesaria a fin de acceder a ellos en el archivo .env. El archivo .env contiene la configuración de las variables de entorno de la aplicación. La aplicación leerá este archivo con la gema de puntoenv. Debido a que los secretos se almacenan en credentials.yml.enc y Secret Manager, no es necesario que .env se encripte porque no contiene credenciales sensibles.

  1. Para configurar la app de Rails para que se conecte con la base de datos y el bucket de almacenamiento, abre el archivo .env.
  2. Modifica la configuración del archivo .env de la siguiente manera. Usa el valor de MEDIA_BUCKET_SUFFIX que usaste cuando creaste el depósito.

    PRODUCTION_DB_NAME: DATABASE_NAME
    PRODUCTION_DB_USERNAME: DATABASE_USERNAME
    CLOUD_SQL_CONNECTION_NAME: PROJECT_ID:REGION:INSTANCE_NAME
    GOOGLE_PROJECT_ID: PROJECT_ID
    STORAGE_BUCKET_NAME: PROJECT_ID-MEDIA_BUCKET_SUFFIX
    

    La app de Rails ahora está configurada para usar Cloud SQL y Cloud Storage cuando se implementa en Cloud Run.

Otorga a Cloud Build acceso a Cloud SQL

A fin de que Cloud Build aplique las migraciones de bases de datos, debes otorgar permisos para que Cloud Build acceda a Cloud SQL.

Console

  1. En Google Cloud Console, ve a la página Administración de identidades y accesos.

    Ir a la página Administración de identidades y accesos

  2. Para editar la entrada del miembro de PROJECTNUM@cloudbuild.gserviceaccount.com, haz clic en Editar miembro.

  3. Haz clic en Agregar otra función.

  4. En el diálogo Selecciona una función, elija Cliente de Cloud SQL.

  5. Haga clic en Guardar.

gcloud

  • Otorga permiso para que Cloud Build acceda a Cloud SQL:

    gcloud projects add-iam-policy-binding PROJECT_ID \
        --member serviceAccount:PROJECTNUM@cloudbuild.gserviceaccount.com \
        --role roles/cloudsql.client
    

Implementa la app en Cloud Run

Con los servicios de copia de seguridad configurados, ahora puedes implementar la app como un servicio de Cloud Run.

  1. Con el cloudbuild.yaml proporcionado, usa Cloud Build para compilar la imagen, ejecutar las migraciones de la base de datos y propagar los elementos estáticos:

    gcloud builds submit --config cloudbuild.yaml \
        --substitutions _SERVICE_NAME=SERVICE_NAME,_INSTANCE_NAME=INSTANCE_NAME,_REGION=REGION,_SECRET_NAME=RAILS_SECRET_NAME
    

    Reemplaza SERVICE_NAME por el nombre de tu servicio. Esta primera compilación tarda unos minutos en completarse. Si se agotó el tiempo de espera de la compilación, aumenta el tiempo de espera mediante la inserción de --timeout=2000s en el comando de compilación anterior.

  2. Cuando la compilación sea exitosa, implementa el servicio de Cloud Run por primera vez y configura la región de servicio, la imagen base y la instancia conectada de Cloud SQL:

    gcloud run deploy SERVICE_NAME \
         --platform managed \
         --region REGION \
         --image gcr.io/PROJECT_ID/SERVICE_NAME \
         --add-cloudsql-instances PROJECT_ID:REGION:INSTANCE_NAME \
         --allow-unauthenticated
    

    Debería ver un resultado que muestre que la implementación se realizó de forma correcta, con una URL de servicio:

    Service [SERVICE_NAME] revision [SERVICE_NAME-00001-tug] has been deployed
     and is serving 100 percent of traffic at https://SERVICE_NAME-HASH-uc.a.run.app

  3. Para ver el servicio implementado, ve a la URL del servicio.

    Captura de pantalla de la página de destino de la aplicación de álbum del gato.
    Si la URL del servicio muestra Álbum de fotos de gatos, significa que estás en la página principal de la app.

  4. Intenta subir una foto nueva. Si la foto se carga correctamente, la aplicación de Rails se implementó correctamente.

    Captura de pantalla de la página de destino de la aplicación del álbum del gato con una foto.

Actualizar la aplicación

Si bien los pasos iniciales de implementación y evaluación fueron complejos, realizar actualizaciones es un proceso más simple:

  1. Ejecuta la compilación de Cloud Build y la secuencia de comandos de migración:

    gcloud builds submit --config cloudbuild.yaml \
         --substitutions _SERVICE_NAME=SERVICE_NAME,_INSTANCE_NAME=INSTANCE_NAME,_REGION=REGION,_SECRET_NAME=RAILS_SECRET_NAME
    
  2. Implementa el servicio y especifica solo la región y la imagen:

    gcloud run deploy SERVICE_NAME \
         --platform managed \
         --region REGION \
         --image gcr.io/PROJECT_ID/SERVICE_NAME
    

Comprende el código

La app de muestra de Rails se creó con los comandos estándar de Rails. Los siguientes comandos crean la app cat_album y usan el comando scaffold para generar un modelo, un controlador y vistas para el recurso de foto:

rails new cat_album
rails generate scaffold Photo caption:text

Conexión a base de datos

El archivo config/database.yml contiene la configuración necesaria para acceder a tus bases de datos en diferentes entornos (desarrollo, prueba y producción). Por ejemplo, la base de datos de producción está configurada para ejecutarse en Cloud SQL para PostgreSQL. El nombre y el nombre de usuario de la base de datos se establecen a través de variables de entorno en el archivo .env, mientras que la contraseña de la base de datos se almacena dentro del archivo config/credentials.yml.enc, que requiere la desencriptación RAILS_MASTER_KEY.

Cuando la app se ejecuta en Cloud Run (completamente administrado), se conecta a la instancia de PostgreSQL mediante un socket proporcionado por el entorno de Cloud Run. Cuando la aplicación se ejecuta en tu máquina local, se conecta a la instancia de PostgreSQL mediante el proxy de autenticación de Cloud SQL.

production:
  <<: *default
  database: <%= ENV["PRODUCTION_DB_NAME"] %>
  username: <%= ENV["PRODUCTION_DB_USERNAME"] %>
  password: <%= Rails.application.credentials.gcp[:db_password] %>
  host: "<%= ENV.fetch("DB_SOCKET_DIR") { '/cloudsql' } %>/<%= ENV["CLOUD_SQL_CONNECTION_NAME"] %>"

Contenido multimedia subido por el usuario almacenado en la nube

Rails usa Active Storage para subir archivos a proveedores de almacenamiento en la nube. Los archivos config/storage.yml y config/environments/production.rb especifican Cloud Storage como el proveedor de servicios en el entorno de producción.

google:
  service: GCS
  project: <%= ENV["GOOGLE_PROJECT_ID"] %>
  bucket: <%= ENV["STORAGE_BUCKET_NAME"] %>
# Store uploaded files on the local file system (see config/storage.yml for options).
config.active_storage.service = :google

Automatización con Cloud Build

El archivo cloudbuild.yaml realiza no solo los pasos típicos de compilación de imágenes (crear la imagen del contenedor y enviarla a Container Registry), sino también las migraciones de la base de datos de Rails. Esto requiere acceso a la base de datos, que se realiza mediante el app-engine-exec-wrapper, un auxiliar para el proxy de autenticación de Cloud SQL.

steps:
  - id: "build image"
    name: "gcr.io/cloud-builders/docker"
    entrypoint: 'bash'
    args: ["-c", "docker build --build-arg MASTER_KEY=$$RAILS_KEY -t gcr.io/${PROJECT_ID}/${_SERVICE_NAME} . "]
    secretEnv: ["RAILS_KEY"]

  - id: "push image"
    name: "gcr.io/cloud-builders/docker"
    args: ["push", "gcr.io/${PROJECT_ID}/${_SERVICE_NAME}"]

  - id: "apply migrations"
    name: "gcr.io/google-appengine/exec-wrapper"
    entrypoint: "bash"
    args:
      [
        "-c",
        "/buildstep/execute.sh -i gcr.io/${PROJECT_ID}/${_SERVICE_NAME} -s ${PROJECT_ID}:${_REGION}:${_INSTANCE_NAME} -e RAILS_MASTER_KEY=$$RAILS_KEY -- bundle exec rails db:migrate"
      ]
    secretEnv: ["RAILS_KEY"]

substitutions:
  _REGION: us-central1
  _SERVICE_NAME: rails-cat-album
  _INSTANCE_NAME: cat-album
  _SECRET_NAME: rails-master-key

availableSecrets:
  secretManager:
  - versionName: projects/${PROJECT_ID}/secrets/${_SECRET_NAME}/versions/latest
    env: RAILS_KEY

images:
  - "gcr.io/${PROJECT_ID}/${_SERVICE_NAME}"

En esta configuración, se usan variables de sustitución. Si se cambian directamente los valores del archivo, se puede descartar la marca --substitutions en el momento de la migración.

En esta configuración, solo se aplican las migraciones existentes en el directorio db/migrate. Para crear archivos de migración, consulta Migraciones de registros activas.

Para compilar la imagen y aplicar migraciones, la configuración de Cloud Build necesita acceder al secreto RAILS_MASTER_KEY desde Secret Manager. El campo availableSecrets establece la versión del secreto y las variables de entorno que se usarán para el secreto. El secreto de clave maestra se pasa como argumento en el paso de compilación de la imagen y, luego, se establece como RAILS_MASTER_KEY en el Dockerfile cuando se compila la imagen.

ARG MASTER_KEY
ENV RAILS_MASTER_KEY=${MASTER_KEY}

A fin de extender la configuración de Cloud Build para incluir la implementación en la configuración única sin tener que ejecutar dos comandos, consulta Implementación continua desde Git con Cloud Build. Esto requiere cambios de IAM, como se describe.

Asistencia para Ruby 2.7

Si bien en este instructivo se usa Ruby 3.0, también es compatible con Ruby 2.7. Para usar Ruby 2.7, cambia la imagen base de Ruby en Dockerfile a 2.7

# Pinning the OS to buster because the nodejs install script is buster-specific.
# Be sure to update the nodejs install command if the base image OS is updated.
FROM ruby:3.0-buster

Limpieza

  1. En la consola de Google Cloud, ve a la página Administrar recursos.

    Ir a Administrar recursos

  2. En la lista de proyectos, elige el proyecto que quieres borrar y haz clic en Borrar.
  3. En el diálogo, escribe el ID del proyecto y, luego, haz clic en Cerrar para borrar el proyecto.