Deploy and run Ruby On Rails apps on Google Cloud Run


Why Google Cloud Run?

  • Write code your way using your favorite languages (Go, Python, Java, Ruby, Node.js, and more)

  • Abstract away all infrastructure management for a simple developer experience

  • Built upon an open standard Knative, enabling the portability of your applications

In terms of “usual” Ruby On Rails application with background jobs for example (let’s say Sidekiq), you’ll need to make some additional code changes, cause you can’t run Sidekiq workers on Cloud Run

Google Cloud Run website:

https://cloud.google.com/run

Some additional links:

https://cloud.google.com/build

https://cloud.google.com/tasks

https://cloud.google.com/scheduler

Great CloudTasker gem for background jobs (you can’t run Sidekiq with Google Cloud Run):

https://github.com/keypup-io/cloudtasker

Good article about Rails Active Job + Google Cloud Tasks:

https://www.keypup.io/blog/google-cloud-tasks-with-rails-active-job

Setup Google Cloud SQL

What is Google Cloud SQL?

https://cloud.google.com/sql

Fully managed relational database service for MySQL, PostgreSQL, and SQL Server.

  • Reduce maintenance cost with fully managed relational databases in the cloud

  • Ensure business continuity with reliable and secure services backed by 24/7 SRE team

  • Automate database provisioning, storage capacity management, and other time-consuming tasks

  • Database observability made easy for developers with Cloud SQL Insights

  • Easy integration with existing apps and Google Cloud services like GKE and BigQuery

Go to the Cloud SQL section in your GCP console.

Create database - mysql/postgresql:

~ ❯❯❯ gcloud beta sql instances create mydbinstance \
    --database-version POSTGRES_9_6 \
    --cpu=1 --memory=3840MiB --region=northamerica-northeast1-a

Create user:

~ ❯❯❯ gcloud sql users set-password postgres \
    --instance=mydbinstance \
    --password=my-password

Google Cloud Build

What is Google Cloud Build?

  • Build software quickly across all programming languages, including Java, Go, Node.js, and more

  • Get complete control over defining custom workflows for building, testing, and deploying

  • Deploy across multiple environments such as VMs, serverless, Kubernetes, or Firebase

  • Perform deep security scans as part of your CI/CD pipeline

  • Package source into containers or non-container artifacts such as Maven, Gradle, Go, or Bazel

Project page:

https://cloud.google.com/build

Set PROJECT_ID

Let’s setup variable with your GCP project ID.

We’ll use it later in our configuration setup.

~ ❯❯❯ PROJECT_ID=project_id
~ ❯❯❯ gcloud auth login your_account_name
~ ❯❯❯ gcloud config set project $PROJECT_ID

Activating API’s

~ ❯❯❯ gcloud services enable run.googleapis.com
~ ❯❯❯ gcloud services enable cloudkms.googleapis.com
~ ❯❯❯ gcloud services enable sqladmin.googleapis.com
~ ❯❯❯ gcloud services enable run.googleapis.com

Google Cloud Run Region

Let’s run configuration config change and set region.

gcloud config set run/region northamerica-northeast1-a

Service account

As a next step we’ll need to create a service account.

~ ❯❯❯ gcloud iam service-accounts create service-account-name --display-name "Cloud Run Service Account"

# Check your accounts
~ ❯❯❯ gcloud iam service-accounts list

Using service account and set access

Let’s set access rights and create key file.

~ ❯❯❯ SERVICE_ACCOUNT=service-account-name@$PROJECT_ID.iam.gserviceaccount.com

# Set cloudsql access rights
~ ❯❯❯ gcloud projects add-iam-policy-binding $PROJECT_ID --member serviceAccount:$SERVICE_ACCOUNT --role roles/cloudsql.client

# Create key file
~ ❯❯❯ gcloud iam service-accounts keys create ./config/my_app.key --iam-account service-account-name@$PROJECT_ID.iam.gserviceaccount.com

Working with secrets

~ ❯❯❯ gcloud kms keyrings create myapp --location=northamerica-northeast1-a

# Service account credentials encrypt
~ ❯❯❯ gcloud kms keys create myapp_key --location northamerica-northeast1-a --keyring myapp --purpose encryption
~ ❯❯❯ gcloud kms encrypt - location northamerica-northeast1-a --keyring myapp \
  --key myapp_key --plaintext-file ./config/myapp_key.key \
  --ciphertext-file ./config/myapp_key.key.enc

# Rails master key
~ ❯❯❯ gcloud kms keys create rails_master_key --location northamerica-northeast1-a --keyring myapp --purpose encryption
~ ❯❯❯ gcloud kms encrypt --location northamerica-northeast1-a --keyring myapp \
  --key rails_master_key --plaintext-file ./config/master.key \
  --ciphertext-file ./config/master.key.enc

Cloud Run deployment config

Before doing what make sure that you have secrets for your DB configuration (DB_PWD variable for example, but it’s optional).

Simple steps:

  • Decrypting master.key file

  • Decrypting service account credentials

  • Building latest image with secrets

timeout: 1200s
steps:
# Decrypt Rails Master key file
- name: gcr.io/cloud-builders/gcloud
  args: ["kms", "decrypt", "--ciphertext-file=./config/master.key.enc", 
         "--plaintext-file=./config/master.key",
         "--location=northamerica-northeast1-a","--keyring=myapp", 
         "--key=rails_master_key"]

# Decrypt app service account credentials
- name: gcr.io/cloud-builders/gcloud
  args: ["kms", "decrypt", "--ciphertext-file=./config/myapp_key.key.enc", 
         "--plaintext-file=./config/myapp_key.key",
         "--location=northamerica-northeast1-a","--keyring=myapp", 
         "--key=myapp_key"]

# Build image with tag 'latest'
- name: 'gcr.io/cloud-builders/docker'
  args: ['build', '--tag', 'gcr.io/PROJECT_URL',
         '--build-arg', 'DB_PWD', '.']
  secretEnv: ['DB_PWD']

# Push new image to Google Container Registry       
- name: 'gcr.io/cloud-builders/docker'
  args: ['push', 'gcr.io/PROJECT_URL']

secrets:
- kmsKeyName: projects/YOUR_PROJECT_ID/locations/northamerica-northeast1-a/keyRings/myapp/cryptoKeys/db_pwd_key
  secretEnv:
    DB_PWD: "XXX"

Let’s take a look to our Dockerfile (depends on your app):

FROM ruby:2.6.2

RUN apt-get update && apt-get install -y build-essential git
RUN apt-get install -y libpq-dev
RUN apt-get install -y nodejs

RUN mkdir -p /app
WORKDIR /app

ENV LANG C.UTF-8

COPY Gemfile Gemfile.lock ./
RUN gem install bundler && bundle install --jobs 20 --retry 5
COPY . $APP_ROOT

ARG BUILD_SHA=unknown
ARG DB_PWD

ENV DATABASE_PASSWORD=${DB_PWD}
ENV BUNDLE_FROZEN=true
ENV BUILD_SHA ${BUILD_SHA}
ENV RAILS_LOG_TO_STDOUT=true
ENV RAILS_ENV production

RUN chmod +x /app/entrypoint.sh
ENTRYPOINT ["/app/entrypoint.sh"]

Entrypoint.sh file example.

#!/bin/bash
cd /app
RAILS_ENV=production bundle exec rake db:create
RAILS_ENV=production bundle exec rake db:migrate
RAILS_ENV=production bundle exec rake db:seed
bundle exec rails s -e production

Submit cloud build run

Our last step - just submit our yaml file and check dashboard for the progress!

~ ❯❯❯ gcloud builds submit --config cloudbuild.yaml

That’s it! Run/deploy/test and stay tuned.