The advent of Docker changed the way we develop applications. Once you write a Dockerfile and build the image, your application will run (almost) anywhere, regardless of the underlying operating system. Best of all, it just works. You can then expose ports, map volumes to persist data, and deploy your image in Docker Swarm or Kubernetes (K8s). With great flexibility, however, comes greater costs, whether they are higher developer skill set requirements, increased time spent on writing multiple K8s YAML configuration files, or pricier services on your favorite cloud provider.
This article presents the usage of different tools and interesting tidbits to ease development with Cloud Foundry, similar to the likes of Docker. These include:
- Developing locally with the CF Local plugin.
- Running Docker images inside a Cloud Foundry container.
- Sequencing the startup of multiple Cloud Foundry applications.
- Configuring container-to-container networking.
For the remainder of this article, I will use toddler-auth as the example.
This article assumes you have a basic understanding of Cloud Foundry, how to use it and how to deploy your application onto IBM Cloud with the IBM Cloud CLI. If any of these topics are unfamiliar to you, have a look at the related links section to learn more about each technology.
If you are using the stand-alone IBM Cloud CLI, it bundles into the Cloud Foundry CLI. Effectively,
ibmcloud cf is equivalent to using
cf. Personally, I use
ibmcloud cf as it provides login mechanisms to access my internal IBM Cloud account and to use other services. I created an alias to
ibmcloud cf with
alias cf='ibmcloud cf'. Henceforth, any mention of
ibmcloud cf is invoked.
Developing locally with the CF Local plugin
CF Local lets you develop locally and requires Docker to be installed. To install CF Local, invoke
cf install-plugin cflocal. CF Local only uses a
local.yml instead of
manifest.yml. You can place multiple applications and specify environment variables just like any Cloud Foundry application.
As an example, below is my toddler-auth
local.yml file. The route is super helpful to enable applications to talk to each other.
--- applications: - name: toddler-auth routes: - route: toddler-auth.apps.internal memory: 128M disk_quota: 128M env: TODDLER_USERNAME: <username> TODDLER_PASSWORD: <password> TODDLER_WORLD_ID: <worldId>
Once you navigate to the application folder, stage the application by invoking:
cf local stage toddler-auth
Then, you run the application by invoking:
cf local run toddler-auth
The first staging period can take some time to download buildpacks and set things up. Once it completes, CF Local creates
.cache files inside the application’s directory. Subsequent runs are faster.
Deploying Docker images into Cloud Foundry containers
Sometime in 2015, Cloud Foundry started to support Docker images by adopting the Open Containers Initiative (OCI) container execution interface. For more information on deploying a Docker image, have a look at the full documentation. Two of the most important tidbits are:
- Only one exposed port per image is supported.
- The exposed port is controlled by the
EXPOSEdirective in the Dockerfile.
While you can control the exposed port by other means outlined in the documentation, using the default exposed port is the easiest way to go. An example of using a Docker image is provided in toddler-auth’s
--- applications: - name: toddler-nats memory: 128M disk_quota: 256M docker: image: bitnami/nats:latest routes: - route: toddler-nats.apps.internal
In the above example, I am running an application named toddler-nats. It uses Bitnami’s nats.io image from Docker Hub and maps an internal route of:
Sequencing applications startup
In the world of Docker Compose, Swarm, and Kubernetes, applications are started in parallel. The applications startup sequence is not controlled by the orchestrator. This can be annoying as you need to write extra Bash files and build in service availability detection.
On the contrary, in Cloud Foundry, if your
manifest.yml contains multiple applications, Cloud Foundry applications are deployed in sequence during deployment from top to bottom. If any application in the chain fails, the entire deployment halts. This is great if you need to sequence the startup of multiple applications.
Configuring container-to-container networking
Before talking about container-to-container (C2C) networking, I just want to touch on why this was significant for my project.
I need TCP routing
My toddler application relies on a messaging service such as NATS as a communication transport between different microservices. NATS is a great alternative to HTTP. It has all of the nice features of a message queue like publish/subscribe, topic routing, very high throughput, and well-developed client libraries.
In mid-2016, Cloud Foundry rolled out support for TCP routing. According to the Cloud Foundry list routes documentation, if
tcp is shown under
type for any route, that route can be configured as a TCP route. Not all cloud providers, however, support this feature. For example, IBM Cloud does not.
However, searching around reveals that you can set up a custom TCP port with C2C networking.
Setting up container-to-container networking
Setting up C2C networking is straightforward and disabled by default. To enable C2C between two applications, you first need both applications deployed. Then invoke:
cf add-network-policy $SOURCE_APP_NAME --destination-app $DEST_APP_NAME --port $PORT --protocol tcp
For my project, I needed to enable port 4222 from toddler-auth to toddler-nats. On my terminal, this was set up with:
cf add-network-policy toddler-auth --destination-app toddler-nats --port 4222 --protocol tcp
For toddler-auth talk to toddler-nats, I mapped an internal route on toddler-nats to be
toddler-nats.apps.internal. Note that I did not have to add a network policy into CF Local when deploying the application locally.
To summarize, this article demonstrated some tools that can help you with:
- Developing and testing locally with CF Local maps to build an image and test a deployment with docker-compose.
- Configuring container-to-container networking maps to call services within a docker-compose file.
- Running a Docker image inside a Cloud Foundry container.
- Sequencing application startup.