Deb Constrictor for Configuration Deployment (Part 3)

Welcome to the thrilling conclusion of this three-part series on deploying this site with Deb Constrictor. This post will cover how to build and deploy DPKGs for configuration (including different configuration for different environments), and how v0.4 of Deb Constrictor adds the ability to mark files as configuration.

This post assumes you’ve read part one, application deployment and part two, virtualenv deployment, so that you’re familiar with the operation and configuration of Deb Constrictor.

Why deploy configuration as a package?

Using a package purely to deploy configuration might not be a familiar approach, and there are other systems more geared towards getting configuration onto your machines, such an Ansible, Salt, Puppet, etc. You can even use them in conjunction with other packaging methods, so your orchestration system will upload config to a host and then install your application and virtualenv packages for you.

If you do deploy configuration as a DPKG, then all your deployment can be part of one system (the apt system) and keeps the whole shebang managed in the same way (configuration and application are both built and deployed with apt instead of one with, say, Ansible, and one with Deb Constrictor). This means you get to keep the “one-command” setup of the application on a new server (apt-get install …).

That being said, I have been experimenting with Ansible and your one-command deployment could just be ansible-playbook instead of apt-get. But I digress…

Build Configuration

As with the previous two posts, I will start by showing the build-config.json file.

{
  "parent": "../parent-config.json",
  "package": "${PROJECT_NAME}-production-config",
  "architecture": "all",
  "version": "1.10",
  "description": "Configuration for BBIT Web Frontend.",
  "extra_control_fields": {
    "Provides": ["${PROJECT_NAME}-config"]
  },
  "directories": [
    {
      "source": "src/etc",
      "destination": "/etc"
    }
  ],
  "links": [
    {
      "path": "/etc/nginx/sites-enabled/${PROJECT_NAME}",
      "target": "/etc/nginx/sites-available/${PROJECT_NAME}"
    }
  ],
  "configuration_files": [
    "/etc/*"
  ]
}

The configuration installs everything from the src/etc directory into the /etc directory on the server. These are basically the nginx, uwsgi and Django configurations. A subset of my source directory is:

  • src/etc/
  • src/etc/nginx/
  • src/etc/nginx/sites-available/
  • src/etc/nginx/sites-available/bbit-web-frontend
  • src/etc/uwsgi/
  • src/etc/uwsgi/apps-available/
  • src/etc/uwsgi/apps-available/bbit-web-frontend.ini
The configuration also creates a link from the web site settings in nginx’s sites-available to sites-enabled.

Looking at some of the other options, you can see the package attribute uses a variable (as covered in the previous post) so it will be interpolated to bbit-web-frontend-production-config. There is also the Provides attribute in the extra_control_fields, this will be bbit-web-frontend-config, which is similar. But why the difference, and what do these fields mean?

Package vs Provides

To give a brief rundown on how these fields work, I’ll give the example of a mail transport agent. For your Linux system to send mail it must have a mail transport agent (MTA) installed. If you install a package that needs to send mail, it will have a dependency on mail-transport-agent, however there are many applications that can act as an MTA, such as postfix or sendmail. The Package field of these applications differs (obviously, as it is the name of the package and otherwise you would not be able to pick which one you wanted to install), but they both have a Provides field of mail-transport-agent. This is so that whichever one the user chooses to install, the generic (or “virtual”) mail-transport-agent dependency has been provided.

Multiple Configurations

The reason for using Provides in the configuration use case, is to provide different packages for staging and production environments without having to specify the environment in the application DPKG. It can just depend on bbit-web-frontend-config and so long as bbit-web-frontend-production-config is installed, which provides that dependency, everything works fine. Apt is even smart enough so that if you only have one package that provides a dependency (as is my case because I don’t have a staging environment) then that package will be installed automatically. Otherwise you can install the config package first, or run seperate apt servers for staging/production/etc and make sure the particular config is only uploaded to the correct repository.

Conffiles

You may have noticed sometimes that you will perform an apt-get upgrade and it will upgrade a particular package, and you’ll get output like this:

Configuration file `/etc/foo/bar.ini'
 ==> Modified (by you or by a script) since installation.
 ==> Package distributor has shipped an updated version.
   What would you like to do about it ?  Your options are:
    Y or I  : install the package maintainer's version
    N or O  : keep your currently-installed version
      D     : show the differences between the versions
      Z     : start a shell to examine the situation
 The default action is to keep your current version.
*** bar.ini (Y/I/N/O/D/Z) [default=N] ?

This prompt comes up because the package has marked the specific file as configuration and it has been changed on the server. Version 0.4 of Deb Constrictor allows the use of a configuration_files attribute, which is an array of file pattern globs. Any files found while assembling the DKPKG that match any entry in this list will be marked as configuration, and the user will be prompted as above if the configuration changes between upgrades.

Is this necessary?

When using a package purely for configuration, there’s a sort of implication that the contents of the package is canonical, and you’d just want to steamroll any configuration that’s in your way. Having configuration prompts coming up can interrupt an automated package upgrade. For these reasons you might want to skip the configuration_files option.

I will leave it up to the packager to decide if they want to use this feature. It is something that I felt had been missing so I thought I should just write about its availability in version 0.4, and of course it’s useful if you’re building a package that includes both application code and configuration, as most vendor provided packages do.

Putting it all together (all of the packages)

So finally we have all the pieces to deploy the application. What is the full process?

  1. cd into the frontend directory and run constrictor-build
  2. cd into the virtualenv directory and run constrictor-build
  3. cd into the config directory and run constrictor-build
  4. SSH into the server and run apt-get update && apt-get install bbit-web-frontend

That’s it, with the magic of dependency and scripts all databases and web servers and configuration will be put into place. The virtualenv is built and setup in a consistent manner using Docker and installs quickly. The Django DB migrations and collectstatic is run to get the schema and static data up to date.

I hope these three posts have helped explain how you can use Deb Constrictor to deploy your applications. Even if you aren’t building web applications, having a pure-Python, easy-to-use, yet powerful tool can streamline your packaging process. Also — spoiler alert — while writing these posts some ideas of how to improve Deb Constrictor further have come to mind, so expect to see a part four as well.

Do you need assistance or with setting up and streamlining your build or deployment system? Please contact for more information or to discuss further.

Previous entry

Next entry

Related entries

Deb Constrictor for Virtualenv Deployment (Part 2)   |   Deb Constrictor for Application Deployment (Part 1)   |   Building Debian Packages with Python (UPDS Part I.V)