For most Cloud Foundry applications and services, you can avoid downtime for maintenance with a combination of following best practices for 12 factor app development and taking advantage of Cloud Foundry’s scaling and flexible routing to implement Blue-Green deployment.
But there will still be times where an application, service, or shared backend component does not support rolling upgrades, and a maintenance window will be required.
When this happens, you must think about the behavior you want to implement for both front-end user applications as well as services. A static page may not adequately serve the needs of end users or consuming clients that rely on your services.
Over simplistic static file solution
It is easy enough to use a Cloud Foundry Staticfile buildpack with an “index.html” page that says “Sorry, in maintenance mode”, but this only captures the minimal requirements.
Just so we can discuss the deficiencies of this approach in subsequent sections, here is how you would create that static buildpack.
$ mkdir simplemaintenance $ cd simplemaintenance $ touch Staticfile $ echo "<h1>Sorry, in maintenance mode</h1>" > index.html $ cf push simplemaintenance -m 64M --no-route
Then, pretending that you want to put an application named “hello-spring” into maintenance, you would append a route going to simplemaintenance, then unmap the route going to hello-spring as shown below.
$ myHostname=`cf app hello-spring | grep routes | awk {'print $2'} | cut -d'.' -f 1` $ myDomain=`cf app hello-spring | grep routes | awk {'print $2'} | cut -d'.' -f 2,3,4,5,6` $ echo going to reroute all traffic going to hello-spring app at http://$myHostname.$myDomain/index.html to simplemaintenance index.html page $ cf map-route simplemaintenance $myDomain --hostname $myHostname $ cf unmap-route hello-spring $myDomain --hostname $myHostname $ curl http://$myHostname.$myDomain/
Now you can visit the original hello-spring URL, and you will see the “Sorry, in maintenance mode” message coming from the simplemaintenance app as the last curl demonstrates.
Reverting the URL back to the hello-spring app requires a map/unmap as shown below.
$ cf map-route hello-spring $myDomain --hostname $myHostname $ cf unmap-route simplemaintenance $myDomain --hostname $myHostname
Deficiencies of static approach
First, this static approach only captures requests that explicitly match the name of static files you have included in static buildpack. We have included a “index.html” so any HTTP requests for “/” or “/index.html” will return a message, but all other requests will return a 404 not found. If an end user has bookmarked “/login” or uses the type ahead feature of their browser and send something like “/app?view=dashboard” those requests will fail with a 404.
Furthermore, this doesn’t take into account something like REST based services. A consuming client of a REST service may expect to catch 503 server errors, or they may expect normal 200 server responses with exception codes in a json field or message in an HTTP header during maintenance windows.
Python based maintenance app
What we need is an application with more intelligence that has the ability to catch all incoming requests and either return a maintenance message, or a 5xx server error code, or a custom response like JSON. This will always vary based on end user or consuming service requirements.
I’ve uploaded a project on github named cf-python-maintenancepage that demonstrates each of these return types. Go ahead and deploy it for a quick test.
$ sudo apt-get install git -y $ git clone https://github.com/fabianlee/cf-python-maintenancepage.git $ cd cf-python-maintenancepage $ cf push
Examine “maintenance.py“, because that is where the program logic lives. In short, based on a request to:
- “/” – sends the MAINTENANCE_MESSAGE from manifest.yml
- “/503” – sends the MAINTENANCE_MESSAGE from manifest.yml as a 503 server error code
- “/json” – sends a json formatted message with a 200 server response, but with error codes embedded in the json and HTTP response header named ‘StatusHeader’
- For all other requests – sends fallback message MAINTENANCE_MESSAGE from manifest.yml
Let’s use curl to examine each of the raw responses, feel free to use your browser as well.
$ mpage=`cf app cf-python-maintenancepage | grep -i Route | awk {'print $2'}` $ curl -i http://$mpage $ curl -i http://$mpage/503 $ curl -i http://$mpage/json $ curl -i http://$mpage/make/something/up
Then, pretending that you want to put an application named “hello-spring” into maintenance, you would append a route going to cf-python-maintenancepage, then unmap the route going to hello-spring as shown below.
$ myHostname=`cf app hello-spring | grep routes | awk {'print $2'} | cut -d'.' -f 1` $ myDomain=`cf app hello-spring | grep routes | awk {'print $2'} | cut -d'.' -f 2,3,4,5,6` $ echo going to reroute all traffic going to hello-spring app at http://$myHostname.$myDomain to cf-python-maintenancepage $ cf map-route cf-python-maintenancepage $myDomain --hostname $myHostname $ cf unmap-route hello-spring $myDomain --hostname $myHostname $ curl http://$myHostname.$myDomain/
Now any of the requests you make to the original hello-spring application are now sent to cf-python-maintenancepage. Clearly, maintenance.py is just a starting point for your own requirements.
Reverting the URL back to the hello-spring app requires a map/unmap as shown below.
$ cf map-route hello-spring $myDomain --hostname $myHostname $ cf unmap-route cf-python-maintenancepage $myDomain --hostname $myHostname
REFERENCES
https://docs.cloudfoundry.org/buildpacks/python/index.html
https://docs.pivotal.io/pivotalcf/1-6/buildpacks/staticfile/index.html
https://gist.github.com/ihuston/e87c1d4719f7e72e9760 (simple python flask app)
http://flask.pocoo.org/snippets/57/(flask catch all for requests)