The main objective in this lab is to deploy our system under test to Pivotal Cloud Foundry (PCF).

This task is facilitated by a product called Spring Cloud Services. This product is implemented as a PCF Tile which has the ability to provision service registries, config servers, and hystrix dashboards in the cloud.

Review the projects you’ve created so far:

  • config-server

  • eureka-server

  • fortune-sevice

  • greeting-app

  • hystrix-dashboard

Of the five services on that list, three are infrastructure services, provided by Spring Cloud. Only two are services specific to the business domain. With Spring Cloud Services in PCF, we deploy only those two: the fortune service and greeting application. Everything else is created with Spring Cloud Services. In other words, config server, eureka server, and the hystrix dashboard are managed services.

1. Cloud Foundry Setup

Make sure you have the cf client installed and that you’re targeting a Pivotal Cloud Foundry instance that has the Spring Cloud Services tile installed.

  1. Verify that the cf client is installed with the command:

    $ cf version
  2. Log in to your Cloud Foundry instance with the cf login command.

    There is a known bug with the cf cli and the git bash shell having to do with capturing passwords from standard input. If you’re on a windows machine using git bash, you’ll have to switch to using the default windows cmd shell instead, for the cf login command to work.
  3. Verify whether the Spring Cloud Services are available by looking in the marketplace:

    $ cf marketplace

    The response from the above command is a list of service types. Make sure that the following three services are showing on that list:

    • p-service-registry

    • p-config-server

    • p-circuit-breaker-dashboard

2. The Process

Pivotal Cloud Foundry has the notion of applications and services. We deploy applications with the cf push command, and we create services with the cf create-service command. Finally, services are attached to applications via a process known as binding, using the cf bind command.

The general process of deploying our system to Cloud Foundry involves:

  1. creating and configuring the backing Spring Cloud Services that our applications depend on

  2. deploying, or pushing fortune and greeting to Cloud Foundry

  3. binding our applications to the backing services, either explicitly using cf bind or implicitly by listing the services in the application’s deployment manifest

The instructions shown below reference marketplace services found on Pivotal Web Services (aka PWS), Pivotal’s public instance of Cloud Foundry. You may have to substitute the names of services and plans to match the ones available in your PCF foundation’s marketplace.

3. Create the Service Registry

To orient ourselves to the syntax for creating our first service, invoke the command:

$ cf help create-service

If you recall from the marketplace listing above, the name of the service type is p-service-registry, and it sports two plans. Select the one named "trial".

⇒ Create the service:

$ cf create-service p-service-registry trial service-registry

Here’s the response:

Creating service instance service-registry in org eitan-org / space eitan-space as esuez@pivotal.io...
OK

Create in progress. Use 'cf services' or 'cf service service-registry' to check operation status.

Notice the message create in progress. At this point, Spring Cloud Services (which from now on I’ll abbreviate as SCS) begins the work of standing up a eureka server on our behalf. This process is asynchronous and can take a couple of minutes.

Check on the status of your service creation with the cf services command:

$ cf services
Getting services in org eitan-org / space eitan-space as esuez@pivotal.io...
OK

name               service              plan   bound apps   last operation
service-registry   p-service-registry   trial               create in progress

Within a short time, the "last operation" value will change to create succeeded.

At this point, it’s worth jumping over to the Apps Manager, the Graphical User Interface for PCF (in PWS, the url is http://console.run.pivotal.io/). Log in to the apps manager and navigate to the org and space that matches your cf target. In the service tab, you should see the service that was just created, as shown here:

first service

Click on the service registry, and in the upper right-hand corner, you’ll see a link titled Manage. This is standard for many managed services in cloud foundry: they expose some kind of management dashboard. For the service registry, the dashboard is essentially the same eureka dashboard we’ve been working with all along, only styled with a different color scheme.

cf eureka dashboard

Of course, we have no registered applications at this time. Let’s move on to creating a configuration server in PCF.

4. Create the Config Server

The process here is very similar, but with one major difference: we must configure our config server with our git repository.

4.1. Push the local git repository to github

Up until now, we’ve been using a git repository on the local file system. We need to make this repository accessible from PCF. One simple solution is to push the repository to github, or perhaps an enterprise github or bitbucket, accessible from your PCF instance.

That’s simple enough to do, though these instructions may vary depending on the git system you’re using. Here are the instructions for github:

  1. Head over to your github account, create a repository, name it config-repo, and click Create repository. Here’s a screenshot:

    create repository
  2. Github makes the next step very convenient: it displays the instructions for synchronizing our local git repository to it, under the heading …or push an existing repository from the command line as shown below:

    add remote

    So, follow those instructions (not the ones in the above screenshot, the git URL’s are unique to each repository). In a local shell, cd over to your config-repo, add the remote and invoke the git push command as instructed. At this point, you can refresh your config-repo github page in your web browser, which should now look something like this:

    github repo view
  3. Finally, copy the URL to this newly-created repository to your clipboard. Use the https URL as it’s usually more convenient and accessible. Click on the "Clone or Download" green button, select "Use HTTPS", and click on the "Copy to clipboard" button:

    grab repo url

Ok, we’re all set for the next step: creating the service.

4.2. Create the Config Service

This time around, we must provide some configuration information to the create-service command, in order to configure the config server with the github repository URL we just created.

  1. Create a simple json file, name it "config.json". Its contents should resemble this:

    { "git": { "uri": "https://github.com/eitansuez/config-repo.git" } }
    The above git URL is just an example. You must substitute your git repository URL in its place.
  2. Now, invoke the command to create the service, like this:

    $ cf create-service p-config-server trial config-server -c config.json

    For a thorough documentation on creating and configuring a config server in PCF with SCS, consult the SCS documentation here.

As before, the service creation should now be in progress:

$ cf services
Getting services in org eitan-org / space eitan-space as esuez@pivotal.io...
OK

name               service              plan    bound apps   last operation
config-server      p-config-server      trial                create in progress
service-registry   p-service-registry   trial                create succeeded

Once created, we should now have a running config server, configured with the proper git repository. Verify this by visiting the Apps Manager once more: navigate to the config-server service, click on the Manage link as before, and you’ll see the dashboard page for the config server:

config server dashboard

It’s important to note that, in PCF, there’s little to no value in having the config server register with eureka in order to make it discoverable. PCF has its own mechanism of relating the config server URL to bound applications via environment variables.

5. Push the Apps

The services that both fortune and greeting depend on are now running.

There’s a difference between the way these services are configured locally and the way they’re configured in PCF:

⇒ The PCF services are secured with an OAuth2 server

This means that clients cannot just connect to these services without authenticating (i.e. providing credentials). SCS boils this task down to modifying the client applications' build dependencies.

5.1. Revise dependencies

Replace the spring cloud "starter" dependencies with SCS-specific verisons:

  1. spring-cloud-starter-eureka becomes spring-cloud-services-starter-service-registry

  2. spring-cloud-starter-config becomes spring-cloud-services-stater-config-client

greeting-app/build.gradle
@@ -22,6 +22,7 @@ repositories {
 }

 ext {
+  springCloudServicesVersion = '1.6.3.RELEASE'
   springCloudVersion = 'Edgware.SR2'
 }

@@ -31,9 +32,10 @@ dependencies {
   compile 'org.springframework.boot:spring-boot-starter-freemarker'

   compile 'org.springframework.cloud:spring-cloud-starter-hystrix'
-  compile 'org.springframework.cloud:spring-cloud-starter-eureka'
   compile 'org.springframework.cloud:spring-cloud-starter-feign'
-  compile 'org.springframework.cloud:spring-cloud-starter-config'
+
+  compile 'io.pivotal.spring.cloud:spring-cloud-services-starter-service-registry'
+  compile 'io.pivotal.spring.cloud:spring-cloud-services-starter-config-client'

   runtime 'org.springframework.boot:spring-boot-devtools'

@@ -45,6 +47,7 @@ dependencies {

 dependencyManagement {
   imports {
+    mavenBom "io.pivotal.spring.cloud:spring-cloud-services-dependencies:${springCloudServicesVersion}"
     mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
   }
 }

⇒ Apply the above change to both the greeting application and the fortune service’s build files.

The principal difference when using the SCS dependencies is that we transitively pull in Spring Security OAuth2.

For good measure, re-run the tests for each project and make sure they still pass.

5.2. Turn off Security

One side-effect of adding security libraries to our clients is that Spring Security defaults to turning on basic authentication. Meaning that we can’t access fortunes without authenticating.

Let’s turn this off, by committing a change to config-repo’s application.yml:

config-repo/application.yml
@@ -2,6 +2,10 @@ management:
   security:
     enabled: false

+security:
+  basic:
+    enabled: false
+
 logging:
   level:
     io:

Make sure to:

$ git add application.yml
$ git commit -m "turn off basic auth"
$ git push origin master

5.3. Write the CF Deployment Manifest

Let’s start with the fortune service.

First, build the project to produce the jar artifact:

$ cd fortune-service
$ gradle assemble

This will produce a jar file in the build/libs/ folder. Note the name of the jar file.

The simplest way to deploy to cloudfoundry is by writing a manifest file. Name the file manifest.yml and place it in the base directory of the fortune project. Let’s analyze its contents:

---
applications:
- name: fortune
  path: build/libs/fortune-service-0.0.1-SNAPSHOT.jar (1)
  host: eitan-fortune-service (2)
  memory: 768M
  services: (3)
  - config-server
  - service-registry
  env:
    SPRING_CLOUD_CONFIG_DISCOVERY_ENABLED: false (4)
1 The path to the jar file we’re deploying
2 Customize the host name to make it unique, as a precaution against other deployed applications with a similar URL (may not be necessary)
3 Cite the services to bind to the application
4 Turn off obtaining the config server URL via discovery; in CloudFoundry, this information is communicated via environment variables

5.4. Deploy fortune

We’re now ready to deploy fortune. From the fortune directory invoke:

$ cf push

As you push the application, it’s a good idea to monitor the application’s log for exceptions, and to observe and follow the startup sequence of the application. In a separate terminal, invoke:

$ cf logs fortune

For example, you should see log messages confirming that the application is fetching configuration from the config server:

c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at: https://config-b8bb6736-900b-456f-b3ee-ab7cb4b7a76f.cfapps.io

Similarly, you should see messages indicating that the application is registering with the discovery server:

o.s.c.n.e.s.EurekaServiceRegistry        : Registering application fortune with eureka with status UP
com.netflix.discovery.DiscoveryClient    : Saw local status change event StatusChangeEvent [timestamp=1504804631510, current=UP, previous=STARTING]
com.netflix.discovery.DiscoveryClient    : DiscoveryClient_FORTUNE/eitan-fortune-service.cfapps.io:da3cbcd2-aeb8-4515-61c9-8e1c: registering service...
com.netflix.discovery.DiscoveryClient    : DiscoveryClient_FORTUNE/eitan-fortune-service.cfapps.io:da3cbcd2-aeb8-4515-61c9-8e1c - registration status: 204
  • Visit the PCF eureka dashboard: do you see the fortune service on the list of registered instances?

    registered instance
  • Visit the application’s URL: do you see fortunes?

  • Can you access the application’s management endpoints? For example, the env/ endpoint?

How did fortune know the URL of the config server? ..of the discovery sever?

To answer that question, we need to look at the value of a special environment variable VCAP_SERVICES, associated with the fortune application, which was set as a consequence of binding the application to the two services. Invoke:

$ cf env fortune

Here is the output:

"VCAP_SERVICES": {
 "p-config-server": [
  {
   "credentials": {
    "access_token_uri": "https://p-spring-cloud-services.uaa.run.pivotal.io/oauth/token",
    "client_id": "p-config-server-c3064cb2-dd1d-40ff-9ccd-84c149194197",
    "client_secret": "******",
    "uri": "https://config-b8bb6736-900b-456f-b3ee-ab7cb4b7a76f.cfapps.io"
   },
   "label": "p-config-server",
   "name": "config-server",
   "plan": "trial",
   "provider": null,
   "syslog_drain_url": null,
   "tags": [ "configuration", "spring-cloud" ],
   "volume_mounts": []
  }
 ],
 "p-service-registry": [
  {
   "credentials": {
    "access_token_uri": "https://p-spring-cloud-services.uaa.run.pivotal.io/oauth/token",
    "client_id": "p-service-registry-7e3db5a8-546f-4b40-a8fd-d8874d316ae0",
    "client_secret": "******",
    "uri": "https://eureka-cd62d1f9-2e56-4a95-9c0c-68cc2f38d691.cfapps.io"
   },
   "label": "p-service-registry",
   "name": "service-registry",
   "plan": "trial",
   "provider": null,
   "syslog_drain_url": null,
   "tags": [ "eureka", "discovery", "registry", "spring-cloud" ],
   "volume_mounts": []
  }
 ]
}

For each service, not only do we have its URL, but also the credentials necessary to access it, and the oauth server’s uri for obtaining tokens using these credentials. All of this is done on our behalf by SCS.

5.5. Deploy greeting

The task here is very similar: build the jar file, write the manifest, and push the application to cloud foundry:

$ cd greeting-app
$ gradle assemble
$ vi manifest.yml ...
$ cf push

And here’s the manifest file:

---
applications:
- name: greeting
  path: build/libs/greeting-app-0.0.1-SNAPSHOT.jar
  host: eitan-greeting
  memory: 768M
  services:
  - config-server
  - service-registry
  env:
    SPRING_CLOUD_CONFIG_DISCOVERY_ENABLED: false

Our system is now functioning in the cloud. Both applications are deployed, running. Both are registered with Eureka, and both obtain their configuration from the Config Server.

6. Spring Cloud Bus

Let’s, once more, run through the scenario of modifying the feature toggle greeting.displayFortune, pushing that change to the github repository, and submitting an HTTP post to the /refresh endpoint of the greeting application in PCF, and observing that the application reloads the configuration and alters its behavior correspondingly.

However, before we do that, consider the following question:

If greeting were scaled to, say 100 instances, how would we go about refreshing the configuration from every single instance?

I suppose we could script submitting a "/refresh" command in a for loop. There’s a better way.

The Spring team offers a project named Spring Cloud Bus designed to solve this very problem, via messaging.

The idea is for all application instances to communicate over a message bus. Sending a /bus/refresh signal (an http POST) to any instance will have the side effect of having that instance broadcast the message to all other running instances.

To add this capability to an application requires:

  • creating and binding an AMQP-compatible message broker service to the application

  • adding the dependency spring-cloud-starter-bus-amqp to the application’s build file

Do this for greeting:

  1. Add the dependency:

    greeting-app/build.gradle
    @@ -37,6 +37,8 @@ dependencies {
       compile 'io.pivotal.spring.cloud:spring-cloud-services-starter-service-registry'
       compile 'io.pivotal.spring.cloud:spring-cloud-services-starter-config-client'
    
    +  compile 'org.springframework.cloud:spring-cloud-starter-bus-amqp'
    +
       runtime 'org.springframework.boot:spring-boot-devtools'
    
       compileOnly 'org.projectlombok:lombok'
  2. Rebuild the jar file:

    $ gradle assemble
  3. Create a rabbitmq service. Pivotal Web Services offers a service named cloudamqp to provision rabbitmq instances:

    $ cf create-service cloudamqp lemur thecloudbus
    Your PCF instance’s marketplace may have a service named p-rabbitmq instead of cloudamqp.
  4. Add the service to manifest.yml:

    greeting-app/manifest.yml
    @@ -7,6 +7,7 @@ applications:
       services:
       - config-server
       - service-registry
    +  - thecloudbus
       env:
         SPRING_CLOUD_CONFIG_DISCOVERY_ENABLED: false
  5. Finally, redeploy the application:

    $ cf push

6.1. Testing Refreshing Multiple Instances

  1. Scale greeting to 3 instances:

    $ cf scale -i 3 greeting
  2. In a browser, visit the greeting application, and note that no fortune is displayed:

    greeting app
  3. In config-repo, modify the configuration of greeting.yml by toggling the property displayFortune:

    config-repo/greeting.yml
    @@ -1,5 +1,5 @@
     greeting:
    -  displayFortune: false
    +  displayFortune: true
    
     fortune:
       ribbon:
  4. Commit and push the change

Before sending the refresh signal, tail the logs for the greeting application, so we can observe what happens when the signal is received:

$ cf logs greeting

Now, in a separate terminal, or tab, send the http POST request:

$ http post https://eitan-greeting.cfapps.io/bus/refresh

In the logs you should see messages relating to a "remote refresh request", similar to this:

[APP/PROC/WEB/1]  : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@61a6d658: startup date [Thu Sep 07 19:01:43 UTC 2017]; root of context hierarchy
[APP/PROC/WEB/1]  : Fetching config from server at: https://config-b8bb6736-900b-456f-b3ee-ab7cb4b7a76f.cfapps.io
[APP/PROC/WEB/1]  : Located property source: CompositePropertySource [name='configService', propertySources=[MapPropertySource [name='configClient'], MapPropertySource [name='https://github.com/eitansuez/config-repo.git/greeting.yml'], MapPropertySource [name='https://github.com/eitansuez/config-repo.git/application.yml']]]
[APP/PROC/WEB/1]  : Located environment: name=greeting, profiles=[cloud], label=null, version=508b14a26415f4581884395594fdc57acf4aed9c, state=null
[APP/PROC/WEB/1]  : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@37d5d92d: startup date [Thu Sep 07 19:01:44 UTC 2017]; parent: org.springframework.context.annotation.AnnotationConfigApplicationContext@61a6d658
[APP/PROC/WEB/1]  : Received remote refresh request. Keys refreshed [config.client.version, greeting.displayFortune]

The above messages should display three times: once for each application instance. i.e. the messages should be prefixed with [APP/PROC/WEB/0], [APP/PROC/WEB/1], and [APP/PROC/WEB/2].

Refresh the greeting page, fortunes should now display. And each time you visit the greeting page, the fortune application’s log should output a message similar to this:

i.p.training.fortune.FortuneController   : retrieving fortune 'You can always find happiness at work on Friday'

7. Load Balancing

In CloudFoundry, all application instances have the same URL, handled by a component known as the Gorouter, which fronts and load-balances requests across application instances.

So the task of load balancing can be left up to the platform, and we don’t need to use Ribbon. It’s not harmful to leave Ribbon in place, but the work that Ribbon does, of selecting an instance from among multiple candidates is for naught, since each service instance resolves to the same URL.

We can glean that this is so by inspecting the eureka dashboard:

same url in eureka

Here we see three instances of GREETING. It happens that each serviceId is composed of multiple pieces of information, the first element being the instance’s hostname. The hostname is clearly the same value, for all three instances.

There is however a use case, and a good reason for using Ribbon instead of the Gorouter. In PCF, we can enable a feature called container-to-container networking, giving applications the permission to contact each other directly (i.e. without going through the Gorouter).

In this configuration, client-side load-balancing with Ribbon becomes meaningful. In fact this configuration is more secure.

In CloudFoundry, when an application is given a route, it becomes directly accessible to the outside world. If some of our services, for example the fortune service, are strictly backing services, not meant to be accessed directly by clients, then we can make them only accessible to the services within PCF that need to talk to them. We can remove the route from fortune altogether, and let greeting contact it directly via its internal IP address.

  1. Allow greeting to access fortune directly:

    $ cf add-network-policy greeting --destination-app fortune --protocol tcp --port 8080
  2. Verify the above succeeded, with this command:

    $ cf network-policies
    Listing policies as esuez@pivotal.io...
    OK
    
    Source		Destination	Protocol	Port
    greeting	fortune		tcp		8080
  3. Configure Spring Cloud Services such that all eureka instances register with their direct ip address, instead of their url. We do this via a configuration property. So, head over to config-repo and edit application.yml as follows:

    config-repo/application.yml
    @@ -6,6 +6,11 @@ security:
       basic:
         enabled: false
    
    +spring:
    +  cloud:
    +    services:
    +      registrationMethod: direct
    +
     logging:
       level:
         io:
  4. Commit and push this change to the github repository.

  5. Remove the route from the fortune service so that it cannot be accessed directly from outside PCF:

    $ cf unmap-route fortune cfapps.io --hostname eitan-fortune-service
  6. Verify that fortune no longer has a URL:

    $ cf apps
    Getting apps in org eitan-org / space eitan-space as esuez@pivotal.io...
    OK
    
    name       requested state   instances   memory   disk   urls
    fortune    started           1/1         768M     1G
    greeting   started           3/3         768M     1G     eitan-greeting.cfapps.io

    Also, visit the fortune url directly and see that you now get a 404 (not found) response.

  7. Restart the fortune service and watch its registration serviceId change in the eureka dashboard, to something resembling 10.251.219.116:d9ebf80e-081f-4ceb-735c-63ce

  8. Scale greeting back to one instance, and fortune to three instances:

$ cf scale -i 1 greeting
$ cf scale -i 3 fortune

You may need to wait a minute or so, for the new instances to come up, for their registrations to show up in eureka, and for the greeting application’s cache of the service registry to expire. Afterwards, greeting should be using ribbon to communicate with fortune directly, not only doing so more securely, but also eliminating an extra network "hop".

Tail the logs for fortune and visit the greeting page multiple times. The output should reveal two things:

  1. HTTP requests no longer go through the Gorouter on behalf of fortune application instances. There should be no log messages prefixed with [RTR], and

  2. Requests should be load-balanced across all three fortune instances, according to the WeightedResponseTime rule

$ cf logs fortune

[APP/PROC/WEB/0] FortuneController   : retrieving fortune 'You learn from your mistakes... You will learn a lot today.'
[APP/PROC/WEB/2] FortuneController   : retrieving fortune 'You can always find happiness at work on Friday'
[APP/PROC/WEB/0] FortuneController   : retrieving fortune 'You can always find happiness at work on Friday'
[APP/PROC/WEB/2] FortuneController   : retrieving fortune 'You will be hungry again in one hour.'
[APP/PROC/WEB/1] FortuneController   : retrieving fortune 'You can always find happiness at work on Friday'
[APP/PROC/WEB/1] FortuneController   : retrieving fortune 'You learn from your mistakes... You will learn a lot today.'
[APP/PROC/WEB/0] FortuneController   : retrieving fortune 'Today will be an awesome day!'
[APP/PROC/WEB/2] FortuneController   : retrieving fortune 'You can always find happiness at work on Friday'
[APP/PROC/WEB/1] FortuneController   : retrieving fortune 'Today will be an awesome day!'
[APP/PROC/WEB/2] FortuneController   : retrieving fortune 'You will be hungry again in one hour.'
[APP/PROC/WEB/0] FortuneController   : retrieving fortune 'Today will be an awesome day!'
[APP/PROC/WEB/1] FortuneController   : retrieving fortune 'You can always find happiness at work on Friday'
[APP/PROC/WEB/0] FortuneController   : retrieving fortune 'You learn from your mistakes... You will learn a lot today.'

8. Hystrix

The goal here is to be able to monitor the greeting application’s circuit breaker in Cloud Foundry. Again, SCS does all of the hard work for us. We simply need to:

  1. create a hystrix-dashboard service

  2. bind the service to the greeting application

  3. visit the hystrix dashboard in cloud foundry

In this scenario, we’ll also scale greeting back up to three instances and observe that Spring Cloud Services automatically aggregates metrics from all application instances into a single dashboard, courtesy of the Netflix Turbine project, and Spring Cloud’s Turbine Stream.

  1. Replace greeting’s hystrix dependency with the spring cloud services version (like we did previously for config and service registry):

    greeting-app/build.gradle
    @@ -31,11 +31,11 @@ dependencies {
       compile 'org.springframework.boot:spring-boot-starter-actuator'
       compile 'org.springframework.boot:spring-boot-starter-freemarker'
    
    -  compile 'org.springframework.cloud:spring-cloud-starter-hystrix'
       compile 'org.springframework.cloud:spring-cloud-starter-feign'
    
       compile 'io.pivotal.spring.cloud:spring-cloud-services-starter-service-registry'
       compile 'io.pivotal.spring.cloud:spring-cloud-services-starter-config-client'
    +  compile 'io.pivotal.spring.cloud:spring-cloud-services-starter-circuit-breaker'
    
       compile 'org.springframework.cloud:spring-cloud-starter-bus-amqp'
  2. Rebuild the jar artifact for greeting:

    $ cd greeting-app
    $ gradle assemble
  3. Create the hystrix dashboard service:

    $ cf create-service p-circuit-breaker-dashboard trial hystrix-dashboard
  4. Add the service to the manifest file for the greeting application:

    greeting-app/manifest.yml
    @@ -8,6 +8,7 @@ applications:
       - config-server
       - service-registry
       - thecloudbus
    +  - hystrix-dashboard
       env:
         SPRING_CLOUD_CONFIG_DISCOVERY_ENABLED: false
  5. Redeploy the application:

    $ cd greeting-app
    $ cf push
  6. Scale greeting back up to three instances

    $ cf scale -i 3 greeting
  7. Make sure all three instances are running:

    $ cf app greeting
    Showing health and status for app greeting in org eitan-org / space eitan-space as esuez@pivotal.io...
    
    name:              greeting
    requested state:   started
    instances:         3/3
    usage:             768M x 3 instances
    routes:            eitan-greeting.cfapps.io
    last uploaded:     Thu 07 Sep 15:21:13 CDT 2017
    stack:             cflinuxfs2
    buildpack:         ...
    
         state      since                  cpu      memory           disk           details
    #0   running    2017-09-07T20:22:41Z   296.2%   288.8M of 768M   172.5M of 1G
    #1   running    2017-09-07T20:22:59Z   0.0%     0 of 768M        0 of 1G
    #2   running    2017-09-07T20:23:36Z   0.0%     0 of 768M        0 of 1G
  8. Visit the greeting application a few times (refresh or reload the page and see fortunes come through)

  9. Next, visit the hystrix dashboard: using the apps manager, navigate to the Services tab, select Circuit Breaker, and click the Manage link from the top right-hand corner of the page.

    hystrix dashboard pcf

In the above screenshot, notice that the graph for the circuit breaker represents aggregate metrics across three instances of greeting. As we did in the circuit breakers lab, at this point, we can experiment by simulating requests against greeting, stopping the fortune application, and seeing the circuit open. In the reverse direction, we can restart fortune, and watch the circuit close once more.

9. Congratulations

Spring Cloud Services helps make Spring Cloud-based microservice applications first-class citizens on Cloud Foundry. It provides a production-grade implementation of a Cloud Foundry service broker that can stand up secure instances of the Spring Cloud services on demand. The product’s documentation goes deeper into more sophisticated configurations, such as standing up discovery services as a cluster with multiple peers.

Congratulations for making it through this course!