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.
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 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.
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!