How to Deploy a Vaadin Application to Google Cloud App Engine
It was not as straightforward as expected so I want to share my findings.
The example project is available on GitHub: https://github.com/simasch/vaadin-appengine-demo
The Vaadin Application
First, I’ve created a new Vaadin application: https://start.vaadin.com
Then I did a production build:
mvn package -Pproduction
That will generate an executable JAR file in the target directory.
This JAR file I’ve deployed to App Engine using the CLI:
gcloud app deploy
This seemed to work, but when I tried to access the app, I got an error in the logs
java.lang.UnsupportedClassVersionError: io/seventytwo/demo/gae/Application has been compiled by a more recent version of the Java Runtime (class file version 61.0), this version of the Java Runtime only recognizes class file versions up to 55.0
Google App Engine uses Java 11 by default, but I want to use Java 17.
Using Java 17
But how can I configure another Java version? After reading the documentation, I found out that I can add an app.yaml in the src/appengine folder with this content:
I also noticed that I could use a Maven plugin to deploy the application:
<plugin> <groupId>com.google.cloud.tools</groupId> <artifactId>appengine-maven-plugin</artifactId> <version>2.4.2</version> <configuration> <projectId>GCLOUD_CONFIG</projectId> <version>GCLOUD_CONFIG</version> </configuration> </plugin>
So I simply can execute:
mvn package appengine:deploy -Pproduction
After the deployment was finished, I tried to access the application again. This time it failed with this message:
java.lang.NullPointerException: Cannot invoke "java.io.RandomAccessFile.length()" because "this.cache" is null at javax.imageio.stream.FileCacheImageOutputStream.close ( javax/imageio.stream/FileCacheImageOutputStream.java:229 ) at javax.imageio.ImageIO.write ( javax/imageio/ImageIO.java:1598 ) at com.vaadin.flow.server.PwaIcon.setImage ( com/vaadin.flow.server/PwaIcon.java:220 )
The exception comes from the PWA support of Vaadin, so I removed the @PWA annotation and the application run. But looking at the logs, I’ve found issues with memory limits:
Exceeded soft memory limit of 256 MB with 262 MB after servicing 1 requests total. Consider setting a larger instance class in app.yaml.
By default, an instance of type F1 is allocated, which was not enough to start the Vaadin application.
So finally, I configured an F2 instance in the app.yaml
runtime: java17 instance_class: F2
As you may know, Vaadin applications have server state and may also have push enabled that will use WebSockets. We must enable session affinity to ensure that our application runs correctly in multiple instances.
This can be done in the app.yaml as well
network: session_affinity: true
But what does session affinity mean? Let’s check the documentation:
|Optional. Set to
true to configure App Engine to route multiple sequential requests for a given user to the same App Engine instance such as when storing user data locally during a session. Session affinity enables inspecting the value of a cookie to identify multiple requests by the same user and then directs all such requests to the same instance. If the instance is rebooted, unhealthy, overloaded or becomes unavailable when the number of instances has been scaled down, session affinity will be broken and further requests are then routed to a different instance. Note that enabling session affinity can affect your load balancing setup. This parameter is disabled by default.
Deploying to Google App Engine is straight-forwarded using the Maven plugin, but you must analyze the log files and probably configure the Java version, the instance size, and session affinity.