While you’ve probably heard that Cloud Foundry allows you to build and scale applications very quickly and boost developer productivity 10x, the platform also allows new services to be added very quickly and easily.
To clarify, I don’t mean adding or provisioning a service instance—those are deployed in seconds. I mean you can add an entirely new service, like adding the ability to provision data source instances from a service in your own cloud when the ability didn’t exist before.
As a field engineer who spends most of my time with customers, one of the most common requests I hear is on the topic of adding new services. People ask, “How do we add new (or existing) services to our Cloud Foundry ecosystem?” I recently spent one work day setting up a brand new service and am here to show others how to quickly and easily add services the same way.
This article provides a) an overview of the Cloud Foundry runtime architecture, b) explains what it means to add a Managed Service to Cloud Foundry using a Service Broker, and c) introduces a working example based on MongoDB. The downloadable, forkable Java bits will give you a Spring MVC-based Service Broker app that includes a) reusable, boilerplate code implementing the Service Broker API required by Cloud Foundry to integrate and talk with your service b) example code that adds MongoDB as a new service. With the boilerplate code from GitHub, you can save a lot of time adding new services to Cloud Foundry using Spring.
While the article explains what is happening at a high level, there are plenty of links to more detailed resources for those that are ready to learn more and get their hands dirty.
Overview: Cloud Foundry’s Runtime, Configuration, and Deployment Architecture
Cloud Foundry supports two service integration models. User Provided Service Instances allow you to expose an existing external service instance, like an existing Oracle database or enterprise service bus, in the platform for consumption by apps. Managed Services are integrated with Cloud Foundry by implementing the Service Broker API. The Service Broker API provides extreme flexibility to service providers, allowing for simple and complex service models to be implemented while ensuring a simple and consistent service interaction model for application developers. This article focuses on the creation of Service Brokers to add new Managed Services to your Cloud Foundry ecosystem.
Before we dive in to the Service Broker explanation, example, and code, it’s important to for us to ground ourselves in the overall picture of Cloud Foundry and where a new service fits in.
While there are other components in the architecture, we need and understanding of the runtime, configuration, and deployment components—the Cloud Controller, Command Line, and DEA. The Cloud Controller (CC) is the horizontally scalable, central brain of Cloud Foundry. It maintains a database of orgs, spaces, applications, services, service instances, versions, and other meta-data. It also stores code in application files to build apps from, provides REST API endpoints, and allows access via the Command Line (CLI) for managing the orgs, apps, services, commands, etc. When you are ready to deploy an app for the first time, you use the CLI to communicate with the Cloud Controller and define your application. The Cloud Controller communicates with the Droplet Execution Agent (DEA) to stage the app—this means it is packaging the application code up with configuration and a Ruby, Java, or Node.js buildpack. When an app has completed the staging process, we have a tarball that can be untarred and run on any DEA in the system. BOSH runs underneath our deployment services, and, with it, we can manually or automatically launch 1 to hundreds or even thousands of additional servers agnostically to Cloud Foundry, Amazon’s cloud, vSphere, or other infrastructure.
Besides staging the app, the DEA manages the runtime in isolated environments as a container within the VM. It manages the application lifecycle (e.g. starts, stops) and monitors state. While they are not critical to the remainder of this article, it is worth mentioning several of the other more intuitive components that help make Cloud Foundry a truly extensible, comprehensive, enterprise-ready PaaS. As you can see in the diagram below, there is an authentication service, traffic router, message bus (NATS), health manager and collector. These are all designed for distributed computing and cloud-ready scale of apps and services.
Adding Services to the Cloud Foundry Platform
Any type of add-on can be considered a service, and people commonly consider databases or messaging as the main type of service for an application. For example, you can use the ClearDB service to get service instances of MySQL or the CloudAMQP service to get services instances of RabbitMQ on the public Cloud Foundry installation at run.pivotal.io. There are a number of existing services in the Cloud Foundry ecosystem for things like MongoDB, PostgreSQL, Memcached, Redis, SendGrid, ElasticSearch, Hadoop, and more. These services are integrated into the platform using the Managed Services functionality and by implementing a Service Broker. Despite the growing ecosystem of services available, you will still likely need to define new services and develop a new Service Broker like SAP did with their HANA database on Cloud Foundry. For example, your first step may be adding a service like Pivotal GemFire or some other open source cache mechanism for managing session state in the Cloud Foundry stateless runtime layer.
The Service Broker API provides a standard interface for the Cloud Controller to manage services. A Service Broker allows for 1) catalog management for the collection of Cloud Controller meta-data, 2) provisioning new service instances of the service (allocating resources), 3) de-provisioning service instances, 4) binding to a service instance (providing credentials to the app to use the service instance), and 5) unbinding from a service instance. The implementation details of each function above are left to the Service Broker implementation, allowing for extreme customization and flexibility including having multiple versions of a service like MongoDB . For example, one service might spin up a new database server for each service instance and another might create a new database in a cluster for each new service instance.
Example Code: A Spring MVC Based App Boilerplate for New Services and a MongoDB Example
As I started down the path of adding a service, I wanted to keep things simple. After reading the documentation and APIs on developing a Service Broker, I decided to use Spring MVC to create a service that just logged requests from the Cloud Controller. Again, this included logging the five Service Broker based requests listed above. This way, I could see what the Cloud Controller wanted to request and receive from my service—more or less a stub logger.
After building this initial stub/logging service, I realized parts of the Service Broker REST API implementation could be re-used by extracting them into a boilerplate project. The boilerplate implements the REST API required by the Cloud Controller, and defines new Java interfaces to be implemented for a new service instead of focusing on the REST contract. The boilerplate allows new services and their associated Service Broker component to be created very quickly by implementing a few interfaces.
Then, I refactored this code into a framework and put it to the test by implementing a new, functional MongoDB service. Writing the MongoDB-specific Service Broker only took about 8 hours because I could focus on the MongoDB-specific code, not the REST interface required by the Cloud Controller. This saved a tremendous amount of time and would probably save time for anyone else who is writing a service’s Service Broker interface.
When developing new Service Broker, a few questions need to be answered:
- What happens when a new service instance is created?
- What happens when a new bind request is received?
- What credentials does an application need to bind to the service instance?
In the case of the MongoDB example, here is how we answered these questions:
- What happens when a new service instance is created? A new database is created on an existing MongoDB server.
- What happens when a new bind request is received? A new user is created for the database instance using the bind request id sent by the cloud controller.
- What credentials does an application need to bind to the service instance? The URI of the MongoDB server, the database created as a service instance, and the user account created as part of the binding request.
There are many options for how the implementation could have been completed. For example, a new service instance request could trigger the deployment of a whole new MongoDB server. However, for the purposes of the example, I decided to keep it simple with the functionality described above.
The Service Broker code has been contributed to the Cloud Foundry Community on GitHub. There are a few additional items to point out:
- The REST API implementation is in the master branch.
- The MongoDB implementation is in the MongoDB branch as an example.
Please fork, use, and extend the code as you see fit!
For more information on the Pivotal products mentioned in this article, visit the websites for cloudfoundry.org, cloudfoundry.com, and spring.io or read additional articles like this on Cloud Foundry, Redis, Pivotal GemFire, and RabbitMQ.