October 29, 2016 · google app engine gcp travis-ci docker

Deploying to Google App Engine from Travis CI

This post details how to automate the deployment of a Docker application to Google App Engine using Travis CI.


In the previous post, we wrote a tiny service called hellogo with a structure like so:

.
├── Dockerfile
├── app.yaml
└── main.go

The app.yaml file instructs Google App Engine to use the Dockerfile to run the application.

Be sure to have your service already running in Google App Engine before continuing!

Grant Travis Access to GAE

First need to create a Service Account, which according to GAE's docs is:

...an account that can be used by applications to access Google services programmatically.

Navigate to the Service Accounts page by typing "Service Accounts" in the search bar:

Click the "Create Service Account" link and fill out the form.

  1. Name the Service account "Travis Deploy"
  2. Select the Role Project -> Editor in the dropdown
  3. Check "Furnish a new private key"

The result should be a page like so:

Click the Create button, and we'll receive a file named a bit like ~/Downloads/hellogo-a142e365006e.json:

{
  "type": "service_account",
  "project_id": "hellogo-147000",
  "private_key_id": "a142e365006e88d8c1dd66effc0d55e84400131f",
  "private_key": "-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----\n",
  "client_email": "travis-deploy@hellogo-147000.iam.gserviceaccount.com",
  "client_id": "102324744274179729731",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://accounts.google.com/o/oauth2/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/travis-deploy%40hellogo-147000.iam.gserviceaccount.com"
}

Note Travis requires the Editor role to access all the APIs needed to deploy our Docker service.

This role is extremely permissive - consider running the service in an isolated project

Ideally we'd be able to select only specific permissions, but currently using Editor is the only way.

Finally, type "App Engine Admin API" in the search bar and click Enable:

This API is required for Travis to make requests on behalf of the Service Account.

Create the .travis.yml

Now that Travis has access to GAE, we're ready to write our .travis.yml:

sudo: required

services:  
  - docker

language: go

deploy:  
  provider: gae
  project: hellogo-147000
  keyfile: gce.json
  verbosity: debug
  on: master

Notice that we've enabled Travis' Docker support by choosing sudo: required and services: - docker - this is required for Travis to build the container and upload it to Google App Engine's Container Registry.

The next line, language: go is required as you must choose at least one language, even if it's not being used.

Finally, we have a deploy target which contains the following lines:

Be sure to use the Project ID, hellogo-147000 in this case, which will likely have some integer appended to it to ensure uniqueness. Type "projects" into the search bar to pull up the IAM & Admin page to locate the Project ID:

Encrypt our deploy key

Start by moving the deploy key to our project and make sure that it won't be committed:

$ mv ~/Downloads/hellogo-a142e365006e.json gce.json
$ echo "gce.json" >> .gitignore

Now navigate to travis-ci.org/profile and enable builds for the project by clicking the switch next to the repo:

Install the Travis cli and login:

$ ruby -v
ruby 2.0.0p648 (2015-12-16 revision 53162) [universal.x86_64-darwin15]

$ sudo gem install travis -v 1.8.2 --no-rdoc --no-ri
$ travis version
1.8.2

$ travis login

Encrypt gce.json by running travis encrypt-file:

$ travis encrypt-file gce.json --add
Detected repository as breerly/hellogo, is this correct? |yes|  
encrypting gce.json for breerly/hellogo  
storing result as gce.json.enc  
storing secure env variables for decryption  

This creates a new file called gce.json.enc and modifies our .travis.yml to add the following section:

before_install:  
  - openssl aes-256-cbc -K $encrypted_7748a1005700_key -iv $encrypted_7748a1005700_iv
    -in gce.json.enc -out gce.json -d

As you might have guessed, the call to openssl is responsible for decrypting our gce.json.enc into gce.json on Travis' end.

Commit the gce.json.enc and the changes to .travis.yml to your repo, leaving the following committed files:

.
├── .gitignore
├── .travis.yml
├── Dockerfile
├── gce.json.enc
└── main.go

Note that gce.json should not be committed!

Push and test

To test our application, let's change the message of our program in main.go:

fmt.Fprintln(w, "Hello Go! Brought to you by Travis CI.")  

Now commit and push; in ~5 minutes the build should have passed:

Check out the production site, and viola:

Cheers.