Blue-green Laravel deployment to Pivotal Cloudfoundry

Blue-green Laravel deployment to Pivotal Cloudfoundry

This post shows how I do blue-green deployment of my Laravel project, that has a queue runner, through GitLab CI onto Pivotal Cloudfoundry. You’ll need to know some basics of GitLab CI, Laravel and Pivotal Cloudfoundry to better understand this post.

More information about Laravel queue runner is available in my other blog post!

Docker Image For Build

My Docker image, woohuiren/laravel-pcf, provides several installed tools. First off, woohuiren/laravel-pcf image is based off my woohuiren/php-laravel-env image which contains whatever PHP libraries Laravel requires to operate. The PCF image comes with blue-green deploy, anti-freeze and PCF scheduler.

Blue-Green Deploy

A term used to describe a method of deployment in which ensures that there’s zero downtime whilst deploying a new application. Once the new application has been successfully deployed, the routes are reconfigured from the old application to the new one. Thereafter, the old application can be removed since there will no longer be routes pointing towards it.

It is also a name of a tool by the Cloudfoundry community to do exactly the above said.

Anti Freeze

Anti freeze is an amazing tool to help prevent environment variable problems associated with blue-green deployments.

Some of us might have created environment variables for the application via the PCF interface. This will result in the environment variable being removed upon recreating the application. Blue green deployments often create a new application, does some re-routing and destroy the old application.

Anti-freeze helps to prevent this problem by checking and ensuring that all variables and services are bound via manifest. If they are not, the deployment will be stopped.

PCF Scheduler

In some Laravel applications, we run scheduled tasks in our app/Console/Kernel.php schedule() method. And hence, we would need to run the scheduler every minute. Typically, we create a cronjob with the following ruleset.

* * * * * php artisan schedule:run

However, for Pivotal Cloudfoundry, there’s a way which we create our jobs and schedule them. You can do so via the GUI but here’s the thing: it doesn’t persist either. There’s no way you can configure the manifest to persist it.

The only way to “persist” it would be to recreate the job and reschedule it every time we do a blue-green deployment. So we would need to have something like this run after deployment.

cf blue-green-deploy $CF_MAIN_APP -f 'main-manifest.yml' --delete-old-apps
cf create-job $CF_MAIN_APP laravel-scheduler "php artisan schedule:run"
cf schedule-job laravel-scheduler "* * ? * *"

You would also notice that the ruleset used isn’t * * * * * but * * ? * *. This is an issue with Pivotal Cloudfoundry which it seems to not allow you to schedule jobs without including a question mark.

GitLab CI Config

Typically, folks would want the artefact from a build stage to be saved and once testing passes, they can deploy the entire artefact from build stage to production. Each stage would require initialization of GitLab runner, pulling of Docker image and pulling of your code committed onto GitLab. Having more stages might be better for clarity, processes, artefact management. However, it would also mean that your CI process will take a longer time.

Through some experiments, I found that the fastest way to build and test my project was to use a “single stage”. This is not really recommended as we will not preserve the artefacts from the previous stage. But I still did it because my deployment doesn’t make use of the artefacts. (*flaw – see room for improvement)

The following is a GitLab CI configuration file that I’ve came up with that will help you do your blue-green deployment.

image: woohuiren/laravel-pcf

stages:
- build_and_test_and_deploy

# Select what we should cache between builds
cache:
  paths:
  - vendor/

services:
- mariadb:latest
- redis:latest

variables:
  # Configure mysql environment variables (https://hub.docker.com/r/_/mariadb/)
  MYSQL_DATABASE: atomapi
  MYSQL_ROOT_PASSWORD: api_password
  REDIS_HOST: redis

build_and_test_and_deploy_job:
  stage: build_and_test_and_deploy
  script:
  - php -v
  - ping -c 3 mariadb
  - composer install --no-interaction --no-suggest
  - vendor/bin/php-cs-fixer fix -v --dry-run --stop-on-violation --using-cache=no
  - php artisan config:cache
  - php artisan route:cache
  - php artisan migrate --seed
  - vendor/bin/phpunit --coverage-text --colors=never
  - cf login -a $CF_API_ENDPOINT -u $CF_USERNAME -p $CF_PASSWORD -o $CF_ORGANIZATION -s $CF_SPACE
  - cf check-manifest $CF_WORKER_APP -f 'worker-manifest.yml'
  - cf check-manifest $CF_MAIN_APP -f 'main-manifest.yml'
  - cf push $CF_WORKER_APP -f 'worker-manifest.yml'
  - cf blue-green-deploy $CF_MAIN_APP -f 'main-manifest.yml' --delete-old-apps
  - cf create-job $CF_MAIN_APP laravel-scheduler "php artisan schedule:run"
  - cf schedule-job laravel-scheduler "* * ? * *"

Room For Improvement

There are some things that I’m still trying out and working on right now to further improve this deployment method.

Currently, I’m trying to swap from Buildpacks to Docker for the deployment environment on Pivotal Web Services. I’m looking at the possibility of using Buildah. This will help reduce the build time by not repeating builds. Other than that, it will also make the whole pipeline become immutable and highly consistent. This would help reduce potential errors that might be caused due to differing environments.

If you have any other suggestions for me to improve, feel free to leave a comment below!

Author: Woo Huiren

Howdy, I'm a IT student who is currently serving my full time national service. I do lots of things related to IT but I have an exceptionally great love for the web and opensource, so expect me to blog a lot about it! :)

Leave a Reply