Schedule containers in Pivotal Cloud Foundry

At Stark & Wayne we’ve been building out a set of internal web apps to help collect and pass information to the staff who need it. For example, one app pulls data from a primary source (such as timesheet entries) and at the end of the day it shares the information into a private Slack channel, or provides a helpful reminder to a staff member to update their timesheet.

Time will tell if this system works well for us or not; but right now it is interesting to share how we use Pivotal Scheduler service to collect time sheet data and determine if/when Slack messages are to be sent.

pcf-scheduler-downloads
The Pivotal Scheduler service is very efficient – it means we do not need to run full-time application containers to watch the clock and decide if they should perform behaviour. Instead, the Scheduler service starts new application containers on demand. On Pivotal Web Service, we only pay for the container whilst the half-hourly task is running. This can be a fantastic discount over running full-time application instances.

There is also flexibility with short-term one-off tasks – you can use any programming language or pre-built binary supported by any Cloud Foundry buildpack. You do not need to write a full web application; instead you’re only writing a finite script. Write it in bash if you like. Install interesting debian packages with the apt_buildpack (see our recent post). Scheduled tasks can be so much easier to write and run than long-running applications.

If you are using on-premise Pivotal Cloud Foundry then you can also use the Pivotal Scheduler service. I’ll introduce how to find and install the p-scheduler.pivotal tile later in the article.

This article will also discuss the complimentary cf CLI plugin which offers a range of helpful commands (cf create-job, cf schedule-job, cf run-job, and more).

The article will also cover the nuances of using the Scheduler service with zero-downtime deployed applications (why? the scheduler can lose track of your scheduled jobs when you perform a zero-downtime deploy)

Installing the Scheduler Service

There are two scenarios to cover: publicly hosted Pivotal Web Service, and on-premise Pivotal Cloud Foundry.

Ready to go in Pivotal Web Services

First, when you use Pivotal Web Services there is nothing special to do. Pivotal Scheduler service is already available to all customers as a free service.

$ cf marketplace -s scheduler-for-pcf
Getting service plan information for service scheduler-for-pcf...
OK
service plan   description         free or paid
standard       Scheduler for PCF   free

The Pivotal Web Services console provides special integration for the Scheduler service. When you navigate to an Application’s page, there is now a "Tasks" tab:

tasks

Following the prompts you can enable the service for your application. But in this article we will focus on using the cf CLI, which will then allow us to setup our scheduled tasks in CI after deployment.

Installing the tile into Pivotal Cloud Foundry

If you are running Pivotal Cloud Foundry (PCF) version 1.11 or higher (at the time of writing the supported versions are 1.11, 1.12, and 2.0), then you can install the Pivotal Scheduler tile.

Visit Pivotal Network for the Scheduler for PCF tile, and download the .pivotal file.

Follow these instructions, including the installation of the dependency Pivotal MySQL tile.

Installing the CLI plugin

I’m a huge fan of Cloud Foundry services having their own cf plugin to expand the set of functionality to developers. The Pivotal Scheduler CLI plugin offers over a dozen new CLI commands.

Unlike nearly all CLI plugins, the Pivotal Scheduler cf plugin is not available at https://plugins.cloudfoundry.org/ nor the built in cf install-plugin command.

As far as I can tell, the CLI plugin is only available via Pivotal Network. This becomes a little bit annoying when we get to setting up the CLI plugin within CI. And it’s a little bit annoying for humans too; each new version will need to be manually downloaded and installed.

Each developer will need to download the plugin, and then install the plugin:

cf install-plugin path/to/scheduler-for-pcf-cliplugin-*

Getting Started

From now on, the instructions for using the Scheduler service is the same for hosted Pivotal Web Services and on-premise Pivotal Cloud Foundry.

To explore the Scheduler service, clone a demo repository that includes a simple hello-world.sh task script.

git clone https://github.com/starkandwayne/pcf-scheduler-demo
cd pcf-scheduler-demo
./hello-world.sh

Next, deploy our application to PWS/PCF. Note that there is no long-running web app.

cf create-space scheduler-demo
cf target -s scheduler-demo
cf push hello-world --health-check-type none -i 0 -m 64M -b binary_buildpack --no-route

The cf push command will sort-of succeed/sort-of complain.

...
start command:     >&2 echo Error: no start command specified during staging or launch && exit 1
There are no running instances of this app.

Cloud Foundry Application Runtime likes to run full-time application instances. We don’t need that. It’s complaint is our compliment.

Next, you will instantiate the Scheduler service for your application.

cf create-service scheduler-for-pcf standard hello-world-scheduler
cf bind-service hello-world hello-world-scheduler

Next, we use the new cf commands to create a scheduler "job" and to run it to test it works.

cf create-job hello-world say-hello "./hello-world.sh"

Explaining the parameters:

  1. hello-world is the name of the application to which we will assign a new job
  2. say-hello is the name of the job; we will use this label to run the job and to schedule the job to run automatically
  3. "./hello-world.sh" is the command to run, relative to the root of the application folder. This will be run within a fresh application container within Cloud Foundry.

To test our job, or to run it whenever we want:

cf run-job say-hello

The output might not be what you are expecting:

Enqueuing job say-hello for app hello-world...
OK

Where is our "Hello world!" output?

Our script prints to standard out/STDOUT. Within Cloud Foundry, anything printed to STDOUT/STDERR pipes is sent to the logging subsystem. To view the recent logs from our task:

cf logs hello-world --recent

The logs associated with our one-off task are:

   2018-02-21T08:51:31.68+1000 [CELL/0] OUT Creating container
   2018-02-21T08:51:32.24+1000 [CELL/0] OUT Successfully created container
   2018-02-21T08:51:33.02+1000 [APP/TASK/892d4e2c-a116-45b2-854e-5118c32e5b73-|-5bdce448-c1f3-4c3c-b585-9815a6b7caaa/0] OUT Hello world!
   2018-02-21T08:51:33.03+1000 [APP/TASK/892d4e2c-a116-45b2-854e-5118c32e5b73-|-5bdce448-c1f3-4c3c-b585-9815a6b7caaa/0] OUT Exit status 0
   2018-02-21T08:51:33.04+1000 [CELL/0] OUT Stopping instance 60fe1646-be1a-4979-a494-7d4086d479fe
   2018-02-21T08:51:33.04+1000 [CELL/0] OUT Destroying container
   2018-02-21T08:51:33.57+1000 [CELL/0] OUT Successfully destroyed container

Nested in the middle of line 3 is OUT Hello world!. This means that Hello world! was sent to STDOUT.

It worked!

We have a one-off task without requiring the costs and hassle of running a full-time web application. Serverless FTW!

Scheduling jobs

We can now schedule our job. Every minute. Every 5 minutes. Every hour. Once a day. Anything you like.

To schedule our say-hello task every 30 minutes:

cf schedule-job say-hello "*/30 * ? * *"

The "*/30 * ? * * " is crontab format.

Except this expression which works in Pivotal Scheduler is not what you’ll get from any crontab generator you might find on the Internet. I found this confusing and disorientating.

The ? question mark, rather than an * asterisk is important. I don’t know why. Perhaps these are bugs which might get fixed in future versions. Perhaps there are reasons for it. Cosmologically significant, end-of-times reasons. Without the ? you might get an error like I do.

Validation of resource failed.
  The cron expression '*/30 * * * *' is invalid.

You can argue with your computer all you like. Pivotal Scheduler wants a ? like "*/30 * ? * *".

Inspecting your jobs

There are additional CLI commands to inspect your jobs and their history:

$ cf jobs
Job Name    App Name      Command
say-hello   hello-world   ./hello-world.sh
OK
$ cf job-history say-hello
Getting scheduled job history for say-hello...
1 - 2 of 2 Total Results
Execution GUID     Execution State ...
d9ce17cd-7909...   SUCCEEDED ...
8b63c600-cfe1...   SUCCEEDED ...

And as discussed before, view your cf logs to see the output and error messages.

Using Pivotal Scheduler in CI

We use the cf zero-downtime-push command to perform zero downtime deploys (via the https://github.com/contraband/autopilot project, bundled in Concourse CI https://github.com/concourse/cf-resource).

Zero downtime deploys are delightful. They do break Pivotal Scheduler jobs. That’s less delightful.

The reason is due to the common implementation of zero downtime deploys (possibly fixed with cf v3-push) – there are two applications deployed. The human labels for application names suggest that after the deploy the application is the same application as before; but it is a new application. And the Pivotal Scheduler does not know this. So it loses track of the association of your jobs to the old application.

My current solution is to recreate the scheduled job after deployment.

That is, after running the cf zero-downtime-push, you then run the cf create-job and cf schedule-job commands again. Fixed.

You do lose your job history from before the deployment. I don’t have a solution to that. Perhaps moving to cf v3-push will be a solution in future.

Summary

It is a wonderful alternate use case of both Pivotal Web Services and Pivotal Cloud Foundry to run serverless-like scheduled tasks.

They can be ridiculously cheap to run (you pay only for the running time of the application instances).

And they can be ridiculously simple to write. Is there any long-running web application that is simpler to write than our example task above:

#!/bin/bash
echo "Hello world!"

Spread the word

twitter icon facebook icon linkedin icon