Deploying Battleship to GAE

Last time I finished writing the battleship server. The next step is to deploy it somewhere. I’m close to my first real deadline, Seattle.rb next Tuesday evening, so I’m taking the easy way out and deploying it on Google App Engine. In the future, I plan to write up a blog post for doing a Kubernetes deployment. I also hope to eventually port the logic to both Node.js and Python because I think the language comparison will be interesting.

Prep Work

I already had a GCP account and the GCP SDK installed on my computer. If you are following along at home there are instructions for these steps in the “Before You Begin” box on the Rails 5 on App Engine tutorial. Since the last time I talked about App Engine the Ruby Software Engineering team has released an App Engine Gem. This gives me some handy features for running rake tasks in production and automatically pulls in GCP’s monitoring tools for Ruby. To use it I have to add gem "appengine" to my Gemfile and add require "appengine" to my Rakefile.

As I established in the last post, I’m using Sinatra. I like that it is lightweight, but it means I have to do some configuration work. To run the app on App Engine I need a config.ru file. I took a generic one from the Sinatra docs and put it at the root of my application.

require './server'
run Sinatra::Application

Database

I’m using PostgreSQL with ActiveRecord for the database. In the spirit of impending deadlines, I’m using managed PostgreSQL from CloudSQL. This way I don’t have to worry about firewall rules and networking between my web servers and my database.

I created a CloudSQL instance following the instructions in the Cloud SQL documentation. Then I created a user for my application. I called it rails because I always call my users rails. I only realized that was inappropriate (I’m using Sinatra) once I started writing this post out. I also created a static IP following the instructions under connecting using IP Addresses. I added the following to my database.yml file:

production:
  <<: *default
  database: "battleship_production"
  username: "rails"
  password: [PASSWORD GOES HERE]
  host:   [YOUR DATABASE IP HERE]
  timeout: 5000

Using a static IP isn’t the best solution. There are a lot of security implications. I should be using the CloudSQL Proxy but I wanted something that would work quickly to prove my deployment. I can go back later and change how the application connects to the database.

To run the migrations using the appengine gem I need to add the following to my Rakefile.

require "appengine"
require "appengine/tasks"

Deploying The App

Deploying an application to App Engine for Ruby is simple, you just type gcloud app deploy. The gcloud SDK will analyze your application and make some recommendations on the first run. The defaults are fine. It will use the defaults to write an app.yaml file that looks like this:

entrypoint: bundle exec rackup -p $PORT
env: flex
runtime: ruby

The actual deployment may take a while. For me, deployments were taking around seven minutes. I wish they were faster and I know the team is working on it but that is something to keep in mind when deciding where to run your application.

Once the app is deployed you can run your migrations like this:

bundle exec rake appengine:exec -- bundle exec rake db:migrate

By default gcloud app deploy routes all traffic to the latest version of your application as soon as it is up and running. If you need your migrations to run before that version can handle traffic you can use the --no-promote flag:

gcloud app deploy --no-promote

Once your migrations are run, you can route traffic to the new version using the web UI.

My deployment to App Engine is at https://battleship-176302.appspot.com/ if you want to try it out yourself. Expect some instability over the next few weeks while I sort out details to make it usable for our workshop at Seattle.rb next week.

The code for this post is located here.