Blue-Green deployment of Pivotal Cloud Foundry

There is down time in traditional deployment process. To overcome this problem, Pivotal cloud foundry came with the concept of Blue-Green deployment. This concept is also known as Zero down time deployment. Now, we look at how do we achieve an application deployment without any down time.

Lets assume the version 1 application already running in Pivotal Cloud Foundry. Make a note of the version1 application by using cf routes command.

We have made changes on top of version 1 application and version 2 is ready to deploy. Let’s push the version2 application with temporary route by using below syntax,

cf push version_two -p version_2.jar -n versiononetemp –no-start

Let’s make a note of host names and domain name to map routes by using cf routes command. Use the map-route command to navigate version1 users to version2 application. Look at the below command,

cf map-route version_two –hostname versionone

Let’s remove version1 application existing route by using unmap-route command. Look at the below command,

cf unmap-route version_one -n versionone

Let’s remove version2 application existing route(temp route) by using below command.

cf unmap-route version_two -n versiononetemp

This complete process explains, how zero downtime / Blue-green deployment process will work.

Note: Let’s assume we have made changes in database(adding/deleting columns from a table) level between one version to another version,

  • If we follow the native queries by using JDBC/Spring JDBC, that may not cause any issues. We can follow Blue-Green deployment. 
  • If we follow entity mapping like JPA/Hibernate, it will cause issues. So, better avoid Blue-Green deployment in this scenario.

Summary: Follow these steps if you use application scaling

  • cf scale version_one -i 2
  • cf routes (make a note of host name)
  • cf push version_two -p version_two.jar -n versiononetemp –no-start
  • cf routes (make sure host and route generated)
  • cf map-route version_two –hostname versionone
  • cf scale version_one -i 1
  • cf scale version_two -i 2
  • cf unmap-route version_one -n versionone
  • cf unmap-route version_two -n versiononetemp

push monolithic application into pcf – 1

In previous posts, we have seen how to create Microservices application with Spring Boot technology and push it into the pivotal cloud foundry. Since Spring Boot works on the concept of auto configuration, we easily pushed Spring Boot application into PCF without worrying about application configuration or running environment like Tomcat server and all.

When it come to normal Spring MVC application, Tomcat or any other web/application server is compulsory to use to run the application. Now the question is, how can we push monolithic Spring MVC application in Pivotal?

To get clarification about above question, we need to follow below steps.

  1. Create Spring MVC application in local environment.
  2. Test to get confirmation either working as expected or not in locally configured server.
  3. When it’s working in locally, push the same jar/war file into pivotal cloud foundry.
  4. Test by using application route provided by pivotal

Step1: Creating Spring MVC application

I am trying to create a sample application of using the below techstack to push that application into Pivotal Cloud Foundry. This application does not contain any database interaction.

Java – 1.8

Spring – 5.x

Maven – 3.5.x

STS – 4

Tomcat – 8.5

If you are not aware of how to create Spring MVC project in Spring Too Suite (STS) as Maven project. Follow the steps explain in this page.

The project might get generated in different structure. The below is my project structure look like,

pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
        http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.monolithic.springmvc</groupId>
    <artifactId>monolithic-springmvc</artifactId>
    <packaging>war</packaging>
    <version>0.0.1-SNAPSHOT</version>
    <name>monolithic-springmvc Maven Webapp</name>
    <url>http://maven.apache.org</url>
    <properties>
        <spring.version>5.1.2.RELEASE</spring.version>
    </properties>
    <dependencies>
 <dependency>
     <groupId>junit</groupId>
     <artifactId>junit</artifactId>
     <version>3.8.1</version>
     <scope>test</scope>
 </dependency>
 <dependency>
            <groupId>org.springframework</groupId>
     <artifactId>spring-core</artifactId>
     <version>${spring.version}</version>
 </dependency>

 <dependency>
            <groupId>org.springframework</groupId>
          <artifactId>spring-web</artifactId>
     <version>${spring.version}</version>
 </dependency>

 <dependency>
            <groupId>org.springframework</groupId>
     <artifactId>spring-webmvc</artifactId>
     <version>${spring.version}</version>
 </dependency>
 <dependency>
     <groupId>javax.servlet</groupId>
     <artifactId>jstl</artifactId>
     <version>1.2</version>
 </dependency>
    </dependencies>
    <build>
 <finalName>monolithic-springmvc</finalName>
 <plugins>
     <plugin>
  <artifactId>maven-war-plugin</artifactId>
  <version>2.6</version>
  <configuration>
        <warSourceDirectory>WebContent</warSourceDirectory>
        <failOnMissingWebXml>false</failOnMissingWebXml>
  </configuration>
     </plugin>
 </plugins>
    </build>
</project>

web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

 <display-name>MonolithicSpringMVC</display-name>
 <description>Monolithic Spring MVC application created for
  pushing into pivotal cloud foundry</description>

 <welcome-file-list>
  <welcome-file>/index.jsp</welcome-file>
 </welcome-file-list>

 <servlet>
     <servlet-name>default</servlet-name>
     <servlet-class> 
                 org.springframework.web.servlet.DispatcherServlet 
            </servlet-class>
     <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>/WEB-INF/spring-servlet.xml</param-value>
            </init-param>
     <load-on-startup>1</load-on-startup>
 </servlet>
 <servlet-mapping>
     <servlet-name>default</servlet-name>
     <url-pattern>/</url-pattern>
 </servlet-mapping>
 <context-param>
     <param-name>contextConfigLocation</param-name>
     <param-value>/WEB-INF/spring-servlet.xml</param-value>
 </context-param>
 <listener>
     <listener-class> 
                org.springframework.web.context.ContextLoaderListener 
            </listener-class>
 </listener>
</web-app>

spring-servlet.xml: This file name you can change as you like. But, you should configure the same name in above web.xml file wherever configured. 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:mvc="http://www.springframework.org/schema/mvc"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="
        http://www.springframework.org/schema/beans     
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc 
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd">

    <mvc:annotation-driven />
    <mvc:default-servlet-handler />
    <context:component-scan
  base-package="com.monolithic.springmvc.controller" />

    <bean id="viewResolver"
     class="org.springframework.web.servlet.view.UrlBasedViewResolver">
 <property name="viewClass"
   value="org.springframework.web.servlet.view.JstlView"/>
 <property name="prefix" value="/WEB-INF/views/"/>
 <property name="suffix" value=".jsp"/>
    </bean>
</beans>
 
index.jsp:

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" 
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Monolithic Spring MVC - Index</title>
</head>
<body>
    <center>
 <h2>Hey Monolithic</h2>
 <h3>
     <a href="welcome?name=JavaIsOcean">Click Here</a>
 </h3>
    </center>
</body>
</html>

welcome.jsp:

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" 
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Monolithic Spring MVC - Welcome</title>
</head>
<body>
    <center>
 <h2>Hello ${name}</h2>
 <h2>
     ${message} 
 </h2>
    </center>
</body>
</html>

MonolithicController.java:

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class MonolithicController {
    String message = "Welcome to Monolithic Spring MVC Application!!!";
    @RequestMapping("/welcome")
    public ModelAndView showMessage(@RequestParam(value = "name", required = false, 
        defaultValue = "JavaIsOcean") String name) {
       ModelAndView mv = new ModelAndView("welcome");
       mv.addObject("name", name);
       mv.addObject("message", message);
       return mv;
    }
}

Let’s build the application source code by using the command of mvn clean install and get war file is generated or not in target directory. If the war file is not generated, your code is not correct. If you don’t wish to write all these code, you can also download from https://github.com/Nallamachu/Pivotal. So, now creating the sample Spring MVC project completed.

Step 2: Test application in local server:

To complete this step, we need a web/application server which is running locally. If you have already have configured any server, you can deploy the generated war file into it and test. Otherwise we need to download and configure.

To make it simple, I have downloaded the tomcat server with the version of 8.5 from official Tomcat website as a zip file and extracted locally. There might be a chance of facing startup issues. To avoid that, do the highlighted settings changes in your local as well.

Once you run the project successfully, you will get the output as index page As on when you hit the below URL. URL will be the combination of  host + port+ context name. Output look like below,

You will get the output as below, as on when you click on the Click Here hyper link.

So, our step 2 also completed.

Step 3: Push war file into Pivotal Cloud Foundry:

You should have valid credentials to login into Pivotal account prior doing any operations with Cloud foundry environment. If you don’t have follow these instructions to register and access in Pivotal and get 2 GB of free space.

If you already have valid account details, login into pivotal by using Cloud Foundry CLI with the command of cf login. If you didn’t have installed Cloud Foundry, follow these instructions.

Navigate into the target directory or where your war file located, and issue the below command to push into cloud foundry.

cf push monolithic-springmvc -p monolithic-springmvc.war

cf push  – is the command to push application into cloud foundry
monolithic-springmvc –  is the application name I am passing
-p  – is the parameter to specify the pushing file path where it located

Once we issue the above command, we will see the pushing progress like below,

Once it completed pushing and starting the application, you can access the URL, which is called as route in Cloud foundry terminology. In this case, monolithic-springmvc.cfapps.io is the route to access application. Look at the below screen to know about application details in pivotal application manager.

So, pushing war file into cloud foundry account is completed. Let’s move on to last step of testing the application.

Step 4: Test by using application route:

As I explained in Step 3, once we push application successfully in pivotal cloud foundry, it will generate a route to access application. As on you open the route, you will get the same output what we have got in Step 2 of testing in local tomcat environment. Look at the below screens,

We will get the below screen as on when we hit the Click Here hyper link shown in above screen,

 

Note: If you observe the above URL / Route, we are not passing any application context name like how we access in local. That will be taken care by Pivotal.

Set environment variables in pivotal application

Environment variable is part of the environment in which a process running. An environment variable is a dynamic-named value that can affect the running processes will behave on a computer. In pivotal, environment variable will play a key role to push and run an application. Cloud foundry will provide different set of command to view and set environment variables through command line interface.

To view specific application environment variables we can use the cf env followed by application name. Syntax look like below,

cf env

 For example, my application_name is usermanagement. Then the command which I need to execute is like below,

cf env usermanagement

The response which I got after the execution of above command is,
Environment Variables:

Getting env variables for app usermanagement in org javaisocean / space development
 as javaisocean@gmail.com...
OK

System-Provided:

{
 "VCAP_APPLICATION": {
  "application_id": "bf7ee518-3722-4dc7-98f4-4c86769769f5",
  "application_name": "usermanagement",
  "application_uris": [
   "usermanagement.cfapps.io"
  ],
  "application_version": "0e6a6fae-4aa5-4d1a-8148-a33a268d62a4",
  "cf_api": "https://api.run.pivotal.io",
  "limits": {
   "disk": 1024,
   "fds": 16384,
   "mem": 650
  },
  "name": "usermanagement",
  "space_id": "ecc72a08-9fd2-4763-bc00-07282a3f528d",
  "space_name": "development",
  "uris": [
   "usermanagement.cfapps.io"
  ],
  "users": null,
  "version": "0e6a6fae-4aa5-4d1a-8148-a33a268d62a4"
 }
}

No user-defined env variables have been set

No running env variables have been set

No staging env variables have been set

If you observe the above information, there are different types of env variables we can see. Those are like, System provided, user-defined, running and staging env variables. We can not change anything which is coming from System provided which was configured by the System while pushing the application or binding services.

As per the above information, there is only VCAP_APPLICATION information and no VCAP_SERVICES information. Because the usermanagement application doesn’t bind with any services. That means, VCAP_APPLICATION which contains the information of application and VCAP_SERVICES will contains the information of services which are bind to the application.

The below is the application environment variables, after binding the ClearDB MySQL database to usermanagement application.

Environment variables after binding ClearDB MySQL service:

Getting env variables for app usermanagement in org javaisocean / space development
as javaisocean@gmail.com...
OK

System-Provided:
{
 "VCAP_SERVICES": {
  "cleardb": [
   {
    "binding_name": "mysql-db",
    "credentials": {
     "hostname": "us-cdbr-iron-east-01.cleardb.net",
     "jdbcUrl": "jdbc:mysql://us-cdbr-iron-east-01.cleardb.net/ad_ce4b59777591895
?user=ba9fca71350825\u0026password=80a3164a",
     "name": "ad_ce4b59777591895",
     "password": "80a3164a",
     "port": "3306",
     "uri": "mysql://ba9fca71350825:80a3164a@us-cdbr-iron-east-01.cleardb.net:3306/
ad_ce4b59777591895?reconnect=true",
     "username": "ba9fca71350825"
    },
    "instance_name": "mysql-spark-db",
    "label": "cleardb",
    "name": "mysql-db",
    "plan": "spark",
    "provider": null,
    "syslog_drain_url": null,
    "tags": [
     "Data Stores",
     "Cloud Databases",
     "Developer Tools",
     "Web-based",
     "Data Store",
     "Online Backup \u0026 Storage",
     "Development and Test Tools",
     "Single Sign-On",
     "Buyable",
     "relational",
     "mysql",
     "Cloud Security and Monitoring"
    ],
    "volume_mounts": []
   }
  ]
 }
}

{
 "VCAP_APPLICATION": {
  "application_id": "5b64b05f-4ba0-4f51-9680-c9fe5c8f109a",
  "application_name": "usermanagement",
  "application_uris": [
   "usermanagement.cfapps.io"
  ],
  "application_version": "d12ee54c-57fd-4973-9eb0-5e20eca520f0",
  "cf_api": "https://api.run.pivotal.io",
  "limits": {
   "disk": 1024,
   "fds": 16384,
   "mem": 650
  },
  "name": "usermanagement",
  "space_id": "ecc72a08-9fd2-4763-bc00-07282a3f528d",
  "space_name": "development",
  "uris": [
   "usermanagement.cfapps.io"
  ],
  "users": null,
  "version": "d12ee54c-57fd-4973-9eb0-5e20eca520f0"
 }
}

No user-defined env variables have been set

No running env variables have been set

No staging env variables have been set

How to set environment variables?

Cloud Foundry provides a command called set-env to set environment variables followed by the application name along with property name and value.

Set-Env command syntax look like,

cf set-env

You can see the environment variables provided by user under User-defined env variables when you ran the below command.

cf env