In this post we going to look at the level of effort it takes to get a Spring Boot application running on Azure’s App Services platform. Since Microsoft traditionally caters more towards .NET workloads, it would be interesting to see the level of integration that exists in their native PaaS offering for Java workloads based based on Spring Boot. For this exercise I decide to use Spring Music which creates a good representation of a single web app with database connectivity.
First lets create an App Service web application via Azure Portal. This can be done from main menu via Create Resource > Web > Web App. At this point we can give our app a name. Apps are assigned unique hostname in the *.azurewebsites.net domain, so whatever app name you pick must not be used by anyone else. Next you need to assign which resource group to use – this is a logical container in Azure for a set of related resources. One of the most important decisions you will have to make is which stack to host it on – Windows or Linux, and the experience on both is vastly different as we’ll see next. Next select app service plan or create a new one, which will determine the service tier assigned to your app and subsequently affect the price of running your app (see pricing chart). Finally select whether you want to create Application Insights for your app, which is Microsoft’s APM, logging, and diagnostics solution.
While Linux seems to be a natural choice for hosting Java apps, the support for it is still in preview mode. In both cases, Azure’s intended usage pattern for deploying Java workloads it to give you a web container in form of Tomcat. On Windows you have a few more options to select which version of Tomcat you want to run or optionally use Jetty. On Linux, Tomcat is the only option and Java support is still in preview mode.
Attempt 1 – Linux Stack
Azure’s documentation does point that the number of features available for Linux stack on app service is significantly less than those on Windows stack, nevertheless I felt that given we’re doing Java and the significant difference in pricing between App Service for Linux vs Windows, this should be my first choice. After provisioning an instance of web service and dropping on the Application Settings page, I was able to pick the Stack for Java which provided a Tomcat container. It should be noted that support for Java is still in preview mode. After consulting Microsoft documentation, it seems that the intended deployment model for Java apps on to App Service is by packaging them as WAR. Given I had a spring boot application, this was somewhat annoying as I had to make changes to my app to make it deployable as WAR files. After some research, I finally found the right set of settings necessary to make the conversion, which included changing the build.gradle and changing my Application.java file to inherit from SpringBootServletInitializer and implement a method to configure SpringApplicationBuilder as following:
... apply plugin: 'war' ... dependencies { ... providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat' ... }
@SpringBootApplication public class Application extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return configureApplication(application); } static SpringApplicationBuilder configureApplication(SpringApplicationBuilder application) { return application .sources(Application.class) .initializers(new SpringApplicationContextInitializer()) .listeners(new AlbumRepositoryPopulator()); } public static void main(String[] args) { configureApplication(new SpringApplicationBuilder()) .application() .run(args); } }
@SpringBootApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class) .initializers(new SpringApplicationContextInitializer()) .listeners(new AlbumRepositoryPopulator()) .application() .run(args); } }
In order to push the app to on to Azure we need to upload the WAR file to the persistent drive associate with the Web App. For this we first need to create FTP credentials which can be done fairly intuitively by going to Deployment Credentials tab. After this the FTP address can be acquired from the Overview page.
After uploading the WAR file I tried navigating to the site URL. After about two minutes of loading, I got a generic service unavailable error page. Normal Tomcat behavior is to explode the WAR file into an unpacked folder, however refreshing the FTP site I did not observe this behavior. Inside the FTP there’s also a folder /LogFiles which I tried using for troubleshooting. The first thing I have noticed is the a number of files similar to 2018_05_20_RD0003FF62A639_docker.log, which indicates that on Linux it tries to run your app by building a docker container. Inside /LogFiles/Application/ folder I did find what I was hoping would indicate the problem – Catalina log files. Alas, no errors were found and it looks like it did pick up my WAR file.
... 20-May-2018 15:34:38.858 INFO [main] org.apache.catalina.startup.Catalina.load Initialization processed in 4510 ms 20-May-2018 15:34:39.013 INFO [main] org.apache.catalina.core.StandardService.startInternal Starting service [Catalina] 20-May-2018 15:34:39.015 INFO [main] org.apache.catalina.core.StandardEngine.startInternal Starting Servlet Engine: Apache Tomcat/9.0.6 20-May-2018 15:34:39.205 INFO [main] org.apache.catalina.startup.HostConfig.deployWAR Deploying web application archive [/home/site/wwwroot/webapps/music.war]
At this point I spent about another hour trying to get this working but finally gave up. Microsoft documentation on deploying Java workloads seem to focus on adding a maven plugin to my code, which seems to by an interesting assumption that I’m using Maven in the first place. I tried another project that was Maven based just to see if I could get it running and ran into exact same issues – it seems the only thing their Maven plugin does is lets you use Maven task to upload the WAR file. I should also note that a big limitation I found on Linux stack is the lack of support for streaming logs. The Kudu portal is also much less feature rich and only offers ability to look at environmental variables and connected to shell. I also found that shell was much less responsive than when I was doing .NET, where each command took a good 5 seconds to respond. I was also not able to find any articles outside of Microsoft’s official documentation to help troubleshoot this and felt stuck. At this point I decided to try to give up on Linux and see if I can get this running on Windows stack.
Attempt 2 – Windows Stack with built in container
After the app finishes the provisioning process which takes about a minute we can access the configuration GUI for the app in the Azure portal. First we need ensure that we have JRE on the target machine. This can be configured on the Application settings > General Settings where you can enable the appropriate Java version and web container, which includes multiple versions of Tomcat or Jetty.
Azure App Service support for Java is done by providing a preconfigured container.