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.
While Google provides a couple of Hello World examples, this won’t help you much when your app won’t start and you can’t figure out why.
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.
Lucky for us, the Flexible Runtimes are configured by YAML files. You can generate them from your exploded App Engine project using appcfg.sh which is included in the Java SDK:
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.To get app.yaml
into production it requires some additional steps …..
App.yaml and its gotchas
Static files … or not?
Our generated 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.
Security Constraints
If you have security constraints for your Servlets/Resources, you’ve expressed them so far in web.xml
:
<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 app.yaml
.Here’s how the example from above looks in the app.yaml
:
- 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.
Cleaning up
Remove the XML configurations
Now that all your YAML files are ready, take off the training wheels and delete the following the XML configurations:
cron.xml
queue.xml
datastore-indexes.xml
dos.xml
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>
Test Run
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 😉 ).
Conclusion
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 🙂