Deploy Play Framework (Scala) on App Engine

27 Dec 2016

We can deploy Play Framework (Scala) on flexible custom-runtime App Engine. In fact, we can deploy anything that serves HTTP traffic on the port 8080 on flexible custom-runtime App Engine. The only requirements are:

  1. Your app must be in Docker's image.
  2. You must use ENTRYPOINT and/or CMD to start your app.
  3. Your app must serve HTTP traffic on the port 8080.
  4. Your app must either handle health check or disable health check in app.yaml.

The official documentation is here.

Our app.yaml is simple and is shown below:

runtime: custom env: flex health_check: enable_health_check: False

Health check is disabled for the sake of simplicity.

An example playframework project is generated with sbt new playframework/play-scala-seed.g8.

We need to modify application.conf to allow hostname. We can add these following lines to application.conf:

play.filters.hosts { allowed = ["[your-appengine-project-id]"] }

Next is the Dockerfile. We need JDK8, Scala, and SBT. Fortunately, Google provides a JDK8 Docker image here. We can re-use the image. Here's our Dockerfile:

FROM COPY ./target/universal/yourapp-1.0-SNAPSHOT.tgz /yourapp-1.0-SNAPSHOT.tgz COPY ./docker-entrypoint.bash /docker-entrypoint.bash RUN tar -xvf yourapp-1.0-SNAPSHOT.tgz WORKDIR /yourapp-1.0-SNAPSHOT RUN chmod 755 /docker-entrypoint.bash ENTRYPOINT ["/docker-entrypoint.bash"] CMD ["bin/yourapp", "-Dlogger.file=conf/prod.xml", "-Dconfig.file=conf/prod.conf", "-Dplay.crypto.secret=somesecret", "-Dhttp.port=8080"]

There's a docker-entrypoint.bash in here. But we can't use that. Because we want to inject JAVA_OPTS into our Play application. Here's our own docker-entrypoint.bash:

#!/bin/bash wget -O /credentials.json --header="Metadata-Flavor: Google" \ && source /setup-env.bash \ && exec env JAVA_OPTS="$JAVA_OPTS" "[email protected]" 2>&1

Note that we also download credentials beforehand; It'll be used in the application.

/setup-env.bash comes with the JDK8 Docker image here.

For deploying, we need to build our app first with sbt universal:packageZipTarball, so that ./target/universal/yourapp-1.0-SNAPSHOT.tgz is built. Then, we can run gcloud app deploy to deploy.

Logging works fine. We can simply log to STDOUT and STDERR. The log entries will show up in Logs Viewer. I haven't figured out how to format logs into JSON yet. I'll figure it out soon.

I haven't tried writing to Datastore, CloudSQL, or TaskQueue yet. But I'm sure they should work if we include the dependencies correctly.

But I hope this is a useful start for anyone who is looking to using Play Framework (Scala) on App Engine.)