An Outstanding Cloud Foundry Feature: App Placement and Host Affinity

by Aliaksandr PrysmakouJuly 18, 2016
With a sample app manifest, this article shows how to define affinity groups in Cloud Foundry to achieve segregation of resources.

Out of the box, Cloud Foundry doesn’t give us the ability to control application placement or to specify a target host(s) for an application. This may be critical when one needs to separate apps between different environments or host types—or for any other type of app segregation. The good news is that this can be resolved very easily.

 

Why bother?

Scenarios where this ability may be needed:

  • physical separation of applications between environments in a single Cloud Foundry deployment (e.g., development / staging / production)
  • segregation of applications by the host’s hardware (slow_but_cheap/fast_but_expensive/with_gpu)
  • placing together two microservices that have a huge amount of communication traffic and will benefit from lower latencies
  • a use case with only asingle app instance per host (e.g., a crypto that encrypts traffic for the whole host)
  • well, actually, for any other type of segregation

One would say, okey, but we already have Availability Zones (AZ)!

It is a different thing. Cloud Foundry tries to balance (using internal algorithms) instances of an application between AZs. There is no way to specify a target AZ for an application or an instance of an application. “Host affinity” or “placement constraints” are needed to control application placement.

For instance, a reference architecture of your deployment may look like the one below.

an-outstanding-cloud-foundry-feature-app-placement-and-host-affinityA sample deployment architecture

 

Solution (well, a workaround)

In brief, it is possible to control application placement by defining stacks with custom names in the Cloud Foundry deployment manifest. There is no need to actually create them, and no release modification is needed.

Cloud Foundry uses the concept of stacks (prebuilt root file systems) to support specific operation systems. Here are some of the examples:

  • cflinuxfs2
  • windows2012R2

However, nothing stops us from listing more stacks, even without creating modifications to them:

  • cflinuxfs2_dev
  • cflinuxfs2_stg
  • cflinuxfs2_prd
  • name_of_your_choice

 

Instructions for Diego

In our example, we define a “production” affinity group of hosts, so that every application for production (and only for production) will run on the specific group of hosts. All you need is to modify your Cloud Foundry deployment manifest. No need to create new stacks. No need to modify a Cloud Foundry release, as well. Just list your stacks in the Cloud Controller properties.

properties:
...
  cc:
    stacks:
      - name: "cflinuxfs2"
        description: "Cloud Foundry Linux-based filesystem"
      - : "cflinuxfs2_prd"
        description: "Cloud Foundry Linux-based filesystem (PRODUCTION)"

Define your affinity group (REP properties).

- instances: 10
  name: cell_z1_prd
  networks:
  - name: diego_prd1
  properties:
    diego:
      rep:
        stack: cflinuxfs2_prd
        preloaded_rootfses: 
          - "cflinuxfs2_prd:/var/vcap/packages/rootfs_cflinuxfs2/rootfs"
        zone: z1

Modify Diego properties for the NSYNC and STAGER components.

properties:
  diego:
...
    nsync:
      lifecycle_bundles:
        - "buildpack/cflinuxfs2:buildpack_app_lifecycle/buildpack_app_lifecycle.tgz"
        - "buildpack/cflinuxfs2_prd:buildpack_app_lifecycle/buildpack_app_lifecycle.tgz"
        - "buildpack/windows2012R2:windows_app_lifecycle/windows_app_lifecycle.tgz"
        - "docker:docker_app_lifecycle/docker_app_lifecycle.tgz"

...
    stager:
      lifecycle_bundles:
        - "buildpack/cflinuxfs2:buildpack_app_lifecycle/buildpack_app_lifecycle.tgz"
        - "buildpack/cflinuxfs2_prd:buildpack_app_lifecycle/buildpack_app_lifecycle.tgz"
        - "buildpack/windows2012R2:windows_app_lifecycle/windows_app_lifecycle.tgz"
        - "docker:docker_app_lifecycle/docker_app_lifecycle.tgz"

 

Instructions for DEA

The same as with Diego, all you need is to modify your Cloud Foundry deployment manifest. List your stacks in the Cloud Controller properties.

properties:
...
  cc:
    stacks:
      - name: "cflinuxfs2"
        description: "Cloud Foundry Linux-based filesystem"
      - name: "cflinuxfs2_prd"
        description: "Cloud Foundry Linux-based filesystem (PRODUCTION)"

Define your affinity group (DEA properties).

- instances: 10
  name: runner_z1_prd
  networks:
  - name: cf1
    static_ips: null
  properties:
    consul:
      agent:
        services:
          dea:
            check:
              interval: 5m
              name: dns_health_check
              script: /var/vcap/jobs/dea_next/bin/dns_health_check
              status: passing
    dea_next:
      zone: z1
      stacks:
        - name: "cflinuxfs2_prd"
          package_path: "/var/vcap/packages/rootfs_cflinuxfs2/rootfs"

 

A sample application manifest

To target specific affinity group (in our example, “production”) just specify the appropriate stack in your application manifest.

---
applications:
- name: hello-spring-cloud
  memory: 512M
  instances: 1
  host: hello-spring-cloud-${random-word}
  path: target/hello-spring-cloud-0.0.1-SNAPSHOT.jar
  buildpack: java_buildpack
  stack: cflinuxfs2_prd
  env:
    SPRING_PROFILES_DEFAULT: cloud

 

Conclusion

So, as you can see, it is pretty easy to define affinity groups in Cloud Foundry and to control application placement. It would be great to see this as a native feature in Cloud Foundry, but this workaround also works well.

We’d also like to see the ability to specify placement constraints in the application manifest, as featured below.

--
constraints:
- production
- with_gpu

Pros:

  • no need to build your own custom stack
  • no need to modify a Cloud Foundry release
  • No need to modify your applications
  • Works for DEA and Diego cells

Cons:

  • No way to request a list of constraints
  • May require buildpack customization (The Java buildpack works well, but I’ve experienced some limitations for the Python buildpack.)

In addition, to separate app traffic, it is also worth considering using different load balancers / routers for different affinity groups (at the DNS level). Please share your thoughts if you have anything else to add.


Many thanks to Sean Keery (Pivotal) and Duncan Winn (Pivotal) for inspiration!


 

Further reading


This post was written by Aliaksandr Prysmakou and edited by Sophia Turol.