When I started building the DrupalCamp Spain 2017 website I was very excited to see how far could I get with Configuration Management. A lot of effort went into Drupal 8 to make the management of configuration seamless without the dependency on contributed modules like Features. Long story short: Configuration Management works wonderfully if you introduce it in your development workflow. In this article, we will walk through how to implement, test, and deploy a release of DrupalCamp Spain’s website, including some issues we ran into and how we resolved them.
The goal: just push the button
Our goal with deployments at DrupalCamp Spain was that there should be nothing left to do on the website after the Jenkins job that performs the deployment has completed. It worked in the following way:
Someone would open the Jenkins production deployment job, select a git tag, and click build:
The above job would, via an Ansible task:
- Deploy the source code.
- Run composer install in a new directory.
- Run database updates:
drush update-db -y
. - Import configuration:
drush config-import -y
. - Purge Varnish caches:
varnish clean domain all
.
Here is the log of a successful deployment. In the following sections, we will see how to setup our project to achieve one-click deployments.
The starting point
The DrupalCamp Spain website has a public repository that you can fork and explore. It uses:
- drupal-project to manage dependencies via Composer.
- Ansible and Ansistrano to describe deployment tasks.
- Drush’s config-export and config-import commands to manage the
config
directory. - Jenkins to deploy changes in the
master
branch to the development environment automatically, and to deploy a Git tag to the production environment manually. Here is a screenshot of our Jenkins setup:
Now, let’s look at one of the past deployments and then dissect it together.
A sample deployment
Even though the DrupalCamp Spain website was a fairly small project, we did many deployments to the production environment. As an example, I have chosen the 2.2.2 release - List session proposals because:
- It includes a database update.
- It contains configuration changes.
- It involves code changes.
- It adds new dependencies.
Here is the list of changes for the release:
If you are wondering how all of the above could be deployed successfully, the answer is twofold. First, we had a Jenkins job in charge of deploying the code. Second, we had a development process which tested deployments that we will look at in the next section.
Working on an issue
Release 2.2.2 was the result of the Call4Papers milestone. This milestone consisted of four issues which were completed via pull requests. We followed this development process:
- Install the site locally following the repository’s README.
- Pick an open issue like List session proposals.
- Create a new branch from
master
with the issue number such as113-list-proposals
. - Implement the requirements and create a pull request when the branch is ready for review. If there are changes in configuration, export them via
drush config-export
and include them in the pull request. - Once the pull request is merged, check Jenkins to verify that the automatic deployment to the development environment worked.
Polishing the deployment
In this case, I merged the pull request, and that triggered a Jenkins job that deployed the changes in the master branch to the development environment. It failed, as I made a change in the pull request just before it was merged that introduced a bug. Once I fixed that in a second pull request, I introduced a different bug (slap me with a trout, please), which needed a third pull request. Once it was merged, Jenkins could run the deployment task to the development environment successfully, and there was much rejoicing.
To verify the deployment, I opened the development environment in the web browser. I was puzzled when I found out that even though the database update that installed the new module did complete successfully, the module was not installed. After some debugging, I discovered that I forgot to export my local environment’s configuration into the config
directory, which, after doing it with drush config-export
, contained a setting to keep the new module installed. Since Jenkins runs drush config-import
after drush update-db
, it uninstalled the module before the module was present in the site configuration. Therefore, I created a fourth pull request that included this fix. When Jenkins deployed it to the development environment, this time, the module was successfully installed. Phew!
It took us four deployments to get the release working in the development environment. For production, we want to accomplish it in a single deployment. In the next section, we will see how to test that.
We use a tool called Tugboat in our client's projects that helps us to spot and fix implementation and deployment bugs in the early stages of development. You can find out further details at https://tugboat.qa.
Testing the deployment to production
Once we completed all the milestone’s issues for listing sessions, we needed to make sure they could be deployed as a whole without causing errors.
Simulating a production deployment
Here is an example of how we test production deployments:
First, we triggered the Jenkins job that copies the production environment's database and files into the development environment:
Here is the log. Once we had fresh data at the development environment, we triggered the job that kicks in when someone makes changes to the master branch (which ran the Ansible task to update the database).
The job completed successfully (Yay!). I also opened the development environment’s website and verified that the new module was installed and that all the new features that we worked on the milestone were working as expected. We were ready to publish this to production!
Hitting the button
The release was complete, and we tested its deployment. Therefore, we were ready to publish it. We used the Jenkins job that takes a git tag as a parameter, deploys the code to the production environment, and finally runs the post-deploy task to update the database. It worked!
When you test deployments beforehand, you get a deep understanding of what happens during such a process. It gets especially interesting when something goes wrong, as it gives you a chance to debug the deployment process locally, fix it, and test it again at the development environment until it works flawlessly.
What happens when a deployment goes wrong in production? Setting up a roll back process is something that deserves its own article. In the meantime, what’s your development and deployment process like? Do you have any suggestions? I am looking forward to hearing your insights below.
Acknowledgements
This article was possible thanks to the following folks:
- Pedro González NITEMAN, for his help with the DrupalCamp Spain’s infrastructure.
- Matt Oliveira, James Sansbury, Andrew Berry, and Seth Brown for their technical and editorial reviews.
Footer image by By włodi from London, UK (The Big Red ButtonUploaded by Yarl) [CC BY-SA 2.0], via Wikimedia Commons.