Deploy a Spring Boot API to GCP App Engine

Scott McCartney
10 min readJun 19, 2020

--

You’ve been learning and developing with Spring Boot and Java, and feel ready to share what you’ve created with the world. You want to move beyond http://localhost:8080 and get your API out on the cloud for others to use. Learning infrastructure and cloud computing seems like a daunting task, but luckily in this day and age cloud providers like Google, Amazon, and Microsoft have made it as easier than ever to get your application out in the cloud.

The goal of this article will help get a simple version of a Spring Boot API out to Google’s App Engine so it can be accessed with a public URL.

Prerequisites:

If you already have a Spring Boot API and a GCP project, feel free to skip to the “Add Configuration Files” section of this tutorial.

What is GCP App Engine?

Google Cloud Platform (GCP) is a cloud computing platform that offers a variety of different solutions for things like databases, security, and computing services without the need for users to setup their own data centers or servers. The two other largest cloud providers are AWS and Azure, but many other cloud solutions exist.

For computing services, Google has several options capable of running your applications. The Compute Engine is Google’s Infrastructure-as-a-Service (IaaS) product, giving you more control (but also more responsibility) over your application’s runtime environment. There is Google’s Kubernetes Engine (GKE) which allows you to deploy containers into clusters capable of scaling rapidly. Cloud Functions are GCP’s “serverless” solution that are capable of running code on demand, saving costs if the computation is simple and may be idle often. Other “-as-a-Service” solutions exist for computing and offer varying levels of configuration for the user.

IaaS vs PaaS vs SaaS (from www.sherweb.com)

App Engine is GCP’s Platform-as-a-Service (PaaS). This means that Google will handle the underlying infrastructure (unlike Compute Engine). Instead, you can specify the runtime environment, deploy the application, and it just runs. Configuration is minimal, and you still get benefits from GCP like security, virtualization, scaling, and high reliability. Each cloud provider likely has their own PaaS solution similar to App Engine.

Create a Spring Boot App

If you do not already have a Spring Boot application made, Spring provides an easy UI to help get you started here. The UI requires some metadata for your project and then allows you to add dependencies to help you implement features like database management, security, or building REST-ful APIs.

IMPORTANT: Much of the metadata is arbitrary and up to you. For this tutorial though, the following properties will be necessary to use App Engine’s Standard environment:

  • Packaging: Jar
  • Java: 11

For dependencies, we’ll include the “Spring Web” dependency to build a REST API and “Spring Boot Actuator” to add a /actuator/health endpoint to check the app’s health status. When you’re done, your configuration should be similar to below.

Example Spring Boot project configurations for this demo
Spring Boot Project Configuration

Click “Generate” to download a .zip file of your Spring Boot project. Extract the folder and import it into your IDE of choice(I’ll be using STS).

Optional: We can rename our application.properties file in src/main/resources to application.yml . Then, paste the following configuration. When we deploy to App Engine later we will specify the production profile so that we have configurable environments in the cloud different than the one on our local computer, which is common.

---
spring:
profiles: default
message: "Hello, from local!"
---
spring:
profiles: production
message: "Hello, from App Engine!"

Next, let’s add an endpoint to our Spring Boot API. As mentioned before, the “Spring Boot Actuator” dependency gives us a health endpoint, but let’s also add our own. To do this, just create a new Java Class. In my case, I’ll name it HelloController.

Creating Java Class for a REST Controller

I specified this class in the “rest” package to help with organization but this isn’t necessary. Once created, you can copy the following code:

import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@Value("${message}")
String message;
@GetMapping(value = "demo")
public ResponseEntity<String> displayHelloMessage() {
return ResponseEntity.ok(message);
}
}

Note: The @Value annotation will use whatever value is in the application.yml file under “message” and store it in the String message; variable. When running the application, if a profile is not defined the “default” profile will be used and “Hello, from local!” will be stored in this variable. If you did not set up the application.yml in the optional section, remove the @Value("${message}") annotation and store whatever String you like in String message;.

The @RestController annotation at the class-level let’s Spring Boot know that this class will contain mappings for endpoints. The @GetMapping annotation actually declares an endpoint that we can hit. In this case, we’ll indicate that the endpoint is a GET request to /demo and will return a String response. A ResponseEntity is returned so that the HTTP Status Code can be specified. For this endpoint, we’ll return a 200 Status Cd of “OK”.

Once this class is added, start the application in order to test this new endpoint. You can either start it in your IDE or by running mvn spring-boot:run in the command line. Once running, navigate to localhost:8080/demo in your browser and confirm you get the following response.

The Response from the “/demo” Endpoint in our API

There you have it! You have an application ready and waiting to be packaged and deployed to GCP.

Create a GCP Project

With the application ready, we’ll need to create or select a GCP project that we’ll use to deploy to. This can be done through the CLI using gcloud projects create command, but I’ll use the online UI found here.

You can create a new project by first selecting the project dropdown in the top left and selecting “New Project”.

Select “New Project” Option

On the next screen, specify the name of your GCP project. A Project ID will be generated based off that name. This Project ID is unique within all of GCP, so some numbers may be added in order to make it unique. Once you’ve filled in the form, it should look similar to below. If so, click “Create” to actually create the project within GCP.

It may take a few minutes to actually create the project within GCP. Once created, navigate to the App Engine resource page via the navigation menu in the top left.

NOTE: Adding App Engine to your project can also be done using the gcloud CLI tool by running gcloud app create in a Terminal if you’ve signed in with the gcloud auth login command and ran gcloud config set project PROJECT_ID using the PROJECT_ID listed above.

App Engine Dashboard

On the App Engine Dashboard, you will have the option to click “Create Application” if you haven’t already provisioned App Engine in your GCP project. During the “Create Application” process, you will select the region to host your App Engine instance. You can also choose the runtime environment, which for the purposes of this tutorial should be “Java” and “Standard”.

Once the App Engine resource has been created, keep the page open and continue. During this, I noticed that closing the page may cause the App Engine resource to not be provisioned.

Add Configuration Files

With the App Engine instance ready, we can now add configuration file to our project. App Engine typically uses an app.yaml file under src/main/appengine in our project directory. Create this file and copy the following configuration:

runtime: java11
env: standard
runtime_config:
jdk: openjdk11
env_variables:
SPRING_PROFILES_ACTIVE: "production"
handlers:
- url: /.*
script: this field is required, but ignored
manual_scaling:
instances: 1

Some important notes in the configuration above:

  • The “standard” environment is compatible with Java 11, but will be incompatible with Java 8 without further configurations
  • The “flex” environment only supports Java 8/9
  • Specifying the “production” Spring profile in the environment variables will automatically run the production configuration of our app as declared in src/main/resources/app.yml if created previously

Some of the documentation in GCP may be misleading because Java 8 is still used as the default Java version. Because of that, most documentation will refer to “Java” when it really means “Java 8” specifically. This has led to GCP recommending the Flex environment for Java, or replacing the Tomcat server that Spring uses by default with a Jetty. Full documentation specifically for Java 11 in App Engine can be found here.

Using Java 11 and the “standard” App Engine environment allows us to deploy a Spring Boot application without any other changes to the configuration!

Deploy to GCP

We now have a Spring Boot API, the necessary configuration app.yaml file for our App Engine instance, and an App Engine resource provisioned in our GCP project. The only steps left are to package our Spring Boot app in a .jar file and deploy it!

To package the application in a .jar, you can either do it through your IDE using the “Run as > Maven Build…” and including clean install as the goal, or through running mvn package in a Terminal window while in the root directory of your project. If run successfully, the output in the terminal window should look like this.

Terminal Output of “mvn package”

Maven will generate a .jar file under the target folder in our project, usually in the format of PROJECT-NAME-0.0.1-SNAPSHOT.jar. You may need to refresh the project directory and then expand the target folder. If viewing in your IDE, the result should look similar to the image below:

Jar of Application in Project Directory

Copy the .jar file into the same folder as the app.yaml configuration file created in the last section, either by creating a new folder or moving the .jar into src/main/appengine. Those two files should be the only ones in the folder. Once done, navigate to that folder in your Terminal window.

Note: To use the gcloud CLI tool, you may need to run gcloud auth login to login into your GCP account and then run gcloud config set project PROJECT_ID, replacing “PROJECT_ID” with the project ID of your GCP project. In my case, this would be appeng-deploy-demo-1.

With the GCP project set in your CLI, run gcloud app deploy to deploy your app! The process will take several minutes to complete, but the result should be similar to below:

Result of Successful “gcloud app deploy”

The output of the successful process will include the endpoint of your deployed API. Copy that and be sure to add the “/demo” to hit our mapped endpoint. The output should match below.

Hitting our Deployed API

And there you have it! As you can see, the “production” Spring profile in our app.yaml was configured correctly and the message was changed to say “Hello, from App Engine!”. This will allow us to configure other things like database connections differently than a local configuration. Your app is now up and running in GCP App Engine and publicly accessible!

Some notes:

  • The deploy may fail if you do not have necessary permissions for the CLI to deploy. In that case, the logs will give you a link to the API to enable so that you can deploy from the CLI
  • If you navigated off the “Create application” screen when using the UI to create the App Engine resource, it may not complete the process of provisioning App Engine. Either recreate it with gcloud app create CLI command or use the “Create application” UI again
  • Your application will incur charges within GCP. You can disable the application easily under “Settings” in the App Engine Dashboard UI

Conclusion

You now have a publicly accessible URL to hit your API, congrats! The next time you make a change and run gcloud app deploy, App Engine will automatically create a new version of your app and migrating all traffic to the new version. A CI/CD pipeline can be built so that anytime you update your code, the changes will be tested, built, and deployed automatically so that code is shipped reliabily and well-tested to App Engine.

Something I also found useful was adding the API under a custom subdomain of api.domainname.com by clicking “Settings” on the App Engine dashboard and selecting the “Custom Domains” tab. There, select “Add a custom domain”. When selecting the mappings, you can remove the domainname.com and www.domainname.com entries and add a mapping for api.domainname.com or whatever subdomain you’d like. This will generate a CNAME entry that you can add to your domain’s DNS settings, leading to consolidation of your API and a UI to a single domain name!

--

--