Skip to content

Commit 1679f46

Browse files
committed
General PostgreSQL role for Debian- and RedHat-based hosts.
0 parents  commit 1679f46

19 files changed

+654
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
syntax: glob
2+
3+
*~
4+
*.*.swp

README.md

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
PostgreSQL
2+
==========
3+
4+
An [Ansible][ansible] role for installing and managing [PostgreSQL][postgresql]
5+
servers. This role works with both Debian and RedHat based systems, and
6+
provides backup scripts for [PostgreSQL Continuous Archiving and Point-in-Time
7+
Recovery][postgresql_pitr].
8+
9+
On RedHat-based platforms, the [PostgreSQL Global Development Group (PGDG)
10+
packages][pgdg_yum] packages will be installed. On Debian-based platforms, you
11+
can choose from the distribution's packages (from APT) or the [PGDG
12+
packages][pgdg_apt].
13+
14+
[ansible]: http://www.ansible.com/
15+
[postgresql]: http://www.postgresql.org/
16+
[postgresql_pitr]: http://www.postgresql.org/docs/9.4/static/continuous-archiving.html
17+
[pgdg_yum]: http://yum.postgresql.org/
18+
[pgdg_apt]: http://apt.postgresql.org/
19+
20+
**Changes that require a restart will not be applied unless you manually
21+
restart PostgreSQL.** This role will reload the server for those configuration
22+
changes that can be updated with only a reload because reloading is a
23+
non-intrusive operation, but options that require a full restart will not cause
24+
the server to restart.
25+
26+
Requirements
27+
------------
28+
29+
This role requires Ansible 1.8+
30+
31+
Role Variables
32+
--------------
33+
34+
### All variables are optional ###
35+
36+
- `postgresql_version`: PostgreSQL version to install. On Debian-based
37+
platforms, the default is whatever version is pointed to by the `postgresql`
38+
metapackage). On RedHat-based platforms, the default is `9.4`.
39+
40+
- `postgresql_flavor`: On Debian-based platforms, this specifies whether you
41+
want to use PostgreSQL packages from pgdg or the distribution's apt
42+
repositories. Possible values: `apt`, `pgdg` (default: `apt`).
43+
44+
- `postgresql_conf`: A hash (dictionary) of `postgresql.conf` options and
45+
values. These options are not added to `postgresql.conf` directly - the role
46+
adds a `conf.d` subdirectory in the configuration directory and an include
47+
statement for that directory to `postgresql.conf`. Options set in
48+
`postgresql_conf` are then set in `conf.d/25ansible_postgresql.conf`.
49+
50+
Due to YAML parsing, you must take care when defining values in
51+
`postgresql_conf` to ensure they are properly written to the config file. For
52+
example:
53+
54+
```yaml
55+
postgresql_conf:
56+
max_connections: 250
57+
archive_mode: "off"
58+
work_mem: "'8MB'"
59+
```
60+
61+
Becomes the following in `25ansible_postgresql.conf`:
62+
63+
```
64+
max_connections = 250
65+
archive_mode = off
66+
work_mem: '8MB'
67+
```
68+
69+
- `postgresql_pg_hba_conf`: A list of lines to add to `pg_hba.conf`
70+
71+
- `postgresql_pg_hba_local_postgres_user`: If set to `false`, this will remove
72+
the `postgres` user's entry from `pg_hba.conf` that is preconfigured on
73+
Debian-based PostgreSQL installations. You probably do not want to do this
74+
unless you know what you're doing.
75+
76+
- `postgresql_pg_hba_local_socket`: If set to `false`, this will remove the
77+
`local` entry from `pg_hba.conf` that is preconfigured by the PostgreSQL
78+
package.
79+
80+
- `postgresql_pg_hba_local_ipv4`: If set to `false`, this will remove the `host
81+
... 127.0.0.1/32` entry from `pg_hba.conf` that is preconfigured by the
82+
PostgreSQL package.
83+
84+
- `postgresql_pg_hba_local_ipv6`: If set to `false`, this will remove the `host
85+
... ::1/128` entry from `pg_hba.conf` that is preconfigured by the PostgreSQL
86+
package.
87+
88+
- `postgresql_pgdata_dir`: Only set this if you have changed the `$PGDATA`
89+
directory from the package default. Note this does not configure PostgreSQL
90+
to actually use a different directory, you will need to do that yourself, it
91+
just allows the role to properly locate the directory.
92+
93+
- `postgresql_conf_dir`: As with `postgresql_pgdata_dir` except for the
94+
configuration directory.
95+
96+
### Backups ###
97+
98+
- `postgresql_backup_dir`: If set, enables [PITR][postgresql_pitr] backups. Set
99+
this to a directory where your database will be backed up.
100+
101+
- `postgresql_backup_local_dir`: Filesystem path on the PostgreSQL server where
102+
backup scripts will be placed and working WALs will be written prior to a WAL
103+
archive.
104+
105+
- `postgresql_backup_[hour|minute]`: Controls what time the cron job will run
106+
to perform a full backup. Defaults to 1:00 AM.
107+
108+
- `postgresql_backup_[day|month|weekday]`: Additional cron controls for when
109+
the full backup is performed (default: `*`).
110+
111+
- `postgresql_backup_mail_recipient`: User or address that should receive mail
112+
from the backup scripts.
113+
114+
Dependencies
115+
------------
116+
117+
None
118+
119+
Example Playbook
120+
----------------
121+
122+
Standard install: Default `postgresql.conf`, `pg_hba.conf` and default version
123+
for the OS:
124+
125+
```yaml
126+
---
127+
128+
- hosts: dbservers
129+
remote_user: root
130+
roles:
131+
- postgresql
132+
```
133+
134+
Use the pgdg packages on a Debian-based host:
135+
136+
```yaml
137+
---
138+
139+
- hosts: dbservers
140+
remote_user: root
141+
vars:
142+
postgresql_flavor: pgdg
143+
roles:
144+
- postgresql
145+
```
146+
147+
Use the PostgreSQL 9.3 packages and set some `postgresql.conf` options and
148+
`pg_hba.conf` entries:
149+
150+
```yaml
151+
---
152+
153+
- hosts: dbservers
154+
remote_user: root
155+
vars:
156+
postgresql_version: 9.3
157+
postgresql_conf:
158+
listen_addresses: "''" # disable network listening (listen on unix socket only)
159+
max_connections: 50 # decrease connection limit
160+
postgresql_pg_hba_conf:
161+
- host all all 10.0.0.0/8 md5
162+
roles:
163+
- postgresql
164+
```
165+
166+
Enable backups to /archive
167+
168+
```yaml
169+
- hosts: all
170+
remote_user: root
171+
vars:
172+
postgresql_backup_dir: /archive
173+
roles:
174+
- postgresql
175+
```
176+
177+
License
178+
-------
179+
180+
[Academic Free License ("AFL") v. 3.0][afl]
181+
182+
[afl]: http://opensource.org/licenses/AFL-3.0
183+
184+
Author Information
185+
------------------
186+
187+
[Nate Coraor](https://github.com/natefoo)

defaults/main.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
3+
postgresql_default_version: 9.4
4+
postgresql_backup_local_dir: ~postgres/backup
5+
postgresql_backup_active_dir: "{{ postgresql_backup_local_dir }}/active"
6+
postgresql_backup_mail_recipient: postgres

files/get_repo_rpm_release.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/usr/bin/env python
2+
"""
3+
Determine the latest version of the yum repository package.
4+
5+
usage: get_repo_rpm_version.py url distribution
6+
7+
e.g.:
8+
9+
get_repo_rpm_version.py http://yum.postgresql.org/9.2/redhat/rhel-6-x86_64/ centos
10+
"""
11+
12+
import re
13+
import sys
14+
import urllib2
15+
16+
url, dist = sys.argv[1:]
17+
18+
try:
19+
repo = urllib2.urlopen(url)
20+
except urllib2.HTTPError, e:
21+
print >>sys.stderr, "Failed to fetch directory list from %s" % url
22+
raise
23+
24+
pg_version = url.split('/')[3]
25+
re_pattern = 'href=[\'"]pgdg-%s%s-%s-([\d+]).noarch.rpm[\'"]' % (dist, pg_version.replace('.', ''), pg_version)
26+
match = re.findall(re_pattern, repo.read(), flags=re.I)
27+
28+
assert match, "No matching %s pgdg repository packages found for version %s at %s" % (dist, pg_version, url)
29+
30+
print max([ int(x) for x in match ])
31+
32+
sys.exit(0)

handlers/main.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
3+
- name: Reload PostgreSQL
4+
service: name={{ postgresql_service_name }} state=reloaded

tasks/backup.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
3+
- name: Create backup directories
4+
file: owner=postgres group=postgres mode=0750 state=directory path={{ item }}
5+
with_items:
6+
- "{{ postgresql_backup_local_dir }}"
7+
- "{{ postgresql_backup_local_dir }}/bin"
8+
- "{{ postgresql_backup_active_dir }}"
9+
10+
- name: Install backup scripts
11+
template: src={{ item }}.j2 dest={{ postgresql_backup_local_dir }}/bin/{{ item }} owner=postgres group=postgres mode=0750
12+
with_items:
13+
- backup_working_wal.sh
14+
- archive_wal.sh
15+
- scheduled_backup.sh
16+
17+
- name: Set WAL archive config options
18+
template: src=20ansible_backup.conf.j2 dest={{ postgresql_conf_dir }}/conf.d/20ansible_backup.conf owner=postgres group=postgres backup=yes
19+
notify: Reload PostgreSQL
20+
21+
- name: Schedule backups
22+
cron: name="PostgreSQL Backup" cron_file=ansible_postgresql_backup user=postgres hour={{ postgresql_backup_hour | default(1) }} minute={{ postgresql_backup_minute | default(0) }} day={{ postgresql_backup_day | default(omit) }} month={{ postgresql_backup_month | default(omit) }} weekday={{ postgresql_backup_weekday | default(omit) }} job={{ postgresql_backup_local_dir }}/bin/scheduled_backup.sh
23+
24+
- name: Schedule PostgreSQL working WAL backup
25+
cron: name="PostgreSQL WAL Backup" cron_file=ansible_postgresql_walbackup user=postgres job={{ postgresql_backup_local_dir }}/bin/backup_working_wal.sh

tasks/debian.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
3+
- name: Install pgdg package signing key (Debian/pgdg)
4+
apt_key: keyserver=pgp.mit.edu id=ACCC4CF8
5+
when: postgresql_flavor is defined and postgresql_flavor == "pgdg"
6+
7+
- name: Install pgdg repository (Debian/pgdg)
8+
apt_repository: repo="deb http://apt.postgresql.org/pub/repos/apt/ {{ ansible_distribution_release }}-pgdg main" update_cache=yes
9+
when: postgresql_flavor is defined and postgresql_flavor == "pgdg"
10+
11+
- name: Install PostgreSQL (Debian)
12+
apt: name=postgresql{{ '-' ~ postgresql_version if postgresql_version is defined else '' }}
13+
14+
- name: Get installed version
15+
command: dpkg-query -f ${Version;3} --show postgresql
16+
when: postgresql_version is not defined
17+
register: postgresql_version_query
18+
changed_when: false
19+
20+
- name: Set version fact
21+
set_fact: postgresql_version={{ postgresql_version_query.stdout }}
22+
when: postgresql_version is not defined

tasks/main.yml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
---
2+
3+
# Convenient ordering - debian.yml sets postgresql_version if it is unset,
4+
# which is needed by the include_vars files.
5+
- include: debian.yml
6+
when: ansible_os_family == "Debian"
7+
8+
# For RedHat we use the value from defaults/main.yml
9+
- name: Set version fact
10+
set_fact: postgresql_version={{ postgresql_default_version }}
11+
when: ansible_os_family == "RedHat" and postgresql_version is not defined
12+
13+
# Sets postgresql_pgdata_dir, postgresql_conf_dir
14+
- include_vars: "{{ ansible_os_family | lower }}.yml"
15+
16+
- name: Set pgdata fact
17+
set_fact: postgresql_pgdata={{ postgresql_pgdata_default }}
18+
when: postgresql_pgdata is not defined
19+
20+
- name: Set conf dir fact
21+
set_fact: postgresql_conf_dir={{ postgresql_conf_dir_default }}
22+
when: postgresql_conf_dir is not defined
23+
24+
# Needs postgresql_pgdata_dir set
25+
- include: redhat.yml
26+
when: ansible_os_family == "RedHat"
27+
28+
- name: Create conf.d
29+
file: path={{ postgresql_conf_dir }}/conf.d state=directory owner=postgres group=postgres
30+
31+
- name: Set conf.d include in postgresql.conf
32+
lineinfile: line="include_dir 'conf.d'" dest={{ postgresql_conf_dir }}/postgresql.conf backup=yes
33+
notify: Reload PostgreSQL
34+
35+
- name: Set config options
36+
template: src=25ansible_postgresql.conf.j2 dest={{ postgresql_conf_dir }}/conf.d/25ansible_postgresql.conf owner=postgres group=postgres backup=yes
37+
notify: Reload PostgreSQL
38+
39+
- name: Install pg_hba.conf
40+
template: src=pg_hba.conf.{{ ansible_os_family | lower }}.j2 dest={{ postgresql_conf_dir }}/pg_hba.conf owner=postgres group=postgres mode=0400 backup=yes
41+
notify: Reload PostgreSQL
42+
43+
- include: backup.yml
44+
when: postgresql_backup_dir is defined
45+
46+
- name: Ensure PostgreSQL is running
47+
service: name={{ postgresql_service_name }} enabled=yes state=started

tasks/redhat.yml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
3+
# Using the rpm URL format of the yum module causes Ansible to download the rpm
4+
# every time to check whether it's installed, so, don't do that.
5+
- name: Check pgdg repository package (RedHat)
6+
yum: name=pgdg-{{ postgresql_pgdg_dists[ansible_distribution] }}{{ postgresql_version | replace('.', '') }}
7+
register: repo_pkg_installed
8+
ignore_errors: yes
9+
10+
# URLs for Fedora look like:
11+
# http://yum.postgresql.org/<pg_version>/fedora/fedora-<os_major_version>-<arch>/pgdg-fedora<pg_version_without_period>-<pg_version>-<rpm_release_version>.noarch.rpm
12+
# URLs for RedHat and all derivatives look like:
13+
# http://yum.postgresql.org/<pg_version>/redhat/rhel-<os_major_version>-<arch>/pgdg-<dist><pg_version_without_period>-<pg_version>-<rpm_release_version>.noarch.rpm
14+
15+
# There's no direct way to determine the latest pacakge, so we have to use a
16+
# helper script to parse the directory list and figure it out.
17+
- name: Determine latest pgdg repository package (RedHat)
18+
script: get_repo_rpm_release.py http://yum.postgresql.org/{{ postgresql_version }}/{{ postgresql_pgdg_families[ansible_distribution] | default("redhat") }}/{{ postgresql_pgdg_shortfamilies[ansible_distribution] | default("rhel") }}-{{ ansible_distribution_major_version }}-{{ ansible_architecture }}/ {{ postgresql_pgdg_dists[ansible_distribution] }}
19+
register: pgdg_repo_pkg_release
20+
when: repo_pkg_installed.failed is defined and repo_pkg_installed.failed
21+
22+
- name: Install pgdg repository package (RedHat)
23+
yum: name=http://yum.postgresql.org/{{ postgresql_version }}/{{ postgresql_pgdg_families[ansible_distribution] | default("redhat") }}/{{ postgresql_pgdg_shortfamilies[ansible_distribution] | default("rhel") }}-{{ ansible_distribution_major_version }}-{{ ansible_architecture }}/pgdg-{{ postgresql_pgdg_dists[ansible_distribution] }}{{ postgresql_version | replace('.', '') }}-{{ postgresql_version }}-{{ pgdg_repo_pkg_release.stdout.strip() }}.noarch.rpm
24+
when: repo_pkg_installed.failed is defined and repo_pkg_installed.failed
25+
26+
- name: Install PostgreSQL (RedHat)
27+
yum: name=postgresql{{ postgresql_version | replace('.', '') }}-server
28+
29+
- name: Check for pgdata directory
30+
stat: path={{ postgresql_pgdata }}/base
31+
register: pgdata_stat
32+
failed_when: false
33+
34+
- name: Initialize database (RedHat < 7)
35+
command: /sbin/service postgresql-{{ postgresql_version }} initdb
36+
when: ansible_distribution_major_version | int < 7 and (pgdata_stat.stat.isdir is not defined or not pgdata_stat.stat.isdir)
37+
38+
- name: Initialize database (RedHat >= 7)
39+
command: /usr/pgsql-{{ postgresql_version }}/bin/postgresql{{ postgresql_version | replace('.', '') }}-setup initdb
40+
when: ansible_distribution_major_version | int >= 7 and (pgdata_stat.stat.isdir is not defined or not pgdata_stat.stat.isdir)

templates/20ansible_backup.conf.j2

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
##
2+
## This file is maintained by Ansible - CHANGES WILL BE OVERWRITTEN
3+
##
4+
5+
{% if postgresql_backup_dir is defined and postgresql_backup_local_dir is defined %}
6+
wal_level = archive
7+
archive_mode = on
8+
archive_command = '{{ postgresql_backup_local_dir | expanduser }}/bin/archive_wal.sh "%p" "%f" main'
9+
{% endif %}

0 commit comments

Comments
 (0)