It’s no secret that we at Small Improvements love to use cutting edge technologies for our application. On the client side, there’s no limit, that’s why we’re rapidly transitioning to React. In the backend, we’re pushing the limits too, but we’re currently bound by what the App Engine has to offer. The main grievance for us is that we’re still using Java 7.
There are hints that Google will bring Java 8 to the App Engine, but during our recent Ship-It week, we decided to take matters into our own hands and run Small Improvements on a Java 8 Flexible Runtime, aka Flexible Environment or Managed VM, the name changes frequently ;).
If you never heard of the Flexible Runtime, it’s basically a Docker container that will run your App Engine application. To get started quickly, you can use Google’s Java 8 / Jetty 9.3 Compat Runtime container without touching (or even seeing) any Dockerfile.
If you’re like us and prefer to use the Cloud SDK to deploy over Maven, please read on and I’ll show you how we managed to get our app running.
Caveat: It’ll work, but it’s definitely not quite production ready. We wouldn’t recommend it for your main app, but if you have a non-mission critical service, you could give it a shot.
Bye bye XML! Hello YAML!
XML was quite nifty when it was introduced 20 years ago. But YAML is so much easier on the eyes.
appengine-java-sdk/bin/bin/appcfg.sh stage\ your-exploded-app stage-directory
Have a look at the generated YAML files: Cron, Dispatch, Dos, Index and Queue. They should all be deployable and contain the exact same configuration as their XML counterparts.
app.yaml into production it requires some additional steps …..
App.yaml and its gotchas
Static files … or not?
app.yaml was a bit crude and yours might be too. For us, the static files and their expiration settings were very verbose:
- url: (/resources/.*\.jpg) static_files: __static__\1 upload: __NOT_USED__ require_matching_file: True login: optional secure: always expiration: 5d - url: (/remote_api/.*\.jpg) static_files: __static__\1 upload: __NOT_USED__ require_matching_file: True login: optional secure: optional expiration: 21d - url: (/api/tasks/.*\.jpg) static_files: __static__\1 upload: __NOT_USED__ require_matching_file: True login: admin secure: optional expiration: 21d ...
You’ll notice a lot of duplications. In our case so many, that the deploy failed since there is a hard limit for the number of entries :D. But no worries the handler syntax supports regular expressions.
So for example, you can configure the serving/caching of your and fonts and images in a single handler:
- url: (/.*\.(ttf|eot|svg|woff|gif|jpg|png|ico)) static_files: __static__\1 upload: __NOT_USED__ require_matching_file: True secure: always expiration: 21d
It’s unclear if Google will support serving static files automagically in the Flexible Environment. Currently, they suggest that you upload the files to a cloud storage bucket and serve them from there.
Our hope is that this is only an intermediate step. Who knows, they are not so forthcoming with their roadmap 😉
What we’ve gathered by monitoring the logs of our deployed app: Currently Flexible Environment deployments ignore the
static_files handlers. So whatever you write in the handlers your application will still serve the files.
If you have security constraints for your Servlets/Resources, you’ve expressed them so far in
<security-constraint> <web-resource-collection> <url-pattern>/admin/tasks/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> </auth-constraint> </security-constraint>
This won’t work in a Flexible Runtime. For us, it closed all the responses of the server unexpectedly. You can safely remove the constraints from this file and express them in
Here’s how the example from above looks in the
- url: /api/tasks/.* script: unused login: admin
Selecting the Runtime
The last missing piece is to actually configure your app to run in a Flexible Environment:
vm: true runtime: java runtime_config: jdk: openjdk8 server: jetty9 threadsafe: True resources: cpu: 4
Line 1 is the big switch that will let your app run in the Flexible Environment.
Line 2 will upgrade you to Java 8.
Lines 3-5 are optional, just in case you’d like to try different Java/Jetty combinations.
Lines 7-8 are specifying how powerful your compute engine machine is – and how expensive.
Check out Google’s documentation to learn what other settings you can play with.
Remove the XML configurations
Now that all your YAML files are ready, take off the training wheels and delete the following the XML configurations:
Bonus (almost) get rid of application-web.xml
Whatever you’ve got in
application-web.xml you can configure it in
app.yaml now. Here are the only settings you’ll need to keep in there:
<?xml version="1.0" encoding="utf-8"?> <appengine-web-app xmlns="http://appengine.google.com/ns/1.0"> <vm>true</vm> <threadsafe>true</threadsafe> <sessions-enabled>true</sessions-enabled> </appengine-web-app>
The Cloud SDK brings its own App Engine development server
dev_appserver.py. You can use it to test your upgraded application in a Flexible Environment locally:
# install the dev_appserver.py gcloud components update app-engine-python dev_appserver.py stage-directory/app.yaml
If everything worked as expected, you’ll be able to access the development server.
Deployment (Fingers Crossed)
For our deployment, we choose not to use the Maven plugin from Google’s examples (who would after getting rid of so many XML files 😉 ).
You can elegantly use gcloud from the Cloud SDK to deploy:
cd stage-directory gcloud\ app deploy\ --no-promote\ --version=any-version\ --project=your-project\ app.yaml\ cron.yaml\ dispatch.yaml\ dos.yaml\ index.yaml\ queue.yaml
Congratulations you’ve upgraded your application to Java 8 and a modern Jetty!
So I can use Flexible Runtimes, or what?
We’ve encountered a lot of errors before the deployment worked.
Sometimes the cloud build timed out. Or the generated
app.yaml file broke the gcloud deploy. (Google support helped us patch the Python executable: Big thanks!)
The main problem we have is, that the deployment of our application – composed of two modules – is taking 15~18 minutes in the Flexible Environment. To put this in perspective: The regular re-build and deploy of our application is well below 10 minutes.
Also from the development perspective, we’re not ready to forgo the convenience of firing up a development server in IntelliJ Idea. The development server from the Cloud SDK is cool, but it would need some more tweaking to develop locally without a lot of restarts (read: too many 😉 ).
All in all, it was a fun and interesting project for us. It’s good to see that our application can run on the latest stable Java version.
The Flexible Environment is still in beta and NOT production ready. It’s NOT covered by any SLA.
We decided to let a lesser important microservice run in the Flexible Environment. It doesn’t require many redeploys and has been happily serving for two months. So far it only had the forgivable quirk of logging to standard error instead of the request log.
Nevertheless, don’t be discouraged. If our instructions worked for you, you’ll be ready when Google finally ships the Flexible Runtime … we know we are 🙂