To take a monolithic app, rebuild it as cloud native app using loosely coupled microservices and deploy it on the platform
-
Java SDK 1.6+
-
Git github.com
-
Cloud Foundry CLI
CF CLI Releases
-
Curl curl
-
Pivotal Web Services Account. Create a free trial account here
Pivotal Web Services
-
Familiarity with Spring Spring IO
-
Fork and Clone
PCF Workspace for Microservices
-
Review the Overview of the
cities
application.
This lab demonstrates the benefits of deploying microservice applications to Cloud Foundry and building those applications with the Spring framework. It will also get you more comfortable working with the CF CLI.
The lab starts with a monolithic v1 version of the cities-app, which is a Angular.js/Spring Boot/Spring Web/Spring Data application that provides a simple UI for searching for cities that are stored in a relational database. It’s pushed as a single CF app.
It’s a perfectly nice app, and does a lot with a little code thanks to the use of Spring Boot, Web and Data projects. But seeing the wisdom of microservices, you decide it’s time to refactor this app into two tiers
-
a microservice tier that implements the core cities CRUD operations and provides a REST API via Spring Data Rest
-
a UI tier that calls this REST API and uses a CF user provided service to reference the connection parameters from the CF environment
You’ll deploy the refactoried v2 cities-app as two separate apps (an app for each tier), giving you the flexibility to update and scale each tier independently.
Then the lab will then take you through a blue/green deployment to route 'production' traffic from your v1 app to your v2 app and demonstrate how the CF router simplifies application upgrades.
Note
|
The instructions in this document are for Mac/Linux based CLI/Shell. If you are using Windows, you can use Windows CLI or a Virtual Box / Vagrant with a Linux VM. |
$ cd pcf-workspace-microservices/mono
$ ./gradlew clean
$ ./gradlew build
$ java -jar build/libs/cities-monolithic.jar
Use cf help
and/or cf <command> --help
for details on each of the commands below.
-
Review the docs: http://docs.pivotal.io/pivotalcf/devguide/deploy-apps/deploy-app.html
-
Login to the PCF Env. Edit and Source the
env
filecities
folder--- export CF_SYSTEM_DOMAIN= export CF_APPS_DOMAIN= export CF_USER= export CF_ORG= export CF_SPACE= ---
$ source ./env $ cf login -a https://api.$CF_SYSTEM_DOMAIN -u $CF_USER -o $CF_ORG -s $CF_SPACE
Noteadd --skip-ssl-validation
if you are pointing to CF installation which is using self signed certificates. -
Verify you are logged in with your own userid (not admin) and targeted to your PCF instance:
$ cf target
-
Push the cities-service: Before pushing the app, check if the route exists and we can reach it.
$ cd mono $ cf check-route <first-initial><last-initial>-cities-monolithic cfapps.io $ cf push <first-initial><last-initial>-cities-monolithic -i 1 -m 512M -p build/libs/cities-monolithic.jar
-
Be sure to name your application '<first-initial><last-initial>-cities-monolithic'
-
-
Verify you can access your application from a Web Browser
http://<first-initial><last-initial>-cities-monolithic.cfapps.io
Learning about how your application is performing is critical to help you diagnose and troubleshoot potential issues. Cloud Foundry gives you options for viewing the logs.
To tail the logs of your application perform this command:
// For recent logs
$ cf logs <first-initial><last-initial>-cities-monolithic.cfapps.io --recent
// For current logs
$ cf logs <first-initial><last-initial>-cities-monolithic.cfapps.io
Notice that nothing is showing because there isn’t any activity. Use the following curl commmand to see the application working:
$ curl -i http://<first-initial><last-initial>-cities-monolithic.cfapps.io/cities/10
For other ways of viewing logs check out the documentation here: http://docs.pivotal.io/pivotalcf/devguide/deploy-apps/streaming-logs.html#view
To view recent events, including application crashes, and error codes, you can see them from the App Manager or from the cli.
$ cf events <first-initial><last-initial>-cities-monolithic
To view the health of the application you can see from the App Manager or from the cli:
+
$ cf app <first-initial><last-initial>-cities-monolithic
+ You will get detailed output of the health
Showing health and status for app cities-monolithic in org Central / space development as ...
OK
requested state: started
instances: 1/1
usage: 512M x 1 instances
urls: cities-app-pisiform-chattiness.cfapps.io, cities-app-unenraptured-shantung.cfapps.io
last uploaded: Fri May 29 15:51:12 UTC 2015
stack: cflinuxfs2
state since cpu memory disk details
#0 running 2015-05-29 11:52:14 AM 0.1% 470M of 512M 148.9M of 1G
View the environment variable and explanation of link:http://docs.cloudfoundry.org/devguide/deploy-apps/environment-variable.html#view-env [VCAP Env]
$ cf env <first-initial><last-initial>-cities-monolithic
You will get the output similar to this on your terminal
Getting env variables for app cities-monolithic in org Central / space development as ...
OK
System-Provided:
{
"VCAP_SERVICES": {
"cleardb": [
{
"credentials": {
"hostname": "us-cdbr-iron-east-02.cleardb.net",
"jdbcUrl": "jdbc:mysql://xxx@us-cdbr-iron-east-02.cleardb.net:3306/xxx",
"name": "xxx",
"password": "xxx",
"port": "3306",
"uri": "mysql://xxx@us-cdbr-iron-east-02.cleardb.net:3306/xxx?reconnect=true",
"username": "xxxx"
},
"label": "cleardb",
"name": "rj-cities-db",
"plan": "spark",
"tags": [
"Data Stores",
"Data Store",
"mysql",
"relational"
]
}
]
}
}
{
"VCAP_APPLICATION": {
"application_name": "cities-monolithic",
"application_uris": [
"cities-app-pisiform-chattiness.cfapps.io",
"cities-app-unenraptured-shantung.cfapps.io"
],
"application_version": "9efbf06e-2e3a-4752-89db-77f59d570128",
"limits": {
"disk": 1024,
"fds": 16384,
"mem": 512
},
"name": "cities-monolithic",
"space_id": "56e1d8ef-e87f-4b1c-930b-e7f46c00e483",
"space_name": "development",
"uris": [
"cities-app-pisiform-chattiness.cfapps.io",
"cities-app-unenraptured-shantung.cfapps.io"
],
"users": null,
"version": "9efbf06e-2e3a-4752-89db-77f59d570128"
}
}
User-Provided:
JAVA_OPTS: -Djava.security.egd=file:///dev/urandom
SPRING_PROFILES_ACTIVE: cloud
VERSION: CITIES_APP_1_0
No running env variables have been set
No staging env variables have been set
Applications can be scaled via the command line or the console. When we talk about scale, there are two different types of scale: Vertical and Horizontal. Read this doc on more details on scaling applications.
When you Vertically scale your application, you are increasing the amount of memory made available to your application. Scaling your application horizontally means that you are adding application instances.
Let’s vertically scale the application to 1 GB of RAM.
$ cf scale <first-initial><last-initial>-cities-mono -m 1G
Now scale your application down to 512 MB.
Next, let’s scale up your application to 2 instances
$ cf scale scale <first-initial><last-initial>-cities-mono -i 2
To check the status of your applications you can check from the command line to see how many instances your app is running and their current state
$ cf app <first-initial><last-initial>-cities-monolithic
Delete the PCF cities-mono app
$ cf delete -r <first-initial><last-initial>-cities-monolithic
Now it’s time to deploy the v2 microservice version of the app:
$ cd micro
$ ./gradlew assemble
Under this folder are 3 subfolders
-
cities-service - the microservice app that exposes a REST API from the CityRepository class via a few simple RestResource annotations
-
cities-ui - the UI app that connects to the microservice
-
cities-client - client API used by cities-ui to connect to cities-service microservice, leveraging Spring Cloud and Netflix Feign
Check out the app docs for a thorough coverage of the application design.
First we’ll deploy the cites-service microservice app, which defines in the manifest.yml the 'host' as 'cities-service-${random-word}' and 'cities-db' as a service the app will bind to (i.e. it will bind to the same db we created for the v1 app).
$ cd cities-service
$ cf push
Use 'cf apps' to determine the URL to reference the cities microservice.
-
We’ll refer to this URL as YOUR_CITIES_SERVICE_URL below, and in the example output below YOUR_CITIES_SERVICE_URL=cities-service-nonterminable-runback.cfapps.io
$ cf apps
…
name requested state instances memory disk urls
cities-service started 1/1 512M 1G cities-service-nonterminable-runback.cfapps.io
Validate that the REST endpoints are working for this service using curl
-
Note: for Windows users, download cURL using following instructions below
$ curl -i YOUR_CITIES_SERVICE_URL/cities
$ curl -i YOUR_CITIES_SERVICE_URL/cities/search
$ curl -i YOUR_CITIES_SERVICE_URL/cities/search/nameContains?q=TEMPLE
$ curl -i YOUR_CITIES_SERVICE_URL/cities/829
Create the cities-ws user provided service that will store the cities-service connection parameters in the CF environment and make them available to the cities-ui app.
-
NOTE: YOU MUST USE 'http://' before the YOUR_CITIES_SERVICE_URL! (https will not work)
-
Don’t forget to substitute your specific URL for YOUR_CITIES_SERVICE_URL
$ cf create-user-provided-service cities-ws -p uri,tag
uri> http://YOUR_CITIES_SERVICE_URL
tag> cities
Creating user provided service cities-ws in org...
OK
Validate the user provided service was created
$ cf services
…
name service plan bound apps
cities-db cleardb spark cities-monolithic, cities-service
cities-ws user-provided
Before pushing the cities-ui app that connects to the cities-service microservice app, take a look at the cities-ui manifest.yml to see
-
The app will bind to the cities-ws user provided service you just created
-
The app will use cities-ui-${random-word} as the host (URL prefix)
-
The app sets the VERSION environment variable to CITIES_APP_2_0
$ cd ../cities-ui
$ cat manifest.yml
---
applications:
- name: cities-ui
memory: 512M
instances: 1
path: build/libs/cities-ui.jar
services: [ cities-ws ]
host: cities-ui-${random-word}
env:
SPRING_PROFILES_ACTIVE: cloud
VERSION: CITIES_APP_2_0
Now deploy the cities-ui app
$ cf push
Test that the app works by opening the cities-ui URL that is displayed by the 'cf apps' command. The UI should look the same as the v1 version, but it’s of course getting the data via REST from the cities-service microservice.
Now that the cities-ui app is pushed and bound to the cities-ws service, you can use 'cf env' to validate the cities-service URL/URI it found in the environment.
$ cf env cities-ui
…
System-Provided:
{
"VCAP_SERVICES": {
"user-provided": [
{
"credentials": {
"tag": "cities",
"uri": "http://cities-service-nonterminable-runback.cfapps.io"
},
"label": "user-provided",
"name": "cities-ws",
"syslog_drain_url": "",
"tags": []
}
]
}
}
User-Provided:
SPRING_PROFILES_ACTIVE: cloud
VERSION: CITIES_APP_2_0
)
At this point, you are prepared to reap the benefits of having a separate microservice that can be scaled and deployed independently of the UI tier. However, let’s hold off on scaling until we’re done with the blue/green deployment and can delete the v1 app (a PWS trial has a 2G limit, which we’re not too far from at this point).
Now you’re ready to perform a blue/green deployment. First we’ll list our existing routing table:
$ cf routes
…
host domain apps
cities-service-nonterminable-runback cfapps.io cities-service
cities-app-unplodding-tetrarch cfapps.io cities-monolithic
cities-ui-slumberous-arroyo cfapps.io cities-ui
The host and domain listed for the cities-monolithic app is the 'production' URL that we want to remain constant during the upgrade process so our users are not aware that the v1 app is being replaced by the v2 app.
We can validate that that this URL is referencing our v1 app by using the /cities/version request mapping to retrieve the VERSION environment variable
-
Substitute your cities-monolithic URL below
$ curl cities-app-unplodding-tetrarch.cfapps.io/cities/version
CITIES_APP_1_0
We can also validate the version of the cities-ui route, which is our v2 app
$ curl cities-ui-slumberous-arroyo.cfapps.io/cities/version
CITIES_APP_2_0
In a more realistic blue/green deployment scenario, we’d have a cluster of multiple v1 app instances deployed already, but since we’re short on memory (i.e. PWS trial 2G limit), we’ll stick with our single instance ''cluster''.
Now we add our v2 'canary' to the v1 cluster my mapping the v1 'production' route to the cities-ui app (i.e. the -n parameter is the host for cities-monolithic returned by 'cf routes')
$ cf map-route cities-ui cfapps.io -n cities-app-unplodding-tetrarch
Now if we repeatedly visit our production URL, we’ll see the CF router is load balancing requests between the v1 and v2 apps.
$ curl cities-app-unplodding-tetrarch.cfapps.io/cities/version
CITIES_APP_1_0
$ curl cities-app-unplodding-tetrarch.cfapps.io/cities/version
CITIES_APP_2_0
Our current deployment architecture looks like this:
Another look at our routes shows both apps are now mapped to the production route as expected
$ cf routes
…
host domain apps
cities-service-nonterminable-runback cfapps.io cities-service
cities-app-unplodding-tetrarch cfapps.io cities-monolithic,cities-ui
cities-ui-slumberous-arroyo cfapps.io cities-ui
After performing some validation that our v2 canary is working as expected, we’re ready to retire the v1 app instances from the cluster by unmapping the production route to the v1 cities-monolithic app, using 'cf unmap'
$ cf unmap-route cities-monolithic cfapps.io -n cities-app-unplodding-tetrarch.cfapps.io
Then test our production URL to see all traffic is going to v2:
$ curl cities-app-unplodding-tetrarch.cfapps.io/cities/version
CITIES_APP_2_0
$ curl cities-app-unplodding-tetrarch.cfapps.io/cities/version
CITIES_APP_2_0
…
At this point we can delete our v1 app
$ cf delete cities-monolithic
Congratulations, you have successfully performed a blue/green deployment, and done so without massive amounts of custom scripting.
Finally, with a bit of spare memory, you can try scaling both tiers of your v2 microservice app.
$ cf scale cities-ui -i 2
$ cf scale cities-service -i 2
$ cf apps
UI request will now be load balanced across two instances of cities-ui, and REST requests made by cities-ui to cities-service will be automatically load balanced as well. Wow, microservices, Spring and CF are a great combination!
Runtime/Platform
-
Scaling ..
Architecture
-
Dependency and Coupling
-
Access to mounted, shared filesystems
-
Peer-to-peer application server clustering
-
Shared libraries
-
Configuration files sitting in well-known locations