Moving Rails to GCP Cloud Run
TL;DR
PORT
is set as environment variable by default, use it for exposing/listening- Cloud SQL uses a specific socket name, i.e.
/cloudsql/*connection name*
(connection name can be found on the Cloud SQL dashboard usually looks like this*project id*:*region*:*instance name*
), use it when you setup your database configuration - activate Rails logging to stdout (
RAILS_LOG_TO_STDOUT=enabled
) - activate Rails serving static stuff from
/public
(RAILS_SERVE_STATIC_FILES=enabled
)
Intro
A couple of years ago i was working for a company which only wrote software in Ruby on Rails. It didn't matter if another language would have been the better tool to use, homogenous Rails development was the only priority.
I actually enjoyed my time there, the team was great and the project i maintained was quite fun to work on. What i didn't like was Ruby.
When i work for a company though i usually try to write my own software during that time in the same language. Just to become better in the language itself and to try something new; things i like i keep, things i don't like i get rid of. So that is why i have a couple of old projects laying around in Rails which i gradually going to rewrite properly.
Usually i like don't having to much to think of when running software in the clouds. That is why i chose Heroku for all Rails projects. Since Heroku has quite a good setup for running Ruby and Rails. Heroku though has become a non-viable solution for me in the recent weeks and months. So now i am moving all the Rails apps to the Google Cloud and let them run alongside my other stuff. That means setting up Cloud SQL and Cloud Run(for its simplicity) to run Rails apps.
The Setup
I always have stuff running in Docker setups. So i won't go over how to run Rails in a container. I won't either go into much detail about how to setup Cloud SQL itself; just a quick tip: if you want to import your SQL dump, you need to put it into a storage bucket from which Cloud SQL will access it.
Considerations
GCP Cloud Run provides some pre-defined environment variables, which can't be
overriden and should be used when setting up your container image. For
example: PORT
is a pre-defined variable. It is by default set to 8080. If
you create your image think about exposing/listening on the PORT
env var.
You must activate that your Cloud Run service should use Cloud SQL in the
Connections
tab.
Rails itself logs preferred to files. You can change that behaviour by setting
the environment variables RAILS_LOG_TO_STDOUT
to enabled
.
To allow that your "packs" are also served correctly also set
RAILS_SERVER_STATIC_FILES
to enabled
.
Running a container in GCP Cloud Run
If you have your Dockerfile ready it is important that you build for
linux/amd64
– just mentioning that since the new Apple SoCs (M1, ...) came
around a few people have had trouble with the fact they can push there image
to registries but are not able to run them anywhere – a quick PSA if you
will. This can be done locally for example by running.
docker buildx build --platform linux/amd64 -t image-name:tag .
When pushing images to GCP you have several options. You can use the GCP
source repositories and hook them up to a build pipeline which pushes the
image to the registry. You can push your images manually (where i would
recommend to give the image name a little thought; since for example if you
prefix it with eu.gcr.io
you can have it stored on the EU registry only).
Connecting to a Cloud SQL database with Rails
Cloud SQL requires the usage of a specific socket file name. This must be considered when configuring your app for production use.
...
production:
<<: *default
username: <%= ENV["DB_USER"] %>
password: <%= ENV["DB_PASSWORD"] %>
database: <%= ENV["DB_NAME"] %>
host: "<%= ENV.fetch("DB_SOCKET_DIR") { '/cloudsql' } %>/<%= ENV["INSTANCE_NAME"] %>"
This configuration requires the setting of the environment variables:
DB_USER
(the database user)DB_PASSWORD
(the database user password)DB_NAME
(the database name)DB_SOCKET_DIR
(the Cloud SQL socket dir, usually/cloudsql
)INSTANCE_NAME
(the Cloud SQL instance name, which can be found on the Cloud SQL dashboard)
If you are a fan of the DATABASE_URL
variable, i would recommend putting it
into a GCP secret (via secret manager). In that form the configuration would
look like the following. Keep in mind though that the auto-generated
passwords for Cloud SQL user accounts can cause trouble when parsing such a
DSN string.
postgresql://user:password@/db_name?host=/cloudsql/instance_name
Conclusion
As you can see, it takes some configuration but comparatively not much effort to run Rails in GCP Cloud Run.
One last note: Cloud Run allows you to scale down to 0 instance, this will cause a cold start problem. So if your application is crucial enough. Pay a little more and have one container always running.