PostgreSQL Deployment & Configuration with Puppet

Hugo Dias

Puppet is open source software for configuration management and deployment. Founded in 2005, it’s multi-platform and even has its own declarative language for configuration.

The tasks related to administration and maintenance of PostgreSQL (or other software really) consists of daily, repetitive processes that require monitoring. This applies even to those tasks operated by scripts or commands through a scheduling tool. The complexity of these tasks increases exponentially when executed on a massive infrastructure, however, using Puppet for these kind of tasks can often solve these types of large scale problems as Puppet centralizes and automates the performance of these operations in a very agile way.

Puppet works within the architecture at the client/server level where the configuration is being performed; these ops are then diffused and executed on all the clients (also known as nodes).

Typically running every 30 minutes, the agents’ node will collect a set of information (type of processor, architecture, IP address, etc..), also called as facts, then sends the information to the master which is waiting for an answer to see if there are any new configurations to apply. 

These facts will allow the master to customize the same configuration for each node.

In a very simplistic way, Puppet is one of the most important DevOps tools available today. In this blog we will take a look at the following...

  • The Use Case for Puppet & PostgreSQL
  • Installing Puppet
  • Configuring & Programming Puppet
  • Configuring Puppet for PostgreSQL 

The installation and setup of Puppet (version 5.3.10) described below were performed in a set of hosts using CentOS 7.0 as operating system.

The Use Case for Puppet & PostgreSQL

Suppose that there is an issue in your firewall on the machines that host all your PostgreSQL servers, it would then be necessary to deny all outbound connections to PostgreSQL, and do it as soon as possible.

Puppet is the perfect tool for this situation, especially because speed and efficiency are essential. We’ll’ talk about this example presented in the section “Configuring Puppet for PostgreSQL” by managing the parameter listen_addresses.

Installing Puppet

There are a set of common steps to perform either on master or agent hosts:

Step One

Updating of /etc/hosts file with host names and their IP address

192.168.1.85 agent agent.severalnines.com

192.168.1.87 master master.severalnines.com puppet

Step Two

Adding the Puppet repositories on the system

$ sudo rpm –Uvh https://yum.puppetlabs.com/puppet5/el/7/x86_64/puppet5-release-5.0.0-1-el7.noarch.rpm

For other operating systems or CentOS versions, the most appropriate repository can be found in Puppet, Inc. Yum Repositories.

Step Three

Configuration of NTP (Network Time Protocol) server

$ sudo yum -y install chrony

Step Four

The chrony is used to synchronize the system clock from different NTP servers and thus keeps the time synchronized between master and agent server.

Once installed chrony  it must be enabled and restarted:

$ sudo systemctl enable chronyd.service

$ sudo systemctl restart chronyd.service

Step Five

Disable the SELinux parameter

On the file /etc/sysconfig/selinux the parameter SELINUX (Security-Enhanced Linux)  must be disabled in order do not restricts access on both hosts.

SELINUX=disabled

Step Six

Before the Puppet installation (either master or agent) the firewall in these hosts must be defined accordingly:

$ sudo firewall-cmd -–add-service=ntp -–permanent 

$ sudo firewall-cmd –-reload 

Installing the Puppet  Master

Once the package repository puppet5-release-5.0.0-1-el7.noarch.rpm added to the system the puppetserver installation can be done:

$ sudo yum install -y puppetserver

The max memory allocation parameter is an important setting to update on /etc/sysconfig/puppetserver file to 2GB (or to 1GB if the service doesn’t start):

JAVA_ARGS="-Xms2g –Xmx2g "

In the configuration file /etc/puppetlabs/puppet/puppet.conf it’s necessary to add the following parameterization:

[master]

dns_alt_names=master.severalnines.com,puppet



[main]

certname = master.severalnines.com

server = master.severalnines.com

environment = production

runinterval = 1h

The puppetserver service uses the  port 8140 to listen to the node requests, thus it's necessary to ensure that this port will be enabled:

$ sudo firewall-cmd --add-port=8140/tcp --permanent

$ sudo firewall-cmd --reload

Once all settings made in puppet master, it’s time to start this service up:

$ sudo systemctl start puppetserver

$ sudo systemctl enable puppetserver

Installing the Puppet Agent

The Puppet agent in the package repository puppet5-release-5.0.0-1-el7.noarch.rpm is also added to the system, the puppet-agent installation can be performed right away:

$ sudo yum install -y puppet-agent

The puppet-agent configuration file /etc/puppetlabs/puppet/puppet.conf needs also to be updated by adding the following parameter:

[main]

certname = agent.severalnines.com

server = master.severalnines.com

environment = production

runinterval = 1h

The next step consists of registering the agent node on the master host by executing the following command:

$ sudo /opt/puppetlabs/bin/puppet resource service puppet ensure=running enable=true

service { ‘puppet’:

ensure => ‘running’,

enable => ‘true’

  }

At this moment, on the master host, there is a pending request from the puppet agent to sign a certificate:

That must be signed by executing one of the following commands:

$ sudo /opt/puppetlabs/bin/puppet cert sign agent.severalnines.com

or

$ sudo /opt/puppetlabs/bin/puppet cert sign --all

Finally (and once the puppet master has signed the certificate) it’s time to apply the configurations to the agent by retrieving the catalog from puppet master:

$ sudo /opt/puppetlabs/bin/puppet agent --test

In this command, the parameter --test doesn’t mean a test, the settings retrieved from the master will be applied to the local agent. In order to test/check the configurations from master the following command must  be executed:

$ sudo /opt/puppetlabs/bin/puppet agent --noop

Configuring & Programming Puppet

Puppet uses a declarative programming approach on which the purpose is to specify what to do and doesn't matter the way to achieve it!

The most elementary piece of code on Puppet is the resource that specifies a system property such as command, service, file, directory, user or package.

Below it’s presented the syntax of a resource to create an user:

user { 'admin_postgresql':

  ensure     => present,

  uid        => '1000',

  gid        => '1000',

  home       => '/home/admin/postresql'

}

Different resources could be joined to the former class (also known as a manifest) of file with “pp” extension (it stands for Puppet Program), nevertheless, several manifests and data (such as facts, files, and templates) will compose a module. All there logical hierarchies and rules are represented in the diagram below:

The purpose of each module is to contain all the needed manifests to execute single tasks in a modular way. On the other hand, the concept of class isn't the same one from object-oriented programming languages, in Puppet, it works as an aggregator of resources.

These files organization has a specific directory structure to follow:

On which the purpose of each folder is the following:

Folder

Description

manifests

Puppet code

files

Static files to be copied to nodes

templates

Template files to be copied to managed nodes(it can be customized with variables)

examples

Manifest to show how to use the module

The classes(manifests) can be used by other classes as shown in the example below: the manifest init.pp on dev_accounts are using the manifest groups from the accounts module.
class dev_accounts {

  $rootgroup = $osfamily ? {

    'Debian'  => 'sudo',

    'RedHat'  => 'wheel',

    default   => warning('This distribution is not supported by the Accounts module'),

  }



  include accounts::groups



  user { 'username':

    ensure      => present,

    home        => '/home/admin/postresql',

    shell       => '/bin/bash',

    managehome  => true,

    gid         => 'admin_db',

    groups      => "$rootgroup",

    password    => '$1$7URTNNqb$65ca6wPFDvixURc/MMg7O1'

  }

}

In the next section, we’ll show you how to generate the contents of the examples folder as well the commands to test and publish each module.

Configuring Puppet for PostgreSQL

Before to present the several configuration examples to deploy and maintain a PostgreSQL database it’s necessary to install the PostgreSQL puppet module (on the server host) to use all of their functionalities:

$ sudo /opt/puppetlabs/bin/puppet module install puppetlabs-postgresql

Currently, thousands of modules ready to use on Puppet are available on the public module repository Puppet Forge.

Step One

Configure and deploy a new PostgreSQL instance. Here is all the necessary programming and configuration to install a new PostgreSQL instance in all nodes.

The first step is to create a new module structure directory as shared previously:

$ cd /etc/puppetlabs/code/environments/production/modules

$ mkdir db_postgresql_admin

$ cd db_postgresql_admin; mkdir{examples,files,manifests,templates}

Then, in the manifest file manifests/init.pp, you need to include the class postgresql::server provided by the installed module :

class db_postgresql_admin{

  include postgresql::server

}

To check the syntax of the manifest, it's a good practice to execute the following command:

$ sudo /opt/puppetlabs/bin/puppet parser validate init.pp

If nothing is returned, it means that the syntax is correct

To show you how to use this module in the example folder, it’s necessary to create a new manifest file init.pp with the following content:

include db_postgresql_admin

The example location in the module must be tested and applied to the master catalog:

$ sudo /opt/puppetlabs/bin/puppet apply --modulepath=/etc/puppetlabs/code/environments/production/modules --noop init.pp

Finally, it’s necessary to define which module each node has access in the file “/etc/puppetlabs/code/environments/production/manifests/site.pp” :

node ’agent.severalnines.com’,’agent2.severalnines.com’{

 include db_postgresql_admin

}

Or a default configuration for all nodes:

node default {

 include db_postgresql_admin

}

Usually each 30min the nodes check the master catalog, nevertheless this query can be forced on node side by the following command:

$ /opt/puppetlabs/bin/puppet agent -t

Or if the purpose is to simulate the differences between the master configuration and the current node settings, it could be used the nopp parameter (no operation):

$ /opt/puppetlabs/bin/puppet agent -t --noop

Step Two

Update the PostgreSQL instance to listen all interfaces. The previous installation defines an instance setting in a very restrictive mode: only allows connections on localhost as can be confirmed by the hosts associated for the port 5432 (defined for PostgreSQL):

$ sudo netstat -ntlp|grep 5432

tcp        0 0 127.0.0.1:5432          0.0.0.0:* LISTEN   3237/postgres       

tcp6       0 0 ::1:5432                :::* LISTEN   3237/postgres       

In order to allow listening all interface, it’s necessary to have the following content in the file /etc/puppetlabs/code/environments/production/modules/db_postgresql_admin/manifests/init.pp

class db_postgresql_admin{

  class{‘postgresql:server’:

        listen_addresses=>’*’ #listening all interfaces

       }

}

In the example above there is declared the class postgresql::server and setting the parameter listen_addresses to “*” that means all interfaces.

Now the port 5432 is associated with all interfaces, it can be confirmed with the following IP address/port: “0.0.0.0:5432”

$ sudo netstat -ntlp|grep 5432

tcp        0 0 0.0.0.0:5432            0.0.0.0:* LISTEN   1232/postgres       

tcp6       0 0 :::5432                 :::* LISTEN   1232/postgres  

To put back the initial setting: only allow database connections from localhost the listen_addresses parameter must be set to “localhost” or specifying a list of hosts, if desired:

listen_addresses = 'agent2.severalnines.com,agent3.severalnines.com,localhost'

To retrieve the new configuration from the master host, only it’s needed to request it on the node:

$ /opt/puppetlabs/bin/puppet agent -t

Step Three

Create a PostgreSQL Database. The PostgreSQL instance can be created with a new database as well as a new user (with password) to use this database and a rule on pg_hab.conf file to allow the database connection for this new user:

class db_postgresql_admin{

  class{‘postgresql:server’:

        listen_addresses=>’*’ #listening all interfaces

  }



   postgresql::server::db{‘nines_blog_db’:

     user => ‘severalnines’,          password=> postgresql_password(‘severalnines’,’passwd12’)

   }



   postgresql::server::pg_hba_rule{‘Authentication for severalnines’:

     Description =>’Open access to severalnines’,

     type => ‘local’,

database => ‘nines_blog_db’,

     user => ‘severalnines’,

address => ‘127.0.0.1/32’

         auth_method => ‘md5’

   }

}

This last resource has the name of “Authentication for severalnines” and the pg_hba.conf file will have one more additional rule:

# Rule Name: Authentication for severalnines

# Description: Open access for severalnines

# Order: 150

local   nines_blog_db   severalnines 127.0.0.1/32    md5

To retrieve the new configuration from the master host, all that is needed is to request it on the node:

$ /opt/puppetlabs/bin/puppet agent -t

Step Four

Create a Read-Only User.  To create a new user, with read only privileges, the following resources need to be added to the previous manifest:

postgresql::server::role{‘Creation of a new role nines_reader’:

createdb   => false,

createrole => false,

superuser => false,     password_hash=> postgresql_password(‘nines_reader’,’passwd13’)

}

postgresql::server::pg_hba_rule{‘Authentication for nines_reader’:

     description =>’Open access to nines_reader’,

     type => ‘host’,

database => ‘nines_blog_db’,

     user => ‘nines_reader’,

address => ‘192.168.1.10/32’,

         auth_method => ‘md5’

   }

To retrieve the new configuration from the master host, all that is needed is to request it on the node:

$ /opt/puppetlabs/bin/puppet agent -t

Conclusion 

In this blog post, we showed you the basic steps to deploy and start configuring your PostgreSQL database through an automatic and customized way on several nodes (which could even be virtual machines).

These types of automation can help you to become more effective then doing it manually and PostgreSQL configuration can easily be performed by using several of the classes available in the puppetforge repository

ClusterControl
The only management system you’ll ever need to take control of your open source database infrastructure.