Managed Services in CloudFoundry
CloudFoundry defines a Service Broker API which can be implemented and added to a CloudFoundry installation to provide managed services for apps. In order to better understand the way managed services are created and integrated with CloudFoundry (and derivative technologies like Stackato and HP Helion Development Platform), I created an example service and implemented the Service Broker API to manage it. The code for both implementations are on github.
Deploy the services
For this exercise I deployed bosh-lite on my Windows 7 laptop. I then follow the documented procedure to push cf-echo-service and cf-service-broker-python to CloudFoundry. The desired end state is two running apps in CloudFoundry that can be used for service broker management and testing.
NOTE: Deploy the echo-service first. Then update the service-broker script so that it will use the cf hosted echo-service. The relevant lines in the service-broker.py script are shown.
# UPDATE THIS FOR YOUR ECHO SERVICE DEPLOYMENT service_base = "echo-service.10.244.0.34.xip.io" |
Security
In this setup, it is necessary for the service-broker app to communicate with the echo-service app within CloudFoundry. This requires adding a security group and applying it to running services. The first step is to create a JSON file with the definition of the new security group.
[ { "protocol": "tcp", "destination": "10.244.0.34", "ports": "80" } ] |
The security group can then be created using create-security-group. This command expects a security group name and a path to the JSON file created above. After creating the security group, it must be bound to running instances (optionally to staging instances too using bind-staging-security-group). If the service-broker was deployed prior to enabling this security group, the app will also need to be restarted.
vagrant@vagrant-ubuntu-trusty-64:~$ cf create-security-group port80 security.json Creating security group port80 as admin OK vagrant@vagrant-ubuntu-trusty-64:~$ cf security-groups Getting security groups as admin OK Name Organization Space #0 public_networks #1 dns #2 port80 vagrant@vagrant-ubuntu-trusty-64:~$ cf bind-running-security-group port80 Binding security group port80 to defaults for running as admin OK TIP: Changes will not apply to existing running applications until they are restarted. vagrant@vagrant-ubuntu-trusty-64:~$ cf restart service-broker Stopping app service-broker in org myorg / space mydept as admin... OK Starting app service-broker in org myorg / space mydept as admin... 0 of 1 instances running, 1 starting 1 of 1 instances running App started OK Showing health and status for app service-broker in org myorg / space mydept as admin... OK requested state: started instances: 1/1 usage: 256M x 1 instances urls: service-broker.10.244.0.34.xip.io last uploaded: Mon Nov 24 15:59:07 UTC 2014 state since cpu memory disk #0 running 2014-11-24 08:13:40 PM 0.0% 56M of 256M 0 of 1G |
NOTE: I deploy them to CloudFoundry for convenience and reliability, but this is not required. The service could be any service and the service broker API implementation can be deployed anywhere.
vagrant@vagrant-ubuntu-trusty-64:~$ cf apps Getting apps in org myorg / space mydept as admin... OK name requested state instances memory disk urls echo-service started 1/1 256M 1G echo-service.10.244.0.34.xip.io service-broker started 1/1 256M 1G service-broker.10.244.0.34.xip.io |
Optional validation
It is possible to validate that the deployed apps work as expected. The curl commands below validate the echo service is working properly.
vagrant@vagrant-ubuntu-trusty-64:~$ curl -X PUT http://echo-service.10.244.0.34.xip.io/echo/51897770-560b-11e4-b75a-9ad2017223a9 -w "\n" {"instance_id": "51897770-560b-11e4-b75a-9ad2017223a9", "state": "provision_success", "dashboard_url": "http://localhost:8090/dashboard/51897770-560b-11e4-b75a-9ad2017223a9"} vagrant@vagrant-ubuntu-trusty-64:~$ curl -X PUT http://echo-service.10.244.0.34.xip.io/echo/51897770-560b-11e4-b75a-9ad2017223a9/myapp -w "\n" {"state": "bind_success", "id": "51897770-560b-11e4-b75a-9ad2017223a9", "app": "myapp"} vagrant@vagrant-ubuntu-trusty-64:~$ curl -X POST http://echo-service.10.244.0.34.xip.io/echo/51897770-560b-11e4-b75a-9ad2017223a9/myapp -H "Content-Type: application/json" -d '{"message": "Hello World"}' -w "\n" {"response": "Hello World"} vagrant@vagrant-ubuntu-trusty-64:~$ curl -X POST http://echo-service.10.244.0.34.xip.io/echo/51897770-560b-11e4-b75a-9ad2017223a9/myapp -H "Content-Type: application/json" -d '{"message": "Hello World 2"}' -w "\n" {"response": "Hello World 2"} vagrant@vagrant-ubuntu-trusty-64:~$ curl -X POST http://echo-service.10.244.0.34.xip.io/echo/51897770-560b-11e4-b75a-9ad2017223a9/myapp -H "Content-Type: application/json" -d '{"message": "Hello Worldz!"}' -w "\n" {"response": "Hello Worldz!"} vagrant@vagrant-ubuntu-trusty-64:~$ curl -X GET http://echo-service.10.244.0.34.xip.io/echo/dashboard/51897770-560b-11e4-b75a-9ad2017223a9 <table class="pure-table"> <thead> <tr> <th>Instance</th> <th>Bindings</th> <th>Messages</th> </tr> </thead> <tbody> <tr> <td>51897770-560b-11e4-b75a-9ad2017223a9</td> <td>1</td> <td>3</td> </tr> </tbody> </table> vagrant@vagrant-ubuntu-trusty-64:~$ curl -X DELETE http://echo-service.10.244.0.34.xip.io/echo/51897770-560b-11e4-b75a-9ad2017223a9/myapp -w "\n" {"state": "unbind_success", "id": "51897770-560b-11e4-b75a-9ad2017223a9", "app": "myapp"} vagrant@vagrant-ubuntu-trusty-64:~$ curl -X DELETE http://echo-service.10.244.0.34.xip.io/echo/51897770-560b-11e4-b75a-9ad2017223a9 -w "\n" {"state": "deprovision_success", "bindings": 0, "id": "51897770-560b-11e4-b75a-9ad2017223a9", "messages": 3} |
The service broker API implementation can also be verified using curl as shown below.
vagrant@vagrant-ubuntu-trusty-64:~$ curl -X GET http://service-broker.10.244.0.34.xip.io/v2/catalog -H "X-Broker-Api-Version: 2.3" -H "Authorization: Basic dXNlcjpwYXNz" -w "\n" {"services": [{"name": "Echo Service", "dashboard_client": {"id": "client-id-1", "redirect_uri": "http://echo-service.10.244.0.34.xip.io/echo/dashboard", "secret": "secret-1"}, "description": "Echo back the value received", "id": "echo_service", "plans": [{"free": false, "description": "A large dedicated service with a big storage quota, lots of RAM, and many connections", "id": "big_0001", "name": "large"}], "bindable": true}, {"name": "Invert Service", "dashboard_client": null, "description": "Invert the value received", "id": "invert_service", "plans": [{"description": "A small shared service with a small storage quota and few connections", "id": "small_0001", "name": "small"}], "bindable": true}]} vagrant@vagrant-ubuntu-trusty-64:~$ curl -X PUT http://service-broker.10.244.0.34.xip.io/v2/service_instances/mynewinstance -H "Content-type: application/json" -H "Authorization: Basic dXNlcjpwYXNz" -d '{"service_id": "echo_service", "plan_id": "small_0001", "organization_guid": "HP", "space_guid": "IT"}' -w "\n" {"dashboard_url": "http://echo-service.10.244.0.34.xip.io/echo/dashboard/mynewinstance"} vagrant@vagrant-ubuntu-trusty-64:~$ curl -X PUT http://service-broker.10.244.0.34.xip.io/v2/service_instances/mynewinstance/service_bindings/myappid -H "Content-type: application/json" -H "Authorization: Basic dXNlcjpwYXNz" -d '{"service_id": "echo_service", "plan_id": "small_0001", "app_guid": "otherappid"}' -w "\n" {"credentials": {"uri": "http://echo-service.10.244.0.34.xip.io/echo/mynewinstance/myappid"}} vagrant@vagrant-ubuntu-trusty-64:~$ curl -X DELETE http://service-broker.10.244.0.34.xip.io/v2/service_instances/mynewinstance/service_bindings/myappid -H "Authorization: Basic dXNlcjpwYXNz" -w "\n" {} vagrant@vagrant-ubuntu-trusty-64:~$ curl -X DELETE http://service-broker.10.244.0.34.xip.io/v2/service_instances/mynewinstance -H "Authorization: Basic dXNlcjpwYXNz" -w "\n" {} |
Manage the Service Broker
In CloudFoundry, the service broker can be added using the create-service-broker command with the cf cli.
vagrant@vagrant-ubuntu-trusty-64:~$ cf create-service-broker echo-broker user pass http://service-broker.10.244.0.34.xip.io Creating service broker echo-broker as admin... OK vagrant@vagrant-ubuntu-trusty-64:~$ cf service-brokers Getting service brokers as admin... name url echo-broker http://service-broker.10.244.0.34.xip.io |
The service broker is added, but the service plans in the catalog do not allow public access by default, as can be seen with the call to service-access below. This also means that calls to marketplace show no available services.
vagrant@vagrant-ubuntu-trusty-64:~$ cf service-access Getting service access as admin... broker: echo-broker service plan access orgs Echo Service large none Invert Service small none vagrant@vagrant-ubuntu-trusty-64:~$ cf marketplace Getting services from marketplace in org myorg / space mydept as admin... OK No service offerings found |
The the service plan must be enabled to accommodate public consumption. This is done using the enable-service-access call and providing the name of the service. In this case, the “Echo Service” is desired. After enabling the service plan, the service-access listing shows it available and a call to marketplace shows it available to apps.
vagrant@vagrant-ubuntu-trusty-64:~$ cf enable-service-access "Echo Service" Enabling access to all plans of service Echo Service for all orgs as admin... OK vagrant@vagrant-ubuntu-trusty-64:~$ cf service-access Getting service access as admin... broker: echo-broker service plan access orgs Echo Service large all Invert Service small none vagrant@vagrant-ubuntu-trusty-64:~$ cf marketplace Getting services from marketplace in org myorg / space mydept as admin... OK service plans description Echo Service large Echo back the value received |
Manage Services
With the new service in the marketplace, it’s now possible to provision, bind and use instances of the new service by way of the cf cli. This happens in a few steps in CloudFoundry. First an instance of the service is provisioned. An app is then pushed to CloudFoundry. Finally the service and the app are associated through a binding. The call to create-service expects three arguments which can be found in the marketplace output above.
- service name
- service plan
- a name for the new service instance
vagrant@vagrant-ubuntu-trusty-64:~$ cf create-service "Echo Service" large test-echo-service Creating service test-echo-service in org myorg / space mydept as admin... OK vagrant@vagrant-ubuntu-trusty-64:~$ cf services Getting services in org myorg / space mydept as admin... OK name service plan bound apps test-echo-service Echo Service large |
Push an app
Next step is to create a simple app to which the service can be bound. It’s important to understand how the service details will be injected into the app environment. Service details will be contained in a JSON document under the key VCAP_SERVICES. The structure of the service details is shown below.
{ "Echo Service": [ { "name": "test-php-echo", "label": "Echo Service", "tags": [ ], "plan": "large", "credentials": { "uri": "http://16.98.49.183:8090/echo/f2c43d9c-e912-4e56-b624-14e8522be912/4a421367-0e1a-4b56-a07f-9a6b404119a5" } } ] } |
For this example, the PHP script below extracts, decodes and isolates the service URI from the VCAP_SERVICES environment variable. The cURL library is used to send a call to that URI containing a basic message JSON document. The response is then printed using var_dump.
<?php // extract injected service details from environment $services_json = getenv("VCAP_SERVICES"); $services = json_decode($services_json); $service_uri = $services->{'Echo Service'}[0]->{'credentials'}->{'uri'}; var_dump($service_uri); // create a message to send $message = '{"message": "Hello CloudFoundry!"}'; // setup and execute the cURL call to the echo service $curl = curl_init($service_uri); curl_setopt($curl, CURLOPT_HEADER, false); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_HTTPHEADER, array("Content-type: application/json")); curl_setopt($curl, CURLOPT_POST, true); curl_setopt($curl, CURLOPT_POSTFIELDS, $message); $response = curl_exec($curl); // handle non-200 response $status = curl_getinfo($curl, CURLINFO_HTTP_CODE); if ( $status != 200 ) { die("Error: call to URL $url failed with status $status, response $response, curl_error " . curl_error($curl) . ", curl_errno " . curl_errno($curl)); } // clean up cURL connection curl_close($curl); // decode response and output $response_as_json = json_decode($response); var_dump($response_as_json); ?> |
A simple manifest file is provided for the deployment.
applications: - name: echo-php framework: php |
The app can then be pushed to CloudFoundry. The resulting list of apps is shown.
vagrant@vagrant-ubuntu-trusty-64:~/echo-php$ cf apps Getting apps in org myorg / space mydept as admin... OK name requested state instances memory disk urls echo-service started 1/1 256M 1G echo-service.10.244.0.34.xip.io, 16.85.146.179.xip.io service-broker started 1/1 256M 1G service-broker.10.244.0.34.xip.io echo-php started 1/1 256M 1G echo-php.10.244.0.34.xip.io |
Bind the service to the app
With an existing service and an existing app, it’s possible to bind the service to the app. This is done using bind-service, as shown below. Note that it is necessary to restage the app after binding the service so that the environment variables will be properly injected.
vagrant@vagrant-ubuntu-trusty-64:~$ cf bind-service echo-php test-echo-service Binding service test-echo-service to app echo-php in org myorg / space mydept as admin... OK TIP: Use 'cf restage' to ensure your env variable changes take effect vagrant@vagrant-ubuntu-trusty-64:~$ cf restage echo-php Restaging app echo-php in org myorg / space mydept as admin... -----> Downloaded app package (4.0K) -----> Downloaded app buildpack cache (4.0K) -------> Buildpack version 1.0.2 Use locally cached dependencies where possible ! WARNING: No composer.json found. Using index.php to declare PHP applications is considered legacy functionality and may lead to unexpected behavior. See https://devcenter.heroku.com/categories/php -----> Setting up runtime environment... - PHP 5.5.12 - Apache 2.4.9 - Nginx 1.4.6 -----> Installing PHP extensions: - opcache (automatic; bundled, using 'ext-opcache.ini') -----> Installing dependencies... Composer version ac497feabaa0d247c441178b7b4aaa4c61b07399 2014-06-10 14:13:12 Warning: This development build of composer is over 30 days old. It is recommended to update it by running "/app/.heroku/php/bin/composer self-update" to get the latest version. Loading composer repositories with package information Installing dependencies Nothing to install or update Generating optimized autoload files -----> Building runtime environment... NOTICE: No Procfile, defaulting to 'web: vendor/bin/heroku-php-apache2' -----> Uploading droplet (64M) 0 of 1 instances running, 1 starting 1 of 1 instances running App started OK Showing health and status for app echo-php in org myorg / space mydept as admin... OK requested state: started instances: 1/1 usage: 256M x 1 instances urls: echo-php.10.244.0.34.xip.io last uploaded: Mon Nov 24 21:34:29 UTC 2014 state since cpu memory disk #0 running 2014-11-24 09:39:27 PM 0.0% 88.4M of 256M 0 of 1G |
The echo-php service can be called using cURL from the command line to verify proper connectivity between the echo php app and the new service.
vagrant@vagrant-ubuntu-trusty-64:$ curl http://echo-php.10.244.0.34.xip.io string(117) "http://echo-service.10.244.0.34.xip.io/echo/8dea3b8e-4230-4509-8879-5f94b4812985/d2944b66-2d9e-46fe-8e3d-d9b102cb5bca" object(stdClass)#4 (1) { ["response"]=> string(19) "Hello CloudFoundry!" } |
The existing service can be bound to additional app instances. It is also trivial to create new service instances. If the service is no longer needed by the application, it is straightforward to unbind and deprovision.
Excellent Post, Thanks much Daniel for the simple and detailed explanation!
Thanks for the nice post.
Cloud Foundry supporting service broker implementation in Python 2.7.?
Please suggest. also please share details steps to push service(Flask Python API development),in to marketplace
I’m not sure exactly what you’re asking. I obviously linked to two repositories I published on github to demonstrate how services work in cloud foundry.