Publish CUBA Project to Azure Website

The following post describes a “Recipe” for building and deploying CUBA Platform
projects to an Azure Web App using an Azure SQL Database.

#How

###Step 1: Create a New Database → SQL database (and server) in Azure.

Make sure that the SQL Server Firewall is configured to “Allow access to Azure services”. The app-core module will not be able to communicate with the database if this is not allowed.

###Step 2: Create a New Web + Mobile → Web App in Azure with the following Application Settings:

  • Java version - Java 8
  • Java minor version - Latest
  • Web container - Newest Tomcat 8.5
  • Platform - 64-bit
  • Web sockets - On
  • Always on - On

If you use one of the Free or Shared plans then Always on will be turned off. This may require you visit the site to force Tomcat to update the war file. These plans are pretty restrictive and not have the Always on feature will likely have significant delays when Tomcat is re-started. My testing has been with a Basic B database and Basic B1 web server both of which have very modest specs.

###Step 3: Add the following code to bottom of your build.gradle and replace the @@ tokens with actual values between the Configuration - Start and End comments:

configurations {
    ftpAntTask
}

dependencies {
    ftpAntTask("org.apache.ant:ant-commons-net:1.9.+");
}

ant.taskdef(
    name: 'ftp',
    classname: 'org.apache.tools.ant.taskdefs.optional.net.FTP',
    classpath: configurations.ftpAntTask.asPath
)

task buildWarAndDeployToAzure(type: CubaWarBuilding) {
    // Configuration - Start
    def contextFile = 'modules/core/web/META-INF/@@your@@-context.xml'
    def appHostName = '@@webSiteName@@.azurewebsites.net'
    def ftpHostName = 'waws-prod-bay-023.ftp.azurewebsites.windows.net'
    def ftpUserName = '@@webSiteName@@\\@@ftpUserName@@'
    def ftpPassword = '@@ftpPassword@@'
    singleWar = false
    // Configuration - End

    appHome = '/home/site/wwwroot/webapps'
    coreContextXmlPath = contextFile
    includeJdbcDriver = true
    includeContextXml = true

    appProperties = [
        'cuba.automaticDatabaseUpdate': true,
        'cuba.logDir': '/home/LogFiles',
        'cuba.webAppUrl': 'https://' + appHostName + '/app',
        'cuba.webHostName': appHostName,
        'cuba.webPort': 443,
        'cuba.web.loginDialogDefaultUser': '',
        'cuba.web.loginDialogDefaultPassword': ''
    ]

    if(singleWar) {
        includeContextXml = true
        webXmlPath = 'modules/web/web/WEB-INF/single-war-web.xml'
        appProperties.put('cuba.useLocalServiceInvocation', true)
    } else {
        appProperties.put('cuba.connectionUrlList', 'https://' + appHostName + '/app-core')
    }

    doLast {
        def webApps = 'site/wwwroot/webapps'
        def buildDir = 'build/distributions/war'

        ant.ftp(action: 'del', server: ftpHostName, remotedir: webApps, userid: ftpUserName, password: ftpPassword) {
            fileset(includes: '*.war')
        }

        ant.ftp(server: ftpHostName, remotedir: webApps, userid: ftpUserName, password: ftpPassword) {
            fileset(dir: buildDir, includes: '*.war')
        }
    }
}

Step 4: Copy your modules/core/web/META-INF/context.xml to modules/core/web/META-INF/@@your@@-context.xml or paste the following and replace the @@ tokens with actual values:

<Context>
    <!-- Database connection -->
    <Resource driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver"
              maxIdle="2"
              maxTotal="20"
              maxWaitMillis="5000"
              name="jdbc/CubaDS"
              password="@@sqlAdminPassword@@"
              type="javax.sql.DataSource"
              url="jdbc:sqlserver://@@sqlServerName@@;databaseName=@@sqlDatabase@@"
              username="@@sqlAdminUserName@@"/>
    <Manager pathname=""/>
</Context>

###Step 5: Make sure that you have used Studio to create the Gradle wrapper and at the command line type:

gradlew buildWarAndDeployToAzure

###Step 6: After 10 - 15 minutes, you can visit the site at:

https://@@webSiteName@@.azurewebsites.net/app 

###Notes:

  1. All these instructions assume version 6.5 of the CUBA Platform.
  2. To create a BuildAndDeploy for a new test site, copy/paste the buildWarAndDeployToAzure and context.xml file for the new environment.
  3. Depending on the performance options you configured for the database and Web App, it will take 10 to 15 minutes to fully deploy.
  4. Setting singleWar = true is considerably faster but requires an additional step using the Studio Deployment settings → WAR → Custom web.xml → Generate to create the modules/web/web/WEB-INF/single-war-web.xml file.
  5. To troubleshoot deployment issues, go to your App service in the Portal, Advanced Tools → Go → Debug Console → CMD. You can then go into the LogFiles folder and view the catalina and localhost log files to determine your issue(s).
  6. Scale out does not work. I am not that the architecture requirements are a good fit as JMX requires the use of hostnames but you have no idea what the hostnames are in a Scale out cluster … besides this is a shared service and relies on the hostname in the http header to route requests. Maybe the CUBA team would have a better idea how this could be accomplished.
  7. There are options to configure a custom Tomcat install/config or the new Uber Jar scenario. The documention can be found here.
  8. This is using FTP which is insecure as the the username and password are sent clear text. There is the option to use FTPS (not to be confused with SFTP) but the ant task does not support FTP over SSL/TLS. If the CUBA team were to productize this “Recipe”, it would not be difficult to add FTPS as a transport.
  9. I was never able to get app installed into ROOT. A sample from the CUBA team on how to do this would be beneficial as the documentation seems to refer to modifying the development Tomcat server config and using that in a deployment.

#Why

Coming from the LightSwitch world, one it’s features was the ease of publishing your application to a Azure Website and SQL Server database. This provides a low friction way to develop small apps or apps for customers have budget constraints and capabilities as you don’t have to worry about the administration of these websites. I realize that these are the usual Cloud benefits but as Azure Websites is a PaaS service and not IaaS, all the software is installed and continually updated for the customer. Backups are seamless and there are both automation interfaces in addition to the Web Portal so my less sophisticated customers can do their own admin.

The deployment to cloud has been one of the larger friction areas in learning the CUBA platform. I looked at Jelastic, created a Linux VM with all the software components, tried a Windows VM and all were not as simple as the Azure solution I have been using.I suspect many people coming from Lightswitch are going to try once and move on. It’s taken a lot of persistence and time to figure out all the pieces and for people coming from a MS technology stack this is a show stopper.

My goal, especially with the smaller solutions, is to not be mired in administration to setup infrastructure and maintain it. I just want to develop solutions for my customers!

A few of my generic requirements:

  1. https by default even is no cert or domain has been purchased.
  2. Backup of database automatic and geo replicated. Point in Time restore min 7 days with min RPO of 5 minutes.
  3. I do not backup the frontend/middle tiers. They are transient hence why I am against having image files on the file system and I would rather store them the database. If I want to deploy the frontend/middle tiers, I do it from Git not from a backup.
  4. One or more testing sites in addition to the Production site (Staging, QA etc)
  5. Ability to easily copy the production database, create new testing site and install new code in testing site. When finished with testing, code is move into production and testing site(s) and database(s) are deleted.
  6. The creation of test databases and sites can either be automated or using a GUI depending on the complexity of the solution.

I have come up with I think is simple solution for the CUBA Platform to have a similar and in fact I believe better solution than what I have had with Lightswitch. It builds on the existing CubaWarBuilding task to easily create a war file for a CUBA app to work with an Azure Website and then deploy it.

There are a number of deployment options in Azure of which this “Recipe” is one way. For larger solutions, I would likely use a PowerShell script outside the Gradle build so that I can automate the provisioning and deployment. This will also allow me to deploy using FTPS as opposed to the insecure FTP.

#Concrete Example

In order to illustrate a working solution, I have forked the cuba-vision-clinic demo and updated it to work in an Azure Web Site. To install it in Azure, follow these directions.

###Step 1: Follow (How, Steps 1 and 2) to provision as database and web server.

###Step 2: Clone a modified version of the cuba-vision-clinic app from GitHub.

###Step 3: Replace the following in the gradle.build file:

  • @@webSiteName@@
  • @@ftpUserName@@
  • @@ftpPassword@@

###Step 4: Replace the following the azure-context.xml file:

  • @@sqlAdminUser@@
  • @@sqlAdminPassword@@
  • @@sqlServerName@@
  • @@sqlDatabaseName@@

###Step 5: Build and deploy (How, Steps 5 and 6).

#Help

There are some areas where some assistance from the CUBA team would help. I am a Java newbie and have been going through a significant learning curve and still fall short in certain areas.

  • Getting app installed into ROOT. I must be missing something here but I start to get all kinds of folders and I have spent hours to try get this to work. For now, I am just going to put a jsp page in ROOT with a redirect to /app.
  • Fast Deployment. I suspect that the catalina config needs to be configured to allow shared libs. I had to delete the war files before copying the new ones to ensure reliable operation. This would likely take you minutes to figure out vs hours for me … if it’s even possible.
  • Evaluate if a Scale Out cluster can be used.
  • Please do look at productizing this as a feature. If it’s important to me it’s likely important to others.

Hi Ian,

Thank you very much for the detailed instruction and explanation of problems.

We’ll deal with it in the near future and let you know about results. I’m sure that running the app in ROOT should not be a problem. But what do you mean by fast deployment - exploded WARs with shared libs like in local Tomcat or just speeding up the process of normal WAR deployment?